diff options
177 files changed, 8771 insertions, 5211 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index d28aabd0a6..738a0e62b5 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -385,6 +385,12 @@ Copyright: 2014-2018, Syoyo Fujita 2002, Industrial Light & Magic, a division of Lucas Digital Ltd. LLC License: BSD-3-clause +Files: ./thirdparty/vhacd/ +Comment: V-HACD +Copyright: 2011, Khaled Mamou + 2003-2009, Erwin Coumans +License: BSD-3-clause + Files: ./thirdparty/zlib/ Comment: zlib Copyright: 1995-2017, Jean-loup Gailly and Mark Adler diff --git a/core/os/input.cpp b/core/os/input.cpp index caa9fb1493..63bf1db499 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -89,6 +89,7 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("action_press", "action", "strength"), &Input::action_press, DEFVAL(1.f)); ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release); ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW)); + ClassDB::bind_method(D_METHOD("get_current_cursor_shape"), &Input::get_current_cursor_shape); ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event); ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input); diff --git a/core/os/input.h b/core/os/input.h index c8b80b28d0..de04f239e6 100644 --- a/core/os/input.h +++ b/core/os/input.h @@ -121,10 +121,10 @@ public: virtual bool is_emulating_touch_from_mouse() const = 0; virtual bool is_emulating_mouse_from_touch() const = 0; - virtual CursorShape get_default_cursor_shape() = 0; + virtual CursorShape get_default_cursor_shape() const = 0; virtual void set_default_cursor_shape(CursorShape p_shape) = 0; + virtual CursorShape get_current_cursor_shape() const = 0; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) = 0; - virtual void set_mouse_in_window(bool p_in_window) = 0; virtual String get_joy_button_string(int p_button) = 0; virtual String get_joy_axis_string(int p_axis) = 0; diff --git a/core/os/os.cpp b/core/os/os.cpp index 03e63f636e..ea378c9e83 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -220,6 +220,16 @@ int OS::get_virtual_keyboard_height() const { return 0; } +void OS::set_cursor_shape(CursorShape p_shape) { +} + +OS::CursorShape OS::get_cursor_shape() const { + return CURSOR_ARROW; +} + +void OS::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { +} + void OS::print_all_resources(String p_to_file) { ERR_FAIL_COND(p_to_file != "" && _OSPRF); diff --git a/core/os/os.h b/core/os/os.h index d02d5a2c84..12012fba80 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -377,8 +377,9 @@ public: // returns height of the currently shown virtual keyboard (0 if keyboard is hidden) virtual int get_virtual_keyboard_height() const; - virtual void set_cursor_shape(CursorShape p_shape) = 0; - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) = 0; + virtual void set_cursor_shape(CursorShape p_shape); + virtual CursorShape get_cursor_shape() const; + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); virtual bool get_swap_ok_cancel() { return false; } virtual void dump_memory_to_file(const char *p_file); diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 5752cdca2b..c86d1567dd 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -501,7 +501,7 @@ Error ProjectSettings::_load_settings_binary(const String p_path) { d.resize(vlen); f->get_buffer(d.ptrw(), vlen); Variant value; - err = decode_variant(value, d.ptr(), d.size(), NULL, false); + err = decode_variant(value, d.ptr(), d.size(), NULL, true); ERR_EXPLAIN("Error decoding property: " + key); ERR_CONTINUE(err != OK); set(key, value); @@ -694,7 +694,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str file->store_string(key); int len; - err = encode_variant(value, NULL, len, false); + err = encode_variant(value, NULL, len, true); if (err != OK) memdelete(file); ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); @@ -702,7 +702,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str Vector<uint8_t> buff; buff.resize(len); - err = encode_variant(value, buff.ptrw(), len, false); + err = encode_variant(value, buff.ptrw(), len, true); if (err != OK) memdelete(file); ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); diff --git a/core/resource.cpp b/core/resource.cpp index 74c93cd790..74e2c1ed6b 100644 --- a/core/resource.cpp +++ b/core/resource.cpp @@ -363,6 +363,26 @@ bool Resource::is_translation_remapped() const { return remapped_list.in_list(); } +#ifdef TOOLS_ENABLED +//helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored +void Resource::set_id_for_path(const String &p_path, int p_id) { + if (p_id == -1) { + id_for_path.erase(p_path); + } else { + id_for_path[p_path] = p_id; + } +} + +int Resource::get_id_for_path(const String &p_path) const { + + if (id_for_path.has(p_path)) { + return id_for_path[p_path]; + } else { + return -1; + } +} +#endif + void Resource::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path", "path"), &Resource::_set_path); diff --git a/core/resource.h b/core/resource.h index a4d9e998ac..853b2859c7 100644 --- a/core/resource.h +++ b/core/resource.h @@ -88,7 +88,9 @@ protected: void _set_path(const String &p_path); void _take_over_path(const String &p_path); - +#ifdef TOOLS_ENABLED + Map<String, int> id_for_path; +#endif public: static Node *(*_get_local_scene_func)(); //used by editor @@ -137,6 +139,12 @@ public: virtual RID get_rid() const; // some resources may offer conversion to RID +#ifdef TOOLS_ENABLED + //helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored + void set_id_for_path(const String &p_path, int p_id); + int get_id_for_path(const String &p_path) const; +#endif + Resource(); ~Resource(); }; 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=""""> </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/AnimationNodeBlendSpace2D.xml b/doc/classes/AnimationNodeBlendSpace2D.xml index ad7d9dd380..449c177924 100644 --- a/doc/classes/AnimationNodeBlendSpace2D.xml +++ b/doc/classes/AnimationNodeBlendSpace2D.xml @@ -136,8 +136,10 @@ <constant name="BLEND_MODE_INTERPOLATED" value="0" enum="BlendMode"> </constant> <constant name="BLEND_MODE_DISCRETE" value="1" enum="BlendMode"> + Useful for frame-by-frame 2D animations. </constant> <constant name="BLEND_MODE_DISCRETE_CARRY" value="2" enum="BlendMode"> + Keep the current play position when switching between discrete animations. </constant> </constants> </class> diff --git a/doc/classes/AnimationNodeStateMachine.xml b/doc/classes/AnimationNodeStateMachine.xml index 6de544c9ac..348bd9eda1 100644 --- a/doc/classes/AnimationNodeStateMachine.xml +++ b/doc/classes/AnimationNodeStateMachine.xml @@ -3,6 +3,11 @@ <brief_description> </brief_description> <description> + Contains multiple root nodes as children in a graph. Each node is used as a state, and provides multiple functions to alternate between states. Retrieve the AnimationNodeStateMachinePlayback object from the [AnimationTree] node to control it programatically. + [codeblock] + var state_machine = anim_tree["parameters/StateMachine/playback"] + state_machine.travel("SomeState") + [codeblock] </description> <tutorials> </tutorials> @@ -170,7 +175,7 @@ <method name="set_graph_offset"> <return type="void"> </return> - <argument index="0" name="name" type="Vector2"> + <argument index="0" name="offset" type="Vector2"> </argument> <description> </description> diff --git a/doc/classes/AnimationNodeStateMachinePlayback.xml b/doc/classes/AnimationNodeStateMachinePlayback.xml index 01bf982281..70a9fed787 100644 --- a/doc/classes/AnimationNodeStateMachinePlayback.xml +++ b/doc/classes/AnimationNodeStateMachinePlayback.xml @@ -47,6 +47,7 @@ <argument index="0" name="to_node" type="String"> </argument> <description> + Transition from the current state to another one, while visiting all the intermediate ones. This is done via the A* algorithm. </description> </method> </methods> diff --git a/doc/classes/AnimationNodeStateMachineTransition.xml b/doc/classes/AnimationNodeStateMachineTransition.xml index a1ee381882..d18d5ee630 100644 --- a/doc/classes/AnimationNodeStateMachineTransition.xml +++ b/doc/classes/AnimationNodeStateMachineTransition.xml @@ -12,16 +12,22 @@ </methods> <members> <member name="advance_condition" type="String" setter="set_advance_condition" getter="get_advance_condition"> + Turn on auto advance when this condition is set. This is a custom text field that can be filled with a variable name. The variable can be modified from code. </member> <member name="auto_advance" type="bool" setter="set_auto_advance" getter="has_auto_advance"> + Turn on the transition automatically when this state is reached. This works best with [code]SWITCH_MODE_AT_END[/code]. </member> <member name="disabled" type="bool" setter="set_disabled" getter="is_disabled"> + Don't use this transition during [method AnimationNodeStateMachinePlayback.travel] or [member auto_advance]. </member> <member name="priority" type="int" setter="set_priority" getter="get_priority"> + Lower priority transitions are preferred when travelling through the tree via [method AnimationNodeStateMachinePlayback.travel] or [member auto_advance]. </member> <member name="switch_mode" type="int" setter="set_switch_mode" getter="get_switch_mode" enum="AnimationNodeStateMachineTransition.SwitchMode"> + The transition type. </member> <member name="xfade_time" type="float" setter="set_xfade_time" getter="get_xfade_time"> + The time to cross-fade between this state and the next. </member> </members> <signals> @@ -32,10 +38,13 @@ </signals> <constants> <constant name="SWITCH_MODE_IMMEDIATE" value="0" enum="SwitchMode"> + Switch to the next state immediately. The current state will end and blend into the beginning of the new one. </constant> <constant name="SWITCH_MODE_SYNC" value="1" enum="SwitchMode"> + Switch to the next state immediately, but will seek the new state to the playback position of the old state. </constant> <constant name="SWITCH_MODE_AT_END" value="2" enum="SwitchMode"> + Wait for the current state playback to end, then switch to the beginning of the next state animation. </constant> </constants> </class> diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml index 1101c0ee62..51812c6b02 100644 --- a/doc/classes/AnimationTree.xml +++ b/doc/classes/AnimationTree.xml @@ -5,8 +5,10 @@ <description> </description> <tutorials> + <link>https://docs.godotengine.org/en/latest/tutorials/animation/animation_tree.html</link> </tutorials> <demos> + <link>https://github.com/godotengine/tps-demo</link> </demos> <methods> <method name="advance"> 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/Slider.xml b/doc/classes/Slider.xml index 1e665f0236..98be77641d 100644 --- a/doc/classes/Slider.xml +++ b/doc/classes/Slider.xml @@ -14,14 +14,18 @@ </methods> <members> <member name="editable" type="bool" setter="set_editable" getter="is_editable"> + If [code]true[/code], the slider can be interacted with. If [code]false[/code], the value can be changed only by code. </member> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" enum="Control.FocusMode"> </member> <member name="scrollable" type="bool" setter="set_scrollable" getter="is_scrollable"> + If [code]true[/code], the value can be changed using the mouse wheel. </member> <member name="tick_count" type="int" setter="set_ticks" getter="get_ticks"> + Number of ticks displayed on the slider, including border ticks. Ticks are uniformly-distributed value markers. </member> <member name="ticks_on_borders" type="bool" setter="set_ticks_on_borders" getter="get_ticks_on_borders"> + If [code]true[/code], the slider will display ticks for minimum and maximum values. </member> </members> <constants> 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 c3c4b6f938..67a9b80602 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -432,6 +432,14 @@ Returns [code]true[/code] if this string is a subsequence of the given string, without considering case. </description> </method> + <method name="is_valid_filename"> + <return type="bool"> + </return> + <description> + Returns [code]true[/code] if this string is free from characters that aren't allowed in file names, those being: + [code]: / \ ? * " | % < >[/code] + </description> + </method> <method name="is_valid_float"> <return type="bool"> </return> 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 < 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/TextEdit.xml b/doc/classes/TextEdit.xml index 0d52245538..c85738609f 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -401,9 +401,10 @@ String value of the [TextEdit]. </member> <member name="v_scroll_speed" type="float" setter="set_v_scroll_speed" getter="get_v_scroll_speed"> - If [code]true[/code], enables text wrapping when it goes beyond he edge of what is visible. + Vertical scroll sensitivity. </member> <member name="wrap_enabled" type="bool" setter="set_wrap_enabled" getter="is_wrap_enabled"> + If [code]true[/code], enables text wrapping when it goes beyond the edge of what is visible. </member> </members> <signals> 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/VSlider.xml b/doc/classes/VSlider.xml index 477c618064..847e5e5272 100644 --- a/doc/classes/VSlider.xml +++ b/doc/classes/VSlider.xml @@ -4,7 +4,7 @@ Vertical slider. </brief_description> <description> - Vertical slider. See [Slider]. This one goes from left (min) to right (max). + Vertical slider. See [Slider]. This one goes from bottom (min) to top (max). </description> <tutorials> </tutorials> 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..9f6fa74dd3 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,24 +917,53 @@ 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, Math::round(EDSCALE)); + + 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, Math::round(EDSCALE)); + 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); + } } } - draw_line(Vector2(0, get_size().height), get_size(), linecolor); + draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE)); } } @@ -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; } @@ -978,7 +1041,7 @@ void AnimationTimelineEdit::_play_position_draw() { if (px >= get_name_limit() && px < (play_position->get_size().width - get_buttons_width())) { Color color = get_color("accent_color", "Editor"); - play_position->draw_line(Point2(px, 0), Point2(px, h), color); + play_position->draw_line(Point2(px, 0), Point2(px, h), color, Math::round(EDSCALE)); } } @@ -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)); @@ -1237,7 +1310,7 @@ void AnimationTrackEdit::_notification(int p_what) { string_pos = string_pos.floor(); draw_string(font, string_pos, text, text_color, limit - ofs - hsep); - draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor); + draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor, Math::round(EDSCALE)); } // KEYFAMES // @@ -1297,7 +1370,7 @@ void AnimationTrackEdit::_notification(int p_what) { Ref<Texture> down_icon = get_icon("select_arrow", "Tree"); - draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor); + draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; { @@ -1344,7 +1417,7 @@ void AnimationTrackEdit::_notification(int p_what) { } ofs += down_icon->get_width(); - draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor); + draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; } @@ -1377,7 +1450,7 @@ void AnimationTrackEdit::_notification(int p_what) { } ofs += down_icon->get_width(); - draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor); + draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; } @@ -1410,7 +1483,7 @@ void AnimationTrackEdit::_notification(int p_what) { } ofs += down_icon->get_width(); - draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor); + draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; } @@ -1428,17 +1501,17 @@ void AnimationTrackEdit::_notification(int p_what) { } if (in_group) { - draw_line(Vector2(timeline->get_name_limit(), get_size().height), get_size(), linecolor); + draw_line(Vector2(timeline->get_name_limit(), get_size().height), get_size(), linecolor, Math::round(EDSCALE)); } else { - draw_line(Vector2(0, get_size().height), get_size(), linecolor); + draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE)); } if (dropping_at != 0) { Color drop_color = get_color("accent_color", "Editor"); if (dropping_at < 0) { - draw_line(Vector2(0, 0), Vector2(get_size().width, 0), drop_color); + draw_line(Vector2(0, 0), Vector2(get_size().width, 0), drop_color, Math::round(EDSCALE)); } else { - draw_line(Vector2(0, get_size().height), get_size(), drop_color); + draw_line(Vector2(0, get_size().height), get_size(), drop_color, Math::round(EDSCALE)); } } } @@ -1487,7 +1560,7 @@ void AnimationTrackEdit::draw_key_link(int p_index, float p_pixels_sec, int p_x, int from_x = MAX(p_x, p_clip_left); int to_x = MIN(p_next_x, p_clip_right); - draw_line(Point2(from_x + 1, get_size().height / 2), Point2(to_x, get_size().height / 2), color, 2); + draw_line(Point2(from_x + 1, get_size().height / 2), Point2(to_x, get_size().height / 2), color, Math::round(2 * EDSCALE)); } void AnimationTrackEdit::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) { @@ -1667,7 +1740,7 @@ void AnimationTrackEdit::_play_position_draw() { if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) { Color color = get_color("accent_color", "Editor"); - play_position->draw_line(Point2(px, 0), Point2(px, h), color); + play_position->draw_line(Point2(px, 0), Point2(px, h), color, Math::round(EDSCALE)); } } @@ -2370,9 +2443,9 @@ void AnimationTrackEditGroup::_notification(int p_what) { Color linecolor = color; linecolor.a = 0.2; - draw_line(Point2(), Point2(get_size().width, 0), linecolor); - draw_line(Point2(timeline->get_name_limit(), 0), Point2(timeline->get_name_limit(), get_size().height), linecolor); - draw_line(Point2(get_size().width - timeline->get_buttons_width(), 0), Point2(get_size().width - timeline->get_buttons_width(), get_size().height), linecolor); + draw_line(Point2(), Point2(get_size().width, 0), linecolor, Math::round(EDSCALE)); + draw_line(Point2(timeline->get_name_limit(), 0), Point2(timeline->get_name_limit(), get_size().height), linecolor, Math::round(EDSCALE)); + draw_line(Point2(get_size().width - timeline->get_buttons_width(), 0), Point2(get_size().width - timeline->get_buttons_width(), get_size().height), linecolor, Math::round(EDSCALE)); int ofs = 0; draw_texture(icon, Point2(ofs, int(get_size().height - icon->get_height()) / 2)); @@ -2383,7 +2456,7 @@ void AnimationTrackEditGroup::_notification(int p_what) { if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) { Color accent = get_color("accent_color", "Editor"); - draw_line(Point2(px, 0), Point2(px, get_size().height), accent); + draw_line(Point2(px, 0), Point2(px, get_size().height), accent, Math::round(EDSCALE)); } } } @@ -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(false); } 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(true); } } @@ -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/connections_dialog.cpp b/editor/connections_dialog.cpp index 045158504a..685c5de76c 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -37,6 +37,25 @@ #include "scene/gui/label.h" #include "scene/gui/popup_menu.h" +static Node *_find_first_script(Node *p_root, Node *p_node) { + if (p_node != p_root && p_node->get_owner() != p_root) { + return NULL; + } + if (!p_node->get_script().is_null()) { + return p_node; + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + + Node *ret = _find_first_script(p_root, p_node->get_child(i)); + if (ret) { + return ret; + } + } + + return NULL; +} + class ConnectDialogBinds : public Object { GDCLASS(ConnectDialogBinds, Object); @@ -122,17 +141,8 @@ void ConnectDialog::_tree_node_selected() { Node *current = tree->get_selected(); - if (!current) { - make_callback->hide(); - return; - } - - if (current->get_script().is_null()) - make_callback->hide(); - else - make_callback->show(); - - dst_path->set_text(source->get_path_to(current)); + dst_path = source->get_path_to(current); + get_ok()->set_disabled(false); } /* @@ -195,6 +205,7 @@ void ConnectDialog::_notification(int p_what) { void ConnectDialog::_bind_methods() { + ClassDB::bind_method("_advanced_pressed", &ConnectDialog::_advanced_pressed); ClassDB::bind_method("_cancel", &ConnectDialog::_cancel_pressed); ClassDB::bind_method("_tree_node_selected", &ConnectDialog::_tree_node_selected); ClassDB::bind_method("_add_bind", &ConnectDialog::_add_bind); @@ -215,7 +226,7 @@ StringName ConnectDialog::get_signal_name() const { NodePath ConnectDialog::get_dst_path() const { - return dst_path->get_text(); + return dst_path; } void ConnectDialog::set_dst_node(Node *p_node) { @@ -272,8 +283,13 @@ void ConnectDialog::init(Connection c, bool bEdit) { tree->set_selected(NULL); tree->set_marked(source, true); - set_dst_node(static_cast<Node *>(c.target)); - set_dst_method(c.method); + if (c.target) { + get_ok()->set_disabled(false); + set_dst_node(static_cast<Node *>(c.target)); + set_dst_method(c.method); + } else { + get_ok()->set_disabled(true); + } bool bDeferred = (c.flags & CONNECT_DEFERRED) == CONNECT_DEFERRED; bool bOneshot = (c.flags & CONNECT_ONESHOT) == CONNECT_ONESHOT; @@ -288,6 +304,36 @@ void ConnectDialog::init(Connection c, bool bEdit) { bEditMode = bEdit; } +void ConnectDialog::popup_dialog(const String &p_for_signal, bool p_advanced) { + + advanced->set_pressed(p_advanced); + from_signal->set_text(p_for_signal); + error_label->add_color_override("font_color", get_color("error_color", "Editor")); + + if (p_advanced) { + + popup_centered(Size2(900, 500) * EDSCALE); + connect_to_label->set_text("Connect to Node:"); + tree->set_connect_to_script_mode(false); + error_label->hide(); + } else { + popup_centered(Size2(700, 500) * EDSCALE); + connect_to_label->set_text("Connect to Script:"); + tree->set_connect_to_script_mode(true); + + if (!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root())) { + error_label->show(); + } else { + error_label->hide(); + } + } +} + +void ConnectDialog::_advanced_pressed() { + vbc_right->set_visible(advanced->is_pressed()); + popup_dialog(from_signal->get_text(), advanced->is_pressed()); +} + ConnectDialog::ConnectDialog() { VBoxContainer *vbc = memnew(VBoxContainer); @@ -301,15 +347,27 @@ ConnectDialog::ConnectDialog() { main_hb->add_child(vbc_left); vbc_left->set_h_size_flags(SIZE_EXPAND_FILL); + from_signal = memnew(LineEdit); + from_signal->set_editable(false); + vbc_left->add_margin_child(TTR("From Signal:"), from_signal); + tree = memnew(SceneTreeEditor(false)); tree->get_scene_tree()->connect("item_activated", this, "_ok"); tree->connect("node_selected", this, "_tree_node_selected"); + tree->set_connect_to_script_mode(true); - vbc_left->add_margin_child(TTR("Connect To Node:"), tree, true); + Node *mc = vbc_left->add_margin_child(TTR("Connect To Script:"), tree, true); + connect_to_label = Object::cast_to<Label>(vbc_left->get_child(mc->get_index() - 1)); - VBoxContainer *vbc_right = memnew(VBoxContainer); + error_label = memnew(Label); + error_label->set_text(TTR("Scene does not contain any script.")); + vbc_left->add_child(error_label); + error_label->hide(); + + vbc_right = memnew(VBoxContainer); main_hb->add_child(vbc_right); vbc_right->set_h_size_flags(SIZE_EXPAND_FILL); + vbc_right->hide(); HBoxContainer *add_bind_hb = memnew(HBoxContainer); @@ -347,16 +405,18 @@ ConnectDialog::ConnectDialog() { vbc_right->add_margin_child(TTR("Extra Call Arguments:"), bind_editor, true); - dst_path = memnew(LineEdit); - vbc->add_margin_child(TTR("Path to Node:"), dst_path); - HBoxContainer *dstm_hb = memnew(HBoxContainer); - vbc->add_margin_child("Method In Node:", dstm_hb); + vbc_left->add_margin_child("Method to Create:", dstm_hb); dst_method = memnew(LineEdit); dst_method->set_h_size_flags(SIZE_EXPAND_FILL); dstm_hb->add_child(dst_method); + advanced = memnew(CheckBox); + dstm_hb->add_child(advanced); + advanced->set_text(TTR("Advanced..")); + advanced->connect("pressed", this, "_advanced_pressed"); + /* dst_method_list = memnew( MenuButton ); dst_method_list->set_text("List..."); @@ -368,19 +428,13 @@ ConnectDialog::ConnectDialog() { dst_method_list->set_end( Point2( 15,39 ) ); */ - make_callback = memnew(CheckButton); - make_callback->set_toggle_mode(true); - make_callback->set_pressed(EDITOR_DEF("text_editor/tools/create_signal_callbacks", true)); - make_callback->set_text(TTR("Make Function")); - dstm_hb->add_child(make_callback); - deferred = memnew(CheckButton); deferred->set_text(TTR("Deferred")); - dstm_hb->add_child(deferred); + vbc_right->add_child(deferred); oneshot = memnew(CheckButton); oneshot->set_text(TTR("Oneshot")); - dstm_hb->add_child(oneshot); + vbc_right->add_child(oneshot); set_as_toplevel(true); @@ -429,7 +483,8 @@ void ConnectionsDock::_make_or_edit_connection() { bool oshot = connect_dialog->get_oneshot(); cToMake.flags = CONNECT_PERSIST | (defer ? CONNECT_DEFERRED : 0) | (oshot ? CONNECT_ONESHOT : 0); - bool add_script_function = connect_dialog->get_make_callback(); + //conditions to add function, must have a script and must have a method + bool add_script_function = !target->get_script().is_null() && !ClassDB::has_method(target->get_class(), cToMake.method); PoolStringArray script_function_args; if (add_script_function) { // pick up args here before "it" is deleted by update_tree @@ -568,6 +623,7 @@ bool ConnectionsDock::_is_item_signal(TreeItem &item) { /* Open connection dialog with TreeItem data to CREATE a brand-new connection. */ + void ConnectionsDock::_open_connection_dialog(TreeItem &item) { String signal = item.get_metadata(0).operator Dictionary()["name"]; @@ -590,6 +646,10 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &item) { } Node *dst_node = selectedNode->get_owner() ? selectedNode->get_owner() : selectedNode; + if (!dst_node || dst_node->get_script().is_null()) { + dst_node = _find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root()); + } + StringName dst_method = "_on_" + midname + "_" + signal; Connection c; @@ -598,9 +658,10 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &item) { c.target = dst_node; c.method = dst_method; + //connect_dialog->set_title(TTR("Connect Signal: ") + signalname); + connect_dialog->popup_dialog(signalname, false); connect_dialog->init(c); - connect_dialog->set_title(TTR("Connect Signal: ") + signalname); - connect_dialog->popup_centered_ratio(); + connect_dialog->set_title(TTR("Connect a Signal to a Method")); } /* @@ -612,9 +673,9 @@ void ConnectionsDock::_open_connection_dialog(Connection cToEdit) { Node *dst = static_cast<Node *>(cToEdit.target); if (src && dst) { - connect_dialog->init(cToEdit, true); - connect_dialog->set_title(TTR("Edit Connection: ") + cToEdit.signal); + connect_dialog->set_title(TTR("Edit Connection:") + cToEdit.signal); connect_dialog->popup_centered_ratio(); + connect_dialog->init(cToEdit, true); } } diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index 0e7e172ebb..59fe6dacfe 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -53,12 +53,15 @@ class ConnectDialog : public ConfirmationDialog { GDCLASS(ConnectDialog, ConfirmationDialog); + Label *connect_to_label; + LineEdit *from_signal; Node *source; StringName signal; - LineEdit *dst_path; LineEdit *dst_method; ConnectDialogBinds *cdbinds; bool bEditMode; + NodePath dst_path; + VBoxContainer *vbc_right; SceneTreeEditor *tree; ConfirmationDialog *error; @@ -66,13 +69,16 @@ class ConnectDialog : public ConfirmationDialog { OptionButton *type_list; CheckButton *deferred; CheckButton *oneshot; - CheckButton *make_callback; + CheckBox *advanced; + + Label *error_label; void ok_pressed(); void _cancel_pressed(); void _tree_node_selected(); void _add_bind(); void _remove_bind(); + void _advanced_pressed(); protected: void _notification(int p_what); @@ -87,13 +93,13 @@ public: void set_dst_method(const StringName &p_method); Vector<Variant> get_binds() const; - bool get_make_callback() { return make_callback->is_visible() && make_callback->is_pressed(); } bool get_deferred() const; bool get_oneshot() const; bool is_editing() const; void init(Connection c, bool bEdit = false); + void popup_dialog(const String &p_for_signal, bool p_advanced); ConnectDialog(); ~ConnectDialog(); }; diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp index 783a2ce74b..f6079624de 100644 --- a/editor/editor_folding.cpp +++ b/editor/editor_folding.cpp @@ -92,7 +92,7 @@ void EditorFolding::load_resource_folding(RES p_resource, const String &p_path) _set_unfolds(p_resource.ptr(), unfolds); } -void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Set<RES> &resources) { +void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Array& nodes_folded,Set<RES> &resources) { if (p_root != p_node) { if (!p_node->get_owner()) { return; //not owned, bye @@ -102,6 +102,9 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p } } + if (p_node->is_displayed_folded()) { + nodes_folded.push_back(p_root->get_path_to(p_node)); + } PoolVector<String> unfolds = _get_unfolds(p_node); if (unfolds.size()) { @@ -127,7 +130,7 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p } for (int i = 0; i < p_node->get_child_count(); i++) { - _fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, resources); + _fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, nodes_folded,resources); } } void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path) { @@ -137,10 +140,12 @@ void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path Array unfolds, res_unfolds; Set<RES> resources; - _fill_folds(p_scene, p_scene, unfolds, res_unfolds, resources); + Array nodes_folded; + _fill_folds(p_scene, p_scene, unfolds, res_unfolds, nodes_folded, resources); config->set_value("folding", "node_unfolds", unfolds); config->set_value("folding", "resource_unfolds", res_unfolds); + config->set_value("folding", "nodes_folded", nodes_folded); String path = EditorSettings::get_singleton()->get_project_settings_dir(); String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; @@ -168,6 +173,10 @@ void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) { if (config->has_section_key("folding", "resource_unfolds")) { res_unfolds = config->get_value("folding", "resource_unfolds"); } + Array nodes_folded; + if (config->has_section_key("folding", "nodes_folded")) { + nodes_folded = config->get_value("folding", "nodes_folded"); + } ERR_FAIL_COND(unfolds.size() & 1); ERR_FAIL_COND(res_unfolds.size() & 1); @@ -195,6 +204,14 @@ void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) { PoolVector<String> unfolds2 = res_unfolds[i + 1]; _set_unfolds(res.ptr(), unfolds2); } + + for(int i=0;i<nodes_folded.size();i++) { + NodePath fold_path = nodes_folded[i]; + if (p_scene->has_node(fold_path)) { + Node *node = p_scene->get_node(fold_path); + node->set_display_folded(true); + } + } } bool EditorFolding::has_folding_data(const String &p_path) { diff --git a/editor/editor_folding.h b/editor/editor_folding.h index e4f7dbba80..5fc980c4a9 100644 --- a/editor/editor_folding.h +++ b/editor/editor_folding.h @@ -38,7 +38,7 @@ class EditorFolding { PoolVector<String> _get_unfolds(const Object *p_object); void _set_unfolds(Object *p_object, const PoolVector<String> &p_unfolds); - void _fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Set<RES> &resources); + void _fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Array &nodes_folded, Set<RES> &resources); void _do_object_unfolds(Object *p_object, Set<RES> &resources); void _do_node_unfolds(Node *p_root, Node *p_node, Set<RES> &resources); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 8a67002503..851d6a0aa6 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1306,6 +1306,7 @@ void EditorNode::_dialog_action(String p_file) { _save_default_environment(); _save_scene_with_preview(p_file, scene_idx); _add_to_recent_scenes(p_file); + save_layout(); if (scene_idx != -1) _discard_changes(); @@ -4069,6 +4070,7 @@ void EditorNode::_load_open_scenes_from_config(Ref<ConfigFile> p_layout, const S for (int i = 0; i < scenes.size(); i++) { load_scene(scenes[i]); } + save_layout(); restoring_scenes = false; } 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/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 5a1383be6d..c32cd1de50 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1021,6 +1021,7 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_ for (int j = 0; j < ed->get_edited_scene_count(); j++) { if (ed->get_scene_path(j) == file_changed_paths[i]) { ed->get_edited_scene_root(j)->set_filename(new_item_path); + editor->save_layout(); break; } } 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/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h index 679235e316..701632e576 100644 --- a/editor/plugins/item_list_editor_plugin.h +++ b/editor/plugins/item_list_editor_plugin.h @@ -157,7 +157,7 @@ public: virtual void set_item_enabled(int p_idx, int p_enabled) { pp->set_item_disabled(p_idx, !p_enabled); } virtual bool is_item_enabled(int p_idx) const { return !pp->is_item_disabled(p_idx); } - virtual void set_item_id(int p_idx, int p_id) { pp->set_item_id(p_idx, p_idx); } + virtual void set_item_id(int p_idx, int p_id) { pp->set_item_id(p_idx, p_id); } virtual int get_item_id(int p_idx) const { return pp->get_item_id(p_idx); } virtual void set_item_separator(int p_idx, bool p_separator) { pp->set_item_as_separator(p_idx, p_separator); } diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 543771ad1c..c586985957 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -81,6 +81,8 @@ void ScriptTextEditor::set_edited_resource(const RES &p_res) { emit_signal("name_changed"); code_editor->update_line_and_column(); + + _validate_script(); } void ScriptTextEditor::_update_member_keywords() { 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/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 03277159fc..8cf00cf67d 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -1038,25 +1038,26 @@ void TileSetEditor::_on_workspace_overlay_draw() { tileset->get_tile_list(tiles); for (List<int>::Element *E = tiles->front(); E; E = E->next()) { int t_id = E->get(); - if (tileset->tile_get_texture(t_id)->get_rid() == current_texture_rid) { - Rect2i region = tileset->tile_get_region(t_id); - region.position += WORKSPACE_MARGIN; - region.position *= workspace->get_scale().x; - Color c; - if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) - c = COLOR_SINGLE; - else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) - c = COLOR_AUTOTILE; - else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) - c = COLOR_ATLAS; - String tile_id_name = String::num(t_id, 0) + ": " + tileset->tile_get_name(t_id); - Ref<Font> font = get_font("font", "Label"); - region.set_size(font->get_string_size(tile_id_name)); - workspace_overlay->draw_rect(region, c); - region.position.y += region.size.y - 2; - c = Color(0.1, 0.1, 0.1); - workspace_overlay->draw_string(font, region.position, tile_id_name, c); - } + if (tileset->tile_get_texture(t_id)->get_rid() != current_texture_rid) + continue; + + Rect2 region = tileset->tile_get_region(t_id); + region.position += WORKSPACE_MARGIN; + region.position *= workspace->get_scale().x; + Color c; + if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) + c = COLOR_SINGLE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) + c = COLOR_AUTOTILE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) + c = COLOR_ATLAS; + String tile_id_name = String::num(t_id, 0) + ": " + tileset->tile_get_name(t_id); + Ref<Font> font = get_font("font", "Label"); + region.set_size(font->get_string_size(tile_id_name)); + workspace_overlay->draw_rect(region, c); + region.position.y += region.size.y - 2; + c = Color(0.1, 0.1, 0.1); + workspace_overlay->draw_string(font, region.position, tile_id_name, c); } } 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 85a56a904d..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]); @@ -1848,6 +1848,8 @@ void SceneTreeDock::_create() { replace_node(n, newnode); } } + + scene_tree->get_scene_tree()->call_deferred("grab_focus"); } void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties) { @@ -2206,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); } @@ -2228,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); @@ -2275,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(); @@ -2506,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); @@ -2527,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); @@ -2549,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/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index c023c41747..f2d11c2753 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -48,6 +48,9 @@ Node *SceneTreeEditor::get_scene_node() { void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_id) { + if (connect_to_script_mode) { + return; //dont do anything in this mode + } TreeItem *item = Object::cast_to<TreeItem>(p_item); ERR_FAIL_COND(!item); @@ -190,7 +193,25 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { item->set_icon(0, icon); item->set_metadata(0, p_node->get_path()); - if (part_of_subscene) { + if (connect_to_script_mode) { + Color accent = get_color("accent_color", "Editor"); + + if (!p_node->get_script().is_null()) { + //has script + item->add_button(0, get_icon("Script", "EditorIcons"), BUTTON_SCRIPT); + } else { + //has no script + item->set_custom_color(0, get_color("disabled_font_color", "Editor")); + item->set_selectable(0, false); + accent.a *= 0.7; + } + + if (marked.has(p_node)) { + item->set_text(0, String(p_node->get_name()) + " " + TTR("(Connecting From)")); + + item->set_custom_color(0, accent); + } + } else if (part_of_subscene) { //item->set_selectable(0,marked_selectable); if (valid_types.size() == 0) { @@ -199,7 +220,9 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { } else if (marked.has(p_node)) { - item->set_selectable(0, marked_selectable); + if (!connect_to_script_mode) { + item->set_selectable(0, marked_selectable); + } item->set_custom_color(0, get_color("error_color", "Editor")); } else if (!marked_selectable && !marked_children_selectable) { @@ -620,6 +643,7 @@ void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) { item->set_as_cursor(0); selected = p_node; tree->ensure_cursor_is_visible(); + } else { if (!p_node) selected = NULL; @@ -974,6 +998,11 @@ void SceneTreeEditor::_warning_changed(Node *p_for_node) { update_timer->start(); } +void SceneTreeEditor::set_connect_to_script_mode(bool p_enable) { + connect_to_script_mode = p_enable; + update_tree(); +} + void SceneTreeEditor::_bind_methods() { ClassDB::bind_method("_tree_changed", &SceneTreeEditor::_tree_changed); @@ -1016,6 +1045,7 @@ void SceneTreeEditor::_bind_methods() { SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_open_instance) { + connect_to_script_mode = false; undo_redo = NULL; tree_dirty = true; selected = NULL; @@ -1091,22 +1121,17 @@ SceneTreeEditor::~SceneTreeEditor() { void SceneTreeDialog::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - connect("confirmed", this, "_select"); - } - - if (p_what == NOTIFICATION_EXIT_TREE) { - disconnect("confirmed", this, "_select"); - } - if (p_what == NOTIFICATION_DRAW) { - - RID ci = get_canvas_item(); - get_stylebox("panel", "PopupMenu")->draw(ci, Rect2(Point2(), get_size())); - } - - if (p_what == NOTIFICATION_VISIBILITY_CHANGED && is_visible_in_tree()) { - - tree->update_tree(); + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + connect("confirmed", this, "_select"); + } break; + case NOTIFICATION_EXIT_TREE: { + disconnect("confirmed", this, "_select"); + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (is_visible_in_tree()) + tree->update_tree(); + } break; } } @@ -1135,8 +1160,6 @@ SceneTreeDialog::SceneTreeDialog() { tree = memnew(SceneTreeEditor(false, false, true)); add_child(tree); - //set_child_rect(tree); - tree->get_scene_tree()->connect("item_activated", this, "_select"); } diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h index aa4d4dd58a..9158c4aa48 100644 --- a/editor/scene_tree_editor.h +++ b/editor/scene_tree_editor.h @@ -67,6 +67,8 @@ class SceneTreeEditor : public Control { AcceptDialog *error; AcceptDialog *warning; + bool connect_to_script_mode; + int blocked; void _compute_hash(Node *p_node, uint64_t &hash); @@ -151,6 +153,8 @@ public: void update_tree() { _update_tree(); } + void set_connect_to_script_mode(bool p_enable); + Tree *get_scene_tree() { return tree; } SceneTreeEditor(bool p_label = true, bool p_can_rename = false, bool p_can_open_instance = false); diff --git a/main/input_default.cpp b/main/input_default.cpp index e9f1eeff6a..6c3252ad1a 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -623,7 +623,8 @@ bool InputDefault::is_emulating_mouse_from_touch() const { return emulate_mouse_from_touch; } -Input::CursorShape InputDefault::get_default_cursor_shape() { +Input::CursorShape InputDefault::get_default_cursor_shape() const { + return default_shape; } @@ -638,6 +639,11 @@ void InputDefault::set_default_cursor_shape(CursorShape p_shape) { parse_input_event(mm); } +Input::CursorShape InputDefault::get_current_cursor_shape() const { + + return (Input::CursorShape)OS::get_singleton()->get_cursor_shape(); +} + void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { if (Engine::get_singleton()->is_editor_hint()) return; @@ -645,21 +651,6 @@ void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_sh OS::get_singleton()->set_custom_mouse_cursor(p_cursor, (OS::CursorShape)p_shape, p_hotspot); } -void InputDefault::set_mouse_in_window(bool p_in_window) { - /* no longer supported, leaving this for reference to anyone who might want to implement hardware cursors - if (custom_cursor.is_valid()) { - - if (p_in_window) { - set_mouse_mode(MOUSE_MODE_HIDDEN); - VisualServer::get_singleton()->cursor_set_visible(true); - } else { - set_mouse_mode(MOUSE_MODE_VISIBLE); - VisualServer::get_singleton()->cursor_set_visible(false); - } - } - */ -} - void InputDefault::accumulate_input_event(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); diff --git a/main/input_default.h b/main/input_default.h index 79a90cc4a4..f9b4cbbd57 100644 --- a/main/input_default.h +++ b/main/input_default.h @@ -242,10 +242,10 @@ public: void set_emulate_mouse_from_touch(bool p_emulate); virtual bool is_emulating_mouse_from_touch() const; - virtual CursorShape get_default_cursor_shape(); + virtual CursorShape get_default_cursor_shape() const; virtual void set_default_cursor_shape(CursorShape p_shape); + virtual CursorShape get_current_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()); - virtual void set_mouse_in_window(bool p_in_window); void parse_mapping(String p_mapping); void joy_button(int p_device, int p_button, bool p_pressed); diff --git a/main/tests/test_gdscript.cpp b/main/tests/test_gdscript.cpp index 8c60b79200..87bd640001 100644 --- a/main/tests/test_gdscript.cpp +++ b/main/tests/test_gdscript.cpp @@ -289,6 +289,11 @@ static String _parser_expr(const GDScriptParser::Node *p_expr) { } } break; + case GDScriptParser::Node::TYPE_CAST: { + const GDScriptParser::CastNode *cast_node = static_cast<const GDScriptParser::CastNode *>(p_expr); + txt = _parser_expr(cast_node->source_node) + " as " + cast_node->cast_type.to_string(); + + } break; case GDScriptParser::Node::TYPE_NEWLINE: { //skippie @@ -669,6 +674,17 @@ static void _disassemble_class(const Ref<GDScript> &p_class, const Vector<String incr += 2; } break; + case GDScriptFunction::OPCODE_CAST_TO_SCRIPT: { + + txt += " cast "; + txt += DADDR(3); + txt += "="; + txt += DADDR(1); + txt += " as "; + txt += DADDR(2); + incr += 4; + + } break; case GDScriptFunction::OPCODE_CONSTRUCT: { Variant::Type t = Variant::Type(code[ip + 1]); @@ -1018,19 +1034,17 @@ MainLoop *test(TestType p_type) { return NULL; } - GDScript *script = memnew(GDScript); + Ref<GDScript> gds; + gds.instance(); GDScriptCompiler gdc; - err = gdc.compile(&parser, script); + err = gdc.compile(&parser, gds.ptr()); if (err) { print_line("Compile Error:\n" + itos(gdc.get_error_line()) + ":" + itos(gdc.get_error_column()) + ":" + gdc.get_error()); - memdelete(script); return NULL; } - Ref<GDScript> gds = Ref<GDScript>(script); - Ref<GDScript> current = gds; while (current.is_valid()) { 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/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 71e7eecd1d..9882a89794 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -6427,7 +6427,24 @@ "major": 3, "minor": 1 }, - "next": null, + "next": { + "type": "NET", + "version": { + "major": 3, + "minor": 2 + }, + "next": null, + "api": [ + { + "name": "godot_net_bind_webrtc_peer", + "return_type": "void", + "arguments": [ + ["godot_object *", "p_obj"], + ["const godot_net_webrtc_peer *", "p_interface"] + ] + } + ] + }, "api": [ { "name": "godot_net_bind_stream_peer", diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h index d7de04e725..c1bc9daab5 100644 --- a/modules/gdnative/include/net/godot_net.h +++ b/modules/gdnative/include/net/godot_net.h @@ -111,6 +111,35 @@ typedef struct { /* Binds a MultiplayerPeerGDNative to the provided interface */ void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *); +typedef struct { + godot_gdnative_api_version version; /* version of our API */ + + godot_object *data; /* User reference */ + + /* This is PacketPeer */ + godot_error (*get_packet)(void *, const uint8_t **, int *); + godot_error (*put_packet)(void *, const uint8_t *, int); + godot_int (*get_available_packet_count)(const void *); + godot_int (*get_max_packet_size)(const void *); + + /* This is WebRTCPeer */ + void (*set_write_mode)(void *, godot_int); + godot_int (*get_write_mode)(const void *); + bool (*was_string_packet)(const void *); + godot_int (*get_connection_state)(const void *); + + godot_error (*create_offer)(void *); + godot_error (*set_remote_description)(void *, const char *, const char *); + godot_error (*set_local_description)(void *, const char *, const char *); + godot_error (*add_ice_candidate)(void *, const char *, int, const char *); + godot_error (*poll)(void *); + + void *next; /* For extension? */ +} godot_net_webrtc_peer; + +/* Binds a PacketPeerGDNative to the provided interface */ +void GDAPI godot_net_bind_webrtc_peer(godot_object *p_obj, const godot_net_webrtc_peer *); + #ifdef __cplusplus } #endif diff --git a/modules/gdnative/net/SCsub b/modules/gdnative/net/SCsub index e915703935..18ab9986b0 100644 --- a/modules/gdnative/net/SCsub +++ b/modules/gdnative/net/SCsub @@ -3,5 +3,11 @@ Import('env') Import('env_gdnative') -env_gdnative.add_source_files(env.modules_sources, '*.cpp') +env_net = env_gdnative.Clone() + +has_webrtc = env_net["module_webrtc_enabled"] +if has_webrtc: + env_net.Append(CPPDEFINES=['WEBRTC_GDNATIVE_ENABLED']) + +env_net.add_source_files(env.modules_sources, '*.cpp') diff --git a/modules/gdnative/net/webrtc_peer_gdnative.cpp b/modules/gdnative/net/webrtc_peer_gdnative.cpp new file mode 100644 index 0000000000..60b1ed4fe4 --- /dev/null +++ b/modules/gdnative/net/webrtc_peer_gdnative.cpp @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* packet_peer_gdnative.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "modules/gdnative/gdnative.h" +#include "modules/gdnative/include/net/godot_net.h" + +#ifdef WEBRTC_GDNATIVE_ENABLED +#include "modules/webrtc/webrtc_peer_gdnative.h" +#endif + +extern "C" { + +void GDAPI godot_net_bind_webrtc_peer(godot_object *p_obj, const godot_net_webrtc_peer *p_impl) { +#ifdef WEBRTC_GDNATIVE_ENABLED + ((WebRTCPeerGDNative *)p_obj)->set_native_webrtc_peer(p_impl); +#endif +} +} diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 0676317f6e..eada389c51 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1945,6 +1945,10 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(1); return "The local variable '" + symbols[0] + "' is declared but never used in the block."; } break; + case SHADOWED_VARIABLE: { + CHECK_SYMBOLS(2); + return "The local variable '" + symbols[0] + "' is shadowing an already defined variable at line " + symbols[1] + "."; + } break; case UNUSED_CLASS_VARIABLE: { CHECK_SYMBOLS(1); return "The class variable '" + symbols[0] + "' is declared but never used in the script."; @@ -2048,6 +2052,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "UNASSIGNED_VARIABLE", "UNASSIGNED_VARIABLE_OP_ASSIGN", "UNUSED_VARIABLE", + "SHADOWED_VARIABLE", "UNUSED_CLASS_VARIABLE", "UNUSED_ARGUMENT", "UNREACHABLE_CODE", diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index c67e390e32..1d75d9e2fe 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -273,6 +273,7 @@ struct GDScriptWarning { UNASSIGNED_VARIABLE, // Variable used but never assigned UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc) UNUSED_VARIABLE, // Local variable is declared but never used + SHADOWED_VARIABLE, // Variable name shadowed by other variable UNUSED_CLASS_VARIABLE, // Class variable is declared but never used in the file UNUSED_ARGUMENT, // Function argument is never used UNREACHABLE_CODE, // Code after a return statement diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index ae67521749..f7be0ce37c 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -480,16 +480,16 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: switch (cast_type.kind) { case GDScriptDataType::BUILTIN: { codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN); - codegen.opcodes.push_back(cn->cast_type.builtin_type); + codegen.opcodes.push_back(cast_type.builtin_type); } break; case GDScriptDataType::NATIVE: { int class_idx; - if (GDScriptLanguage::get_singleton()->get_global_map().has(cn->cast_type.native_type)) { + if (GDScriptLanguage::get_singleton()->get_global_map().has(cast_type.native_type)) { - class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cn->cast_type.native_type]; + class_idx = GDScriptLanguage::get_singleton()->get_global_map()[cast_type.native_type]; class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS); //argument (stack root) } else { - _set_error("Invalid native class type '" + String(cn->cast_type.native_type) + "'.", cn); + _set_error("Invalid native class type '" + String(cast_type.native_type) + "'.", cn); return -1; } codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator @@ -498,7 +498,7 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser:: case GDScriptDataType::SCRIPT: case GDScriptDataType::GDSCRIPT: { - Variant script = cn->cast_type.script_type; + Variant script = cast_type.script_type; int idx = codegen.get_constant_pos(script); idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access) @@ -1863,6 +1863,19 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->base = base; p_script->_base = base.ptr(); p_script->member_indices = base->member_indices; + + if (p_class->base_type.kind == GDScriptParser::DataType::CLASS) { + if (!parsed_classes.has(p_script->_base)) { + if (parsing_classes.has(p_script->_base)) { + _set_error("Cyclic class reference for '" + String(p_class->name) + "'.", p_class); + return ERR_PARSE_ERROR; + } + Error err = _parse_class_level(p_script->_base, p_class->base_type.class_type, p_keep_state); + if (err) { + return err; + } + } + } } break; default: { _set_error("Parser bug: invalid inheritance.", p_class); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 6ef906b98d..80af094c2c 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3690,6 +3690,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_VARIABLE, -1, name); } } + for (int i = 0; i < p_class->subclasses.size(); i++) { + if (p_class->subclasses[i]->name == name) { + _add_warning(GDScriptWarning::FUNCTION_CONFLICTS_CONSTANT, -1, name); + } + } #endif // DEBUG_ENABLED if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_OPEN) { @@ -4634,6 +4639,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } } + + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == member.identifier) { + _set_error("A class named '" + String(member.identifier) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } #ifdef DEBUG_ENABLED for (int i = 0; i < current_class->functions.size(); i++) { if (current_class->functions[i]->name == member.identifier) { @@ -4878,6 +4890,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == const_id) { + _set_error("A class named '" + String(const_id) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + tokenizer->advance(); if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON) { @@ -4948,6 +4967,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == enum_name) { + _set_error("A class named '" + String(enum_name) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + tokenizer->advance(); } if (tokenizer->get_token() != GDScriptTokenizer::TK_CURLY_BRACKET_OPEN) { @@ -5033,6 +5059,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + for (int i = 0; i < current_class->subclasses.size(); i++) { + if (current_class->subclasses[i]->name == const_id) { + _set_error("A class named '" + String(const_id) + "' already exists in this class (at line " + itos(current_class->subclasses[i]->line) + ")."); + return; + } + } + ClassNode::Constant constant; constant.type.has_type = true; constant.type.kind = DataType::BUILTIN; @@ -7625,6 +7658,11 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { if (p_function->arguments_usage[i] == 0 && !p_function->arguments[i].operator String().begins_with("_")) { _add_warning(GDScriptWarning::UNUSED_ARGUMENT, p_function->line, p_function->name, p_function->arguments[i].operator String()); } + for (int j = 0; j < current_class->variables.size(); j++) { + if (current_class->variables[j].identifier == p_function->arguments[i]) { + _add_warning(GDScriptWarning::SHADOWED_VARIABLE, p_function->line, p_function->arguments[i], itos(current_class->variables[j].line)); + } + } #endif // DEBUG_ENABLED } @@ -7698,6 +7736,17 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { p_function->return_type.has_type = false; p_function->return_type.may_yield = true; } + +#ifdef DEBUG_ENABLED + for (Map<StringName, LocalVarNode *>::Element *E = p_function->body->variables.front(); E; E = E->next()) { + LocalVarNode *lv = E->get(); + for (int i = 0; i < current_class->variables.size(); i++) { + if (current_class->variables[i].identifier == lv->name) { + _add_warning(GDScriptWarning::SHADOWED_VARIABLE, lv->line, lv->name, itos(current_class->variables[i].line)); + } + } + } +#endif // DEBUG_ENABLED } void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) { 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/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp index ec89df1959..3191cdbd53 100644 --- a/modules/mono/mono_gd/gd_mono_log.cpp +++ b/modules/mono/mono_gd/gd_mono_log.cpp @@ -72,8 +72,11 @@ static void mono_log_callback(const char *log_domain, const char *log_level, con if (fatal) { ERR_PRINTS("Mono: FATAL ERROR, ABORTING! Logfile: " + GDMonoLog::get_singleton()->get_log_file_path() + "\n"); - // If we were to abort without flushing, the log wouldn't get written. + // Make sure to flush before aborting f->flush(); + f->close(); + memdelete(f); + abort(); } } @@ -93,17 +96,9 @@ bool GDMonoLog::_try_create_logs_dir(const String &p_logs_dir) { return true; } -void GDMonoLog::_open_log_file(const String &p_file_path) { - - log_file = FileAccess::open(p_file_path, FileAccess::WRITE); - - ERR_EXPLAIN("Failed to create log file"); - ERR_FAIL_COND(!log_file); -} - void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { - static const uint64_t MAX_SECS = 5 * 86400; + static const uint64_t MAX_SECS = 5 * 86400; // 5 days DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ERR_FAIL_COND(!da); @@ -120,10 +115,9 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { if (!current.ends_with(".txt")) continue; - String name = current.get_basename(); - uint64_t unixtime = (uint64_t)name.to_int64(); + uint64_t modified_time = FileAccess::get_modified_time(da->get_current_dir().plus_file(current)); - if (OS::get_singleton()->get_unix_time() - unixtime > MAX_SECS) { + if (OS::get_singleton()->get_unix_time() - modified_time > MAX_SECS) { da->remove(current); } } @@ -131,28 +125,70 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) { da->list_dir_end(); } +static String format(const char *p_fmt, ...) { + va_list args; + + va_start(args, p_fmt); + int len = vsnprintf(NULL, 0, p_fmt, args); + va_end(args); + + len += 1; // for the trailing '/0' + + char *buffer(memnew_arr(char, len)); + + va_start(args, p_fmt); + vsnprintf(buffer, len, p_fmt, args); + va_end(args); + + String res(buffer); + memdelete_arr(buffer); + + return res; +} + void GDMonoLog::initialize() { + CharString log_level = OS::get_singleton()->get_environment("GODOT_MONO_LOG_LEVEL").utf8(); + + if (log_level.length() != 0 && log_level_get_id(log_level.get_data()) == -1) { + ERR_PRINTS(String() + "Mono: Ignoring invalid log level (GODOT_MONO_LOG_LEVEL): " + log_level.get_data()); + log_level = CharString(); + } + + if (log_level.length() == 0) { #ifdef DEBUG_ENABLED - const char *log_level = "debug"; + log_level = String("info").utf8(); #else - const char *log_level = "warning"; + log_level = String("warning").utf8(); #endif + } String logs_dir = GodotSharpDirs::get_mono_logs_dir(); if (_try_create_logs_dir(logs_dir)) { _delete_old_log_files(logs_dir); - log_file_path = logs_dir.plus_file(String::num_int64(OS::get_singleton()->get_unix_time()) + ".txt"); - _open_log_file(log_file_path); + OS::Date date_now = OS::get_singleton()->get_date(); + OS::Time time_now = OS::get_singleton()->get_time(); + int pid = OS::get_singleton()->get_process_id(); + + String log_file_name = format("%d-%02d-%02d %02d:%02d:%02d (%d).txt", + date_now.year, date_now.month, date_now.day, + time_now.hour, time_now.min, time_now.sec, pid); + + log_file_path = logs_dir.plus_file(log_file_name); + + log_file = FileAccess::open(log_file_path, FileAccess::WRITE); + if (!log_file) { + ERR_PRINT("Mono: Cannot create log file"); + } } - mono_trace_set_level_string(log_level); - log_level_id = log_level_get_id(log_level); + mono_trace_set_level_string(log_level.get_data()); + log_level_id = log_level_get_id(log_level.get_data()); if (log_file) { - print_verbose("Mono: Logfile is " + log_file_path); + OS::get_singleton()->print("Mono: Logfile is: %s\n", log_file_path.utf8().get_data()); mono_trace_set_log_handler(mono_log_callback, this); } else { OS::get_singleton()->printerr("Mono: No log file, using default log handler\n"); diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h index a477e5edee..91d0557aa3 100644 --- a/modules/mono/mono_gd/gd_mono_log.h +++ b/modules/mono/mono_gd/gd_mono_log.h @@ -41,7 +41,6 @@ class GDMonoLog { String log_file_path; bool _try_create_logs_dir(const String &p_logs_dir); - void _open_log_file(const String &p_file_path); void _delete_old_log_files(const String &p_logs_dir); static GDMonoLog *singleton; 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/modules/webrtc/SCsub b/modules/webrtc/SCsub new file mode 100644 index 0000000000..446bd530c2 --- /dev/null +++ b/modules/webrtc/SCsub @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +# Thirdparty source files + +env_webrtc = env_modules.Clone() +use_gdnative = env_webrtc["module_gdnative_enabled"] + +if use_gdnative: # GDNative is retained in Javascript for export compatibility + env_webrtc.Append(CPPDEFINES=['WEBRTC_GDNATIVE_ENABLED']) + gdnative_includes = ["#modules/gdnative/include/"] + env_webrtc.Append(CPPPATH=gdnative_includes) + +env_webrtc.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/webrtc/config.py b/modules/webrtc/config.py new file mode 100644 index 0000000000..5ed245bad2 --- /dev/null +++ b/modules/webrtc/config.py @@ -0,0 +1,13 @@ +def can_build(env, platform): + return True + +def configure(env): + pass + +def get_doc_classes(): + return [ + "WebRTCPeer" + ] + +def get_doc_path(): + return "doc_classes" diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp new file mode 100644 index 0000000000..ee7a766bd9 --- /dev/null +++ b/modules/webrtc/register_types.cpp @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "register_types.h" +#include "webrtc_peer.h" + +#ifdef JAVASCRIPT_ENABLED +#include "emscripten.h" +#include "webrtc_peer_js.h" +#endif +#ifdef WEBRTC_GDNATIVE_ENABLED +#include "webrtc_peer_gdnative.h" +#endif + +void register_webrtc_types() { +#ifdef JAVASCRIPT_ENABLED + WebRTCPeerJS::make_default(); +#elif defined(WEBRTC_GDNATIVE_ENABLED) + WebRTCPeerGDNative::make_default(); +#endif + + ClassDB::register_custom_instance_class<WebRTCPeer>(); +#ifdef WEBRTC_GDNATIVE_ENABLED + ClassDB::register_class<WebRTCPeerGDNative>(); +#endif +} + +void unregister_webrtc_types() {} diff --git a/modules/webrtc/register_types.h b/modules/webrtc/register_types.h new file mode 100644 index 0000000000..18a5dcc5aa --- /dev/null +++ b/modules/webrtc/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +void register_webrtc_types(); +void unregister_webrtc_types(); diff --git a/modules/webrtc/webrtc_peer.cpp b/modules/webrtc/webrtc_peer.cpp new file mode 100644 index 0000000000..30c4505df9 --- /dev/null +++ b/modules/webrtc/webrtc_peer.cpp @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* webrtc_peer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "webrtc_peer.h" + +WebRTCPeer *(*WebRTCPeer::_create)() = NULL; + +Ref<WebRTCPeer> WebRTCPeer::create_ref() { + + if (!_create) + return Ref<WebRTCPeer>(); + return Ref<WebRTCPeer>(_create()); +} + +WebRTCPeer *WebRTCPeer::create() { + + if (!_create) + return NULL; + return _create(); +} + +void WebRTCPeer::_bind_methods() { + ClassDB::bind_method(D_METHOD("create_offer"), &WebRTCPeer::create_offer); + ClassDB::bind_method(D_METHOD("set_local_description", "type", "sdp"), &WebRTCPeer::set_local_description); + ClassDB::bind_method(D_METHOD("set_remote_description", "type", "sdp"), &WebRTCPeer::set_remote_description); + ClassDB::bind_method(D_METHOD("poll"), &WebRTCPeer::poll); + ClassDB::bind_method(D_METHOD("add_ice_candidate", "media", "index", "name"), &WebRTCPeer::add_ice_candidate); + + ClassDB::bind_method(D_METHOD("was_string_packet"), &WebRTCPeer::was_string_packet); + ClassDB::bind_method(D_METHOD("set_write_mode", "write_mode"), &WebRTCPeer::set_write_mode); + ClassDB::bind_method(D_METHOD("get_write_mode"), &WebRTCPeer::get_write_mode); + ClassDB::bind_method(D_METHOD("get_connection_state"), &WebRTCPeer::get_connection_state); + + ADD_SIGNAL(MethodInfo("offer_created", PropertyInfo(Variant::STRING, "type"), PropertyInfo(Variant::STRING, "sdp"))); + ADD_SIGNAL(MethodInfo("new_ice_candidate", PropertyInfo(Variant::STRING, "media"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::STRING, "name"))); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "write_mode", PROPERTY_HINT_ENUM), "set_write_mode", "get_write_mode"); + + BIND_ENUM_CONSTANT(WRITE_MODE_TEXT); + BIND_ENUM_CONSTANT(WRITE_MODE_BINARY); + + BIND_ENUM_CONSTANT(STATE_NEW); + BIND_ENUM_CONSTANT(STATE_CONNECTING); + BIND_ENUM_CONSTANT(STATE_CONNECTED); + BIND_ENUM_CONSTANT(STATE_DISCONNECTED); + BIND_ENUM_CONSTANT(STATE_FAILED); + BIND_ENUM_CONSTANT(STATE_CLOSED); +} + +WebRTCPeer::WebRTCPeer() { +} + +WebRTCPeer::~WebRTCPeer() { +} diff --git a/modules/webrtc/webrtc_peer.h b/modules/webrtc/webrtc_peer.h new file mode 100644 index 0000000000..e141c14655 --- /dev/null +++ b/modules/webrtc/webrtc_peer.h @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* webrtc_peer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WEBRTC_PEER_H +#define WEBRTC_PEER_H + +#include "core/io/packet_peer.h" + +class WebRTCPeer : public PacketPeer { + GDCLASS(WebRTCPeer, PacketPeer); + +public: + enum WriteMode { + WRITE_MODE_TEXT, + WRITE_MODE_BINARY, + }; + + enum ConnectionState { + STATE_NEW, + STATE_CONNECTING, + STATE_CONNECTED, + STATE_DISCONNECTED, + STATE_FAILED, + STATE_CLOSED + }; + +protected: + static void _bind_methods(); + static WebRTCPeer *(*_create)(); + +public: + virtual void set_write_mode(WriteMode mode) = 0; + virtual WriteMode get_write_mode() const = 0; + virtual bool was_string_packet() const = 0; + virtual ConnectionState get_connection_state() const = 0; + + virtual Error create_offer() = 0; + virtual Error set_remote_description(String type, String sdp) = 0; + virtual Error set_local_description(String type, String sdp) = 0; + virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) = 0; + virtual Error poll() = 0; + + /** Inherited from PacketPeer: **/ + virtual int get_available_packet_count() const = 0; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) = 0; ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) = 0; + + virtual int get_max_packet_size() const = 0; + + static Ref<WebRTCPeer> create_ref(); + static WebRTCPeer *create(); + + WebRTCPeer(); + ~WebRTCPeer(); +}; + +VARIANT_ENUM_CAST(WebRTCPeer::WriteMode); +VARIANT_ENUM_CAST(WebRTCPeer::ConnectionState); +#endif // WEBRTC_PEER_H diff --git a/modules/webrtc/webrtc_peer_gdnative.cpp b/modules/webrtc/webrtc_peer_gdnative.cpp new file mode 100644 index 0000000000..f782944980 --- /dev/null +++ b/modules/webrtc/webrtc_peer_gdnative.cpp @@ -0,0 +1,114 @@ +/*************************************************************************/ +/* webrtc_peer_gdnative.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef WEBRTC_GDNATIVE_ENABLED + +#include "webrtc_peer_gdnative.h" + +void WebRTCPeerGDNative::_bind_methods() { +} + +WebRTCPeerGDNative::WebRTCPeerGDNative() { + interface = NULL; +} + +WebRTCPeerGDNative::~WebRTCPeerGDNative() { +} + +Error WebRTCPeerGDNative::create_offer() { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->create_offer(interface->data); +} + +Error WebRTCPeerGDNative::set_local_description(String p_type, String p_sdp) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->set_local_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data()); +} + +Error WebRTCPeerGDNative::set_remote_description(String p_type, String p_sdp) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->set_remote_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data()); +} + +Error WebRTCPeerGDNative::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->add_ice_candidate(interface->data, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data()); +} + +Error WebRTCPeerGDNative::poll() { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->poll(interface->data); +} + +void WebRTCPeerGDNative::set_write_mode(WriteMode p_mode) { + ERR_FAIL_COND(interface == NULL); + interface->set_write_mode(interface->data, p_mode); +} + +WebRTCPeer::WriteMode WebRTCPeerGDNative::get_write_mode() const { + ERR_FAIL_COND_V(interface == NULL, WRITE_MODE_BINARY); + return (WriteMode)interface->get_write_mode(interface->data); +} + +bool WebRTCPeerGDNative::was_string_packet() const { + ERR_FAIL_COND_V(interface == NULL, false); + return interface->was_string_packet(interface->data); +} + +WebRTCPeer::ConnectionState WebRTCPeerGDNative::get_connection_state() const { + ERR_FAIL_COND_V(interface == NULL, STATE_DISCONNECTED); + return STATE_DISCONNECTED; +} + +Error WebRTCPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size); +} + +Error WebRTCPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + ERR_FAIL_COND_V(interface == NULL, ERR_UNCONFIGURED); + return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size); +} + +int WebRTCPeerGDNative::get_max_packet_size() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_max_packet_size(interface->data); +} + +int WebRTCPeerGDNative::get_available_packet_count() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_available_packet_count(interface->data); +} + +void WebRTCPeerGDNative::set_native_webrtc_peer(const godot_net_webrtc_peer *p_impl) { + interface = p_impl; +} + +#endif // WEBRTC_GDNATIVE_ENABLED diff --git a/modules/webrtc/webrtc_peer_gdnative.h b/modules/webrtc/webrtc_peer_gdnative.h new file mode 100644 index 0000000000..6786cec8ea --- /dev/null +++ b/modules/webrtc/webrtc_peer_gdnative.h @@ -0,0 +1,78 @@ +/*************************************************************************/ +/* webrtc_peer_gdnative.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef WEBRTC_GDNATIVE_ENABLED + +#ifndef WEBRTC_PEER_GDNATIVE_H +#define WEBRTC_PEER_GDNATIVE_H + +#include "modules/gdnative/include/net/godot_net.h" +#include "webrtc_peer.h" + +class WebRTCPeerGDNative : public WebRTCPeer { + GDCLASS(WebRTCPeerGDNative, WebRTCPeer); + +protected: + static void _bind_methods(); + +private: + const godot_net_webrtc_peer *interface; + +public: + static WebRTCPeer *_create() { return memnew(WebRTCPeerGDNative); } + static void make_default() { WebRTCPeer::_create = WebRTCPeerGDNative::_create; } + + void set_native_webrtc_peer(const godot_net_webrtc_peer *p_impl); + + virtual void set_write_mode(WriteMode mode); + virtual WriteMode get_write_mode() const; + virtual bool was_string_packet() const; + virtual ConnectionState get_connection_state() const; + + virtual Error create_offer(); + virtual Error set_remote_description(String type, String sdp); + virtual Error set_local_description(String type, String sdp); + virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName); + virtual Error poll(); + + /** Inherited from PacketPeer: **/ + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + + virtual int get_max_packet_size() const; + + WebRTCPeerGDNative(); + ~WebRTCPeerGDNative(); +}; + +#endif // WEBRTC_PEER_GDNATIVE_H + +#endif // WEBRTC_GDNATIVE_ENABLED diff --git a/modules/webrtc/webrtc_peer_js.cpp b/modules/webrtc/webrtc_peer_js.cpp new file mode 100644 index 0000000000..1282e075ab --- /dev/null +++ b/modules/webrtc/webrtc_peer_js.cpp @@ -0,0 +1,455 @@ +/*************************************************************************/ +/* webrtc_peer_js.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifdef JAVASCRIPT_ENABLED + +#include "webrtc_peer_js.h" +#include "emscripten.h" + +extern "C" { +EMSCRIPTEN_KEEPALIVE void _emrtc_on_ice_candidate(void *obj, char *p_MidName, int p_MlineIndexName, char *p_sdpName) { + WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj); + peer->emit_signal("new_ice_candidate", String(p_MidName), p_MlineIndexName, String(p_sdpName)); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_offer_created(void *obj, char *p_type, char *p_offer) { + WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj); + peer->emit_signal("offer_created", String(p_type), String(p_offer)); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_error(void *obj) { + WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj); + peer->_on_error(); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_open(void *obj) { + WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj); + peer->_on_open(); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_close(void *obj) { + WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj); + peer->_on_close(); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_on_message(void *obj, uint8_t *p_data, uint32_t p_size, bool p_is_string) { + WebRTCPeerJS *peer = static_cast<WebRTCPeerJS *>(obj); + peer->_on_message(p_data, p_size, p_is_string); +} + +EMSCRIPTEN_KEEPALIVE void _emrtc_bind_channel(int p_id) { + /* clang-format off */ + EM_ASM({ + if (!Module.IDHandler.has($0)) { + return; // Godot Object is gone! + } + var dict = Module.IDHandler.get($0); + var channel = dict["channel"]; + var c_ptr = dict["ptr"]; + + channel.onopen = function (evt) { + ccall("_emrtc_on_open", + "void", + ["number"], + [c_ptr] + ); + }; + channel.onclose = function (evt) { + ccall("_emrtc_on_close", + "void", + ["number"], + [c_ptr] + ); + }; + channel.onerror = function (evt) { + ccall("_emrtc_on_error", + "void", + ["number"], + [c_ptr] + ); + }; + + channel.binaryType = "arraybuffer"; + channel.onmessage = function(event) { + var buffer; + var is_string = 0; + if (event.data instanceof ArrayBuffer) { + + buffer = new Uint8Array(event.data); + + } else if (event.data instanceof Blob) { + + alert("Blob type not supported"); + return; + + } else if (typeof event.data === "string") { + + is_string = 1; + var enc = new TextEncoder("utf-8"); + buffer = new Uint8Array(enc.encode(event.data)); + + } else { + + alert("Unknown message type"); + return; + + } + var len = buffer.length*buffer.BYTES_PER_ELEMENT; + var out = Module._malloc(len); + Module.HEAPU8.set(buffer, out); + ccall("_emrtc_on_message", + "void", + ["number", "number", "number", "number"], + [c_ptr, out, len, is_string] + ); + Module._free(out); + } + }, p_id); + /* clang-format on */ +} +} + +void _emrtc_create_pc(int p_id) { + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var c_ptr = dict["ptr"]; + // Setup local connaction + var conn = new RTCPeerConnection(); + conn.onicecandidate = function(event) { + if (!Module.IDHandler.get($0)) return; + if (!event.candidate) return; + + var c = event.candidate; + // should emit on ice candidate + ccall("_emrtc_on_ice_candidate", + "void", + ["number", "string", "number", "string"], + [c_ptr, c.sdpMid, c.sdpMLineIndex, c.candidate] + ); + }; + conn.ondatachannel = function (evt) { + var dict = Module.IDHandler.get($0); + if (!dict || dict["channel"]) { + return; + } + var channel = evt.channel; + dict["channel"] = channel; + ccall("_emrtc_bind_channel", + "void", + ["number"], + [$0] + ); + }; + dict["conn"] = conn; + }, p_id); + /* clang-format on */ +} + +void WebRTCPeerJS::_on_open() { + in_buffer.resize(16); + _conn_state = STATE_CONNECTED; +} + +void WebRTCPeerJS::_on_close() { + close(); +} + +void WebRTCPeerJS::_on_error() { + close(); + _conn_state = STATE_FAILED; +} + +void WebRTCPeerJS::_on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string) { + if (in_buffer.space_left() < p_size + 5) { + ERR_EXPLAIN("Buffer full! Dropping data"); + ERR_FAIL(); + } + + uint8_t is_string = p_is_string ? 1 : 0; + in_buffer.write((uint8_t *)&p_size, 4); + in_buffer.write((uint8_t *)&is_string, 1); + in_buffer.write(p_data, p_size); + queue_count++; +} + +void WebRTCPeerJS::close() { + in_buffer.resize(0); + queue_count = 0; + _was_string = false; + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + if (!dict) return; + if (dict["channel"]) { + dict["channel"].close(); + dict["channel"] = null; + } + if (dict["conn"]) { + dict["conn"].close(); + } + }, _js_id); + /* clang-format on */ + _conn_state = STATE_CLOSED; +} + +Error WebRTCPeerJS::create_offer() { + ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED); + + _conn_state = STATE_CONNECTING; + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var conn = dict["conn"]; + var c_ptr = dict["ptr"]; + var onError = function(error) { + console.log(error); + ccall("_emrtc_on_error", + "void", + ["number"], + [c_ptr] + ); + }; + var onCreated = function(offer) { + ccall("_emrtc_offer_created", + "void", + ["number", "string", "string"], + [c_ptr, offer.type, offer.sdp] + ); + }; + + var channel = conn.createDataChannel("default"); + dict["channel"] = channel; + ccall("_emrtc_bind_channel", + "void", + ["number"], + [$0] + ); + conn.createOffer().then(onCreated).catch(onError); + }, _js_id); + /* clang-format on */ + return OK; +} + +Error WebRTCPeerJS::set_local_description(String type, String sdp) { + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var conn = dict["conn"]; + var c_ptr = dict["ptr"]; + var type = UTF8ToString($1); + var sdp = UTF8ToString($2); + var onError = function(error) { + console.log(error); + ccall("_emrtc_on_error", + "void", + ["number"], + [c_ptr] + ); + }; + conn.setLocalDescription({ + "sdp": sdp, + "type": type + }).catch(onError); + }, _js_id, type.utf8().get_data(), sdp.utf8().get_data()); + /* clang-format on */ + return OK; +} + +Error WebRTCPeerJS::set_remote_description(String type, String sdp) { + if (type == "offer") { + ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED); + _conn_state = STATE_CONNECTING; + } + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var conn = dict["conn"]; + var c_ptr = dict["ptr"]; + var type = UTF8ToString($1); + var sdp = UTF8ToString($2); + + var onError = function(error) { + console.log(error); + ccall("_emrtc_on_error", + "void", + ["number"], + [c_ptr] + ); + }; + var onCreated = function(offer) { + ccall("_emrtc_offer_created", + "void", + ["number", "string", "string"], + [c_ptr, offer.type, offer.sdp] + ); + }; + var onSet = function() { + if (type != "offer") { + return; + } + conn.createAnswer().then(onCreated); + }; + conn.setRemoteDescription({ + "sdp": sdp, + "type": type + }).then(onSet).catch(onError); + }, _js_id, type.utf8().get_data(), sdp.utf8().get_data()); + /* clang-format on */ + return OK; +} + +Error WebRTCPeerJS::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) { + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var conn = dict["conn"]; + var c_ptr = dict["ptr"]; + var sdpMidName = UTF8ToString($1); + var sdpMlineIndexName = UTF8ToString($2); + var sdpName = UTF8ToString($3); + conn.addIceCandidate(new RTCIceCandidate({ + "candidate": sdpName, + "sdpMid": sdpMidName, + "sdpMlineIndex": sdpMlineIndexName + })); + }, _js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data()); + /* clang-format on */ + return OK; +} + +Error WebRTCPeerJS::poll() { + return OK; +} + +WebRTCPeer::ConnectionState WebRTCPeerJS::get_connection_state() const { + return _conn_state; +} + +int WebRTCPeerJS::get_available_packet_count() const { + return queue_count; +} + +Error WebRTCPeerJS::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + ERR_FAIL_COND_V(_conn_state != STATE_CONNECTED, ERR_UNCONFIGURED); + + if (queue_count == 0) + return ERR_UNAVAILABLE; + + uint32_t to_read = 0; + uint32_t left = 0; + uint8_t is_string = 0; + r_buffer_size = 0; + + in_buffer.read((uint8_t *)&to_read, 4); + --queue_count; + left = in_buffer.data_left(); + + if (left < to_read + 1) { + in_buffer.advance_read(left); + return FAILED; + } + + in_buffer.read(&is_string, 1); + _was_string = is_string == 1; + in_buffer.read(packet_buffer, to_read); + *r_buffer = packet_buffer; + r_buffer_size = to_read; + + return OK; +} + +Error WebRTCPeerJS::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + ERR_FAIL_COND_V(_conn_state != STATE_CONNECTED, ERR_UNCONFIGURED); + + int is_bin = _write_mode == WebRTCPeer::WRITE_MODE_BINARY ? 1 : 0; + + /* clang-format off */ + EM_ASM({ + var dict = Module.IDHandler.get($0); + var channel = dict["channel"]; + var bytes_array = new Uint8Array($2); + var i = 0; + + for(i=0; i<$2; i++) { + bytes_array[i] = getValue($1+i, 'i8'); + } + + if ($3) { + channel.send(bytes_array.buffer); + } else { + var string = new TextDecoder("utf-8").decode(bytes_array); + channel.send(string); + } + }, _js_id, p_buffer, p_buffer_size, is_bin); + /* clang-format on */ + + return OK; +} + +int WebRTCPeerJS::get_max_packet_size() const { + return 1200; +} + +void WebRTCPeerJS::set_write_mode(WriteMode p_mode) { + _write_mode = p_mode; +} + +WebRTCPeer::WriteMode WebRTCPeerJS::get_write_mode() const { + return _write_mode; +} + +bool WebRTCPeerJS::was_string_packet() const { + return _was_string; +} + +WebRTCPeerJS::WebRTCPeerJS() { + queue_count = 0; + _was_string = false; + _write_mode = WRITE_MODE_BINARY; + _conn_state = STATE_NEW; + + /* clang-format off */ + _js_id = EM_ASM_INT({ + return Module.IDHandler.add({"conn": null, "ptr": $0, "channel": null}); + }, this); + /* clang-format on */ + _emrtc_create_pc(_js_id); +} + +WebRTCPeerJS::~WebRTCPeerJS() { + close(); + /* clang-format off */ + EM_ASM({ + Module.IDHandler.remove($0); + }, _js_id); + /* clang-format on */ +}; +#endif diff --git a/modules/webrtc/webrtc_peer_js.h b/modules/webrtc/webrtc_peer_js.h new file mode 100644 index 0000000000..02f0c9b55d --- /dev/null +++ b/modules/webrtc/webrtc_peer_js.h @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* webrtc_peer_js.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WEBRTC_PEER_JS_H +#define WEBRTC_PEER_JS_H + +#ifdef JAVASCRIPT_ENABLED + +#include "webrtc_peer.h" + +class WebRTCPeerJS : public WebRTCPeer { + +private: + enum { + PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type + }; + + bool _was_string; + WriteMode _write_mode; + + int _js_id; + RingBuffer<uint8_t> in_buffer; + int queue_count; + uint8_t packet_buffer[PACKET_BUFFER_SIZE]; + ConnectionState _conn_state; + +public: + static WebRTCPeer *_create() { return memnew(WebRTCPeerJS); } + static void make_default() { WebRTCPeer::_create = WebRTCPeerJS::_create; } + + virtual void set_write_mode(WriteMode mode); + virtual WriteMode get_write_mode() const; + virtual bool was_string_packet() const; + virtual ConnectionState get_connection_state() const; + + virtual Error create_offer(); + virtual Error set_remote_description(String type, String sdp); + virtual Error set_local_description(String type, String sdp); + virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName); + virtual Error poll(); + + /** Inherited from PacketPeer: **/ + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + + virtual int get_max_packet_size() const; + + void close(); + void _on_open(); + void _on_close(); + void _on_error(); + void _on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string); + + WebRTCPeerJS(); + ~WebRTCPeerJS(); +}; + +#endif + +#endif // WEBRTC_PEER_JS_H diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp index ed6cc5638e..39bf3de982 100644 --- a/modules/websocket/register_types.cpp +++ b/modules/websocket/register_types.cpp @@ -60,25 +60,6 @@ void register_websocket_types() { _SET_HINT(WSS_OUT_PKT, 1024, 16384); #ifdef JAVASCRIPT_ENABLED - EM_ASM({ - var IDHandler = {}; - IDHandler["ids"] = {}; - IDHandler["has"] = function(id) { - return IDHandler.ids.hasOwnProperty(id); - }; - IDHandler["add"] = function(obj) { - var id = crypto.getRandomValues(new Int32Array(32))[0]; - IDHandler.ids[id] = obj; - return id; - }; - IDHandler["get"] = function(id) { - return IDHandler.ids[id]; - }; - IDHandler["remove"] = function(id) { - delete IDHandler.ids[id]; - }; - Module["IDHandler"] = IDHandler; - }); EMWSPeer::make_default(); EMWSClient::make_default(); EMWSServer::make_default(); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index d65cf2f31e..93d39859f2 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -292,14 +292,6 @@ bool OS_Android::can_draw() const { return true; //always? } -void OS_Android::set_cursor_shape(CursorShape p_shape) { - - //android really really really has no mouse.. how amazing.. -} - -void OS_Android::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { -} - void OS_Android::main_loop_begin() { if (main_loop) diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 3a5404124a..d2198b0579 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -144,9 +144,6 @@ public: virtual bool can_draw() const; - virtual void set_cursor_shape(CursorShape p_shape); - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); - void main_loop_begin(); bool main_loop_iterate(); void main_loop_request_go_back(); diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp index a6d5a00852..f3fed6669b 100644 --- a/platform/haiku/os_haiku.cpp +++ b/platform/haiku/os_haiku.cpp @@ -203,6 +203,10 @@ void OS_Haiku::set_cursor_shape(CursorShape p_shape) { //ERR_PRINT("set_cursor_shape() NOT IMPLEMENTED"); } +OS::CursorShape OS_Haiku::get_cursor_shape() const { + // TODO: implement get_cursor_shape +} + void OS_Haiku::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { // TODO } diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h index d7eac10635..6ab006843a 100644 --- a/platform/haiku/os_haiku.h +++ b/platform/haiku/os_haiku.h @@ -86,6 +86,7 @@ public: virtual Point2 get_mouse_position() const; virtual int get_mouse_button_state() const; virtual void set_cursor_shape(CursorShape p_shape); + virtual CursorShape get_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); virtual int get_screen_count() const; diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 7d0fdd2078..e25efc813b 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -490,17 +490,11 @@ void OSIPhone::set_keep_screen_on(bool p_enabled) { _set_keep_screen_on(p_enabled); }; -void OSIPhone::set_cursor_shape(CursorShape p_shape){ - -}; - String OSIPhone::get_user_data_dir() const { return data_dir; }; -void OSIPhone::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot){}; - String OSIPhone::get_name() { return "iOS"; diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index 30d7a1ba41..49c6475cf9 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -167,9 +167,6 @@ public: virtual void hide_virtual_keyboard(); virtual int get_virtual_keyboard_height() const; - virtual void set_cursor_shape(CursorShape p_shape); - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); - virtual Size2 get_window_size() const; virtual Rect2 get_window_safe_area() const; diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index a93c98a89f..85a633442e 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -20,6 +20,13 @@ for lib in js_libraries: env.Append(LINKFLAGS=['--js-library', env.File(lib).path]) env.Depends(build, js_libraries) +js_modules = [ + 'id_handler.js', +] +for module in js_modules: + env.Append(LINKFLAGS=['--pre-js', env.File(module).path]) +env.Depends(build, js_modules) + wrapper_start = env.File('pre.js') wrapper_end = env.File('engine.js') js_wrapped = env.Textfile('#bin/godot', [wrapper_start, js, wrapper_end], TEXTFILESUFFIX='${PROGSUFFIX}.wrapped.js') diff --git a/platform/javascript/id_handler.js b/platform/javascript/id_handler.js new file mode 100644 index 0000000000..36ef5aa8ef --- /dev/null +++ b/platform/javascript/id_handler.js @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* id_handler.js */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +var IDHandler = function() { + + var ids = {}; + var size = 0; + + this.has = function(id) { + return ids.hasOwnProperty(id); + } + + this.add = function(obj) { + size += 1; + var id = crypto.getRandomValues(new Int32Array(32))[0]; + ids[id] = obj; + return id; + } + + this.get = function(id) { + return ids[id]; + } + + this.remove = function(id) { + size -= 1; + delete ids[id]; + } + + this.size = function() { + return size; + } + + this.ids = ids; +}; + +Module.IDHandler = new IDHandler; diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index dfe7b27bd0..125a88ab6d 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -172,6 +172,7 @@ public: virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); virtual void set_cursor_shape(CursorShape p_shape); + virtual CursorShape get_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); virtual void set_mouse_show(bool p_show); diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 4bbe30a7d1..5a4c0593bc 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -712,8 +712,6 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED) OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT); - if (OS_OSX::singleton->input) - OS_OSX::singleton->input->set_mouse_in_window(false); } - (void)mouseEntered:(NSEvent *)event { @@ -721,8 +719,6 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { return; if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED) OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER); - if (OS_OSX::singleton->input) - OS_OSX::singleton->input->set_mouse_in_window(true); OS::CursorShape p_shape = OS_OSX::singleton->cursor_shape; OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX; @@ -1696,6 +1692,11 @@ void OS_OSX::set_cursor_shape(CursorShape p_shape) { cursor_shape = p_shape; } +OS::CursorShape OS_OSX::get_cursor_shape() const { + + return cursor_shape; +} + void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { if (p_cursor.is_valid()) { Ref<Texture> texture = p_cursor; diff --git a/platform/server/os_server.cpp b/platform/server/os_server.cpp index e643d3e8bb..53f2a65c8e 100644 --- a/platform/server/os_server.cpp +++ b/platform/server/os_server.cpp @@ -198,12 +198,6 @@ String OS_Server::get_name() { void OS_Server::move_window_to_foreground() { } -void OS_Server::set_cursor_shape(CursorShape p_shape) { -} - -void OS_Server::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { -} - OS::PowerState OS_Server::get_power_state() { return power_manager->get_power_state(); } diff --git a/platform/server/os_server.h b/platform/server/os_server.h index eebe8ae777..7441064790 100644 --- a/platform/server/os_server.h +++ b/platform/server/os_server.h @@ -95,9 +95,6 @@ protected: public: virtual String get_name(); - virtual void set_cursor_shape(CursorShape p_shape); - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); - virtual void set_mouse_show(bool p_show); virtual void set_mouse_grab(bool p_grab); virtual bool is_mouse_grab_enabled() const; diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index bc74da8a1b..82f09032f5 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -704,6 +704,11 @@ void OS_UWP::set_cursor_shape(CursorShape p_shape) { cursor_shape = p_shape; } +OS::CursorShape OS_UWP::get_cursor_shape() const { + + return cursor_shape; +} + void OS_UWP::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { // TODO } diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index fd78b3cdf7..00f79efb04 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -219,6 +219,7 @@ public: virtual String get_clipboard() const; void set_cursor_shape(CursorShape p_shape); + CursorShape get_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); void set_icon(const Ref<Image> &p_icon); diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 0118b5bae2..15e8f33eae 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -195,7 +195,7 @@ def configure_msvc(env, manual_msvc_config): ## Compile/link flags - env.AppendUnique(CCFLAGS=['/MT', '/Gd', '/GR', '/nologo']) + env.AppendUnique(CCFLAGS=['/MT', '/Gd', '/GR', '/nologo', '/utf-8']) env.AppendUnique(CXXFLAGS=['/TP']) # assume all sources are C++ if manual_msvc_config: # should be automatic if SCons found it if os.getenv("WindowsSdkDir") is not None: diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 093fb05bae..1a5050a15a 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -384,8 +384,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) outside = true; if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED) main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT); - if (input) - input->set_mouse_in_window(false); } break; case WM_INPUT: { @@ -480,8 +478,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED) main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER); - if (input) - input->set_mouse_in_window(true); CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; @@ -2299,6 +2295,11 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) { cursor_shape = p_shape; } +OS::CursorShape OS_Windows::get_cursor_shape() const { + + return cursor_shape; +} + void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { if (p_cursor.is_valid()) { Ref<Texture> texture = p_cursor; diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 2d03532c69..c15e1cabc3 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -273,6 +273,7 @@ public: virtual String get_clipboard() const; void set_cursor_shape(CursorShape p_shape); + CursorShape get_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap); void set_icon(const Ref<Image> &p_icon); diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index ba74ba2437..c96047dfbd 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -2041,15 +2041,11 @@ void OS_X11::process_xevents() { case LeaveNotify: { if (main_loop && !mouse_mode_grab) main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT); - if (input) - input->set_mouse_in_window(false); } break; case EnterNotify: { if (main_loop && !mouse_mode_grab) main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER); - if (input) - input->set_mouse_in_window(true); } break; case FocusIn: minimized = false; @@ -2722,6 +2718,11 @@ void OS_X11::set_cursor_shape(CursorShape p_shape) { current_cursor = p_shape; } +OS::CursorShape OS_X11::get_cursor_shape() const { + + return current_cursor; +} + void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { if (p_cursor.is_valid()) { Ref<Texture> texture = p_cursor; diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 6d1a66af84..a54851d4e7 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -219,6 +219,7 @@ public: virtual String get_name(); virtual void set_cursor_shape(CursorShape p_shape); + virtual CursorShape get_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); void set_mouse_mode(MouseMode p_mode); diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 2a225e5797..b322cfe8f1 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -261,8 +261,9 @@ void Area2D::_area_inout(int p_status, const RID &p_area, int p_instance, int p_ Map<ObjectID, AreaState>::Element *E = area_map.find(objid); - ERR_FAIL_COND(!area_in && !E); - + if (!area_in && !E) { + return; //likely removed from the tree + } locked = true; if (area_in) { diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp index 5334caed67..3a3f90ac4b 100644 --- a/scene/2d/light_occluder_2d.cpp +++ b/scene/2d/light_occluder_2d.cpp @@ -32,9 +32,59 @@ #include "core/engine.h" +#define LINE_GRAB_WIDTH 8 +Rect2 OccluderPolygon2D::_edit_get_rect() const { + + if (rect_cache_dirty) { + if (closed) { + PoolVector<Vector2>::Read r = polygon.read(); + item_rect = Rect2(); + for (int i = 0; i < polygon.size(); i++) { + Vector2 pos = r[i]; + if (i == 0) + item_rect.position = pos; + else + item_rect.expand_to(pos); + } + rect_cache_dirty = false; + } else { + if (polygon.size() == 0) { + item_rect = Rect2(); + } else { + Vector2 d = Vector2(LINE_GRAB_WIDTH, LINE_GRAB_WIDTH); + item_rect = Rect2(polygon[0] - d, 2 * d); + for (int i = 1; i < polygon.size(); i++) { + item_rect.expand_to(polygon[i] - d); + item_rect.expand_to(polygon[i] + d); + } + } + } + } + + return item_rect; +} + +bool OccluderPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + if (closed) { + return Geometry::is_point_in_polygon(p_point, Variant(polygon)); + } else { + const real_t d = LINE_GRAB_WIDTH / 2 + p_tolerance; + PoolVector<Vector2>::Read points = polygon.read(); + for (int i = 0; i < polygon.size() - 1; i++) { + Vector2 p = Geometry::get_closest_point_to_segment_2d(p_point, &points[i]); + if (p.distance_to(p_point) <= d) + return true; + } + + return false; + } +} + void OccluderPolygon2D::set_polygon(const PoolVector<Vector2> &p_polygon) { polygon = p_polygon; + rect_cache_dirty = true; VS::get_singleton()->canvas_occluder_polygon_set_shape(occ_polygon, p_polygon, closed); emit_changed(); } @@ -100,6 +150,7 @@ OccluderPolygon2D::OccluderPolygon2D() { occ_polygon = VS::get_singleton()->canvas_occluder_polygon_create(); closed = true; cull = CULL_DISABLED; + rect_cache_dirty = true; } OccluderPolygon2D::~OccluderPolygon2D() { @@ -164,6 +215,16 @@ void LightOccluder2D::_notification(int p_what) { } } +Rect2 LightOccluder2D::_edit_get_rect() const { + + return occluder_polygon.is_valid() ? occluder_polygon->_edit_get_rect() : Rect2(); +} + +bool LightOccluder2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + + return occluder_polygon.is_valid() ? occluder_polygon->_edit_is_selected_on_click(p_point, p_tolerance) : false; +} + void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon) { #ifdef DEBUG_ENABLED diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h index 498d65d764..7931cd0b30 100644 --- a/scene/2d/light_occluder_2d.h +++ b/scene/2d/light_occluder_2d.h @@ -50,10 +50,16 @@ private: bool closed; CullMode cull; + mutable Rect2 item_rect; + mutable bool rect_cache_dirty; + protected: static void _bind_methods(); public: + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_polygon(const PoolVector<Vector2> &p_polygon); PoolVector<Vector2> get_polygon() const; @@ -85,6 +91,9 @@ protected: static void _bind_methods(); public: + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; + void set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon); Ref<OccluderPolygon2D> get_occluder_polygon() const; diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp index 13d9181082..3557f0425c 100644 --- a/scene/3d/area.cpp +++ b/scene/3d/area.cpp @@ -356,7 +356,9 @@ void Area::_area_inout(int p_status, const RID &p_area, int p_instance, int p_ar Map<ObjectID, AreaState>::Element *E = area_map.find(objid); - ERR_FAIL_COND(!area_in && !E); + if (!area_in && !E) { + return; //likely removed from the tree + } locked = true; 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/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index 68ad71d01c..1e3470cd90 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -960,7 +960,7 @@ void AnimationNodeStateMachine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_end_node", "name"), &AnimationNodeStateMachine::set_end_node); ClassDB::bind_method(D_METHOD("get_end_node"), &AnimationNodeStateMachine::get_end_node); - ClassDB::bind_method(D_METHOD("set_graph_offset", "name"), &AnimationNodeStateMachine::set_graph_offset); + ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeStateMachine::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset); ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeStateMachine::_tree_changed); diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index eb04b85931..028ca41cbf 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -34,8 +34,15 @@ Size2 Slider::get_minimum_size() const { Ref<StyleBox> style = get_stylebox("slider"); - Size2i ms = style->get_minimum_size() + style->get_center_size(); - return ms; + Size2i ss = style->get_minimum_size() + style->get_center_size(); + + Ref<Texture> grabber = get_icon("grabber"); + Size2i rs = grabber->get_size(); + + if (orientation == HORIZONTAL) + return Size2i(ss.width, MAX(ss.height, rs.height)); + else + return Size2i(MAX(ss.width, rs.width), ss.height); } void Slider::_gui_input(Ref<InputEvent> p_event) { @@ -134,7 +141,11 @@ void Slider::_gui_input(Ref<InputEvent> p_event) { void Slider::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + minimum_size_changed(); + update(); + } break; case NOTIFICATION_MOUSE_ENTER: { mouse_inside = true; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 9acf8d40c9..39a0b0aaf2 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -590,6 +590,12 @@ void TextEdit::_notification(int p_what) { } } break; case NOTIFICATION_DRAW: { + + if (first_draw) { + //size may not be the final one, so attempts to ensure cursor was visible may have failed + adjust_viewport_to_cursor(); + first_draw = false; + } Size2 size = get_size(); if ((!has_focus() && !menu->has_focus()) || !window_has_focus) { draw_caret = false; @@ -6356,6 +6362,7 @@ TextEdit::TextEdit() { menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z); menu->connect("id_pressed", this, "menu_option"); + first_draw = true; } TextEdit::~TextEdit() { diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 33f0a3f45d..96b409c6d4 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -275,6 +275,7 @@ private: int wrap_at; int wrap_right_offset; + bool first_draw; bool setting_row; bool draw_tabs; bool override_selected_font_color; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 7f00e7bd24..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); @@ -3388,10 +3388,13 @@ void Tree::ensure_cursor_is_visible() { int h = compute_item_height(selected) + cache.vseparation; int screenh = get_size().height - h_scroll->get_combined_minimum_size().height; - if (ofs + h > v_scroll->get_value() + screenh) + if (h > screenh) { //screen size is too small, maybe it was not resized yet. + v_scroll->set_value(ofs); + } else if (ofs + h > v_scroll->get_value() + screenh) { v_scroll->call_deferred("set_value", ofs - screenh + h); - else if (ofs < v_scroll->get_value()) + } else if (ofs < v_scroll->get_value()) { v_scroll->set_value(ofs); + } } int Tree::get_pressed_button() const { diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 128168ca72..5f9c187e0b 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2759,6 +2759,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("can_process"), &Node::can_process); ClassDB::bind_method(D_METHOD("print_stray_nodes"), &Node::_print_stray_nodes); ClassDB::bind_method(D_METHOD("get_position_in_parent"), &Node::get_position_in_parent); + ClassDB::bind_method(D_METHOD("set_display_folded", "fold"), &Node::set_display_folded); ClassDB::bind_method(D_METHOD("is_displayed_folded"), &Node::is_displayed_folded); @@ -2871,7 +2872,12 @@ void Node::_bind_methods() { //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/unhandled_input" ), "set_process_unhandled_input","is_processing_unhandled_input" ) ; ADD_GROUP("Pause", "pause_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor/display_folded", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_display_folded", "is_displayed_folded"); + +#ifdef ENABLE_DEPRECATED + //no longer exists, but remains for compatibility (keep previous scenes folded + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor/display_folded", PROPERTY_HINT_NONE, "", 0), "set_display_folded", "is_displayed_folded"); +#endif + ADD_PROPERTY(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner"); 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/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 2e54424c90..e9f90fc85f 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -445,6 +445,11 @@ Error ResourceInteractiveLoaderText::poll() { } else { resource_cache.push_back(res); +#ifdef TOOLS_ENABLED + //remember ID for saving + res->set_id_for_path(local_path,index); +#endif + } ExtResource er; @@ -1355,7 +1360,7 @@ String ResourceFormatSaverTextInstance::_write_resource(const RES &res) { if (external_resources.has(res)) { - return "ExtResource( " + itos(external_resources[res] + 1) + " )"; + return "ExtResource( " + itos(external_resources[res]) + " )"; } else { if (internal_resources.has(res)) { @@ -1539,18 +1544,65 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r f->store_line("]\n"); //one empty line } - Vector<RES> sorted_er; - sorted_er.resize(external_resources.size()); + { + + + + } + +#ifdef TOOLS_ENABLED + //keep order from cached ids + Set<int> cached_ids_found; + for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { + int cached_id = E->key()->get_id_for_path(local_path); + if (cached_id < 0 || cached_ids_found.has(cached_id)) { + E->get() = -1; //reset + } else { + E->get() = cached_id; + cached_ids_found.insert(cached_id); + } + } + //create IDs for non cached resources + for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { + if (cached_ids_found.has(E->get())) { //already cached, go on + continue; + } + + int attempt = 1; //start from one, more readable format + while(cached_ids_found.has(attempt)) { + attempt++; + } + + cached_ids_found.insert(attempt); + E->get() = attempt; + //update also in resource + Ref<Resource> res = E->key(); + res->set_id_for_path(local_path,attempt); + } +#else + //make sure to start from one, as it makes format more readable + for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { + E->get() = E->get() + 1; + } +#endif + + Vector<ResourceSort> sorted_er; for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { - sorted_er.write[E->get()] = E->key(); + ResourceSort rs; + rs.resource = E->key(); + rs.index = E->get(); + sorted_er.push_back(rs); } + sorted_er.sort(); + + for (int i = 0; i < sorted_er.size(); i++) { - String p = sorted_er[i]->get_path(); + String p = sorted_er[i].resource->get_path(); - f->store_string("[ext_resource path=\"" + p + "\" type=\"" + sorted_er[i]->get_save_class() + "\" id=" + itos(i + 1) + "]\n"); //bundled + f->store_string("[ext_resource path=\"" + p + "\" type=\"" + sorted_er[i].resource->get_save_class() + "\" id=" + itos(sorted_er[i].index) + "]\n"); //bundled } if (external_resources.size()) diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index 8d78ab33b0..ab6f94986c 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -169,6 +169,15 @@ class ResourceFormatSaverTextInstance { Map<RES, int> external_resources; Map<RES, int> internal_resources; + struct ResourceSort { + RES resource; + int index; + bool operator<(const ResourceSort& p_right) const { + return index < p_right.index; + } + + }; + void _find_resources(const Variant &p_variant, bool p_main = false); static String _write_resources(void *ud, const RES &p_resource); 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 diff --git a/servers/audio/effects/audio_effect_spectrum_analyzer.cpp b/servers/audio/effects/audio_effect_spectrum_analyzer.cpp index 71a8904472..01a52aa01f 100644 --- a/servers/audio/effects/audio_effect_spectrum_analyzer.cpp +++ b/servers/audio/effects/audio_effect_spectrum_analyzer.cpp @@ -240,6 +240,13 @@ void AudioEffectSpectrumAnalyzer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "buffer_length", PROPERTY_HINT_RANGE, "0.1,4,0.1"), "set_buffer_length", "get_buffer_length"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "tap_back_pos", PROPERTY_HINT_RANGE, "0.1,4,0.1"), "set_tap_back_pos", "get_tap_back_pos"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fft_size", PROPERTY_HINT_ENUM, "256,512,1024,2048,4096"), "set_fft_size", "get_fft_size"); + + BIND_ENUM_CONSTANT(FFT_SIZE_256); + BIND_ENUM_CONSTANT(FFT_SIZE_512); + BIND_ENUM_CONSTANT(FFT_SIZE_1024); + BIND_ENUM_CONSTANT(FFT_SIZE_2048); + BIND_ENUM_CONSTANT(FFT_SIZE_4096); + BIND_ENUM_CONSTANT(FFT_SIZE_MAX); } AudioEffectSpectrumAnalyzer::AudioEffectSpectrumAnalyzer() { diff --git a/thirdparty/README.md b/thirdparty/README.md index 7f9fc28b3e..6997a9ba96 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -1,5 +1,6 @@ # Third party libraries + ## assimp - Upstream: http://github.com/assimp/assimp @@ -294,8 +295,12 @@ Godot build configurations, check them out when updating. File extracted from upstream release tarball `mbedtls-2.16.0-apache.tgz`: - All `*.h` from `include/mbedtls/` to `thirdparty/mbedtls/include/mbedtls/` - All `*.c` from `library/` to `thirdparty/mbedtls/library/` -- Applied the patch in `thirdparty/mbedtls/1453.diff` (PR 1453). Soon to be merged upstream. Check it out at next update. -- Applied the patch in `thirdparty/mbedtls/padlock.diff`. This disables VIA padlock support which defines a symbol `unsupported` which clashses with a symbol in libwebsockets. +- Applied the patch in `thirdparty/mbedtls/1453.diff` (PR 1453). + Soon to be merged upstream. Check it out at next update. +- Applied the patch in `thirdparty/mbedtls/padlock.diff`. This disables VIA + padlock support which defines a symbol `unsupported` which clashes with + a symbol in libwebsockets. + ## miniupnpc @@ -523,6 +528,23 @@ Files extracted from upstream source: - `tinyexr.{cc,h}` +## vhacd + +- Upstream: https://github.com/kmammou/v-hacd +- Version: git (2297aa1, 2018) +- License: BSD-3-Clause + +Files extracted from upstream source: + +- From `src/VHACD_Lib/`: `inc`, `public` and `src` +- `LICENSE` + +Some downstream changes have been made and are identified by +`// -- GODOT start --` and `// -- GODOT end --` comments. +They can be reapplied using the patches included in the `vhacd` +folder. + + ## zlib - Upstream: http://www.zlib.net diff --git a/thirdparty/vhacd/0001-bullet-namespace.patch b/thirdparty/vhacd/0001-bullet-namespace.patch new file mode 100644 index 0000000000..cfb1ffeff0 --- /dev/null +++ b/thirdparty/vhacd/0001-bullet-namespace.patch @@ -0,0 +1,213 @@ +diff --git a/thirdparty/vhacd/inc/btAlignedAllocator.h b/thirdparty/vhacd/inc/btAlignedAllocator.h +index 11f6e12dc..94e71d512 100644 +--- a/thirdparty/vhacd/inc/btAlignedAllocator.h ++++ b/thirdparty/vhacd/inc/btAlignedAllocator.h +@@ -21,6 +21,11 @@ subject to the following restrictions: + ///that is better portable and more predictable + + #include "btScalar.h" ++ ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + //#define BT_DEBUG_MEMORY_ALLOCATIONS 1 + #ifdef BT_DEBUG_MEMORY_ALLOCATIONS + +@@ -101,4 +106,8 @@ public: + friend bool operator==(const self_type&, const self_type&) { return true; } + }; + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_ALIGNED_ALLOCATOR +diff --git a/thirdparty/vhacd/inc/btAlignedObjectArray.h b/thirdparty/vhacd/inc/btAlignedObjectArray.h +index e6620adf6..1ce03d21b 100644 +--- a/thirdparty/vhacd/inc/btAlignedObjectArray.h ++++ b/thirdparty/vhacd/inc/btAlignedObjectArray.h +@@ -38,6 +38,10 @@ subject to the following restrictions: + #include <new> //for placement new + #endif //BT_USE_PLACEMENT_NEW + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + ///The btAlignedObjectArray template class uses a subset of the stl::vector interface for its methods + ///It is developed to replace stl::vector to avoid portability issues, including STL alignment issues to add SIMD/SSE data + template <typename T> +@@ -445,4 +449,8 @@ public: + } + }; + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_OBJECT_ARRAY__ +diff --git a/thirdparty/vhacd/inc/btConvexHullComputer.h b/thirdparty/vhacd/inc/btConvexHullComputer.h +index 3c5075c2c..04bb96f64 100644 +--- a/thirdparty/vhacd/inc/btConvexHullComputer.h ++++ b/thirdparty/vhacd/inc/btConvexHullComputer.h +@@ -18,6 +18,10 @@ subject to the following restrictions: + #include "btAlignedObjectArray.h" + #include "btVector3.h" + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + /// Convex hull implementation based on Preparata and Hong + /// See http://code.google.com/p/bullet/issues/detail?id=275 + /// Ole Kniemeyer, MAXON Computer GmbH +@@ -94,4 +98,8 @@ public: + } + }; + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_CONVEX_HULL_COMPUTER_H +diff --git a/thirdparty/vhacd/inc/btMinMax.h b/thirdparty/vhacd/inc/btMinMax.h +index 40b0ceb6e..9bc1e1c77 100644 +--- a/thirdparty/vhacd/inc/btMinMax.h ++++ b/thirdparty/vhacd/inc/btMinMax.h +@@ -17,6 +17,10 @@ subject to the following restrictions: + + #include "btScalar.h" + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + template <class T> + SIMD_FORCE_INLINE const T& btMin(const T& a, const T& b) + { +@@ -62,4 +66,8 @@ SIMD_FORCE_INLINE void btClamp(T& a, const T& lb, const T& ub) + } + } + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_GEN_MINMAX_H +diff --git a/thirdparty/vhacd/inc/btScalar.h b/thirdparty/vhacd/inc/btScalar.h +index b814474bd..617fd7c44 100644 +--- a/thirdparty/vhacd/inc/btScalar.h ++++ b/thirdparty/vhacd/inc/btScalar.h +@@ -28,11 +28,19 @@ subject to the following restrictions: + /* SVN $Revision$ on $Date$ from http://bullet.googlecode.com*/ + #define BT_BULLET_VERSION 279 + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + inline int32_t btGetVersion() + { + return BT_BULLET_VERSION; + } + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #if defined(DEBUG) || defined(_DEBUG) + #define BT_DEBUG + #endif +@@ -199,6 +207,10 @@ inline int32_t btGetVersion() + #endif //__CELLOS_LV2__ + #endif + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + ///The btScalar type abstracts floating point numbers, to easily switch between double and single floating point precision. + #if defined(BT_USE_DOUBLE_PRECISION) + typedef double btScalar; +@@ -530,4 +542,9 @@ struct btTypedObject { + return m_objectType; + } + }; ++ ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_SCALAR_H +diff --git a/thirdparty/vhacd/inc/btVector3.h b/thirdparty/vhacd/inc/btVector3.h +index 0f2fefbbd..4ed971673 100644 +--- a/thirdparty/vhacd/inc/btVector3.h ++++ b/thirdparty/vhacd/inc/btVector3.h +@@ -26,6 +26,10 @@ subject to the following restrictions: + #define btVector3DataName "btVector3FloatData" + #endif //BT_USE_DOUBLE_PRECISION + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + /**@brief btVector3 can be used to represent 3D points and vectors. + * It has an un-used w component to suit 16-byte alignment when btVector3 is stored in containers. This extra component can be used by derived classes (Quaternion?) or by user + * Ideally, this class should be replaced by a platform optimized SIMD version that keeps the data in registers +@@ -712,4 +716,8 @@ SIMD_FORCE_INLINE void btVector3::deSerialize(const struct btVector3Data& dataIn + m_floats[i] = dataIn.m_floats[i]; + } + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_VECTOR3_H +diff --git a/thirdparty/vhacd/src/btAlignedAllocator.cpp b/thirdparty/vhacd/src/btAlignedAllocator.cpp +index 11d594f6c..ce0e7f26f 100644 +--- a/thirdparty/vhacd/src/btAlignedAllocator.cpp ++++ b/thirdparty/vhacd/src/btAlignedAllocator.cpp +@@ -15,6 +15,10 @@ subject to the following restrictions: + + #include "btAlignedAllocator.h" + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + #ifdef _MSC_VER + #pragma warning(disable:4311 4302) + #endif +@@ -177,4 +181,8 @@ void btAlignedFreeInternal(void* ptr) + sAlignedFreeFunc(ptr); + } + ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- ++ + #endif //BT_DEBUG_MEMORY_ALLOCATIONS +diff --git a/thirdparty/vhacd/src/btConvexHullComputer.cpp b/thirdparty/vhacd/src/btConvexHullComputer.cpp +index d3d749adb..8ab34af2a 100644 +--- a/thirdparty/vhacd/src/btConvexHullComputer.cpp ++++ b/thirdparty/vhacd/src/btConvexHullComputer.cpp +@@ -49,6 +49,10 @@ typedef unsigned long long int32_t uint64_t; + #include <stdio.h> + #endif + ++// -- GODOT start -- ++namespace VHACD { ++// -- GODOT end -- ++ + // Convex hull implementation based on Preparata and Hong + // Ole Kniemeyer, MAXON Computer GmbH + class btConvexHullInternal { +@@ -2477,3 +2481,7 @@ btScalar btConvexHullComputer::compute(const void* coords, bool doubleCoords, in + + return shift; + } ++ ++// -- GODOT start -- ++}; // namespace VHACD ++// -- GODOT end -- diff --git a/thirdparty/vhacd/0002-fpermissive-fix.patch b/thirdparty/vhacd/0002-fpermissive-fix.patch new file mode 100644 index 0000000000..965df2944a --- /dev/null +++ b/thirdparty/vhacd/0002-fpermissive-fix.patch @@ -0,0 +1,53 @@ +diff --git a/thirdparty/vhacd/inc/btScalar.h b/thirdparty/vhacd/inc/btScalar.h +index 487205062..52297cd78 100644 +--- a/thirdparty/vhacd/inc/btScalar.h ++++ b/thirdparty/vhacd/inc/btScalar.h +@@ -535,6 +535,29 @@ struct btTypedObject { + } + }; + ++// -- GODOT start -- ++// Cherry-picked from Bullet 2.88 to fix GH-27926 ++///align a pointer to the provided alignment, upwards ++template <typename T> ++T *btAlignPointer(T *unalignedPtr, size_t alignment) ++{ ++ struct btConvertPointerSizeT ++ { ++ union { ++ T *ptr; ++ size_t integer; ++ }; ++ }; ++ btConvertPointerSizeT converter; ++ ++ const size_t bit_mask = ~(alignment - 1); ++ converter.ptr = unalignedPtr; ++ converter.integer += alignment - 1; ++ converter.integer &= bit_mask; ++ return converter.ptr; ++} ++// -- GODOT end -- ++ + // -- GODOT start -- + }; // namespace VHACD + // -- GODOT end -- +diff --git a/thirdparty/vhacd/src/btAlignedAllocator.cpp b/thirdparty/vhacd/src/btAlignedAllocator.cpp +index ce0e7f26f..8dee31e7e 100644 +--- a/thirdparty/vhacd/src/btAlignedAllocator.cpp ++++ b/thirdparty/vhacd/src/btAlignedAllocator.cpp +@@ -72,8 +72,12 @@ static inline void* btAlignedAllocDefault(size_t size, int32_t alignment) + + real = (char*)sAllocFunc(size + sizeof(void*) + (alignment - 1)); + if (real) { +- offset = (alignment - (unsigned long)(real + sizeof(void*))) & (alignment - 1); +- ret = (void*)((real + sizeof(void*)) + offset); ++ // -- GODOT start -- ++ // Synced with Bullet 2.88 to fix GH-27926 ++ //offset = (alignment - (unsigned long)(real + sizeof(void*))) & (alignment - 1); ++ //ret = (void*)((real + sizeof(void*)) + offset); ++ ret = btAlignPointer(real + sizeof(void *), alignment); ++ // -- GODOT end -- + *((void**)(ret)-1) = (void*)(real); + } + else { diff --git a/thirdparty/vhacd/LICENSE b/thirdparty/vhacd/LICENSE new file mode 100644 index 0000000000..efc7f64399 --- /dev/null +++ b/thirdparty/vhacd/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2011, Khaled Mamou (kmamou at gmail dot com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/thirdparty/vhacd/inc/btAlignedAllocator.h b/thirdparty/vhacd/inc/btAlignedAllocator.h index 4f534e43f0..94e71d5125 100644 --- a/thirdparty/vhacd/inc/btAlignedAllocator.h +++ b/thirdparty/vhacd/inc/btAlignedAllocator.h @@ -22,26 +22,26 @@ subject to the following restrictions: #include "btScalar.h" -//GODOT ADDITION +// -- GODOT start -- namespace VHACD { -// +// -- GODOT end -- //#define BT_DEBUG_MEMORY_ALLOCATIONS 1 #ifdef BT_DEBUG_MEMORY_ALLOCATIONS #define btAlignedAlloc(a, b) \ - btAlignedAllocInternal(a, b, __LINE__, __FILE__) + btAlignedAllocInternal(a, b, __LINE__, __FILE__) #define btAlignedFree(ptr) \ - btAlignedFreeInternal(ptr, __LINE__, __FILE__) + btAlignedFreeInternal(ptr, __LINE__, __FILE__) -void *btAlignedAllocInternal(size_t size, int32_t alignment, int32_t line, char *filename); +void* btAlignedAllocInternal(size_t size, int32_t alignment, int32_t line, char* filename); -void btAlignedFreeInternal(void *ptr, int32_t line, char *filename); +void btAlignedFreeInternal(void* ptr, int32_t line, char* filename); #else -void *btAlignedAllocInternal(size_t size, int32_t alignment); -void btAlignedFreeInternal(void *ptr); +void* btAlignedAllocInternal(size_t size, int32_t alignment); +void btAlignedFreeInternal(void* ptr); #define btAlignedAlloc(size, alignment) btAlignedAllocInternal(size, alignment) #define btAlignedFree(ptr) btAlignedFreeInternal(ptr) @@ -49,63 +49,65 @@ void btAlignedFreeInternal(void *ptr); #endif typedef int32_t size_type; -typedef void *(btAlignedAllocFunc)(size_t size, int32_t alignment); -typedef void(btAlignedFreeFunc)(void *memblock); -typedef void *(btAllocFunc)(size_t size); -typedef void(btFreeFunc)(void *memblock); +typedef void*(btAlignedAllocFunc)(size_t size, int32_t alignment); +typedef void(btAlignedFreeFunc)(void* memblock); +typedef void*(btAllocFunc)(size_t size); +typedef void(btFreeFunc)(void* memblock); ///The developer can let all Bullet memory allocations go through a custom memory allocator, using btAlignedAllocSetCustom -void btAlignedAllocSetCustom(btAllocFunc *allocFunc, btFreeFunc *freeFunc); +void btAlignedAllocSetCustom(btAllocFunc* allocFunc, btFreeFunc* freeFunc); ///If the developer has already an custom aligned allocator, then btAlignedAllocSetCustomAligned can be used. The default aligned allocator pre-allocates extra memory using the non-aligned allocator, and instruments it. -void btAlignedAllocSetCustomAligned(btAlignedAllocFunc *allocFunc, btAlignedFreeFunc *freeFunc); +void btAlignedAllocSetCustomAligned(btAlignedAllocFunc* allocFunc, btAlignedFreeFunc* freeFunc); ///The btAlignedAllocator is a portable class for aligned memory allocations. ///Default implementations for unaligned and aligned allocations can be overridden by a custom allocator using btAlignedAllocSetCustom and btAlignedAllocSetCustomAligned. template <typename T, unsigned Alignment> class btAlignedAllocator { - typedef btAlignedAllocator<T, Alignment> self_type; + typedef btAlignedAllocator<T, Alignment> self_type; public: - //just going down a list: - btAlignedAllocator() {} - /* + //just going down a list: + btAlignedAllocator() {} + /* btAlignedAllocator( const self_type & ) {} */ - template <typename Other> - btAlignedAllocator(const btAlignedAllocator<Other, Alignment> &) {} - - typedef const T *const_pointer; - typedef const T &const_reference; - typedef T *pointer; - typedef T &reference; - typedef T value_type; - - pointer address(reference ref) const { return &ref; } - const_pointer address(const_reference ref) const { return &ref; } - pointer allocate(size_type n, const_pointer *hint = 0) { - (void)hint; - return reinterpret_cast<pointer>(btAlignedAlloc(sizeof(value_type) * n, Alignment)); - } - void construct(pointer ptr, const value_type &value) { new (ptr) value_type(value); } - void deallocate(pointer ptr) { - btAlignedFree(reinterpret_cast<void *>(ptr)); - } - void destroy(pointer ptr) { ptr->~value_type(); } - - template <typename O> - struct rebind { - typedef btAlignedAllocator<O, Alignment> other; - }; - template <typename O> - self_type &operator=(const btAlignedAllocator<O, Alignment> &) { return *this; } - - friend bool operator==(const self_type &, const self_type &) { return true; } + template <typename Other> + btAlignedAllocator(const btAlignedAllocator<Other, Alignment>&) {} + + typedef const T* const_pointer; + typedef const T& const_reference; + typedef T* pointer; + typedef T& reference; + typedef T value_type; + + pointer address(reference ref) const { return &ref; } + const_pointer address(const_reference ref) const { return &ref; } + pointer allocate(size_type n, const_pointer* hint = 0) + { + (void)hint; + return reinterpret_cast<pointer>(btAlignedAlloc(sizeof(value_type) * n, Alignment)); + } + void construct(pointer ptr, const value_type& value) { new (ptr) value_type(value); } + void deallocate(pointer ptr) + { + btAlignedFree(reinterpret_cast<void*>(ptr)); + } + void destroy(pointer ptr) { ptr->~value_type(); } + + template <typename O> + struct rebind { + typedef btAlignedAllocator<O, Alignment> other; + }; + template <typename O> + self_type& operator=(const btAlignedAllocator<O, Alignment>&) { return *this; } + + friend bool operator==(const self_type&, const self_type&) { return true; } }; -//GODOT ADDITION +// -- GODOT start -- }; // namespace VHACD -// +// -- GODOT end -- #endif //BT_ALIGNED_ALLOCATOR diff --git a/thirdparty/vhacd/inc/btAlignedObjectArray.h b/thirdparty/vhacd/inc/btAlignedObjectArray.h index d82449e8fd..1ce03d21bc 100644 --- a/thirdparty/vhacd/inc/btAlignedObjectArray.h +++ b/thirdparty/vhacd/inc/btAlignedObjectArray.h @@ -38,383 +38,419 @@ subject to the following restrictions: #include <new> //for placement new #endif //BT_USE_PLACEMENT_NEW -//GODOT ADDITION +// -- GODOT start -- namespace VHACD { -// +// -- GODOT end -- ///The btAlignedObjectArray template class uses a subset of the stl::vector interface for its methods ///It is developed to replace stl::vector to avoid portability issues, including STL alignment issues to add SIMD/SSE data template <typename T> //template <class T> class btAlignedObjectArray { - btAlignedAllocator<T, 16> m_allocator; + btAlignedAllocator<T, 16> m_allocator; - int32_t m_size; - int32_t m_capacity; - T *m_data; - //PCK: added this line - bool m_ownsMemory; + int32_t m_size; + int32_t m_capacity; + T* m_data; + //PCK: added this line + bool m_ownsMemory; #ifdef BT_ALLOW_ARRAY_COPY_OPERATOR public: - SIMD_FORCE_INLINE btAlignedObjectArray<T> &operator=(const btAlignedObjectArray<T> &other) { - copyFromArray(other); - return *this; - } + SIMD_FORCE_INLINE btAlignedObjectArray<T>& operator=(const btAlignedObjectArray<T>& other) + { + copyFromArray(other); + return *this; + } #else //BT_ALLOW_ARRAY_COPY_OPERATOR private: - SIMD_FORCE_INLINE btAlignedObjectArray<T> &operator=(const btAlignedObjectArray<T> &other); + SIMD_FORCE_INLINE btAlignedObjectArray<T>& operator=(const btAlignedObjectArray<T>& other); #endif //BT_ALLOW_ARRAY_COPY_OPERATOR protected: - SIMD_FORCE_INLINE int32_t allocSize(int32_t size) { - return (size ? size * 2 : 1); - } - SIMD_FORCE_INLINE void copy(int32_t start, int32_t end, T *dest) const { - int32_t i; - for (i = start; i < end; ++i) + SIMD_FORCE_INLINE int32_t allocSize(int32_t size) + { + return (size ? size * 2 : 1); + } + SIMD_FORCE_INLINE void copy(int32_t start, int32_t end, T* dest) const + { + int32_t i; + for (i = start; i < end; ++i) #ifdef BT_USE_PLACEMENT_NEW - new (&dest[i]) T(m_data[i]); + new (&dest[i]) T(m_data[i]); #else - dest[i] = m_data[i]; + dest[i] = m_data[i]; #endif //BT_USE_PLACEMENT_NEW - } - - SIMD_FORCE_INLINE void init() { - //PCK: added this line - m_ownsMemory = true; - m_data = 0; - m_size = 0; - m_capacity = 0; - } - SIMD_FORCE_INLINE void destroy(int32_t first, int32_t last) { - int32_t i; - for (i = first; i < last; i++) { - m_data[i].~T(); - } - } - - SIMD_FORCE_INLINE void *allocate(int32_t size) { - if (size) - return m_allocator.allocate(size); - return 0; - } - - SIMD_FORCE_INLINE void deallocate() { - if (m_data) { - //PCK: enclosed the deallocation in this block - if (m_ownsMemory) { - m_allocator.deallocate(m_data); - } - m_data = 0; - } - } + } + + SIMD_FORCE_INLINE void init() + { + //PCK: added this line + m_ownsMemory = true; + m_data = 0; + m_size = 0; + m_capacity = 0; + } + SIMD_FORCE_INLINE void destroy(int32_t first, int32_t last) + { + int32_t i; + for (i = first; i < last; i++) { + m_data[i].~T(); + } + } + + SIMD_FORCE_INLINE void* allocate(int32_t size) + { + if (size) + return m_allocator.allocate(size); + return 0; + } + + SIMD_FORCE_INLINE void deallocate() + { + if (m_data) { + //PCK: enclosed the deallocation in this block + if (m_ownsMemory) { + m_allocator.deallocate(m_data); + } + m_data = 0; + } + } public: - btAlignedObjectArray() { - init(); - } - - ~btAlignedObjectArray() { - clear(); - } - - ///Generally it is best to avoid using the copy constructor of an btAlignedObjectArray, and use a (const) reference to the array instead. - btAlignedObjectArray(const btAlignedObjectArray &otherArray) { - init(); - - int32_t otherSize = otherArray.size(); - resize(otherSize); - otherArray.copy(0, otherSize, m_data); - } - - /// return the number of elements in the array - SIMD_FORCE_INLINE int32_t size() const { - return m_size; - } - - SIMD_FORCE_INLINE const T &at(int32_t n) const { - btAssert(n >= 0); - btAssert(n < size()); - return m_data[n]; - } - - SIMD_FORCE_INLINE T &at(int32_t n) { - btAssert(n >= 0); - btAssert(n < size()); - return m_data[n]; - } - - SIMD_FORCE_INLINE const T &operator[](int32_t n) const { - btAssert(n >= 0); - btAssert(n < size()); - return m_data[n]; - } - - SIMD_FORCE_INLINE T &operator[](int32_t n) { - btAssert(n >= 0); - btAssert(n < size()); - return m_data[n]; - } - - ///clear the array, deallocated memory. Generally it is better to use array.resize(0), to reduce performance overhead of run-time memory (de)allocations. - SIMD_FORCE_INLINE void clear() { - destroy(0, size()); - - deallocate(); - - init(); - } - - SIMD_FORCE_INLINE void pop_back() { - btAssert(m_size > 0); - m_size--; - m_data[m_size].~T(); - } - - ///resize changes the number of elements in the array. If the new size is larger, the new elements will be constructed using the optional second argument. - ///when the new number of elements is smaller, the destructor will be called, but memory will not be freed, to reduce performance overhead of run-time memory (de)allocations. - SIMD_FORCE_INLINE void resize(int32_t newsize, const T &fillData = T()) { - int32_t curSize = size(); - - if (newsize < curSize) { - for (int32_t i = newsize; i < curSize; i++) { - m_data[i].~T(); - } - } else { - if (newsize > size()) { - reserve(newsize); - } + btAlignedObjectArray() + { + init(); + } + + ~btAlignedObjectArray() + { + clear(); + } + + ///Generally it is best to avoid using the copy constructor of an btAlignedObjectArray, and use a (const) reference to the array instead. + btAlignedObjectArray(const btAlignedObjectArray& otherArray) + { + init(); + + int32_t otherSize = otherArray.size(); + resize(otherSize); + otherArray.copy(0, otherSize, m_data); + } + + /// return the number of elements in the array + SIMD_FORCE_INLINE int32_t size() const + { + return m_size; + } + + SIMD_FORCE_INLINE const T& at(int32_t n) const + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + SIMD_FORCE_INLINE T& at(int32_t n) + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + SIMD_FORCE_INLINE const T& operator[](int32_t n) const + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + SIMD_FORCE_INLINE T& operator[](int32_t n) + { + btAssert(n >= 0); + btAssert(n < size()); + return m_data[n]; + } + + ///clear the array, deallocated memory. Generally it is better to use array.resize(0), to reduce performance overhead of run-time memory (de)allocations. + SIMD_FORCE_INLINE void clear() + { + destroy(0, size()); + + deallocate(); + + init(); + } + + SIMD_FORCE_INLINE void pop_back() + { + btAssert(m_size > 0); + m_size--; + m_data[m_size].~T(); + } + + ///resize changes the number of elements in the array. If the new size is larger, the new elements will be constructed using the optional second argument. + ///when the new number of elements is smaller, the destructor will be called, but memory will not be freed, to reduce performance overhead of run-time memory (de)allocations. + SIMD_FORCE_INLINE void resize(int32_t newsize, const T& fillData = T()) + { + int32_t curSize = size(); + + if (newsize < curSize) { + for (int32_t i = newsize; i < curSize; i++) { + m_data[i].~T(); + } + } + else { + if (newsize > size()) { + reserve(newsize); + } #ifdef BT_USE_PLACEMENT_NEW - for (int32_t i = curSize; i < newsize; i++) { - new (&m_data[i]) T(fillData); - } + for (int32_t i = curSize; i < newsize; i++) { + new (&m_data[i]) T(fillData); + } #endif //BT_USE_PLACEMENT_NEW - } - - m_size = newsize; - } - - SIMD_FORCE_INLINE T &expandNonInitializing() { - int32_t sz = size(); - if (sz == capacity()) { - reserve(allocSize(size())); - } - m_size++; - - return m_data[sz]; - } - - SIMD_FORCE_INLINE T &expand(const T &fillValue = T()) { - int32_t sz = size(); - if (sz == capacity()) { - reserve(allocSize(size())); - } - m_size++; + } + + m_size = newsize; + } + + SIMD_FORCE_INLINE T& expandNonInitializing() + { + int32_t sz = size(); + if (sz == capacity()) { + reserve(allocSize(size())); + } + m_size++; + + return m_data[sz]; + } + + SIMD_FORCE_INLINE T& expand(const T& fillValue = T()) + { + int32_t sz = size(); + if (sz == capacity()) { + reserve(allocSize(size())); + } + m_size++; #ifdef BT_USE_PLACEMENT_NEW - new (&m_data[sz]) T(fillValue); //use the in-place new (not really allocating heap memory) + new (&m_data[sz]) T(fillValue); //use the in-place new (not really allocating heap memory) #endif - return m_data[sz]; - } + return m_data[sz]; + } - SIMD_FORCE_INLINE void push_back(const T &_Val) { - int32_t sz = size(); - if (sz == capacity()) { - reserve(allocSize(size())); - } + SIMD_FORCE_INLINE void push_back(const T& _Val) + { + int32_t sz = size(); + if (sz == capacity()) { + reserve(allocSize(size())); + } #ifdef BT_USE_PLACEMENT_NEW - new (&m_data[m_size]) T(_Val); + new (&m_data[m_size]) T(_Val); #else - m_data[size()] = _Val; + m_data[size()] = _Val; #endif //BT_USE_PLACEMENT_NEW - m_size++; - } - - /// return the pre-allocated (reserved) elements, this is at least as large as the total number of elements,see size() and reserve() - SIMD_FORCE_INLINE int32_t capacity() const { - return m_capacity; - } - - SIMD_FORCE_INLINE void reserve(int32_t _Count) { // determine new minimum length of allocated storage - if (capacity() < _Count) { // not enough room, reallocate - T *s = (T *)allocate(_Count); - - copy(0, size(), s); - - destroy(0, size()); - - deallocate(); - - //PCK: added this line - m_ownsMemory = true; - - m_data = s; - - m_capacity = _Count; - } - } - - class less { - public: - bool operator()(const T &a, const T &b) { - return (a < b); - } - }; - - template <typename L> - void quickSortInternal(const L &CompareFunc, int32_t lo, int32_t hi) { - // lo is the lower index, hi is the upper index - // of the region of array a that is to be sorted - int32_t i = lo, j = hi; - T x = m_data[(lo + hi) / 2]; - - // partition - do { - while (CompareFunc(m_data[i], x)) - i++; - while (CompareFunc(x, m_data[j])) - j--; - if (i <= j) { - swap(i, j); - i++; - j--; - } - } while (i <= j); - - // recursion - if (lo < j) - quickSortInternal(CompareFunc, lo, j); - if (i < hi) - quickSortInternal(CompareFunc, i, hi); - } - - template <typename L> - void quickSort(const L &CompareFunc) { - //don't sort 0 or 1 elements - if (size() > 1) { - quickSortInternal(CompareFunc, 0, size() - 1); - } - } - - ///heap sort from http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Sort/Heap/ - template <typename L> - void downHeap(T *pArr, int32_t k, int32_t n, const L &CompareFunc) { - /* PRE: a[k+1..N] is a heap */ - /* POST: a[k..N] is a heap */ - - T temp = pArr[k - 1]; - /* k has child(s) */ - while (k <= n / 2) { - int32_t child = 2 * k; - - if ((child < n) && CompareFunc(pArr[child - 1], pArr[child])) { - child++; - } - /* pick larger child */ - if (CompareFunc(temp, pArr[child - 1])) { - /* move child up */ - pArr[k - 1] = pArr[child - 1]; - k = child; - } else { - break; - } - } - pArr[k - 1] = temp; - } /*downHeap*/ - - void swap(int32_t index0, int32_t index1) { + m_size++; + } + + /// return the pre-allocated (reserved) elements, this is at least as large as the total number of elements,see size() and reserve() + SIMD_FORCE_INLINE int32_t capacity() const + { + return m_capacity; + } + + SIMD_FORCE_INLINE void reserve(int32_t _Count) + { // determine new minimum length of allocated storage + if (capacity() < _Count) { // not enough room, reallocate + T* s = (T*)allocate(_Count); + + copy(0, size(), s); + + destroy(0, size()); + + deallocate(); + + //PCK: added this line + m_ownsMemory = true; + + m_data = s; + + m_capacity = _Count; + } + } + + class less { + public: + bool operator()(const T& a, const T& b) + { + return (a < b); + } + }; + + template <typename L> + void quickSortInternal(const L& CompareFunc, int32_t lo, int32_t hi) + { + // lo is the lower index, hi is the upper index + // of the region of array a that is to be sorted + int32_t i = lo, j = hi; + T x = m_data[(lo + hi) / 2]; + + // partition + do { + while (CompareFunc(m_data[i], x)) + i++; + while (CompareFunc(x, m_data[j])) + j--; + if (i <= j) { + swap(i, j); + i++; + j--; + } + } while (i <= j); + + // recursion + if (lo < j) + quickSortInternal(CompareFunc, lo, j); + if (i < hi) + quickSortInternal(CompareFunc, i, hi); + } + + template <typename L> + void quickSort(const L& CompareFunc) + { + //don't sort 0 or 1 elements + if (size() > 1) { + quickSortInternal(CompareFunc, 0, size() - 1); + } + } + + ///heap sort from http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Sort/Heap/ + template <typename L> + void downHeap(T* pArr, int32_t k, int32_t n, const L& CompareFunc) + { + /* PRE: a[k+1..N] is a heap */ + /* POST: a[k..N] is a heap */ + + T temp = pArr[k - 1]; + /* k has child(s) */ + while (k <= n / 2) { + int32_t child = 2 * k; + + if ((child < n) && CompareFunc(pArr[child - 1], pArr[child])) { + child++; + } + /* pick larger child */ + if (CompareFunc(temp, pArr[child - 1])) { + /* move child up */ + pArr[k - 1] = pArr[child - 1]; + k = child; + } + else { + break; + } + } + pArr[k - 1] = temp; + } /*downHeap*/ + + void swap(int32_t index0, int32_t index1) + { #ifdef BT_USE_MEMCPY - char temp[sizeof(T)]; - memcpy(temp, &m_data[index0], sizeof(T)); - memcpy(&m_data[index0], &m_data[index1], sizeof(T)); - memcpy(&m_data[index1], temp, sizeof(T)); + char temp[sizeof(T)]; + memcpy(temp, &m_data[index0], sizeof(T)); + memcpy(&m_data[index0], &m_data[index1], sizeof(T)); + memcpy(&m_data[index1], temp, sizeof(T)); #else - T temp = m_data[index0]; - m_data[index0] = m_data[index1]; - m_data[index1] = temp; + T temp = m_data[index0]; + m_data[index0] = m_data[index1]; + m_data[index1] = temp; #endif //BT_USE_PLACEMENT_NEW - } - - template <typename L> - void heapSort(const L &CompareFunc) { - /* sort a[0..N-1], N.B. 0 to N-1 */ - int32_t k; - int32_t n = m_size; - for (k = n / 2; k > 0; k--) { - downHeap(m_data, k, n, CompareFunc); - } - - /* a[1..N] is now a heap */ - while (n >= 1) { - swap(0, n - 1); /* largest of a[0..n-1] */ - - n = n - 1; - /* restore a[1..i-1] heap */ - downHeap(m_data, 1, n, CompareFunc); - } - } - - ///non-recursive binary search, assumes sorted array - int32_t findBinarySearch(const T &key) const { - int32_t first = 0; - int32_t last = size() - 1; - - //assume sorted array - while (first <= last) { - int32_t mid = (first + last) / 2; // compute mid point. - if (key > m_data[mid]) - first = mid + 1; // repeat search in top half. - else if (key < m_data[mid]) - last = mid - 1; // repeat search in bottom half. - else - return mid; // found it. return position ///// - } - return size(); // failed to find key - } - - int32_t findLinearSearch(const T &key) const { - int32_t index = size(); - int32_t i; - - for (i = 0; i < size(); i++) { - if (m_data[i] == key) { - index = i; - break; - } - } - return index; - } - - void remove(const T &key) { - - int32_t findIndex = findLinearSearch(key); - if (findIndex < size()) { - swap(findIndex, size() - 1); - pop_back(); - } - } - - //PCK: whole function - void initializeFromBuffer(void *buffer, int32_t size, int32_t capacity) { - clear(); - m_ownsMemory = false; - m_data = (T *)buffer; - m_size = size; - m_capacity = capacity; - } - - void copyFromArray(const btAlignedObjectArray &otherArray) { - int32_t otherSize = otherArray.size(); - resize(otherSize); - otherArray.copy(0, otherSize, m_data); - } + } + + template <typename L> + void heapSort(const L& CompareFunc) + { + /* sort a[0..N-1], N.B. 0 to N-1 */ + int32_t k; + int32_t n = m_size; + for (k = n / 2; k > 0; k--) { + downHeap(m_data, k, n, CompareFunc); + } + + /* a[1..N] is now a heap */ + while (n >= 1) { + swap(0, n - 1); /* largest of a[0..n-1] */ + + n = n - 1; + /* restore a[1..i-1] heap */ + downHeap(m_data, 1, n, CompareFunc); + } + } + + ///non-recursive binary search, assumes sorted array + int32_t findBinarySearch(const T& key) const + { + int32_t first = 0; + int32_t last = size() - 1; + + //assume sorted array + while (first <= last) { + int32_t mid = (first + last) / 2; // compute mid point. + if (key > m_data[mid]) + first = mid + 1; // repeat search in top half. + else if (key < m_data[mid]) + last = mid - 1; // repeat search in bottom half. + else + return mid; // found it. return position ///// + } + return size(); // failed to find key + } + + int32_t findLinearSearch(const T& key) const + { + int32_t index = size(); + int32_t i; + + for (i = 0; i < size(); i++) { + if (m_data[i] == key) { + index = i; + break; + } + } + return index; + } + + void remove(const T& key) + { + + int32_t findIndex = findLinearSearch(key); + if (findIndex < size()) { + swap(findIndex, size() - 1); + pop_back(); + } + } + + //PCK: whole function + void initializeFromBuffer(void* buffer, int32_t size, int32_t capacity) + { + clear(); + m_ownsMemory = false; + m_data = (T*)buffer; + m_size = size; + m_capacity = capacity; + } + + void copyFromArray(const btAlignedObjectArray& otherArray) + { + int32_t otherSize = otherArray.size(); + resize(otherSize); + otherArray.copy(0, otherSize, m_data); + } }; -//GODOT ADDITION +// -- GODOT start -- }; // namespace VHACD -// +// -- GODOT end -- #endif //BT_OBJECT_ARRAY__ diff --git a/thirdparty/vhacd/inc/btConvexHullComputer.h b/thirdparty/vhacd/inc/btConvexHullComputer.h index f10a3cf86f..04bb96f64a 100644 --- a/thirdparty/vhacd/inc/btConvexHullComputer.h +++ b/thirdparty/vhacd/inc/btConvexHullComputer.h @@ -18,60 +18,63 @@ subject to the following restrictions: #include "btAlignedObjectArray.h" #include "btVector3.h" -//GODOT ADDITION +// -- GODOT start -- namespace VHACD { -// +// -- GODOT end -- /// Convex hull implementation based on Preparata and Hong /// See http://code.google.com/p/bullet/issues/detail?id=275 /// Ole Kniemeyer, MAXON Computer GmbH class btConvexHullComputer { private: - btScalar compute(const void *coords, bool doubleCoords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp); + btScalar compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp); public: - class Edge { - private: - int32_t next; - int32_t reverse; - int32_t targetVertex; - - friend class btConvexHullComputer; - - public: - int32_t getSourceVertex() const { - return (this + reverse)->targetVertex; - } - - int32_t getTargetVertex() const { - return targetVertex; - } - - const Edge *getNextEdgeOfVertex() const // clockwise list of all edges of a vertex - { - return this + next; - } - - const Edge *getNextEdgeOfFace() const // counter-clockwise list of all edges of a face - { - return (this + reverse)->getNextEdgeOfVertex(); - } - - const Edge *getReverseEdge() const { - return this + reverse; - } - }; - - // Vertices of the output hull - btAlignedObjectArray<btVector3> vertices; - - // Edges of the output hull - btAlignedObjectArray<Edge> edges; - - // Faces of the convex hull. Each entry is an index into the "edges" array pointing to an edge of the face. Faces are planar n-gons - btAlignedObjectArray<int32_t> faces; - - /* + class Edge { + private: + int32_t next; + int32_t reverse; + int32_t targetVertex; + + friend class btConvexHullComputer; + + public: + int32_t getSourceVertex() const + { + return (this + reverse)->targetVertex; + } + + int32_t getTargetVertex() const + { + return targetVertex; + } + + const Edge* getNextEdgeOfVertex() const // clockwise list of all edges of a vertex + { + return this + next; + } + + const Edge* getNextEdgeOfFace() const // counter-clockwise list of all edges of a face + { + return (this + reverse)->getNextEdgeOfVertex(); + } + + const Edge* getReverseEdge() const + { + return this + reverse; + } + }; + + // Vertices of the output hull + btAlignedObjectArray<btVector3> vertices; + + // Edges of the output hull + btAlignedObjectArray<Edge> edges; + + // Faces of the convex hull. Each entry is an index into the "edges" array pointing to an edge of the face. Faces are planar n-gons + btAlignedObjectArray<int32_t> faces; + + /* Compute convex hull of "count" vertices stored in "coords". "stride" is the difference in bytes between the addresses of consecutive vertices. If "shrink" is positive, the convex hull is shrunken by that amount (each face is moved by "shrink" length units towards the center along its normal). @@ -83,18 +86,20 @@ public: The output convex hull can be found in the member variables "vertices", "edges", "faces". */ - btScalar compute(const float *coords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp) { - return compute(coords, false, stride, count, shrink, shrinkClamp); - } - - // same as above, but double precision - btScalar compute(const double *coords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp) { - return compute(coords, true, stride, count, shrink, shrinkClamp); - } + btScalar compute(const float* coords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp) + { + return compute(coords, false, stride, count, shrink, shrinkClamp); + } + + // same as above, but double precision + btScalar compute(const double* coords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp) + { + return compute(coords, true, stride, count, shrink, shrinkClamp); + } }; -//GODOT ADDITION +// -- GODOT start -- }; // namespace VHACD -// +// -- GODOT end -- #endif //BT_CONVEX_HULL_COMPUTER_H diff --git a/thirdparty/vhacd/inc/btMinMax.h b/thirdparty/vhacd/inc/btMinMax.h index a5be258667..9bc1e1c770 100644 --- a/thirdparty/vhacd/inc/btMinMax.h +++ b/thirdparty/vhacd/inc/btMinMax.h @@ -17,50 +17,57 @@ subject to the following restrictions: #include "btScalar.h" -//GODOT ADDITION +// -- GODOT start -- namespace VHACD { -// +// -- GODOT end -- template <class T> -SIMD_FORCE_INLINE const T &btMin(const T &a, const T &b) { - return a < b ? a : b; +SIMD_FORCE_INLINE const T& btMin(const T& a, const T& b) +{ + return a < b ? a : b; } template <class T> -SIMD_FORCE_INLINE const T &btMax(const T &a, const T &b) { - return a > b ? a : b; +SIMD_FORCE_INLINE const T& btMax(const T& a, const T& b) +{ + return a > b ? a : b; } template <class T> -SIMD_FORCE_INLINE const T &btClamped(const T &a, const T &lb, const T &ub) { - return a < lb ? lb : (ub < a ? ub : a); +SIMD_FORCE_INLINE const T& btClamped(const T& a, const T& lb, const T& ub) +{ + return a < lb ? lb : (ub < a ? ub : a); } template <class T> -SIMD_FORCE_INLINE void btSetMin(T &a, const T &b) { - if (b < a) { - a = b; - } +SIMD_FORCE_INLINE void btSetMin(T& a, const T& b) +{ + if (b < a) { + a = b; + } } template <class T> -SIMD_FORCE_INLINE void btSetMax(T &a, const T &b) { - if (a < b) { - a = b; - } +SIMD_FORCE_INLINE void btSetMax(T& a, const T& b) +{ + if (a < b) { + a = b; + } } template <class T> -SIMD_FORCE_INLINE void btClamp(T &a, const T &lb, const T &ub) { - if (a < lb) { - a = lb; - } else if (ub < a) { - a = ub; - } +SIMD_FORCE_INLINE void btClamp(T& a, const T& lb, const T& ub) +{ + if (a < lb) { + a = lb; + } + else if (ub < a) { + a = ub; + } } -//GODOT ADDITION +// -- GODOT start -- }; // namespace VHACD -// +// -- GODOT end -- #endif //BT_GEN_MINMAX_H diff --git a/thirdparty/vhacd/inc/btScalar.h b/thirdparty/vhacd/inc/btScalar.h index a9f836f9f2..3999a71521 100644 --- a/thirdparty/vhacd/inc/btScalar.h +++ b/thirdparty/vhacd/inc/btScalar.h @@ -22,23 +22,24 @@ subject to the following restrictions: #include <float.h> #include <math.h> -#include <stdint.h> #include <stdlib.h> //size_t for MSVC 6.0 +#include <stdint.h> /* SVN $Revision$ on $Date$ from http://bullet.googlecode.com*/ #define BT_BULLET_VERSION 279 -//GODOT ADDITION +// -- GODOT start -- namespace VHACD { -// +// -- GODOT end -- -inline int32_t btGetVersion() { - return BT_BULLET_VERSION; +inline int32_t btGetVersion() +{ + return BT_BULLET_VERSION; } -//GODOT ADDITION +// -- GODOT start -- }; // namespace VHACD -// +// -- GODOT end -- #if defined(DEBUG) || defined(_DEBUG) #define BT_DEBUG @@ -107,12 +108,12 @@ inline int32_t btGetVersion() { #include <spu_printf.h> #define printf spu_printf #define btAssert(x) \ - { \ - if (!(x)) { \ - printf("Assert " __FILE__ ":%u (" #x ")\n", __LINE__); \ - spu_hcmpeq(0, 0); \ - } \ - } + { \ + if (!(x)) { \ + printf("Assert " __FILE__ ":%u (" #x ")\n", __LINE__); \ + spu_hcmpeq(0, 0); \ + } \ + } #else #define btAssert assert #endif @@ -206,9 +207,9 @@ inline int32_t btGetVersion() { #endif //__CELLOS_LV2__ #endif -//GODOT ADDITION +// -- GODOT start -- namespace VHACD { -// +// -- GODOT end -- ///The btScalar type abstracts floating point numbers, to easily switch between double and single floating point precision. #if defined(BT_USE_DOUBLE_PRECISION) @@ -222,130 +223,96 @@ typedef float btScalar; #endif #define BT_DECLARE_ALIGNED_ALLOCATOR() \ - SIMD_FORCE_INLINE void *operator new(size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes, 16); } \ - SIMD_FORCE_INLINE void operator delete(void *ptr) { btAlignedFree(ptr); } \ - SIMD_FORCE_INLINE void *operator new(size_t, void *ptr) { return ptr; } \ - SIMD_FORCE_INLINE void operator delete(void *, void *) {} \ - SIMD_FORCE_INLINE void *operator new[](size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes, 16); } \ - SIMD_FORCE_INLINE void operator delete[](void *ptr) { btAlignedFree(ptr); } \ - SIMD_FORCE_INLINE void *operator new[](size_t, void *ptr) { return ptr; } \ - SIMD_FORCE_INLINE void operator delete[](void *, void *) {} + SIMD_FORCE_INLINE void* operator new(size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes, 16); } \ + SIMD_FORCE_INLINE void operator delete(void* ptr) { btAlignedFree(ptr); } \ + SIMD_FORCE_INLINE void* operator new(size_t, void* ptr) { return ptr; } \ + SIMD_FORCE_INLINE void operator delete(void*, void*) {} \ + SIMD_FORCE_INLINE void* operator new[](size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes, 16); } \ + SIMD_FORCE_INLINE void operator delete[](void* ptr) { btAlignedFree(ptr); } \ + SIMD_FORCE_INLINE void* operator new[](size_t, void* ptr) { return ptr; } \ + SIMD_FORCE_INLINE void operator delete[](void*, void*) {} #if defined(BT_USE_DOUBLE_PRECISION) || defined(BT_FORCE_DOUBLE_FUNCTIONS) -SIMD_FORCE_INLINE btScalar btSqrt(btScalar x) { - return sqrt(x); -} -SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { - return fabs(x); -} -SIMD_FORCE_INLINE btScalar btCos(btScalar x) { - return cos(x); -} -SIMD_FORCE_INLINE btScalar btSin(btScalar x) { - return sin(x); -} -SIMD_FORCE_INLINE btScalar btTan(btScalar x) { - return tan(x); -} -SIMD_FORCE_INLINE btScalar btAcos(btScalar x) { - if (x < btScalar(-1)) - x = btScalar(-1); - if (x > btScalar(1)) - x = btScalar(1); - return acos(x); -} -SIMD_FORCE_INLINE btScalar btAsin(btScalar x) { - if (x < btScalar(-1)) - x = btScalar(-1); - if (x > btScalar(1)) - x = btScalar(1); - return asin(x); -} -SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { - return atan(x); -} -SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { - return atan2(x, y); -} -SIMD_FORCE_INLINE btScalar btExp(btScalar x) { - return exp(x); -} -SIMD_FORCE_INLINE btScalar btLog(btScalar x) { - return log(x); -} -SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y) { - return pow(x, y); -} -SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { - return fmod(x, y); -} +SIMD_FORCE_INLINE btScalar btSqrt(btScalar x) +{ + return sqrt(x); +} +SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabs(x); } +SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cos(x); } +SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sin(x); } +SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tan(x); } +SIMD_FORCE_INLINE btScalar btAcos(btScalar x) +{ + if (x < btScalar(-1)) + x = btScalar(-1); + if (x > btScalar(1)) + x = btScalar(1); + return acos(x); +} +SIMD_FORCE_INLINE btScalar btAsin(btScalar x) +{ + if (x < btScalar(-1)) + x = btScalar(-1); + if (x > btScalar(1)) + x = btScalar(1); + return asin(x); +} +SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atan(x); } +SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2(x, y); } +SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return exp(x); } +SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return log(x); } +SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y) { return pow(x, y); } +SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { return fmod(x, y); } #else -SIMD_FORCE_INLINE btScalar btSqrt(btScalar y) { +SIMD_FORCE_INLINE btScalar btSqrt(btScalar y) +{ #ifdef USE_APPROXIMATION - double x, z, tempf; - unsigned long *tfptr = ((unsigned long *)&tempf) + 1; - - tempf = y; - *tfptr = (0xbfcdd90a - *tfptr) >> 1; /* estimate of 1/sqrt(y) */ - x = tempf; - z = y * btScalar(0.5); - x = (btScalar(1.5) * x) - (x * x) * (x * z); /* iteration formula */ - x = (btScalar(1.5) * x) - (x * x) * (x * z); - x = (btScalar(1.5) * x) - (x * x) * (x * z); - x = (btScalar(1.5) * x) - (x * x) * (x * z); - x = (btScalar(1.5) * x) - (x * x) * (x * z); - return x * y; + double x, z, tempf; + unsigned long* tfptr = ((unsigned long*)&tempf) + 1; + + tempf = y; + *tfptr = (0xbfcdd90a - *tfptr) >> 1; /* estimate of 1/sqrt(y) */ + x = tempf; + z = y * btScalar(0.5); + x = (btScalar(1.5) * x) - (x * x) * (x * z); /* iteration formula */ + x = (btScalar(1.5) * x) - (x * x) * (x * z); + x = (btScalar(1.5) * x) - (x * x) * (x * z); + x = (btScalar(1.5) * x) - (x * x) * (x * z); + x = (btScalar(1.5) * x) - (x * x) * (x * z); + return x * y; #else - return sqrtf(y); + return sqrtf(y); #endif } -SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { - return fabsf(x); -} -SIMD_FORCE_INLINE btScalar btCos(btScalar x) { - return cosf(x); -} -SIMD_FORCE_INLINE btScalar btSin(btScalar x) { - return sinf(x); -} -SIMD_FORCE_INLINE btScalar btTan(btScalar x) { - return tanf(x); -} -SIMD_FORCE_INLINE btScalar btAcos(btScalar x) { - if (x < btScalar(-1)) - x = btScalar(-1); - if (x > btScalar(1)) - x = btScalar(1); - return acosf(x); -} -SIMD_FORCE_INLINE btScalar btAsin(btScalar x) { - if (x < btScalar(-1)) - x = btScalar(-1); - if (x > btScalar(1)) - x = btScalar(1); - return asinf(x); -} -SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { - return atanf(x); -} -SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { - return atan2f(x, y); -} -SIMD_FORCE_INLINE btScalar btExp(btScalar x) { - return expf(x); -} -SIMD_FORCE_INLINE btScalar btLog(btScalar x) { - return logf(x); -} -SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y) { - return powf(x, y); -} -SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { - return fmodf(x, y); -} +SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabsf(x); } +SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cosf(x); } +SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sinf(x); } +SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tanf(x); } +SIMD_FORCE_INLINE btScalar btAcos(btScalar x) +{ + if (x < btScalar(-1)) + x = btScalar(-1); + if (x > btScalar(1)) + x = btScalar(1); + return acosf(x); +} +SIMD_FORCE_INLINE btScalar btAsin(btScalar x) +{ + if (x < btScalar(-1)) + x = btScalar(-1); + if (x > btScalar(1)) + x = btScalar(1); + return asinf(x); +} +SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atanf(x); } +SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2f(x, y); } +SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return expf(x); } +SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return logf(x); } +SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y) { return powf(x, y); } +SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { return fmodf(x, y); } #endif @@ -366,110 +333,119 @@ SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { #define SIMD_INFINITY FLT_MAX #endif -SIMD_FORCE_INLINE btScalar btAtan2Fast(btScalar y, btScalar x) { - btScalar coeff_1 = SIMD_PI / 4.0f; - btScalar coeff_2 = 3.0f * coeff_1; - btScalar abs_y = btFabs(y); - btScalar angle; - if (x >= 0.0f) { - btScalar r = (x - abs_y) / (x + abs_y); - angle = coeff_1 - coeff_1 * r; - } else { - btScalar r = (x + abs_y) / (abs_y - x); - angle = coeff_2 - coeff_1 * r; - } - return (y < 0.0f) ? -angle : angle; +SIMD_FORCE_INLINE btScalar btAtan2Fast(btScalar y, btScalar x) +{ + btScalar coeff_1 = SIMD_PI / 4.0f; + btScalar coeff_2 = 3.0f * coeff_1; + btScalar abs_y = btFabs(y); + btScalar angle; + if (x >= 0.0f) { + btScalar r = (x - abs_y) / (x + abs_y); + angle = coeff_1 - coeff_1 * r; + } + else { + btScalar r = (x + abs_y) / (abs_y - x); + angle = coeff_2 - coeff_1 * r; + } + return (y < 0.0f) ? -angle : angle; } -SIMD_FORCE_INLINE bool btFuzzyZero(btScalar x) { - return btFabs(x) < SIMD_EPSILON; -} +SIMD_FORCE_INLINE bool btFuzzyZero(btScalar x) { return btFabs(x) < SIMD_EPSILON; } -SIMD_FORCE_INLINE bool btEqual(btScalar a, btScalar eps) { - return (((a) <= eps) && !((a) < -eps)); +SIMD_FORCE_INLINE bool btEqual(btScalar a, btScalar eps) +{ + return (((a) <= eps) && !((a) < -eps)); } -SIMD_FORCE_INLINE bool btGreaterEqual(btScalar a, btScalar eps) { - return (!((a) <= eps)); +SIMD_FORCE_INLINE bool btGreaterEqual(btScalar a, btScalar eps) +{ + return (!((a) <= eps)); } -SIMD_FORCE_INLINE int32_t btIsNegative(btScalar x) { - return x < btScalar(0.0) ? 1 : 0; +SIMD_FORCE_INLINE int32_t btIsNegative(btScalar x) +{ + return x < btScalar(0.0) ? 1 : 0; } -SIMD_FORCE_INLINE btScalar btRadians(btScalar x) { - return x * SIMD_RADS_PER_DEG; -} -SIMD_FORCE_INLINE btScalar btDegrees(btScalar x) { - return x * SIMD_DEGS_PER_RAD; -} +SIMD_FORCE_INLINE btScalar btRadians(btScalar x) { return x * SIMD_RADS_PER_DEG; } +SIMD_FORCE_INLINE btScalar btDegrees(btScalar x) { return x * SIMD_DEGS_PER_RAD; } #define BT_DECLARE_HANDLE(name) \ - typedef struct name##__ { \ - int32_t unused; \ - } * name + typedef struct name##__ { \ + int32_t unused; \ + } * name #ifndef btFsel -SIMD_FORCE_INLINE btScalar btFsel(btScalar a, btScalar b, btScalar c) { - return a >= 0 ? b : c; +SIMD_FORCE_INLINE btScalar btFsel(btScalar a, btScalar b, btScalar c) +{ + return a >= 0 ? b : c; } #endif #define btFsels(a, b, c) (btScalar) btFsel(a, b, c) -SIMD_FORCE_INLINE bool btMachineIsLittleEndian() { - long int i = 1; - const char *p = (const char *)&i; - if (p[0] == 1) // Lowest address contains the least significant byte - return true; - else - return false; +SIMD_FORCE_INLINE bool btMachineIsLittleEndian() +{ + long int i = 1; + const char* p = (const char*)&i; + if (p[0] == 1) // Lowest address contains the least significant byte + return true; + else + return false; } ///btSelect avoids branches, which makes performance much better for consoles like Playstation 3 and XBox 360 ///Thanks Phil Knight. See also http://www.cellperformance.com/articles/2006/04/more_techniques_for_eliminatin_1.html -SIMD_FORCE_INLINE unsigned btSelect(unsigned condition, unsigned valueIfConditionNonZero, unsigned valueIfConditionZero) { - // Set testNz to 0xFFFFFFFF if condition is nonzero, 0x00000000 if condition is zero - // Rely on positive value or'ed with its negative having sign bit on - // and zero value or'ed with its negative (which is still zero) having sign bit off - // Use arithmetic shift right, shifting the sign bit through all 32 bits - unsigned testNz = (unsigned)(((int32_t)condition | -(int32_t)condition) >> 31); - unsigned testEqz = ~testNz; - return ((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); -} -SIMD_FORCE_INLINE int32_t btSelect(unsigned condition, int32_t valueIfConditionNonZero, int32_t valueIfConditionZero) { - unsigned testNz = (unsigned)(((int32_t)condition | -(int32_t)condition) >> 31); - unsigned testEqz = ~testNz; - return static_cast<int32_t>((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); -} -SIMD_FORCE_INLINE float btSelect(unsigned condition, float valueIfConditionNonZero, float valueIfConditionZero) { +SIMD_FORCE_INLINE unsigned btSelect(unsigned condition, unsigned valueIfConditionNonZero, unsigned valueIfConditionZero) +{ + // Set testNz to 0xFFFFFFFF if condition is nonzero, 0x00000000 if condition is zero + // Rely on positive value or'ed with its negative having sign bit on + // and zero value or'ed with its negative (which is still zero) having sign bit off + // Use arithmetic shift right, shifting the sign bit through all 32 bits + unsigned testNz = (unsigned)(((int32_t)condition | -(int32_t)condition) >> 31); + unsigned testEqz = ~testNz; + return ((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); +} +SIMD_FORCE_INLINE int32_t btSelect(unsigned condition, int32_t valueIfConditionNonZero, int32_t valueIfConditionZero) +{ + unsigned testNz = (unsigned)(((int32_t)condition | -(int32_t)condition) >> 31); + unsigned testEqz = ~testNz; + return static_cast<int32_t>((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); +} +SIMD_FORCE_INLINE float btSelect(unsigned condition, float valueIfConditionNonZero, float valueIfConditionZero) +{ #ifdef BT_HAVE_NATIVE_FSEL - return (float)btFsel((btScalar)condition - btScalar(1.0f), valueIfConditionNonZero, valueIfConditionZero); + return (float)btFsel((btScalar)condition - btScalar(1.0f), valueIfConditionNonZero, valueIfConditionZero); #else - return (condition != 0) ? valueIfConditionNonZero : valueIfConditionZero; + return (condition != 0) ? valueIfConditionNonZero : valueIfConditionZero; #endif } template <typename T> -SIMD_FORCE_INLINE void btSwap(T &a, T &b) { - T tmp = a; - a = b; - b = tmp; +SIMD_FORCE_INLINE void btSwap(T& a, T& b) +{ + T tmp = a; + a = b; + b = tmp; } //PCK: endian swapping functions -SIMD_FORCE_INLINE unsigned btSwapEndian(unsigned val) { - return (((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24)); +SIMD_FORCE_INLINE unsigned btSwapEndian(unsigned val) +{ + return (((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24)); } -SIMD_FORCE_INLINE unsigned short btSwapEndian(unsigned short val) { - return static_cast<unsigned short>(((val & 0xff00) >> 8) | ((val & 0x00ff) << 8)); +SIMD_FORCE_INLINE unsigned short btSwapEndian(unsigned short val) +{ + return static_cast<unsigned short>(((val & 0xff00) >> 8) | ((val & 0x00ff) << 8)); } -SIMD_FORCE_INLINE unsigned btSwapEndian(int32_t val) { - return btSwapEndian((unsigned)val); +SIMD_FORCE_INLINE unsigned btSwapEndian(int32_t val) +{ + return btSwapEndian((unsigned)val); } -SIMD_FORCE_INLINE unsigned short btSwapEndian(short val) { - return btSwapEndian((unsigned short)val); +SIMD_FORCE_INLINE unsigned short btSwapEndian(short val) +{ + return btSwapEndian((unsigned short)val); } ///btSwapFloat uses using char pointers to swap the endianness @@ -478,88 +454,120 @@ SIMD_FORCE_INLINE unsigned short btSwapEndian(short val) { ///When a floating point unit is faced with an invalid value, it may actually change the value, or worse, throw an exception. ///In most systems, running user mode code, you wouldn't get an exception, but instead the hardware/os/runtime will 'fix' the number for you. ///so instead of returning a float/double, we return integer/long long integer -SIMD_FORCE_INLINE uint32_t btSwapEndianFloat(float d) { - uint32_t a = 0; - unsigned char *dst = (unsigned char *)&a; - unsigned char *src = (unsigned char *)&d; - - dst[0] = src[3]; - dst[1] = src[2]; - dst[2] = src[1]; - dst[3] = src[0]; - return a; +SIMD_FORCE_INLINE uint32_t btSwapEndianFloat(float d) +{ + uint32_t a = 0; + unsigned char* dst = (unsigned char*)&a; + unsigned char* src = (unsigned char*)&d; + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; + return a; } // unswap using char pointers -SIMD_FORCE_INLINE float btUnswapEndianFloat(uint32_t a) { - float d = 0.0f; - unsigned char *src = (unsigned char *)&a; - unsigned char *dst = (unsigned char *)&d; +SIMD_FORCE_INLINE float btUnswapEndianFloat(uint32_t a) +{ + float d = 0.0f; + unsigned char* src = (unsigned char*)&a; + unsigned char* dst = (unsigned char*)&d; - dst[0] = src[3]; - dst[1] = src[2]; - dst[2] = src[1]; - dst[3] = src[0]; + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; - return d; + return d; } // swap using char pointers -SIMD_FORCE_INLINE void btSwapEndianDouble(double d, unsigned char *dst) { - unsigned char *src = (unsigned char *)&d; - - dst[0] = src[7]; - dst[1] = src[6]; - dst[2] = src[5]; - dst[3] = src[4]; - dst[4] = src[3]; - dst[5] = src[2]; - dst[6] = src[1]; - dst[7] = src[0]; +SIMD_FORCE_INLINE void btSwapEndianDouble(double d, unsigned char* dst) +{ + unsigned char* src = (unsigned char*)&d; + + dst[0] = src[7]; + dst[1] = src[6]; + dst[2] = src[5]; + dst[3] = src[4]; + dst[4] = src[3]; + dst[5] = src[2]; + dst[6] = src[1]; + dst[7] = src[0]; } // unswap using char pointers -SIMD_FORCE_INLINE double btUnswapEndianDouble(const unsigned char *src) { - double d = 0.0; - unsigned char *dst = (unsigned char *)&d; - - dst[0] = src[7]; - dst[1] = src[6]; - dst[2] = src[5]; - dst[3] = src[4]; - dst[4] = src[3]; - dst[5] = src[2]; - dst[6] = src[1]; - dst[7] = src[0]; - - return d; +SIMD_FORCE_INLINE double btUnswapEndianDouble(const unsigned char* src) +{ + double d = 0.0; + unsigned char* dst = (unsigned char*)&d; + + dst[0] = src[7]; + dst[1] = src[6]; + dst[2] = src[5]; + dst[3] = src[4]; + dst[4] = src[3]; + dst[5] = src[2]; + dst[6] = src[1]; + dst[7] = src[0]; + + return d; } // returns normalized value in range [-SIMD_PI, SIMD_PI] -SIMD_FORCE_INLINE btScalar btNormalizeAngle(btScalar angleInRadians) { - angleInRadians = btFmod(angleInRadians, SIMD_2_PI); - if (angleInRadians < -SIMD_PI) { - return angleInRadians + SIMD_2_PI; - } else if (angleInRadians > SIMD_PI) { - return angleInRadians - SIMD_2_PI; - } else { - return angleInRadians; - } +SIMD_FORCE_INLINE btScalar btNormalizeAngle(btScalar angleInRadians) +{ + angleInRadians = btFmod(angleInRadians, SIMD_2_PI); + if (angleInRadians < -SIMD_PI) { + return angleInRadians + SIMD_2_PI; + } + else if (angleInRadians > SIMD_PI) { + return angleInRadians - SIMD_2_PI; + } + else { + return angleInRadians; + } } ///rudimentary class to provide type info struct btTypedObject { - btTypedObject(int32_t objectType) : - m_objectType(objectType) { - } - int32_t m_objectType; - inline int32_t getObjectType() const { - return m_objectType; - } + btTypedObject(int32_t objectType) + : m_objectType(objectType) + { + } + int32_t m_objectType; + inline int32_t getObjectType() const + { + return m_objectType; + } }; -//GODOT ADDITION +// -- GODOT start -- +// Cherry-picked from Bullet 2.88 to fix GH-27926 +///align a pointer to the provided alignment, upwards +template <typename T> +T *btAlignPointer(T *unalignedPtr, size_t alignment) +{ + struct btConvertPointerSizeT + { + union { + T *ptr; + size_t integer; + }; + }; + btConvertPointerSizeT converter; + + const size_t bit_mask = ~(alignment - 1); + converter.ptr = unalignedPtr; + converter.integer += alignment - 1; + converter.integer &= bit_mask; + return converter.ptr; +} +// -- GODOT end -- + +// -- GODOT start -- }; // namespace VHACD -// +// -- GODOT end -- #endif //BT_SCALAR_H diff --git a/thirdparty/vhacd/inc/btVector3.h b/thirdparty/vhacd/inc/btVector3.h index 664728a419..4ed9716734 100644 --- a/thirdparty/vhacd/inc/btVector3.h +++ b/thirdparty/vhacd/inc/btVector3.h @@ -26,391 +26,439 @@ subject to the following restrictions: #define btVector3DataName "btVector3FloatData" #endif //BT_USE_DOUBLE_PRECISION +// -- GODOT start -- +namespace VHACD { +// -- GODOT end -- + /**@brief btVector3 can be used to represent 3D points and vectors. * It has an un-used w component to suit 16-byte alignment when btVector3 is stored in containers. This extra component can be used by derived classes (Quaternion?) or by user * Ideally, this class should be replaced by a platform optimized SIMD version that keeps the data in registers */ -//GODOT ADDITION -namespace VHACD { -// - ATTRIBUTE_ALIGNED16(class) -btVector3 { +btVector3 +{ public: #if defined(__SPU__) && defined(__CELLOS_LV2__) - btScalar m_floats[4]; + btScalar m_floats[4]; public: - SIMD_FORCE_INLINE const vec_float4 &get128() const { - return *((const vec_float4 *)&m_floats[0]); - } + SIMD_FORCE_INLINE const vec_float4& get128() const + { + return *((const vec_float4*)&m_floats[0]); + } public: #else //__CELLOS_LV2__ __SPU__ #ifdef BT_USE_SSE // _WIN32 - union { - __m128 mVec128; - btScalar m_floats[4]; - }; - SIMD_FORCE_INLINE __m128 get128() const { - return mVec128; - } - SIMD_FORCE_INLINE void set128(__m128 v128) { - mVec128 = v128; - } + union { + __m128 mVec128; + btScalar m_floats[4]; + }; + SIMD_FORCE_INLINE __m128 get128() const + { + return mVec128; + } + SIMD_FORCE_INLINE void set128(__m128 v128) + { + mVec128 = v128; + } #else - btScalar m_floats[4]; + btScalar m_floats[4]; #endif #endif //__CELLOS_LV2__ __SPU__ public: - /**@brief No initialization constructor */ - SIMD_FORCE_INLINE btVector3() {} + /**@brief No initialization constructor */ + SIMD_FORCE_INLINE btVector3() {} - /**@brief Constructor from scalars + /**@brief Constructor from scalars * @param x X value * @param y Y value * @param z Z value */ - SIMD_FORCE_INLINE btVector3(const btScalar &x, const btScalar &y, const btScalar &z) { - m_floats[0] = x; - m_floats[1] = y; - m_floats[2] = z; - m_floats[3] = btScalar(0.); - } - - /**@brief Add a vector to this one + SIMD_FORCE_INLINE btVector3(const btScalar& x, const btScalar& y, const btScalar& z) + { + m_floats[0] = x; + m_floats[1] = y; + m_floats[2] = z; + m_floats[3] = btScalar(0.); + } + + /**@brief Add a vector to this one * @param The vector to add to this one */ - SIMD_FORCE_INLINE btVector3 &operator+=(const btVector3 &v) { + SIMD_FORCE_INLINE btVector3& operator+=(const btVector3& v) + { - m_floats[0] += v.m_floats[0]; - m_floats[1] += v.m_floats[1]; - m_floats[2] += v.m_floats[2]; - return *this; - } + m_floats[0] += v.m_floats[0]; + m_floats[1] += v.m_floats[1]; + m_floats[2] += v.m_floats[2]; + return *this; + } - /**@brief Subtract a vector from this one + /**@brief Subtract a vector from this one * @param The vector to subtract */ - SIMD_FORCE_INLINE btVector3 &operator-=(const btVector3 &v) { - m_floats[0] -= v.m_floats[0]; - m_floats[1] -= v.m_floats[1]; - m_floats[2] -= v.m_floats[2]; - return *this; - } - /**@brief Scale the vector + SIMD_FORCE_INLINE btVector3& operator-=(const btVector3& v) + { + m_floats[0] -= v.m_floats[0]; + m_floats[1] -= v.m_floats[1]; + m_floats[2] -= v.m_floats[2]; + return *this; + } + /**@brief Scale the vector * @param s Scale factor */ - SIMD_FORCE_INLINE btVector3 &operator*=(const btScalar &s) { - m_floats[0] *= s; - m_floats[1] *= s; - m_floats[2] *= s; - return *this; - } - - /**@brief Inversely scale the vector + SIMD_FORCE_INLINE btVector3& operator*=(const btScalar& s) + { + m_floats[0] *= s; + m_floats[1] *= s; + m_floats[2] *= s; + return *this; + } + + /**@brief Inversely scale the vector * @param s Scale factor to divide by */ - SIMD_FORCE_INLINE btVector3 &operator/=(const btScalar &s) { - btFullAssert(s != btScalar(0.0)); - return *this *= btScalar(1.0) / s; - } + SIMD_FORCE_INLINE btVector3& operator/=(const btScalar& s) + { + btFullAssert(s != btScalar(0.0)); + return * this *= btScalar(1.0) / s; + } - /**@brief Return the dot product + /**@brief Return the dot product * @param v The other vector in the dot product */ - SIMD_FORCE_INLINE btScalar dot(const btVector3 &v) const { - return m_floats[0] * v.m_floats[0] + m_floats[1] * v.m_floats[1] + m_floats[2] * v.m_floats[2]; - } - - /**@brief Return the length of the vector squared */ - SIMD_FORCE_INLINE btScalar length2() const { - return dot(*this); - } - - /**@brief Return the length of the vector */ - SIMD_FORCE_INLINE btScalar length() const { - return btSqrt(length2()); - } - - /**@brief Return the distance squared between the ends of this and another vector + SIMD_FORCE_INLINE btScalar dot(const btVector3& v) const + { + return m_floats[0] * v.m_floats[0] + m_floats[1] * v.m_floats[1] + m_floats[2] * v.m_floats[2]; + } + + /**@brief Return the length of the vector squared */ + SIMD_FORCE_INLINE btScalar length2() const + { + return dot(*this); + } + + /**@brief Return the length of the vector */ + SIMD_FORCE_INLINE btScalar length() const + { + return btSqrt(length2()); + } + + /**@brief Return the distance squared between the ends of this and another vector * This is symantically treating the vector like a point */ - SIMD_FORCE_INLINE btScalar distance2(const btVector3 &v) const; + SIMD_FORCE_INLINE btScalar distance2(const btVector3& v) const; - /**@brief Return the distance between the ends of this and another vector + /**@brief Return the distance between the ends of this and another vector * This is symantically treating the vector like a point */ - SIMD_FORCE_INLINE btScalar distance(const btVector3 &v) const; - - SIMD_FORCE_INLINE btVector3 &safeNormalize() { - btVector3 absVec = this->absolute(); - int32_t maxIndex = absVec.maxAxis(); - if (absVec[maxIndex] > 0) { - *this /= absVec[maxIndex]; - return *this /= length(); - } - setValue(1, 0, 0); - return *this; - } - - /**@brief Normalize this vector + SIMD_FORCE_INLINE btScalar distance(const btVector3& v) const; + + SIMD_FORCE_INLINE btVector3& safeNormalize() + { + btVector3 absVec = this->absolute(); + int32_t maxIndex = absVec.maxAxis(); + if (absVec[maxIndex] > 0) { + *this /= absVec[maxIndex]; + return * this /= length(); + } + setValue(1, 0, 0); + return *this; + } + + /**@brief Normalize this vector * x^2 + y^2 + z^2 = 1 */ - SIMD_FORCE_INLINE btVector3 &normalize() { - return *this /= length(); - } + SIMD_FORCE_INLINE btVector3& normalize() + { + return * this /= length(); + } - /**@brief Return a normalized version of this vector */ - SIMD_FORCE_INLINE btVector3 normalized() const; + /**@brief Return a normalized version of this vector */ + SIMD_FORCE_INLINE btVector3 normalized() const; - /**@brief Return a rotated version of this vector + /**@brief Return a rotated version of this vector * @param wAxis The axis to rotate about * @param angle The angle to rotate by */ - SIMD_FORCE_INLINE btVector3 rotate(const btVector3 &wAxis, const btScalar angle) const; + SIMD_FORCE_INLINE btVector3 rotate(const btVector3& wAxis, const btScalar angle) const; - /**@brief Return the angle between this and another vector + /**@brief Return the angle between this and another vector * @param v The other vector */ - SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const { - btScalar s = btSqrt(length2() * v.length2()); - btFullAssert(s != btScalar(0.0)); - return btAcos(dot(v) / s); - } - /**@brief Return a vector will the absolute values of each element */ - SIMD_FORCE_INLINE btVector3 absolute() const { - return btVector3( - btFabs(m_floats[0]), - btFabs(m_floats[1]), - btFabs(m_floats[2])); - } - /**@brief Return the cross product between this and another vector + SIMD_FORCE_INLINE btScalar angle(const btVector3& v) const + { + btScalar s = btSqrt(length2() * v.length2()); + btFullAssert(s != btScalar(0.0)); + return btAcos(dot(v) / s); + } + /**@brief Return a vector will the absolute values of each element */ + SIMD_FORCE_INLINE btVector3 absolute() const + { + return btVector3( + btFabs(m_floats[0]), + btFabs(m_floats[1]), + btFabs(m_floats[2])); + } + /**@brief Return the cross product between this and another vector * @param v The other vector */ - SIMD_FORCE_INLINE btVector3 cross(const btVector3 &v) const { - return btVector3( - m_floats[1] * v.m_floats[2] - m_floats[2] * v.m_floats[1], - m_floats[2] * v.m_floats[0] - m_floats[0] * v.m_floats[2], - m_floats[0] * v.m_floats[1] - m_floats[1] * v.m_floats[0]); - } - - SIMD_FORCE_INLINE btScalar triple(const btVector3 &v1, const btVector3 &v2) const { - return m_floats[0] * (v1.m_floats[1] * v2.m_floats[2] - v1.m_floats[2] * v2.m_floats[1]) + m_floats[1] * (v1.m_floats[2] * v2.m_floats[0] - v1.m_floats[0] * v2.m_floats[2]) + m_floats[2] * (v1.m_floats[0] * v2.m_floats[1] - v1.m_floats[1] * v2.m_floats[0]); - } - - /**@brief Return the axis with the smallest value + SIMD_FORCE_INLINE btVector3 cross(const btVector3& v) const + { + return btVector3( + m_floats[1] * v.m_floats[2] - m_floats[2] * v.m_floats[1], + m_floats[2] * v.m_floats[0] - m_floats[0] * v.m_floats[2], + m_floats[0] * v.m_floats[1] - m_floats[1] * v.m_floats[0]); + } + + SIMD_FORCE_INLINE btScalar triple(const btVector3& v1, const btVector3& v2) const + { + return m_floats[0] * (v1.m_floats[1] * v2.m_floats[2] - v1.m_floats[2] * v2.m_floats[1]) + m_floats[1] * (v1.m_floats[2] * v2.m_floats[0] - v1.m_floats[0] * v2.m_floats[2]) + m_floats[2] * (v1.m_floats[0] * v2.m_floats[1] - v1.m_floats[1] * v2.m_floats[0]); + } + + /**@brief Return the axis with the smallest value * Note return values are 0,1,2 for x, y, or z */ - SIMD_FORCE_INLINE int32_t minAxis() const { - return m_floats[0] < m_floats[1] ? (m_floats[0] < m_floats[2] ? 0 : 2) : (m_floats[1] < m_floats[2] ? 1 : 2); - } + SIMD_FORCE_INLINE int32_t minAxis() const + { + return m_floats[0] < m_floats[1] ? (m_floats[0] < m_floats[2] ? 0 : 2) : (m_floats[1] < m_floats[2] ? 1 : 2); + } - /**@brief Return the axis with the largest value + /**@brief Return the axis with the largest value * Note return values are 0,1,2 for x, y, or z */ - SIMD_FORCE_INLINE int32_t maxAxis() const { - return m_floats[0] < m_floats[1] ? (m_floats[1] < m_floats[2] ? 2 : 1) : (m_floats[0] < m_floats[2] ? 2 : 0); - } - - SIMD_FORCE_INLINE int32_t furthestAxis() const { - return absolute().minAxis(); - } - - SIMD_FORCE_INLINE int32_t closestAxis() const { - return absolute().maxAxis(); - } - - SIMD_FORCE_INLINE void setInterpolate3(const btVector3 &v0, const btVector3 &v1, btScalar rt) { - btScalar s = btScalar(1.0) - rt; - m_floats[0] = s * v0.m_floats[0] + rt * v1.m_floats[0]; - m_floats[1] = s * v0.m_floats[1] + rt * v1.m_floats[1]; - m_floats[2] = s * v0.m_floats[2] + rt * v1.m_floats[2]; - //don't do the unused w component - // m_co[3] = s * v0[3] + rt * v1[3]; - } - - /**@brief Return the linear interpolation between this and another vector + SIMD_FORCE_INLINE int32_t maxAxis() const + { + return m_floats[0] < m_floats[1] ? (m_floats[1] < m_floats[2] ? 2 : 1) : (m_floats[0] < m_floats[2] ? 2 : 0); + } + + SIMD_FORCE_INLINE int32_t furthestAxis() const + { + return absolute().minAxis(); + } + + SIMD_FORCE_INLINE int32_t closestAxis() const + { + return absolute().maxAxis(); + } + + SIMD_FORCE_INLINE void setInterpolate3(const btVector3& v0, const btVector3& v1, btScalar rt) + { + btScalar s = btScalar(1.0) - rt; + m_floats[0] = s * v0.m_floats[0] + rt * v1.m_floats[0]; + m_floats[1] = s * v0.m_floats[1] + rt * v1.m_floats[1]; + m_floats[2] = s * v0.m_floats[2] + rt * v1.m_floats[2]; + //don't do the unused w component + // m_co[3] = s * v0[3] + rt * v1[3]; + } + + /**@brief Return the linear interpolation between this and another vector * @param v The other vector * @param t The ration of this to v (t = 0 => return this, t=1 => return other) */ - SIMD_FORCE_INLINE btVector3 lerp(const btVector3 &v, const btScalar &t) const { - return btVector3(m_floats[0] + (v.m_floats[0] - m_floats[0]) * t, - m_floats[1] + (v.m_floats[1] - m_floats[1]) * t, - m_floats[2] + (v.m_floats[2] - m_floats[2]) * t); - } - - /**@brief Elementwise multiply this vector by the other + SIMD_FORCE_INLINE btVector3 lerp(const btVector3& v, const btScalar& t) const + { + return btVector3(m_floats[0] + (v.m_floats[0] - m_floats[0]) * t, + m_floats[1] + (v.m_floats[1] - m_floats[1]) * t, + m_floats[2] + (v.m_floats[2] - m_floats[2]) * t); + } + + /**@brief Elementwise multiply this vector by the other * @param v The other vector */ - SIMD_FORCE_INLINE btVector3 &operator*=(const btVector3 &v) { - m_floats[0] *= v.m_floats[0]; - m_floats[1] *= v.m_floats[1]; - m_floats[2] *= v.m_floats[2]; - return *this; - } - - /**@brief Return the x value */ - SIMD_FORCE_INLINE const btScalar &getX() const { return m_floats[0]; } - /**@brief Return the y value */ - SIMD_FORCE_INLINE const btScalar &getY() const { return m_floats[1]; } - /**@brief Return the z value */ - SIMD_FORCE_INLINE const btScalar &getZ() const { return m_floats[2]; } - /**@brief Set the x value */ - SIMD_FORCE_INLINE void setX(btScalar x) { m_floats[0] = x; }; - /**@brief Set the y value */ - SIMD_FORCE_INLINE void setY(btScalar y) { m_floats[1] = y; }; - /**@brief Set the z value */ - SIMD_FORCE_INLINE void setZ(btScalar z) { m_floats[2] = z; }; - /**@brief Set the w value */ - SIMD_FORCE_INLINE void setW(btScalar w) { m_floats[3] = w; }; - /**@brief Return the x value */ - SIMD_FORCE_INLINE const btScalar &x() const { return m_floats[0]; } - /**@brief Return the y value */ - SIMD_FORCE_INLINE const btScalar &y() const { return m_floats[1]; } - /**@brief Return the z value */ - SIMD_FORCE_INLINE const btScalar &z() const { return m_floats[2]; } - /**@brief Return the w value */ - SIMD_FORCE_INLINE const btScalar &w() const { return m_floats[3]; } - - //SIMD_FORCE_INLINE btScalar& operator[](int32_t i) { return (&m_floats[0])[i]; } - //SIMD_FORCE_INLINE const btScalar& operator[](int32_t i) const { return (&m_floats[0])[i]; } - ///operator btScalar*() replaces operator[], using implicit conversion. We added operator != and operator == to avoid pointer comparisons. - SIMD_FORCE_INLINE operator btScalar *() { return &m_floats[0]; } - SIMD_FORCE_INLINE operator const btScalar *() const { return &m_floats[0]; } - - SIMD_FORCE_INLINE bool operator==(const btVector3 &other) const { - return ((m_floats[3] == other.m_floats[3]) && (m_floats[2] == other.m_floats[2]) && (m_floats[1] == other.m_floats[1]) && (m_floats[0] == other.m_floats[0])); - } - - SIMD_FORCE_INLINE bool operator!=(const btVector3 &other) const { - return !(*this == other); - } - - /**@brief Set each element to the max of the current values and the values of another btVector3 + SIMD_FORCE_INLINE btVector3& operator*=(const btVector3& v) + { + m_floats[0] *= v.m_floats[0]; + m_floats[1] *= v.m_floats[1]; + m_floats[2] *= v.m_floats[2]; + return *this; + } + + /**@brief Return the x value */ + SIMD_FORCE_INLINE const btScalar& getX() const { return m_floats[0]; } + /**@brief Return the y value */ + SIMD_FORCE_INLINE const btScalar& getY() const { return m_floats[1]; } + /**@brief Return the z value */ + SIMD_FORCE_INLINE const btScalar& getZ() const { return m_floats[2]; } + /**@brief Set the x value */ + SIMD_FORCE_INLINE void setX(btScalar x) { m_floats[0] = x; }; + /**@brief Set the y value */ + SIMD_FORCE_INLINE void setY(btScalar y) { m_floats[1] = y; }; + /**@brief Set the z value */ + SIMD_FORCE_INLINE void setZ(btScalar z) { m_floats[2] = z; }; + /**@brief Set the w value */ + SIMD_FORCE_INLINE void setW(btScalar w) { m_floats[3] = w; }; + /**@brief Return the x value */ + SIMD_FORCE_INLINE const btScalar& x() const { return m_floats[0]; } + /**@brief Return the y value */ + SIMD_FORCE_INLINE const btScalar& y() const { return m_floats[1]; } + /**@brief Return the z value */ + SIMD_FORCE_INLINE const btScalar& z() const { return m_floats[2]; } + /**@brief Return the w value */ + SIMD_FORCE_INLINE const btScalar& w() const { return m_floats[3]; } + + //SIMD_FORCE_INLINE btScalar& operator[](int32_t i) { return (&m_floats[0])[i]; } + //SIMD_FORCE_INLINE const btScalar& operator[](int32_t i) const { return (&m_floats[0])[i]; } + ///operator btScalar*() replaces operator[], using implicit conversion. We added operator != and operator == to avoid pointer comparisons. + SIMD_FORCE_INLINE operator btScalar*() { return &m_floats[0]; } + SIMD_FORCE_INLINE operator const btScalar*() const { return &m_floats[0]; } + + SIMD_FORCE_INLINE bool operator==(const btVector3& other) const + { + return ((m_floats[3] == other.m_floats[3]) && (m_floats[2] == other.m_floats[2]) && (m_floats[1] == other.m_floats[1]) && (m_floats[0] == other.m_floats[0])); + } + + SIMD_FORCE_INLINE bool operator!=(const btVector3& other) const + { + return !(*this == other); + } + + /**@brief Set each element to the max of the current values and the values of another btVector3 * @param other The other btVector3 to compare with */ - SIMD_FORCE_INLINE void setMax(const btVector3 &other) { - btSetMax(m_floats[0], other.m_floats[0]); - btSetMax(m_floats[1], other.m_floats[1]); - btSetMax(m_floats[2], other.m_floats[2]); - btSetMax(m_floats[3], other.w()); - } - /**@brief Set each element to the min of the current values and the values of another btVector3 + SIMD_FORCE_INLINE void setMax(const btVector3& other) + { + btSetMax(m_floats[0], other.m_floats[0]); + btSetMax(m_floats[1], other.m_floats[1]); + btSetMax(m_floats[2], other.m_floats[2]); + btSetMax(m_floats[3], other.w()); + } + /**@brief Set each element to the min of the current values and the values of another btVector3 * @param other The other btVector3 to compare with */ - SIMD_FORCE_INLINE void setMin(const btVector3 &other) { - btSetMin(m_floats[0], other.m_floats[0]); - btSetMin(m_floats[1], other.m_floats[1]); - btSetMin(m_floats[2], other.m_floats[2]); - btSetMin(m_floats[3], other.w()); - } - - SIMD_FORCE_INLINE void setValue(const btScalar &x, const btScalar &y, const btScalar &z) { - m_floats[0] = x; - m_floats[1] = y; - m_floats[2] = z; - m_floats[3] = btScalar(0.); - } - - void getSkewSymmetricMatrix(btVector3 * v0, btVector3 * v1, btVector3 * v2) const { - v0->setValue(0., -z(), y()); - v1->setValue(z(), 0., -x()); - v2->setValue(-y(), x(), 0.); - } - - void setZero() { - setValue(btScalar(0.), btScalar(0.), btScalar(0.)); - } - - SIMD_FORCE_INLINE bool isZero() const { - return m_floats[0] == btScalar(0) && m_floats[1] == btScalar(0) && m_floats[2] == btScalar(0); - } - - SIMD_FORCE_INLINE bool fuzzyZero() const { - return length2() < SIMD_EPSILON; - } - - SIMD_FORCE_INLINE void serialize(struct btVector3Data & dataOut) const; - - SIMD_FORCE_INLINE void deSerialize(const struct btVector3Data &dataIn); - - SIMD_FORCE_INLINE void serializeFloat(struct btVector3FloatData & dataOut) const; - - SIMD_FORCE_INLINE void deSerializeFloat(const struct btVector3FloatData &dataIn); - - SIMD_FORCE_INLINE void serializeDouble(struct btVector3DoubleData & dataOut) const; - - SIMD_FORCE_INLINE void deSerializeDouble(const struct btVector3DoubleData &dataIn); + SIMD_FORCE_INLINE void setMin(const btVector3& other) + { + btSetMin(m_floats[0], other.m_floats[0]); + btSetMin(m_floats[1], other.m_floats[1]); + btSetMin(m_floats[2], other.m_floats[2]); + btSetMin(m_floats[3], other.w()); + } + + SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z) + { + m_floats[0] = x; + m_floats[1] = y; + m_floats[2] = z; + m_floats[3] = btScalar(0.); + } + + void getSkewSymmetricMatrix(btVector3 * v0, btVector3 * v1, btVector3 * v2) const + { + v0->setValue(0., -z(), y()); + v1->setValue(z(), 0., -x()); + v2->setValue(-y(), x(), 0.); + } + + void setZero() + { + setValue(btScalar(0.), btScalar(0.), btScalar(0.)); + } + + SIMD_FORCE_INLINE bool isZero() const + { + return m_floats[0] == btScalar(0) && m_floats[1] == btScalar(0) && m_floats[2] == btScalar(0); + } + + SIMD_FORCE_INLINE bool fuzzyZero() const + { + return length2() < SIMD_EPSILON; + } + + SIMD_FORCE_INLINE void serialize(struct btVector3Data & dataOut) const; + + SIMD_FORCE_INLINE void deSerialize(const struct btVector3Data& dataIn); + + SIMD_FORCE_INLINE void serializeFloat(struct btVector3FloatData & dataOut) const; + + SIMD_FORCE_INLINE void deSerializeFloat(const struct btVector3FloatData& dataIn); + + SIMD_FORCE_INLINE void serializeDouble(struct btVector3DoubleData & dataOut) const; + + SIMD_FORCE_INLINE void deSerializeDouble(const struct btVector3DoubleData& dataIn); }; /**@brief Return the sum of two vectors (Point symantics)*/ SIMD_FORCE_INLINE btVector3 -operator+(const btVector3 &v1, const btVector3 &v2) { - return btVector3(v1.m_floats[0] + v2.m_floats[0], v1.m_floats[1] + v2.m_floats[1], v1.m_floats[2] + v2.m_floats[2]); +operator+(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] + v2.m_floats[0], v1.m_floats[1] + v2.m_floats[1], v1.m_floats[2] + v2.m_floats[2]); } /**@brief Return the elementwise product of two vectors */ SIMD_FORCE_INLINE btVector3 -operator*(const btVector3 &v1, const btVector3 &v2) { - return btVector3(v1.m_floats[0] * v2.m_floats[0], v1.m_floats[1] * v2.m_floats[1], v1.m_floats[2] * v2.m_floats[2]); +operator*(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] * v2.m_floats[0], v1.m_floats[1] * v2.m_floats[1], v1.m_floats[2] * v2.m_floats[2]); } /**@brief Return the difference between two vectors */ SIMD_FORCE_INLINE btVector3 -operator-(const btVector3 &v1, const btVector3 &v2) { - return btVector3(v1.m_floats[0] - v2.m_floats[0], v1.m_floats[1] - v2.m_floats[1], v1.m_floats[2] - v2.m_floats[2]); +operator-(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] - v2.m_floats[0], v1.m_floats[1] - v2.m_floats[1], v1.m_floats[2] - v2.m_floats[2]); } /**@brief Return the negative of the vector */ SIMD_FORCE_INLINE btVector3 -operator-(const btVector3 &v) { - return btVector3(-v.m_floats[0], -v.m_floats[1], -v.m_floats[2]); +operator-(const btVector3& v) +{ + return btVector3(-v.m_floats[0], -v.m_floats[1], -v.m_floats[2]); } /**@brief Return the vector scaled by s */ SIMD_FORCE_INLINE btVector3 -operator*(const btVector3 &v, const btScalar &s) { - return btVector3(v.m_floats[0] * s, v.m_floats[1] * s, v.m_floats[2] * s); +operator*(const btVector3& v, const btScalar& s) +{ + return btVector3(v.m_floats[0] * s, v.m_floats[1] * s, v.m_floats[2] * s); } /**@brief Return the vector scaled by s */ SIMD_FORCE_INLINE btVector3 -operator*(const btScalar &s, const btVector3 &v) { - return v * s; +operator*(const btScalar& s, const btVector3& v) +{ + return v * s; } /**@brief Return the vector inversely scaled by s */ SIMD_FORCE_INLINE btVector3 -operator/(const btVector3 &v, const btScalar &s) { - btFullAssert(s != btScalar(0.0)); - return v * (btScalar(1.0) / s); +operator/(const btVector3& v, const btScalar& s) +{ + btFullAssert(s != btScalar(0.0)); + return v * (btScalar(1.0) / s); } /**@brief Return the vector inversely scaled by s */ SIMD_FORCE_INLINE btVector3 -operator/(const btVector3 &v1, const btVector3 &v2) { - return btVector3(v1.m_floats[0] / v2.m_floats[0], v1.m_floats[1] / v2.m_floats[1], v1.m_floats[2] / v2.m_floats[2]); +operator/(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] / v2.m_floats[0], v1.m_floats[1] / v2.m_floats[1], v1.m_floats[2] / v2.m_floats[2]); } /**@brief Return the dot product between two vectors */ SIMD_FORCE_INLINE btScalar -btDot(const btVector3 &v1, const btVector3 &v2) { - return v1.dot(v2); +btDot(const btVector3& v1, const btVector3& v2) +{ + return v1.dot(v2); } /**@brief Return the distance squared between two vectors */ SIMD_FORCE_INLINE btScalar -btDistance2(const btVector3 &v1, const btVector3 &v2) { - return v1.distance2(v2); +btDistance2(const btVector3& v1, const btVector3& v2) +{ + return v1.distance2(v2); } /**@brief Return the distance between two vectors */ SIMD_FORCE_INLINE btScalar -btDistance(const btVector3 &v1, const btVector3 &v2) { - return v1.distance(v2); +btDistance(const btVector3& v1, const btVector3& v2) +{ + return v1.distance(v2); } /**@brief Return the angle between two vectors */ SIMD_FORCE_INLINE btScalar -btAngle(const btVector3 &v1, const btVector3 &v2) { - return v1.angle(v2); +btAngle(const btVector3& v1, const btVector3& v2) +{ + return v1.angle(v2); } /**@brief Return the cross product of two vectors */ SIMD_FORCE_INLINE btVector3 -btCross(const btVector3 &v1, const btVector3 &v2) { - return v1.cross(v2); +btCross(const btVector3& v1, const btVector3& v2) +{ + return v1.cross(v2); } SIMD_FORCE_INLINE btScalar -btTriple(const btVector3 &v1, const btVector3 &v2, const btVector3 &v3) { - return v1.triple(v2, v3); +btTriple(const btVector3& v1, const btVector3& v2, const btVector3& v3) +{ + return v1.triple(v2, v3); } /**@brief Return the linear interpolation between two vectors @@ -418,236 +466,258 @@ btTriple(const btVector3 &v1, const btVector3 &v2, const btVector3 &v3) { * @param v2 The other vector * @param t The ration of this to v (t = 0 => return v1, t=1 => return v2) */ SIMD_FORCE_INLINE btVector3 -lerp(const btVector3 &v1, const btVector3 &v2, const btScalar &t) { - return v1.lerp(v2, t); +lerp(const btVector3& v1, const btVector3& v2, const btScalar& t) +{ + return v1.lerp(v2, t); } -SIMD_FORCE_INLINE btScalar btVector3::distance2(const btVector3 &v) const { - return (v - *this).length2(); +SIMD_FORCE_INLINE btScalar btVector3::distance2(const btVector3& v) const +{ + return (v - *this).length2(); } -SIMD_FORCE_INLINE btScalar btVector3::distance(const btVector3 &v) const { - return (v - *this).length(); +SIMD_FORCE_INLINE btScalar btVector3::distance(const btVector3& v) const +{ + return (v - *this).length(); } -SIMD_FORCE_INLINE btVector3 btVector3::normalized() const { - return *this / length(); +SIMD_FORCE_INLINE btVector3 btVector3::normalized() const +{ + return *this / length(); } -SIMD_FORCE_INLINE btVector3 btVector3::rotate(const btVector3 &wAxis, const btScalar angle) const { - // wAxis must be a unit lenght vector +SIMD_FORCE_INLINE btVector3 btVector3::rotate(const btVector3& wAxis, const btScalar angle) const +{ + // wAxis must be a unit lenght vector - btVector3 o = wAxis * wAxis.dot(*this); - btVector3 x = *this - o; - btVector3 y; + btVector3 o = wAxis * wAxis.dot(*this); + btVector3 x = *this - o; + btVector3 y; - y = wAxis.cross(*this); + y = wAxis.cross(*this); - return (o + x * btCos(angle) + y * btSin(angle)); + return (o + x * btCos(angle) + y * btSin(angle)); } class btVector4 : public btVector3 { public: - SIMD_FORCE_INLINE btVector4() {} - - SIMD_FORCE_INLINE btVector4(const btScalar &x, const btScalar &y, const btScalar &z, const btScalar &w) : - btVector3(x, y, z) { - m_floats[3] = w; - } - - SIMD_FORCE_INLINE btVector4 absolute4() const { - return btVector4( - btFabs(m_floats[0]), - btFabs(m_floats[1]), - btFabs(m_floats[2]), - btFabs(m_floats[3])); - } - - btScalar getW() const { return m_floats[3]; } - - SIMD_FORCE_INLINE int32_t maxAxis4() const { - int32_t maxIndex = -1; - btScalar maxVal = btScalar(-BT_LARGE_FLOAT); - if (m_floats[0] > maxVal) { - maxIndex = 0; - maxVal = m_floats[0]; - } - if (m_floats[1] > maxVal) { - maxIndex = 1; - maxVal = m_floats[1]; - } - if (m_floats[2] > maxVal) { - maxIndex = 2; - maxVal = m_floats[2]; - } - if (m_floats[3] > maxVal) { - maxIndex = 3; - } - return maxIndex; - } - - SIMD_FORCE_INLINE int32_t minAxis4() const { - int32_t minIndex = -1; - btScalar minVal = btScalar(BT_LARGE_FLOAT); - if (m_floats[0] < minVal) { - minIndex = 0; - minVal = m_floats[0]; - } - if (m_floats[1] < minVal) { - minIndex = 1; - minVal = m_floats[1]; - } - if (m_floats[2] < minVal) { - minIndex = 2; - minVal = m_floats[2]; - } - if (m_floats[3] < minVal) { - minIndex = 3; - } - - return minIndex; - } - - SIMD_FORCE_INLINE int32_t closestAxis4() const { - return absolute4().maxAxis4(); - } - - /**@brief Set x,y,z and zero w + SIMD_FORCE_INLINE btVector4() {} + + SIMD_FORCE_INLINE btVector4(const btScalar& x, const btScalar& y, const btScalar& z, const btScalar& w) + : btVector3(x, y, z) + { + m_floats[3] = w; + } + + SIMD_FORCE_INLINE btVector4 absolute4() const + { + return btVector4( + btFabs(m_floats[0]), + btFabs(m_floats[1]), + btFabs(m_floats[2]), + btFabs(m_floats[3])); + } + + btScalar getW() const { return m_floats[3]; } + + SIMD_FORCE_INLINE int32_t maxAxis4() const + { + int32_t maxIndex = -1; + btScalar maxVal = btScalar(-BT_LARGE_FLOAT); + if (m_floats[0] > maxVal) { + maxIndex = 0; + maxVal = m_floats[0]; + } + if (m_floats[1] > maxVal) { + maxIndex = 1; + maxVal = m_floats[1]; + } + if (m_floats[2] > maxVal) { + maxIndex = 2; + maxVal = m_floats[2]; + } + if (m_floats[3] > maxVal) { + maxIndex = 3; + } + return maxIndex; + } + + SIMD_FORCE_INLINE int32_t minAxis4() const + { + int32_t minIndex = -1; + btScalar minVal = btScalar(BT_LARGE_FLOAT); + if (m_floats[0] < minVal) { + minIndex = 0; + minVal = m_floats[0]; + } + if (m_floats[1] < minVal) { + minIndex = 1; + minVal = m_floats[1]; + } + if (m_floats[2] < minVal) { + minIndex = 2; + minVal = m_floats[2]; + } + if (m_floats[3] < minVal) { + minIndex = 3; + } + + return minIndex; + } + + SIMD_FORCE_INLINE int32_t closestAxis4() const + { + return absolute4().maxAxis4(); + } + + /**@brief Set x,y,z and zero w * @param x Value of x * @param y Value of y * @param z Value of z */ - /* void getValue(btScalar *m) const + /* void getValue(btScalar *m) const { m[0] = m_floats[0]; m[1] = m_floats[1]; m[2] =m_floats[2]; } */ - /**@brief Set the values + /**@brief Set the values * @param x Value of x * @param y Value of y * @param z Value of z * @param w Value of w */ - SIMD_FORCE_INLINE void setValue(const btScalar &x, const btScalar &y, const btScalar &z, const btScalar &w) { - m_floats[0] = x; - m_floats[1] = y; - m_floats[2] = z; - m_floats[3] = w; - } + SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z, const btScalar& w) + { + m_floats[0] = x; + m_floats[1] = y; + m_floats[2] = z; + m_floats[3] = w; + } }; ///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization -SIMD_FORCE_INLINE void btSwapScalarEndian(const btScalar &sourceVal, btScalar &destVal) { +SIMD_FORCE_INLINE void btSwapScalarEndian(const btScalar& sourceVal, btScalar& destVal) +{ #ifdef BT_USE_DOUBLE_PRECISION - unsigned char *dest = (unsigned char *)&destVal; - unsigned char *src = (unsigned char *)&sourceVal; - dest[0] = src[7]; - dest[1] = src[6]; - dest[2] = src[5]; - dest[3] = src[4]; - dest[4] = src[3]; - dest[5] = src[2]; - dest[6] = src[1]; - dest[7] = src[0]; + unsigned char* dest = (unsigned char*)&destVal; + unsigned char* src = (unsigned char*)&sourceVal; + dest[0] = src[7]; + dest[1] = src[6]; + dest[2] = src[5]; + dest[3] = src[4]; + dest[4] = src[3]; + dest[5] = src[2]; + dest[6] = src[1]; + dest[7] = src[0]; #else - unsigned char *dest = (unsigned char *)&destVal; - unsigned char *src = (unsigned char *)&sourceVal; - dest[0] = src[3]; - dest[1] = src[2]; - dest[2] = src[1]; - dest[3] = src[0]; + unsigned char* dest = (unsigned char*)&destVal; + unsigned char* src = (unsigned char*)&sourceVal; + dest[0] = src[3]; + dest[1] = src[2]; + dest[2] = src[1]; + dest[3] = src[0]; #endif //BT_USE_DOUBLE_PRECISION } ///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization -SIMD_FORCE_INLINE void btSwapVector3Endian(const btVector3 &sourceVec, btVector3 &destVec) { - for (int32_t i = 0; i < 4; i++) { - btSwapScalarEndian(sourceVec[i], destVec[i]); - } +SIMD_FORCE_INLINE void btSwapVector3Endian(const btVector3& sourceVec, btVector3& destVec) +{ + for (int32_t i = 0; i < 4; i++) { + btSwapScalarEndian(sourceVec[i], destVec[i]); + } } ///btUnSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization -SIMD_FORCE_INLINE void btUnSwapVector3Endian(btVector3 &vector) { - - btVector3 swappedVec; - for (int32_t i = 0; i < 4; i++) { - btSwapScalarEndian(vector[i], swappedVec[i]); - } - vector = swappedVec; +SIMD_FORCE_INLINE void btUnSwapVector3Endian(btVector3& vector) +{ + + btVector3 swappedVec; + for (int32_t i = 0; i < 4; i++) { + btSwapScalarEndian(vector[i], swappedVec[i]); + } + vector = swappedVec; } template <class T> -SIMD_FORCE_INLINE void btPlaneSpace1(const T &n, T &p, T &q) { - if (btFabs(n[2]) > SIMDSQRT12) { - // choose p in y-z plane - btScalar a = n[1] * n[1] + n[2] * n[2]; - btScalar k = btRecipSqrt(a); - p[0] = 0; - p[1] = -n[2] * k; - p[2] = n[1] * k; - // set q = n x p - q[0] = a * k; - q[1] = -n[0] * p[2]; - q[2] = n[0] * p[1]; - } else { - // choose p in x-y plane - btScalar a = n[0] * n[0] + n[1] * n[1]; - btScalar k = btRecipSqrt(a); - p[0] = -n[1] * k; - p[1] = n[0] * k; - p[2] = 0; - // set q = n x p - q[0] = -n[2] * p[1]; - q[1] = n[2] * p[0]; - q[2] = a * k; - } +SIMD_FORCE_INLINE void btPlaneSpace1(const T& n, T& p, T& q) +{ + if (btFabs(n[2]) > SIMDSQRT12) { + // choose p in y-z plane + btScalar a = n[1] * n[1] + n[2] * n[2]; + btScalar k = btRecipSqrt(a); + p[0] = 0; + p[1] = -n[2] * k; + p[2] = n[1] * k; + // set q = n x p + q[0] = a * k; + q[1] = -n[0] * p[2]; + q[2] = n[0] * p[1]; + } + else { + // choose p in x-y plane + btScalar a = n[0] * n[0] + n[1] * n[1]; + btScalar k = btRecipSqrt(a); + p[0] = -n[1] * k; + p[1] = n[0] * k; + p[2] = 0; + // set q = n x p + q[0] = -n[2] * p[1]; + q[1] = n[2] * p[0]; + q[2] = a * k; + } } struct btVector3FloatData { - float m_floats[4]; + float m_floats[4]; }; struct btVector3DoubleData { - double m_floats[4]; + double m_floats[4]; }; -SIMD_FORCE_INLINE void btVector3::serializeFloat(struct btVector3FloatData &dataOut) const { - ///could also do a memcpy, check if it is worth it - for (int32_t i = 0; i < 4; i++) - dataOut.m_floats[i] = float(m_floats[i]); +SIMD_FORCE_INLINE void btVector3::serializeFloat(struct btVector3FloatData& dataOut) const +{ + ///could also do a memcpy, check if it is worth it + for (int32_t i = 0; i < 4; i++) + dataOut.m_floats[i] = float(m_floats[i]); } -SIMD_FORCE_INLINE void btVector3::deSerializeFloat(const struct btVector3FloatData &dataIn) { - for (int32_t i = 0; i < 4; i++) - m_floats[i] = btScalar(dataIn.m_floats[i]); +SIMD_FORCE_INLINE void btVector3::deSerializeFloat(const struct btVector3FloatData& dataIn) +{ + for (int32_t i = 0; i < 4; i++) + m_floats[i] = btScalar(dataIn.m_floats[i]); } -SIMD_FORCE_INLINE void btVector3::serializeDouble(struct btVector3DoubleData &dataOut) const { - ///could also do a memcpy, check if it is worth it - for (int32_t i = 0; i < 4; i++) - dataOut.m_floats[i] = double(m_floats[i]); +SIMD_FORCE_INLINE void btVector3::serializeDouble(struct btVector3DoubleData& dataOut) const +{ + ///could also do a memcpy, check if it is worth it + for (int32_t i = 0; i < 4; i++) + dataOut.m_floats[i] = double(m_floats[i]); } -SIMD_FORCE_INLINE void btVector3::deSerializeDouble(const struct btVector3DoubleData &dataIn) { - for (int32_t i = 0; i < 4; i++) - m_floats[i] = btScalar(dataIn.m_floats[i]); +SIMD_FORCE_INLINE void btVector3::deSerializeDouble(const struct btVector3DoubleData& dataIn) +{ + for (int32_t i = 0; i < 4; i++) + m_floats[i] = btScalar(dataIn.m_floats[i]); } -SIMD_FORCE_INLINE void btVector3::serialize(struct btVector3Data &dataOut) const { - ///could also do a memcpy, check if it is worth it - for (int32_t i = 0; i < 4; i++) - dataOut.m_floats[i] = m_floats[i]; +SIMD_FORCE_INLINE void btVector3::serialize(struct btVector3Data& dataOut) const +{ + ///could also do a memcpy, check if it is worth it + for (int32_t i = 0; i < 4; i++) + dataOut.m_floats[i] = m_floats[i]; } -SIMD_FORCE_INLINE void btVector3::deSerialize(const struct btVector3Data &dataIn) { - for (int32_t i = 0; i < 4; i++) - m_floats[i] = dataIn.m_floats[i]; +SIMD_FORCE_INLINE void btVector3::deSerialize(const struct btVector3Data& dataIn) +{ + for (int32_t i = 0; i < 4; i++) + m_floats[i] = dataIn.m_floats[i]; } -//GODOT ADDITION +// -- GODOT start -- }; // namespace VHACD -// +// -- GODOT end -- #endif //BT_VECTOR3_H diff --git a/thirdparty/vhacd/src/btAlignedAllocator.cpp b/thirdparty/vhacd/src/btAlignedAllocator.cpp index e137032463..8dee31e7e3 100644 --- a/thirdparty/vhacd/src/btAlignedAllocator.cpp +++ b/thirdparty/vhacd/src/btAlignedAllocator.cpp @@ -15,157 +15,178 @@ subject to the following restrictions: #include "btAlignedAllocator.h" -//GODOT ADDITION +// -- GODOT start -- namespace VHACD { -// +// -- GODOT end -- #ifdef _MSC_VER -#pragma warning(disable : 4311 4302) +#pragma warning(disable:4311 4302) #endif int32_t gNumAlignedAllocs = 0; int32_t gNumAlignedFree = 0; int32_t gTotalBytesAlignedAllocs = 0; //detect memory leaks -static void *btAllocDefault(size_t size) { - return malloc(size); +static void* btAllocDefault(size_t size) +{ + return malloc(size); } -static void btFreeDefault(void *ptr) { - free(ptr); +static void btFreeDefault(void* ptr) +{ + free(ptr); } -static btAllocFunc *sAllocFunc = btAllocDefault; -static btFreeFunc *sFreeFunc = btFreeDefault; +static btAllocFunc* sAllocFunc = btAllocDefault; +static btFreeFunc* sFreeFunc = btFreeDefault; #if defined(BT_HAS_ALIGNED_ALLOCATOR) #include <malloc.h> -static void *btAlignedAllocDefault(size_t size, int32_t alignment) { - return _aligned_malloc(size, (size_t)alignment); +static void* btAlignedAllocDefault(size_t size, int32_t alignment) +{ + return _aligned_malloc(size, (size_t)alignment); } -static void btAlignedFreeDefault(void *ptr) { - _aligned_free(ptr); +static void btAlignedFreeDefault(void* ptr) +{ + _aligned_free(ptr); } #elif defined(__CELLOS_LV2__) #include <stdlib.h> -static inline void *btAlignedAllocDefault(size_t size, int32_t alignment) { - return memalign(alignment, size); +static inline void* btAlignedAllocDefault(size_t size, int32_t alignment) +{ + return memalign(alignment, size); } -static inline void btAlignedFreeDefault(void *ptr) { - free(ptr); +static inline void btAlignedFreeDefault(void* ptr) +{ + free(ptr); } #else -static inline void *btAlignedAllocDefault(size_t size, int32_t alignment) { - void *ret; - char *real; - unsigned long offset; - - real = (char *)sAllocFunc(size + sizeof(void *) + (alignment - 1)); - if (real) { - offset = (alignment - (unsigned long)(real + sizeof(void *))) & (alignment - 1); - ret = (void *)((real + sizeof(void *)) + offset); - *((void **)(ret)-1) = (void *)(real); - } else { - ret = (void *)(real); - } - return (ret); +static inline void* btAlignedAllocDefault(size_t size, int32_t alignment) +{ + void* ret; + char* real; + unsigned long offset; + + real = (char*)sAllocFunc(size + sizeof(void*) + (alignment - 1)); + if (real) { + // -- GODOT start -- + // Synced with Bullet 2.88 to fix GH-27926 + //offset = (alignment - (unsigned long)(real + sizeof(void*))) & (alignment - 1); + //ret = (void*)((real + sizeof(void*)) + offset); + ret = btAlignPointer(real + sizeof(void *), alignment); + // -- GODOT end -- + *((void**)(ret)-1) = (void*)(real); + } + else { + ret = (void*)(real); + } + return (ret); } -static inline void btAlignedFreeDefault(void *ptr) { - void *real; +static inline void btAlignedFreeDefault(void* ptr) +{ + void* real; - if (ptr) { - real = *((void **)(ptr)-1); - sFreeFunc(real); - } + if (ptr) { + real = *((void**)(ptr)-1); + sFreeFunc(real); + } } #endif -static btAlignedAllocFunc *sAlignedAllocFunc = btAlignedAllocDefault; -static btAlignedFreeFunc *sAlignedFreeFunc = btAlignedFreeDefault; +static btAlignedAllocFunc* sAlignedAllocFunc = btAlignedAllocDefault; +static btAlignedFreeFunc* sAlignedFreeFunc = btAlignedFreeDefault; -void btAlignedAllocSetCustomAligned(btAlignedAllocFunc *allocFunc, btAlignedFreeFunc *freeFunc) { - sAlignedAllocFunc = allocFunc ? allocFunc : btAlignedAllocDefault; - sAlignedFreeFunc = freeFunc ? freeFunc : btAlignedFreeDefault; +void btAlignedAllocSetCustomAligned(btAlignedAllocFunc* allocFunc, btAlignedFreeFunc* freeFunc) +{ + sAlignedAllocFunc = allocFunc ? allocFunc : btAlignedAllocDefault; + sAlignedFreeFunc = freeFunc ? freeFunc : btAlignedFreeDefault; } -void btAlignedAllocSetCustom(btAllocFunc *allocFunc, btFreeFunc *freeFunc) { - sAllocFunc = allocFunc ? allocFunc : btAllocDefault; - sFreeFunc = freeFunc ? freeFunc : btFreeDefault; +void btAlignedAllocSetCustom(btAllocFunc* allocFunc, btFreeFunc* freeFunc) +{ + sAllocFunc = allocFunc ? allocFunc : btAllocDefault; + sFreeFunc = freeFunc ? freeFunc : btFreeDefault; } #ifdef BT_DEBUG_MEMORY_ALLOCATIONS //this generic allocator provides the total allocated number of bytes #include <stdio.h> -void *btAlignedAllocInternal(size_t size, int32_t alignment, int32_t line, char *filename) { - void *ret; - char *real; - unsigned long offset; - - gTotalBytesAlignedAllocs += size; - gNumAlignedAllocs++; - - real = (char *)sAllocFunc(size + 2 * sizeof(void *) + (alignment - 1)); - if (real) { - offset = (alignment - (unsigned long)(real + 2 * sizeof(void *))) & (alignment - 1); - ret = (void *)((real + 2 * sizeof(void *)) + offset); - *((void **)(ret)-1) = (void *)(real); - *((int32_t *)(ret)-2) = size; - } else { - ret = (void *)(real); //?? - } - - printf("allocation#%d at address %x, from %s,line %d, size %d\n", gNumAlignedAllocs, real, filename, line, size); - - int32_t *ptr = (int32_t *)ret; - *ptr = 12; - return (ret); +void* btAlignedAllocInternal(size_t size, int32_t alignment, int32_t line, char* filename) +{ + void* ret; + char* real; + unsigned long offset; + + gTotalBytesAlignedAllocs += size; + gNumAlignedAllocs++; + + real = (char*)sAllocFunc(size + 2 * sizeof(void*) + (alignment - 1)); + if (real) { + offset = (alignment - (unsigned long)(real + 2 * sizeof(void*))) & (alignment - 1); + ret = (void*)((real + 2 * sizeof(void*)) + offset); + *((void**)(ret)-1) = (void*)(real); + *((int32_t*)(ret)-2) = size; + } + else { + ret = (void*)(real); //?? + } + + printf("allocation#%d at address %x, from %s,line %d, size %d\n", gNumAlignedAllocs, real, filename, line, size); + + int32_t* ptr = (int32_t*)ret; + *ptr = 12; + return (ret); } -void btAlignedFreeInternal(void *ptr, int32_t line, char *filename) { +void btAlignedFreeInternal(void* ptr, int32_t line, char* filename) +{ - void *real; - gNumAlignedFree++; + void* real; + gNumAlignedFree++; - if (ptr) { - real = *((void **)(ptr)-1); - int32_t size = *((int32_t *)(ptr)-2); - gTotalBytesAlignedAllocs -= size; + if (ptr) { + real = *((void**)(ptr)-1); + int32_t size = *((int32_t*)(ptr)-2); + gTotalBytesAlignedAllocs -= size; - printf("free #%d at address %x, from %s,line %d, size %d\n", gNumAlignedFree, real, filename, line, size); + printf("free #%d at address %x, from %s,line %d, size %d\n", gNumAlignedFree, real, filename, line, size); - sFreeFunc(real); - } else { - printf("NULL ptr\n"); - } + sFreeFunc(real); + } + else { + printf("NULL ptr\n"); + } } #else //BT_DEBUG_MEMORY_ALLOCATIONS -void *btAlignedAllocInternal(size_t size, int32_t alignment) { - gNumAlignedAllocs++; - void *ptr; - ptr = sAlignedAllocFunc(size, alignment); - // printf("btAlignedAllocInternal %d, %x\n",size,ptr); - return ptr; +void* btAlignedAllocInternal(size_t size, int32_t alignment) +{ + gNumAlignedAllocs++; + void* ptr; + ptr = sAlignedAllocFunc(size, alignment); + // printf("btAlignedAllocInternal %d, %x\n",size,ptr); + return ptr; } -void btAlignedFreeInternal(void *ptr) { - if (!ptr) { - return; - } +void btAlignedFreeInternal(void* ptr) +{ + if (!ptr) { + return; + } - gNumAlignedFree++; - // printf("btAlignedFreeInternal %x\n",ptr); - sAlignedFreeFunc(ptr); + gNumAlignedFree++; + // printf("btAlignedFreeInternal %x\n",ptr); + sAlignedFreeFunc(ptr); } -//GODOT ADDITION -}; -// +// -- GODOT start -- +}; // namespace VHACD +// -- GODOT end -- #endif //BT_DEBUG_MEMORY_ALLOCATIONS diff --git a/thirdparty/vhacd/src/btConvexHullComputer.cpp b/thirdparty/vhacd/src/btConvexHullComputer.cpp index 15956fc0c4..8ab34af2ab 100644 --- a/thirdparty/vhacd/src/btConvexHullComputer.cpp +++ b/thirdparty/vhacd/src/btConvexHullComputer.cpp @@ -3,8 +3,8 @@ Copyright (c) 2011 Ole Kniemeyer, MAXON, www.maxon.net This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. @@ -34,7 +34,7 @@ typedef unsigned long long int32_t uint64_t; #endif #ifdef _MSC_VER -#pragma warning(disable : 4458) +#pragma warning(disable:4458) #endif //The definition of USE_X86_64_ASM is moved into the build system. You can enable it manually by commenting out the following lines @@ -49,2287 +49,2439 @@ typedef unsigned long long int32_t uint64_t; #include <stdio.h> #endif -//GODOT ADDITION +// -- GODOT start -- namespace VHACD { -// +// -- GODOT end -- // Convex hull implementation based on Preparata and Hong // Ole Kniemeyer, MAXON Computer GmbH class btConvexHullInternal { public: - class Point64 { - public: - int64_t x; - int64_t y; - int64_t z; - - Point64(int64_t x, int64_t y, int64_t z) : - x(x), - y(y), - z(z) { - } - - bool isZero() { - return (x == 0) && (y == 0) && (z == 0); - } - - int64_t dot(const Point64 &b) const { - return x * b.x + y * b.y + z * b.z; - } - }; - - class Point32 { - public: - int32_t x; - int32_t y; - int32_t z; - int32_t index; - - Point32() { - } - - Point32(int32_t x, int32_t y, int32_t z) : - x(x), - y(y), - z(z), - index(-1) { - } - - bool operator==(const Point32 &b) const { - return (x == b.x) && (y == b.y) && (z == b.z); - } - - bool operator!=(const Point32 &b) const { - return (x != b.x) || (y != b.y) || (z != b.z); - } - - bool isZero() { - return (x == 0) && (y == 0) && (z == 0); - } - - Point64 cross(const Point32 &b) const { - return Point64(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x); - } - - Point64 cross(const Point64 &b) const { - return Point64(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x); - } - - int64_t dot(const Point32 &b) const { - return x * b.x + y * b.y + z * b.z; - } - - int64_t dot(const Point64 &b) const { - return x * b.x + y * b.y + z * b.z; - } - - Point32 operator+(const Point32 &b) const { - return Point32(x + b.x, y + b.y, z + b.z); - } - - Point32 operator-(const Point32 &b) const { - return Point32(x - b.x, y - b.y, z - b.z); - } - }; - - class Int128 { - public: - uint64_t low; - uint64_t high; - - Int128() { - } - - Int128(uint64_t low, uint64_t high) : - low(low), - high(high) { - } - - Int128(uint64_t low) : - low(low), - high(0) { - } - - Int128(int64_t value) : - low(value), - high((value >= 0) ? 0 : (uint64_t)-1LL) { - } - - static Int128 mul(int64_t a, int64_t b); - - static Int128 mul(uint64_t a, uint64_t b); - - Int128 operator-() const { - return Int128((uint64_t) - (int64_t)low, ~high + (low == 0)); - } - - Int128 operator+(const Int128 &b) const { + class Point64 { + public: + int64_t x; + int64_t y; + int64_t z; + + Point64(int64_t x, int64_t y, int64_t z) + : x(x) + , y(y) + , z(z) + { + } + + bool isZero() + { + return (x == 0) && (y == 0) && (z == 0); + } + + int64_t dot(const Point64& b) const + { + return x * b.x + y * b.y + z * b.z; + } + }; + + class Point32 { + public: + int32_t x; + int32_t y; + int32_t z; + int32_t index; + + Point32() + { + } + + Point32(int32_t x, int32_t y, int32_t z) + : x(x) + , y(y) + , z(z) + , index(-1) + { + } + + bool operator==(const Point32& b) const + { + return (x == b.x) && (y == b.y) && (z == b.z); + } + + bool operator!=(const Point32& b) const + { + return (x != b.x) || (y != b.y) || (z != b.z); + } + + bool isZero() + { + return (x == 0) && (y == 0) && (z == 0); + } + + Point64 cross(const Point32& b) const + { + return Point64(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x); + } + + Point64 cross(const Point64& b) const + { + return Point64(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x); + } + + int64_t dot(const Point32& b) const + { + return x * b.x + y * b.y + z * b.z; + } + + int64_t dot(const Point64& b) const + { + return x * b.x + y * b.y + z * b.z; + } + + Point32 operator+(const Point32& b) const + { + return Point32(x + b.x, y + b.y, z + b.z); + } + + Point32 operator-(const Point32& b) const + { + return Point32(x - b.x, y - b.y, z - b.z); + } + }; + + class Int128 { + public: + uint64_t low; + uint64_t high; + + Int128() + { + } + + Int128(uint64_t low, uint64_t high) + : low(low) + , high(high) + { + } + + Int128(uint64_t low) + : low(low) + , high(0) + { + } + + Int128(int64_t value) + : low(value) + , high((value >= 0) ? 0 : (uint64_t)-1LL) + { + } + + static Int128 mul(int64_t a, int64_t b); + + static Int128 mul(uint64_t a, uint64_t b); + + Int128 operator-() const + { + return Int128((uint64_t) - (int64_t)low, ~high + (low == 0)); + } + + Int128 operator+(const Int128& b) const + { #ifdef USE_X86_64_ASM - Int128 result; - __asm__("addq %[bl], %[rl]\n\t" - "adcq %[bh], %[rh]\n\t" - : [rl] "=r"(result.low), [rh] "=r"(result.high) - : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) - : "cc"); - return result; + Int128 result; + __asm__("addq %[bl], %[rl]\n\t" + "adcq %[bh], %[rh]\n\t" + : [rl] "=r"(result.low), [rh] "=r"(result.high) + : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) + : "cc"); + return result; #else - uint64_t lo = low + b.low; - return Int128(lo, high + b.high + (lo < low)); + uint64_t lo = low + b.low; + return Int128(lo, high + b.high + (lo < low)); #endif - } + } - Int128 operator-(const Int128 &b) const { + Int128 operator-(const Int128& b) const + { #ifdef USE_X86_64_ASM - Int128 result; - __asm__("subq %[bl], %[rl]\n\t" - "sbbq %[bh], %[rh]\n\t" - : [rl] "=r"(result.low), [rh] "=r"(result.high) - : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) - : "cc"); - return result; + Int128 result; + __asm__("subq %[bl], %[rl]\n\t" + "sbbq %[bh], %[rh]\n\t" + : [rl] "=r"(result.low), [rh] "=r"(result.high) + : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) + : "cc"); + return result; #else - return *this + -b; + return *this + -b; #endif - } + } - Int128 &operator+=(const Int128 &b) { + Int128& operator+=(const Int128& b) + { #ifdef USE_X86_64_ASM - __asm__("addq %[bl], %[rl]\n\t" - "adcq %[bh], %[rh]\n\t" - : [rl] "=r"(low), [rh] "=r"(high) - : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) - : "cc"); + __asm__("addq %[bl], %[rl]\n\t" + "adcq %[bh], %[rh]\n\t" + : [rl] "=r"(low), [rh] "=r"(high) + : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high) + : "cc"); #else - uint64_t lo = low + b.low; - if (lo < low) { - ++high; - } - low = lo; - high += b.high; + uint64_t lo = low + b.low; + if (lo < low) { + ++high; + } + low = lo; + high += b.high; #endif - return *this; - } - - Int128 &operator++() { - if (++low == 0) { - ++high; - } - return *this; - } - - Int128 operator*(int64_t b) const; - - btScalar toScalar() const { - return ((int64_t)high >= 0) ? btScalar(high) * (btScalar(0x100000000LL) * btScalar(0x100000000LL)) + btScalar(low) : -(-*this).toScalar(); - } - - int32_t getSign() const { - return ((int64_t)high < 0) ? -1 : (high || low) ? 1 : 0; - } - - bool operator<(const Int128 &b) const { - return (high < b.high) || ((high == b.high) && (low < b.low)); - } - - int32_t ucmp(const Int128 &b) const { - if (high < b.high) { - return -1; - } - if (high > b.high) { - return 1; - } - if (low < b.low) { - return -1; - } - if (low > b.low) { - return 1; - } - return 0; - } - }; - - class Rational64 { - private: - uint64_t m_numerator; - uint64_t m_denominator; - int32_t sign; - - public: - Rational64(int64_t numerator, int64_t denominator) { - if (numerator > 0) { - sign = 1; - m_numerator = (uint64_t)numerator; - } else if (numerator < 0) { - sign = -1; - m_numerator = (uint64_t)-numerator; - } else { - sign = 0; - m_numerator = 0; - } - if (denominator > 0) { - m_denominator = (uint64_t)denominator; - } else if (denominator < 0) { - sign = -sign; - m_denominator = (uint64_t)-denominator; - } else { - m_denominator = 0; - } - } - - bool isNegativeInfinity() const { - return (sign < 0) && (m_denominator == 0); - } - - bool isNaN() const { - return (sign == 0) && (m_denominator == 0); - } - - int32_t compare(const Rational64 &b) const; - - btScalar toScalar() const { - return sign * ((m_denominator == 0) ? SIMD_INFINITY : (btScalar)m_numerator / m_denominator); - } - }; - - class Rational128 { - private: - Int128 numerator; - Int128 denominator; - int32_t sign; - bool isInt64; - - public: - Rational128(int64_t value) { - if (value > 0) { - sign = 1; - this->numerator = value; - } else if (value < 0) { - sign = -1; - this->numerator = -value; - } else { - sign = 0; - this->numerator = (uint64_t)0; - } - this->denominator = (uint64_t)1; - isInt64 = true; - } - - Rational128(const Int128 &numerator, const Int128 &denominator) { - sign = numerator.getSign(); - if (sign >= 0) { - this->numerator = numerator; - } else { - this->numerator = -numerator; - } - int32_t dsign = denominator.getSign(); - if (dsign >= 0) { - this->denominator = denominator; - } else { - sign = -sign; - this->denominator = -denominator; - } - isInt64 = false; - } - - int32_t compare(const Rational128 &b) const; - - int32_t compare(int64_t b) const; - - btScalar toScalar() const { - return sign * ((denominator.getSign() == 0) ? SIMD_INFINITY : numerator.toScalar() / denominator.toScalar()); - } - }; - - class PointR128 { - public: - Int128 x; - Int128 y; - Int128 z; - Int128 denominator; - - PointR128() { - } - - PointR128(Int128 x, Int128 y, Int128 z, Int128 denominator) : - x(x), - y(y), - z(z), - denominator(denominator) { - } - - btScalar xvalue() const { - return x.toScalar() / denominator.toScalar(); - } - - btScalar yvalue() const { - return y.toScalar() / denominator.toScalar(); - } - - btScalar zvalue() const { - return z.toScalar() / denominator.toScalar(); - } - }; - - class Edge; - class Face; - - class Vertex { - public: - Vertex *next; - Vertex *prev; - Edge *edges; - Face *firstNearbyFace; - Face *lastNearbyFace; - PointR128 point128; - Point32 point; - int32_t copy; - - Vertex() : - next(NULL), - prev(NULL), - edges(NULL), - firstNearbyFace(NULL), - lastNearbyFace(NULL), - copy(-1) { - } + return *this; + } + + Int128& operator++() + { + if (++low == 0) { + ++high; + } + return *this; + } + + Int128 operator*(int64_t b) const; + + btScalar toScalar() const + { + return ((int64_t)high >= 0) ? btScalar(high) * (btScalar(0x100000000LL) * btScalar(0x100000000LL)) + btScalar(low) + : -(-*this).toScalar(); + } + + int32_t getSign() const + { + return ((int64_t)high < 0) ? -1 : (high || low) ? 1 : 0; + } + + bool operator<(const Int128& b) const + { + return (high < b.high) || ((high == b.high) && (low < b.low)); + } + + int32_t ucmp(const Int128& b) const + { + if (high < b.high) { + return -1; + } + if (high > b.high) { + return 1; + } + if (low < b.low) { + return -1; + } + if (low > b.low) { + return 1; + } + return 0; + } + }; + + class Rational64 { + private: + uint64_t m_numerator; + uint64_t m_denominator; + int32_t sign; + + public: + Rational64(int64_t numerator, int64_t denominator) + { + if (numerator > 0) { + sign = 1; + m_numerator = (uint64_t)numerator; + } + else if (numerator < 0) { + sign = -1; + m_numerator = (uint64_t)-numerator; + } + else { + sign = 0; + m_numerator = 0; + } + if (denominator > 0) { + m_denominator = (uint64_t)denominator; + } + else if (denominator < 0) { + sign = -sign; + m_denominator = (uint64_t)-denominator; + } + else { + m_denominator = 0; + } + } + + bool isNegativeInfinity() const + { + return (sign < 0) && (m_denominator == 0); + } + + bool isNaN() const + { + return (sign == 0) && (m_denominator == 0); + } + + int32_t compare(const Rational64& b) const; + + btScalar toScalar() const + { + return sign * ((m_denominator == 0) ? SIMD_INFINITY : (btScalar)m_numerator / m_denominator); + } + }; + + class Rational128 { + private: + Int128 numerator; + Int128 denominator; + int32_t sign; + bool isInt64; + + public: + Rational128(int64_t value) + { + if (value > 0) { + sign = 1; + this->numerator = value; + } + else if (value < 0) { + sign = -1; + this->numerator = -value; + } + else { + sign = 0; + this->numerator = (uint64_t)0; + } + this->denominator = (uint64_t)1; + isInt64 = true; + } + + Rational128(const Int128& numerator, const Int128& denominator) + { + sign = numerator.getSign(); + if (sign >= 0) { + this->numerator = numerator; + } + else { + this->numerator = -numerator; + } + int32_t dsign = denominator.getSign(); + if (dsign >= 0) { + this->denominator = denominator; + } + else { + sign = -sign; + this->denominator = -denominator; + } + isInt64 = false; + } + + int32_t compare(const Rational128& b) const; + + int32_t compare(int64_t b) const; + + btScalar toScalar() const + { + return sign * ((denominator.getSign() == 0) ? SIMD_INFINITY : numerator.toScalar() / denominator.toScalar()); + } + }; + + class PointR128 { + public: + Int128 x; + Int128 y; + Int128 z; + Int128 denominator; + + PointR128() + { + } + + PointR128(Int128 x, Int128 y, Int128 z, Int128 denominator) + : x(x) + , y(y) + , z(z) + , denominator(denominator) + { + } + + btScalar xvalue() const + { + return x.toScalar() / denominator.toScalar(); + } + + btScalar yvalue() const + { + return y.toScalar() / denominator.toScalar(); + } + + btScalar zvalue() const + { + return z.toScalar() / denominator.toScalar(); + } + }; + + class Edge; + class Face; + + class Vertex { + public: + Vertex* next; + Vertex* prev; + Edge* edges; + Face* firstNearbyFace; + Face* lastNearbyFace; + PointR128 point128; + Point32 point; + int32_t copy; + + Vertex() + : next(NULL) + , prev(NULL) + , edges(NULL) + , firstNearbyFace(NULL) + , lastNearbyFace(NULL) + , copy(-1) + { + } #ifdef DEBUG_CONVEX_HULL - void print() { - printf("V%d (%d, %d, %d)", point.index, point.x, point.y, point.z); - } + void print() + { + printf("V%d (%d, %d, %d)", point.index, point.x, point.y, point.z); + } - void printGraph(); + void printGraph(); #endif - Point32 operator-(const Vertex &b) const { - return point - b.point; - } - - Rational128 dot(const Point64 &b) const { - return (point.index >= 0) ? Rational128(point.dot(b)) : Rational128(point128.x * b.x + point128.y * b.y + point128.z * b.z, point128.denominator); - } - - btScalar xvalue() const { - return (point.index >= 0) ? btScalar(point.x) : point128.xvalue(); - } - - btScalar yvalue() const { - return (point.index >= 0) ? btScalar(point.y) : point128.yvalue(); - } - - btScalar zvalue() const { - return (point.index >= 0) ? btScalar(point.z) : point128.zvalue(); - } - - void receiveNearbyFaces(Vertex *src) { - if (lastNearbyFace) { - lastNearbyFace->nextWithSameNearbyVertex = src->firstNearbyFace; - } else { - firstNearbyFace = src->firstNearbyFace; - } - if (src->lastNearbyFace) { - lastNearbyFace = src->lastNearbyFace; - } - for (Face *f = src->firstNearbyFace; f; f = f->nextWithSameNearbyVertex) { - btAssert(f->nearbyVertex == src); - f->nearbyVertex = this; - } - src->firstNearbyFace = NULL; - src->lastNearbyFace = NULL; - } - }; - - class Edge { - public: - Edge *next; - Edge *prev; - Edge *reverse; - Vertex *target; - Face *face; - int32_t copy; - - ~Edge() { - next = NULL; - prev = NULL; - reverse = NULL; - target = NULL; - face = NULL; - } - - void link(Edge *n) { - btAssert(reverse->target == n->reverse->target); - next = n; - n->prev = this; - } + Point32 operator-(const Vertex& b) const + { + return point - b.point; + } + + Rational128 dot(const Point64& b) const + { + return (point.index >= 0) ? Rational128(point.dot(b)) + : Rational128(point128.x * b.x + point128.y * b.y + point128.z * b.z, point128.denominator); + } + + btScalar xvalue() const + { + return (point.index >= 0) ? btScalar(point.x) : point128.xvalue(); + } + + btScalar yvalue() const + { + return (point.index >= 0) ? btScalar(point.y) : point128.yvalue(); + } + + btScalar zvalue() const + { + return (point.index >= 0) ? btScalar(point.z) : point128.zvalue(); + } + + void receiveNearbyFaces(Vertex* src) + { + if (lastNearbyFace) { + lastNearbyFace->nextWithSameNearbyVertex = src->firstNearbyFace; + } + else { + firstNearbyFace = src->firstNearbyFace; + } + if (src->lastNearbyFace) { + lastNearbyFace = src->lastNearbyFace; + } + for (Face* f = src->firstNearbyFace; f; f = f->nextWithSameNearbyVertex) { + btAssert(f->nearbyVertex == src); + f->nearbyVertex = this; + } + src->firstNearbyFace = NULL; + src->lastNearbyFace = NULL; + } + }; + + class Edge { + public: + Edge* next; + Edge* prev; + Edge* reverse; + Vertex* target; + Face* face; + int32_t copy; + + ~Edge() + { + next = NULL; + prev = NULL; + reverse = NULL; + target = NULL; + face = NULL; + } + + void link(Edge* n) + { + btAssert(reverse->target == n->reverse->target); + next = n; + n->prev = this; + } #ifdef DEBUG_CONVEX_HULL - void print() { - printf("E%p : %d -> %d, n=%p p=%p (0 %d\t%d\t%d) -> (%d %d %d)", this, reverse->target->point.index, target->point.index, next, prev, - reverse->target->point.x, reverse->target->point.y, reverse->target->point.z, target->point.x, target->point.y, target->point.z); - } + void print() + { + printf("E%p : %d -> %d, n=%p p=%p (0 %d\t%d\t%d) -> (%d %d %d)", this, reverse->target->point.index, target->point.index, next, prev, + reverse->target->point.x, reverse->target->point.y, reverse->target->point.z, target->point.x, target->point.y, target->point.z); + } #endif - }; - - class Face { - public: - Face *next; - Vertex *nearbyVertex; - Face *nextWithSameNearbyVertex; - Point32 origin; - Point32 dir0; - Point32 dir1; - - Face() : - next(NULL), - nearbyVertex(NULL), - nextWithSameNearbyVertex(NULL) { - } - - void init(Vertex *a, Vertex *b, Vertex *c) { - nearbyVertex = a; - origin = a->point; - dir0 = *b - *a; - dir1 = *c - *a; - if (a->lastNearbyFace) { - a->lastNearbyFace->nextWithSameNearbyVertex = this; - } else { - a->firstNearbyFace = this; - } - a->lastNearbyFace = this; - } - - Point64 getNormal() { - return dir0.cross(dir1); - } - }; - - template <typename UWord, typename UHWord> - class DMul { - private: - static uint32_t high(uint64_t value) { - return (uint32_t)(value >> 32); - } - - static uint32_t low(uint64_t value) { - return (uint32_t)value; - } - - static uint64_t mul(uint32_t a, uint32_t b) { - return (uint64_t)a * (uint64_t)b; - } - - static void shlHalf(uint64_t &value) { - value <<= 32; - } - - static uint64_t high(Int128 value) { - return value.high; - } - - static uint64_t low(Int128 value) { - return value.low; - } - - static Int128 mul(uint64_t a, uint64_t b) { - return Int128::mul(a, b); - } - - static void shlHalf(Int128 &value) { - value.high = value.low; - value.low = 0; - } - - public: - static void mul(UWord a, UWord b, UWord &resLow, UWord &resHigh) { - UWord p00 = mul(low(a), low(b)); - UWord p01 = mul(low(a), high(b)); - UWord p10 = mul(high(a), low(b)); - UWord p11 = mul(high(a), high(b)); - UWord p0110 = UWord(low(p01)) + UWord(low(p10)); - p11 += high(p01); - p11 += high(p10); - p11 += high(p0110); - shlHalf(p0110); - p00 += p0110; - if (p00 < p0110) { - ++p11; - } - resLow = p00; - resHigh = p11; - } - }; + }; + + class Face { + public: + Face* next; + Vertex* nearbyVertex; + Face* nextWithSameNearbyVertex; + Point32 origin; + Point32 dir0; + Point32 dir1; + + Face() + : next(NULL) + , nearbyVertex(NULL) + , nextWithSameNearbyVertex(NULL) + { + } + + void init(Vertex* a, Vertex* b, Vertex* c) + { + nearbyVertex = a; + origin = a->point; + dir0 = *b - *a; + dir1 = *c - *a; + if (a->lastNearbyFace) { + a->lastNearbyFace->nextWithSameNearbyVertex = this; + } + else { + a->firstNearbyFace = this; + } + a->lastNearbyFace = this; + } + + Point64 getNormal() + { + return dir0.cross(dir1); + } + }; + + template <typename UWord, typename UHWord> + class DMul { + private: + static uint32_t high(uint64_t value) + { + return (uint32_t)(value >> 32); + } + + static uint32_t low(uint64_t value) + { + return (uint32_t)value; + } + + static uint64_t mul(uint32_t a, uint32_t b) + { + return (uint64_t)a * (uint64_t)b; + } + + static void shlHalf(uint64_t& value) + { + value <<= 32; + } + + static uint64_t high(Int128 value) + { + return value.high; + } + + static uint64_t low(Int128 value) + { + return value.low; + } + + static Int128 mul(uint64_t a, uint64_t b) + { + return Int128::mul(a, b); + } + + static void shlHalf(Int128& value) + { + value.high = value.low; + value.low = 0; + } + + public: + static void mul(UWord a, UWord b, UWord& resLow, UWord& resHigh) + { + UWord p00 = mul(low(a), low(b)); + UWord p01 = mul(low(a), high(b)); + UWord p10 = mul(high(a), low(b)); + UWord p11 = mul(high(a), high(b)); + UWord p0110 = UWord(low(p01)) + UWord(low(p10)); + p11 += high(p01); + p11 += high(p10); + p11 += high(p0110); + shlHalf(p0110); + p00 += p0110; + if (p00 < p0110) { + ++p11; + } + resLow = p00; + resHigh = p11; + } + }; private: - class IntermediateHull { - public: - Vertex *minXy; - Vertex *maxXy; - Vertex *minYx; - Vertex *maxYx; - - IntermediateHull() : - minXy(NULL), - maxXy(NULL), - minYx(NULL), - maxYx(NULL) { - } - - void print(); - }; - - enum Orientation { NONE, - CLOCKWISE, - COUNTER_CLOCKWISE }; - - template <typename T> - class PoolArray { - private: - T *array; - int32_t size; - - public: - PoolArray<T> *next; - - PoolArray(int32_t size) : - size(size), - next(NULL) { - array = (T *)btAlignedAlloc(sizeof(T) * size, 16); - } - - ~PoolArray() { - btAlignedFree(array); - } - - T *init() { - T *o = array; - for (int32_t i = 0; i < size; i++, o++) { - o->next = (i + 1 < size) ? o + 1 : NULL; - } - return array; - } - }; - - template <typename T> - class Pool { - private: - PoolArray<T> *arrays; - PoolArray<T> *nextArray; - T *freeObjects; - int32_t arraySize; - - public: - Pool() : - arrays(NULL), - nextArray(NULL), - freeObjects(NULL), - arraySize(256) { - } - - ~Pool() { - while (arrays) { - PoolArray<T> *p = arrays; - arrays = p->next; - p->~PoolArray<T>(); - btAlignedFree(p); - } - } - - void reset() { - nextArray = arrays; - freeObjects = NULL; - } - - void setArraySize(int32_t arraySize) { - this->arraySize = arraySize; - } - - T *newObject() { - T *o = freeObjects; - if (!o) { - PoolArray<T> *p = nextArray; - if (p) { - nextArray = p->next; - } else { - p = new (btAlignedAlloc(sizeof(PoolArray<T>), 16)) PoolArray<T>(arraySize); - p->next = arrays; - arrays = p; - } - o = p->init(); - } - freeObjects = o->next; - return new (o) T(); - }; - - void freeObject(T *object) { - object->~T(); - object->next = freeObjects; - freeObjects = object; - } - }; - - btVector3 scaling; - btVector3 center; - Pool<Vertex> vertexPool; - Pool<Edge> edgePool; - Pool<Face> facePool; - btAlignedObjectArray<Vertex *> originalVertices; - int32_t mergeStamp; - int32_t minAxis; - int32_t medAxis; - int32_t maxAxis; - int32_t usedEdgePairs; - int32_t maxUsedEdgePairs; - - static Orientation getOrientation(const Edge *prev, const Edge *next, const Point32 &s, const Point32 &t); - Edge *findMaxAngle(bool ccw, const Vertex *start, const Point32 &s, const Point64 &rxs, const Point64 &sxrxs, Rational64 &minCot); - void findEdgeForCoplanarFaces(Vertex *c0, Vertex *c1, Edge *&e0, Edge *&e1, Vertex *stop0, Vertex *stop1); - - Edge *newEdgePair(Vertex *from, Vertex *to); - - void removeEdgePair(Edge *edge) { - Edge *n = edge->next; - Edge *r = edge->reverse; - - btAssert(edge->target && r->target); - - if (n != edge) { - n->prev = edge->prev; - edge->prev->next = n; - r->target->edges = n; - } else { - r->target->edges = NULL; - } - - n = r->next; - - if (n != r) { - n->prev = r->prev; - r->prev->next = n; - edge->target->edges = n; - } else { - edge->target->edges = NULL; - } - - edgePool.freeObject(edge); - edgePool.freeObject(r); - usedEdgePairs--; - } - - void computeInternal(int32_t start, int32_t end, IntermediateHull &result); - - bool mergeProjection(IntermediateHull &h0, IntermediateHull &h1, Vertex *&c0, Vertex *&c1); - - void merge(IntermediateHull &h0, IntermediateHull &h1); - - btVector3 toBtVector(const Point32 &v); - - btVector3 getBtNormal(Face *face); - - bool shiftFace(Face *face, btScalar amount, btAlignedObjectArray<Vertex *> stack); + class IntermediateHull { + public: + Vertex* minXy; + Vertex* maxXy; + Vertex* minYx; + Vertex* maxYx; + + IntermediateHull() + : minXy(NULL) + , maxXy(NULL) + , minYx(NULL) + , maxYx(NULL) + { + } + + void print(); + }; + + enum Orientation { NONE, + CLOCKWISE, + COUNTER_CLOCKWISE }; + + template <typename T> + class PoolArray { + private: + T* array; + int32_t size; + + public: + PoolArray<T>* next; + + PoolArray(int32_t size) + : size(size) + , next(NULL) + { + array = (T*)btAlignedAlloc(sizeof(T) * size, 16); + } + + ~PoolArray() + { + btAlignedFree(array); + } + + T* init() + { + T* o = array; + for (int32_t i = 0; i < size; i++, o++) { + o->next = (i + 1 < size) ? o + 1 : NULL; + } + return array; + } + }; + + template <typename T> + class Pool { + private: + PoolArray<T>* arrays; + PoolArray<T>* nextArray; + T* freeObjects; + int32_t arraySize; + + public: + Pool() + : arrays(NULL) + , nextArray(NULL) + , freeObjects(NULL) + , arraySize(256) + { + } + + ~Pool() + { + while (arrays) { + PoolArray<T>* p = arrays; + arrays = p->next; + p->~PoolArray<T>(); + btAlignedFree(p); + } + } + + void reset() + { + nextArray = arrays; + freeObjects = NULL; + } + + void setArraySize(int32_t arraySize) + { + this->arraySize = arraySize; + } + + T* newObject() + { + T* o = freeObjects; + if (!o) { + PoolArray<T>* p = nextArray; + if (p) { + nextArray = p->next; + } + else { + p = new (btAlignedAlloc(sizeof(PoolArray<T>), 16)) PoolArray<T>(arraySize); + p->next = arrays; + arrays = p; + } + o = p->init(); + } + freeObjects = o->next; + return new (o) T(); + }; + + void freeObject(T* object) + { + object->~T(); + object->next = freeObjects; + freeObjects = object; + } + }; + + btVector3 scaling; + btVector3 center; + Pool<Vertex> vertexPool; + Pool<Edge> edgePool; + Pool<Face> facePool; + btAlignedObjectArray<Vertex*> originalVertices; + int32_t mergeStamp; + int32_t minAxis; + int32_t medAxis; + int32_t maxAxis; + int32_t usedEdgePairs; + int32_t maxUsedEdgePairs; + + static Orientation getOrientation(const Edge* prev, const Edge* next, const Point32& s, const Point32& t); + Edge* findMaxAngle(bool ccw, const Vertex* start, const Point32& s, const Point64& rxs, const Point64& sxrxs, Rational64& minCot); + void findEdgeForCoplanarFaces(Vertex* c0, Vertex* c1, Edge*& e0, Edge*& e1, Vertex* stop0, Vertex* stop1); + + Edge* newEdgePair(Vertex* from, Vertex* to); + + void removeEdgePair(Edge* edge) + { + Edge* n = edge->next; + Edge* r = edge->reverse; + + btAssert(edge->target && r->target); + + if (n != edge) { + n->prev = edge->prev; + edge->prev->next = n; + r->target->edges = n; + } + else { + r->target->edges = NULL; + } + + n = r->next; + + if (n != r) { + n->prev = r->prev; + r->prev->next = n; + edge->target->edges = n; + } + else { + edge->target->edges = NULL; + } + + edgePool.freeObject(edge); + edgePool.freeObject(r); + usedEdgePairs--; + } + + void computeInternal(int32_t start, int32_t end, IntermediateHull& result); + + bool mergeProjection(IntermediateHull& h0, IntermediateHull& h1, Vertex*& c0, Vertex*& c1); + + void merge(IntermediateHull& h0, IntermediateHull& h1); + + btVector3 toBtVector(const Point32& v); + + btVector3 getBtNormal(Face* face); + + bool shiftFace(Face* face, btScalar amount, btAlignedObjectArray<Vertex*> stack); public: - Vertex *vertexList; + Vertex* vertexList; - void compute(const void *coords, bool doubleCoords, int32_t stride, int32_t count); + void compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count); - btVector3 getCoordinates(const Vertex *v); + btVector3 getCoordinates(const Vertex* v); - btScalar shrink(btScalar amount, btScalar clampAmount); + btScalar shrink(btScalar amount, btScalar clampAmount); }; -btConvexHullInternal::Int128 btConvexHullInternal::Int128::operator*(int64_t b) const { - bool negative = (int64_t)high < 0; - Int128 a = negative ? -*this : *this; - if (b < 0) { - negative = !negative; - b = -b; - } - Int128 result = mul(a.low, (uint64_t)b); - result.high += a.high * (uint64_t)b; - return negative ? -result : result; +btConvexHullInternal::Int128 btConvexHullInternal::Int128::operator*(int64_t b) const +{ + bool negative = (int64_t)high < 0; + Int128 a = negative ? -*this : *this; + if (b < 0) { + negative = !negative; + b = -b; + } + Int128 result = mul(a.low, (uint64_t)b); + result.high += a.high * (uint64_t)b; + return negative ? -result : result; } -btConvexHullInternal::Int128 btConvexHullInternal::Int128::mul(int64_t a, int64_t b) { - Int128 result; +btConvexHullInternal::Int128 btConvexHullInternal::Int128::mul(int64_t a, int64_t b) +{ + Int128 result; #ifdef USE_X86_64_ASM - __asm__("imulq %[b]" - : "=a"(result.low), "=d"(result.high) - : "0"(a), [b] "r"(b) - : "cc"); - return result; + __asm__("imulq %[b]" + : "=a"(result.low), "=d"(result.high) + : "0"(a), [b] "r"(b) + : "cc"); + return result; #else - bool negative = a < 0; - if (negative) { - a = -a; - } - if (b < 0) { - negative = !negative; - b = -b; - } - DMul<uint64_t, uint32_t>::mul((uint64_t)a, (uint64_t)b, result.low, result.high); - return negative ? -result : result; + bool negative = a < 0; + if (negative) { + a = -a; + } + if (b < 0) { + negative = !negative; + b = -b; + } + DMul<uint64_t, uint32_t>::mul((uint64_t)a, (uint64_t)b, result.low, result.high); + return negative ? -result : result; #endif } -btConvexHullInternal::Int128 btConvexHullInternal::Int128::mul(uint64_t a, uint64_t b) { - Int128 result; +btConvexHullInternal::Int128 btConvexHullInternal::Int128::mul(uint64_t a, uint64_t b) +{ + Int128 result; #ifdef USE_X86_64_ASM - __asm__("mulq %[b]" - : "=a"(result.low), "=d"(result.high) - : "0"(a), [b] "r"(b) - : "cc"); + __asm__("mulq %[b]" + : "=a"(result.low), "=d"(result.high) + : "0"(a), [b] "r"(b) + : "cc"); #else - DMul<uint64_t, uint32_t>::mul(a, b, result.low, result.high); + DMul<uint64_t, uint32_t>::mul(a, b, result.low, result.high); #endif - return result; + return result; } -int32_t btConvexHullInternal::Rational64::compare(const Rational64 &b) const { - if (sign != b.sign) { - return sign - b.sign; - } else if (sign == 0) { - return 0; - } +int32_t btConvexHullInternal::Rational64::compare(const Rational64& b) const +{ + if (sign != b.sign) { + return sign - b.sign; + } + else if (sign == 0) { + return 0; + } - // return (numerator * b.denominator > b.numerator * denominator) ? sign : (numerator * b.denominator < b.numerator * denominator) ? -sign : 0; +// return (numerator * b.denominator > b.numerator * denominator) ? sign : (numerator * b.denominator < b.numerator * denominator) ? -sign : 0; #ifdef USE_X86_64_ASM - int32_t result; - int64_t tmp; - int64_t dummy; - __asm__("mulq %[bn]\n\t" - "movq %%rax, %[tmp]\n\t" - "movq %%rdx, %%rbx\n\t" - "movq %[tn], %%rax\n\t" - "mulq %[bd]\n\t" - "subq %[tmp], %%rax\n\t" - "sbbq %%rbx, %%rdx\n\t" // rdx:rax contains 128-bit-difference "numerator*b.denominator - b.numerator*denominator" - "setnsb %%bh\n\t" // bh=1 if difference is non-negative, bh=0 otherwise - "orq %%rdx, %%rax\n\t" - "setnzb %%bl\n\t" // bl=1 if difference if non-zero, bl=0 if it is zero - "decb %%bh\n\t" // now bx=0x0000 if difference is zero, 0xff01 if it is negative, 0x0001 if it is positive (i.e., same sign as difference) - "shll $16, %%ebx\n\t" // ebx has same sign as difference - : "=&b"(result), [tmp] "=&r"(tmp), "=a"(dummy) - : "a"(denominator), [bn] "g"(b.numerator), [tn] "g"(numerator), [bd] "g"(b.denominator) - : "%rdx", "cc"); - return result ? result ^ sign // if sign is +1, only bit 0 of result is inverted, which does not change the sign of result (and cannot result in zero) - // if sign is -1, all bits of result are inverted, which changes the sign of result (and again cannot result in zero) - : - 0; + int32_t result; + int64_t tmp; + int64_t dummy; + __asm__("mulq %[bn]\n\t" + "movq %%rax, %[tmp]\n\t" + "movq %%rdx, %%rbx\n\t" + "movq %[tn], %%rax\n\t" + "mulq %[bd]\n\t" + "subq %[tmp], %%rax\n\t" + "sbbq %%rbx, %%rdx\n\t" // rdx:rax contains 128-bit-difference "numerator*b.denominator - b.numerator*denominator" + "setnsb %%bh\n\t" // bh=1 if difference is non-negative, bh=0 otherwise + "orq %%rdx, %%rax\n\t" + "setnzb %%bl\n\t" // bl=1 if difference if non-zero, bl=0 if it is zero + "decb %%bh\n\t" // now bx=0x0000 if difference is zero, 0xff01 if it is negative, 0x0001 if it is positive (i.e., same sign as difference) + "shll $16, %%ebx\n\t" // ebx has same sign as difference + : "=&b"(result), [tmp] "=&r"(tmp), "=a"(dummy) + : "a"(denominator), [bn] "g"(b.numerator), [tn] "g"(numerator), [bd] "g"(b.denominator) + : "%rdx", "cc"); + return result ? result ^ sign // if sign is +1, only bit 0 of result is inverted, which does not change the sign of result (and cannot result in zero) + // if sign is -1, all bits of result are inverted, which changes the sign of result (and again cannot result in zero) + : 0; #else - return sign * Int128::mul(m_numerator, b.m_denominator).ucmp(Int128::mul(m_denominator, b.m_numerator)); + return sign * Int128::mul(m_numerator, b.m_denominator).ucmp(Int128::mul(m_denominator, b.m_numerator)); #endif } -int32_t btConvexHullInternal::Rational128::compare(const Rational128 &b) const { - if (sign != b.sign) { - return sign - b.sign; - } else if (sign == 0) { - return 0; - } - if (isInt64) { - return -b.compare(sign * (int64_t)numerator.low); - } - - Int128 nbdLow, nbdHigh, dbnLow, dbnHigh; - DMul<Int128, uint64_t>::mul(numerator, b.denominator, nbdLow, nbdHigh); - DMul<Int128, uint64_t>::mul(denominator, b.numerator, dbnLow, dbnHigh); - - int32_t cmp = nbdHigh.ucmp(dbnHigh); - if (cmp) { - return cmp * sign; - } - return nbdLow.ucmp(dbnLow) * sign; +int32_t btConvexHullInternal::Rational128::compare(const Rational128& b) const +{ + if (sign != b.sign) { + return sign - b.sign; + } + else if (sign == 0) { + return 0; + } + if (isInt64) { + return -b.compare(sign * (int64_t)numerator.low); + } + + Int128 nbdLow, nbdHigh, dbnLow, dbnHigh; + DMul<Int128, uint64_t>::mul(numerator, b.denominator, nbdLow, nbdHigh); + DMul<Int128, uint64_t>::mul(denominator, b.numerator, dbnLow, dbnHigh); + + int32_t cmp = nbdHigh.ucmp(dbnHigh); + if (cmp) { + return cmp * sign; + } + return nbdLow.ucmp(dbnLow) * sign; } -int32_t btConvexHullInternal::Rational128::compare(int64_t b) const { - if (isInt64) { - int64_t a = sign * (int64_t)numerator.low; - return (a > b) ? 1 : (a < b) ? -1 : 0; - } - if (b > 0) { - if (sign <= 0) { - return -1; - } - } else if (b < 0) { - if (sign >= 0) { - return 1; - } - b = -b; - } else { - return sign; - } - - return numerator.ucmp(denominator * b) * sign; +int32_t btConvexHullInternal::Rational128::compare(int64_t b) const +{ + if (isInt64) { + int64_t a = sign * (int64_t)numerator.low; + return (a > b) ? 1 : (a < b) ? -1 : 0; + } + if (b > 0) { + if (sign <= 0) { + return -1; + } + } + else if (b < 0) { + if (sign >= 0) { + return 1; + } + b = -b; + } + else { + return sign; + } + + return numerator.ucmp(denominator * b) * sign; } -btConvexHullInternal::Edge *btConvexHullInternal::newEdgePair(Vertex *from, Vertex *to) { - btAssert(from && to); - Edge *e = edgePool.newObject(); - Edge *r = edgePool.newObject(); - e->reverse = r; - r->reverse = e; - e->copy = mergeStamp; - r->copy = mergeStamp; - e->target = to; - r->target = from; - e->face = NULL; - r->face = NULL; - usedEdgePairs++; - if (usedEdgePairs > maxUsedEdgePairs) { - maxUsedEdgePairs = usedEdgePairs; - } - return e; +btConvexHullInternal::Edge* btConvexHullInternal::newEdgePair(Vertex* from, Vertex* to) +{ + btAssert(from && to); + Edge* e = edgePool.newObject(); + Edge* r = edgePool.newObject(); + e->reverse = r; + r->reverse = e; + e->copy = mergeStamp; + r->copy = mergeStamp; + e->target = to; + r->target = from; + e->face = NULL; + r->face = NULL; + usedEdgePairs++; + if (usedEdgePairs > maxUsedEdgePairs) { + maxUsedEdgePairs = usedEdgePairs; + } + return e; } -bool btConvexHullInternal::mergeProjection(IntermediateHull &h0, IntermediateHull &h1, Vertex *&c0, Vertex *&c1) { - Vertex *v0 = h0.maxYx; - Vertex *v1 = h1.minYx; - if ((v0->point.x == v1->point.x) && (v0->point.y == v1->point.y)) { - btAssert(v0->point.z < v1->point.z); - Vertex *v1p = v1->prev; - if (v1p == v1) { - c0 = v0; - if (v1->edges) { - btAssert(v1->edges->next == v1->edges); - v1 = v1->edges->target; - btAssert(v1->edges->next == v1->edges); - } - c1 = v1; - return false; - } - Vertex *v1n = v1->next; - v1p->next = v1n; - v1n->prev = v1p; - if (v1 == h1.minXy) { - if ((v1n->point.x < v1p->point.x) || ((v1n->point.x == v1p->point.x) && (v1n->point.y < v1p->point.y))) { - h1.minXy = v1n; - } else { - h1.minXy = v1p; - } - } - if (v1 == h1.maxXy) { - if ((v1n->point.x > v1p->point.x) || ((v1n->point.x == v1p->point.x) && (v1n->point.y > v1p->point.y))) { - h1.maxXy = v1n; - } else { - h1.maxXy = v1p; - } - } - } - - v0 = h0.maxXy; - v1 = h1.maxXy; - Vertex *v00 = NULL; - Vertex *v10 = NULL; - int32_t sign = 1; - - for (int32_t side = 0; side <= 1; side++) { - int32_t dx = (v1->point.x - v0->point.x) * sign; - if (dx > 0) { - while (true) { - int32_t dy = v1->point.y - v0->point.y; - - Vertex *w0 = side ? v0->next : v0->prev; - if (w0 != v0) { - int32_t dx0 = (w0->point.x - v0->point.x) * sign; - int32_t dy0 = w0->point.y - v0->point.y; - if ((dy0 <= 0) && ((dx0 == 0) || ((dx0 < 0) && (dy0 * dx <= dy * dx0)))) { - v0 = w0; - dx = (v1->point.x - v0->point.x) * sign; - continue; - } - } - - Vertex *w1 = side ? v1->next : v1->prev; - if (w1 != v1) { - int32_t dx1 = (w1->point.x - v1->point.x) * sign; - int32_t dy1 = w1->point.y - v1->point.y; - int32_t dxn = (w1->point.x - v0->point.x) * sign; - if ((dxn > 0) && (dy1 < 0) && ((dx1 == 0) || ((dx1 < 0) && (dy1 * dx < dy * dx1)))) { - v1 = w1; - dx = dxn; - continue; - } - } - - break; - } - } else if (dx < 0) { - while (true) { - int32_t dy = v1->point.y - v0->point.y; - - Vertex *w1 = side ? v1->prev : v1->next; - if (w1 != v1) { - int32_t dx1 = (w1->point.x - v1->point.x) * sign; - int32_t dy1 = w1->point.y - v1->point.y; - if ((dy1 >= 0) && ((dx1 == 0) || ((dx1 < 0) && (dy1 * dx <= dy * dx1)))) { - v1 = w1; - dx = (v1->point.x - v0->point.x) * sign; - continue; - } - } - - Vertex *w0 = side ? v0->prev : v0->next; - if (w0 != v0) { - int32_t dx0 = (w0->point.x - v0->point.x) * sign; - int32_t dy0 = w0->point.y - v0->point.y; - int32_t dxn = (v1->point.x - w0->point.x) * sign; - if ((dxn < 0) && (dy0 > 0) && ((dx0 == 0) || ((dx0 < 0) && (dy0 * dx < dy * dx0)))) { - v0 = w0; - dx = dxn; - continue; - } - } - - break; - } - } else { - int32_t x = v0->point.x; - int32_t y0 = v0->point.y; - Vertex *w0 = v0; - Vertex *t; - while (((t = side ? w0->next : w0->prev) != v0) && (t->point.x == x) && (t->point.y <= y0)) { - w0 = t; - y0 = t->point.y; - } - v0 = w0; - - int32_t y1 = v1->point.y; - Vertex *w1 = v1; - while (((t = side ? w1->prev : w1->next) != v1) && (t->point.x == x) && (t->point.y >= y1)) { - w1 = t; - y1 = t->point.y; - } - v1 = w1; - } - - if (side == 0) { - v00 = v0; - v10 = v1; - - v0 = h0.minXy; - v1 = h1.minXy; - sign = -1; - } - } - - v0->prev = v1; - v1->next = v0; - - v00->next = v10; - v10->prev = v00; - - if (h1.minXy->point.x < h0.minXy->point.x) { - h0.minXy = h1.minXy; - } - if (h1.maxXy->point.x >= h0.maxXy->point.x) { - h0.maxXy = h1.maxXy; - } - - h0.maxYx = h1.maxYx; - - c0 = v00; - c1 = v10; - - return true; +bool btConvexHullInternal::mergeProjection(IntermediateHull& h0, IntermediateHull& h1, Vertex*& c0, Vertex*& c1) +{ + Vertex* v0 = h0.maxYx; + Vertex* v1 = h1.minYx; + if ((v0->point.x == v1->point.x) && (v0->point.y == v1->point.y)) { + btAssert(v0->point.z < v1->point.z); + Vertex* v1p = v1->prev; + if (v1p == v1) { + c0 = v0; + if (v1->edges) { + btAssert(v1->edges->next == v1->edges); + v1 = v1->edges->target; + btAssert(v1->edges->next == v1->edges); + } + c1 = v1; + return false; + } + Vertex* v1n = v1->next; + v1p->next = v1n; + v1n->prev = v1p; + if (v1 == h1.minXy) { + if ((v1n->point.x < v1p->point.x) || ((v1n->point.x == v1p->point.x) && (v1n->point.y < v1p->point.y))) { + h1.minXy = v1n; + } + else { + h1.minXy = v1p; + } + } + if (v1 == h1.maxXy) { + if ((v1n->point.x > v1p->point.x) || ((v1n->point.x == v1p->point.x) && (v1n->point.y > v1p->point.y))) { + h1.maxXy = v1n; + } + else { + h1.maxXy = v1p; + } + } + } + + v0 = h0.maxXy; + v1 = h1.maxXy; + Vertex* v00 = NULL; + Vertex* v10 = NULL; + int32_t sign = 1; + + for (int32_t side = 0; side <= 1; side++) { + int32_t dx = (v1->point.x - v0->point.x) * sign; + if (dx > 0) { + while (true) { + int32_t dy = v1->point.y - v0->point.y; + + Vertex* w0 = side ? v0->next : v0->prev; + if (w0 != v0) { + int32_t dx0 = (w0->point.x - v0->point.x) * sign; + int32_t dy0 = w0->point.y - v0->point.y; + if ((dy0 <= 0) && ((dx0 == 0) || ((dx0 < 0) && (dy0 * dx <= dy * dx0)))) { + v0 = w0; + dx = (v1->point.x - v0->point.x) * sign; + continue; + } + } + + Vertex* w1 = side ? v1->next : v1->prev; + if (w1 != v1) { + int32_t dx1 = (w1->point.x - v1->point.x) * sign; + int32_t dy1 = w1->point.y - v1->point.y; + int32_t dxn = (w1->point.x - v0->point.x) * sign; + if ((dxn > 0) && (dy1 < 0) && ((dx1 == 0) || ((dx1 < 0) && (dy1 * dx < dy * dx1)))) { + v1 = w1; + dx = dxn; + continue; + } + } + + break; + } + } + else if (dx < 0) { + while (true) { + int32_t dy = v1->point.y - v0->point.y; + + Vertex* w1 = side ? v1->prev : v1->next; + if (w1 != v1) { + int32_t dx1 = (w1->point.x - v1->point.x) * sign; + int32_t dy1 = w1->point.y - v1->point.y; + if ((dy1 >= 0) && ((dx1 == 0) || ((dx1 < 0) && (dy1 * dx <= dy * dx1)))) { + v1 = w1; + dx = (v1->point.x - v0->point.x) * sign; + continue; + } + } + + Vertex* w0 = side ? v0->prev : v0->next; + if (w0 != v0) { + int32_t dx0 = (w0->point.x - v0->point.x) * sign; + int32_t dy0 = w0->point.y - v0->point.y; + int32_t dxn = (v1->point.x - w0->point.x) * sign; + if ((dxn < 0) && (dy0 > 0) && ((dx0 == 0) || ((dx0 < 0) && (dy0 * dx < dy * dx0)))) { + v0 = w0; + dx = dxn; + continue; + } + } + + break; + } + } + else { + int32_t x = v0->point.x; + int32_t y0 = v0->point.y; + Vertex* w0 = v0; + Vertex* t; + while (((t = side ? w0->next : w0->prev) != v0) && (t->point.x == x) && (t->point.y <= y0)) { + w0 = t; + y0 = t->point.y; + } + v0 = w0; + + int32_t y1 = v1->point.y; + Vertex* w1 = v1; + while (((t = side ? w1->prev : w1->next) != v1) && (t->point.x == x) && (t->point.y >= y1)) { + w1 = t; + y1 = t->point.y; + } + v1 = w1; + } + + if (side == 0) { + v00 = v0; + v10 = v1; + + v0 = h0.minXy; + v1 = h1.minXy; + sign = -1; + } + } + + v0->prev = v1; + v1->next = v0; + + v00->next = v10; + v10->prev = v00; + + if (h1.minXy->point.x < h0.minXy->point.x) { + h0.minXy = h1.minXy; + } + if (h1.maxXy->point.x >= h0.maxXy->point.x) { + h0.maxXy = h1.maxXy; + } + + h0.maxYx = h1.maxYx; + + c0 = v00; + c1 = v10; + + return true; } -void btConvexHullInternal::computeInternal(int32_t start, int32_t end, IntermediateHull &result) { - int32_t n = end - start; - switch (n) { - case 0: - result.minXy = NULL; - result.maxXy = NULL; - result.minYx = NULL; - result.maxYx = NULL; - return; - case 2: { - Vertex *v = originalVertices[start]; - Vertex *w = v + 1; - if (v->point != w->point) { - int32_t dx = v->point.x - w->point.x; - int32_t dy = v->point.y - w->point.y; - - if ((dx == 0) && (dy == 0)) { - if (v->point.z > w->point.z) { - Vertex *t = w; - w = v; - v = t; - } - btAssert(v->point.z < w->point.z); - v->next = v; - v->prev = v; - result.minXy = v; - result.maxXy = v; - result.minYx = v; - result.maxYx = v; - } else { - v->next = w; - v->prev = w; - w->next = v; - w->prev = v; - - if ((dx < 0) || ((dx == 0) && (dy < 0))) { - result.minXy = v; - result.maxXy = w; - } else { - result.minXy = w; - result.maxXy = v; - } - - if ((dy < 0) || ((dy == 0) && (dx < 0))) { - result.minYx = v; - result.maxYx = w; - } else { - result.minYx = w; - result.maxYx = v; - } - } - - Edge *e = newEdgePair(v, w); - e->link(e); - v->edges = e; - - e = e->reverse; - e->link(e); - w->edges = e; - - return; - } - } - // lint -fallthrough - case 1: { - Vertex *v = originalVertices[start]; - v->edges = NULL; - v->next = v; - v->prev = v; - - result.minXy = v; - result.maxXy = v; - result.minYx = v; - result.maxYx = v; - - return; - } - } - - int32_t split0 = start + n / 2; - Point32 p = originalVertices[split0 - 1]->point; - int32_t split1 = split0; - while ((split1 < end) && (originalVertices[split1]->point == p)) { - split1++; - } - computeInternal(start, split0, result); - IntermediateHull hull1; - computeInternal(split1, end, hull1); +void btConvexHullInternal::computeInternal(int32_t start, int32_t end, IntermediateHull& result) +{ + int32_t n = end - start; + switch (n) { + case 0: + result.minXy = NULL; + result.maxXy = NULL; + result.minYx = NULL; + result.maxYx = NULL; + return; + case 2: { + Vertex* v = originalVertices[start]; + Vertex* w = v + 1; + if (v->point != w->point) { + int32_t dx = v->point.x - w->point.x; + int32_t dy = v->point.y - w->point.y; + + if ((dx == 0) && (dy == 0)) { + if (v->point.z > w->point.z) { + Vertex* t = w; + w = v; + v = t; + } + btAssert(v->point.z < w->point.z); + v->next = v; + v->prev = v; + result.minXy = v; + result.maxXy = v; + result.minYx = v; + result.maxYx = v; + } + else { + v->next = w; + v->prev = w; + w->next = v; + w->prev = v; + + if ((dx < 0) || ((dx == 0) && (dy < 0))) { + result.minXy = v; + result.maxXy = w; + } + else { + result.minXy = w; + result.maxXy = v; + } + + if ((dy < 0) || ((dy == 0) && (dx < 0))) { + result.minYx = v; + result.maxYx = w; + } + else { + result.minYx = w; + result.maxYx = v; + } + } + + Edge* e = newEdgePair(v, w); + e->link(e); + v->edges = e; + + e = e->reverse; + e->link(e); + w->edges = e; + + return; + } + } + // lint -fallthrough + case 1: { + Vertex* v = originalVertices[start]; + v->edges = NULL; + v->next = v; + v->prev = v; + + result.minXy = v; + result.maxXy = v; + result.minYx = v; + result.maxYx = v; + + return; + } + } + + int32_t split0 = start + n / 2; + Point32 p = originalVertices[split0 - 1]->point; + int32_t split1 = split0; + while ((split1 < end) && (originalVertices[split1]->point == p)) { + split1++; + } + computeInternal(start, split0, result); + IntermediateHull hull1; + computeInternal(split1, end, hull1); #ifdef DEBUG_CONVEX_HULL - printf("\n\nMerge\n"); - result.print(); - hull1.print(); + printf("\n\nMerge\n"); + result.print(); + hull1.print(); #endif - merge(result, hull1); + merge(result, hull1); #ifdef DEBUG_CONVEX_HULL - printf("\n Result\n"); - result.print(); + printf("\n Result\n"); + result.print(); #endif } #ifdef DEBUG_CONVEX_HULL -void btConvexHullInternal::IntermediateHull::print() { - printf(" Hull\n"); - for (Vertex *v = minXy; v;) { - printf(" "); - v->print(); - if (v == maxXy) { - printf(" maxXy"); - } - if (v == minYx) { - printf(" minYx"); - } - if (v == maxYx) { - printf(" maxYx"); - } - if (v->next->prev != v) { - printf(" Inconsistency"); - } - printf("\n"); - v = v->next; - if (v == minXy) { - break; - } - } - if (minXy) { - minXy->copy = (minXy->copy == -1) ? -2 : -1; - minXy->printGraph(); - } +void btConvexHullInternal::IntermediateHull::print() +{ + printf(" Hull\n"); + for (Vertex* v = minXy; v;) { + printf(" "); + v->print(); + if (v == maxXy) { + printf(" maxXy"); + } + if (v == minYx) { + printf(" minYx"); + } + if (v == maxYx) { + printf(" maxYx"); + } + if (v->next->prev != v) { + printf(" Inconsistency"); + } + printf("\n"); + v = v->next; + if (v == minXy) { + break; + } + } + if (minXy) { + minXy->copy = (minXy->copy == -1) ? -2 : -1; + minXy->printGraph(); + } } -void btConvexHullInternal::Vertex::printGraph() { - print(); - printf("\nEdges\n"); - Edge *e = edges; - if (e) { - do { - e->print(); - printf("\n"); - e = e->next; - } while (e != edges); - do { - Vertex *v = e->target; - if (v->copy != copy) { - v->copy = copy; - v->printGraph(); - } - e = e->next; - } while (e != edges); - } +void btConvexHullInternal::Vertex::printGraph() +{ + print(); + printf("\nEdges\n"); + Edge* e = edges; + if (e) { + do { + e->print(); + printf("\n"); + e = e->next; + } while (e != edges); + do { + Vertex* v = e->target; + if (v->copy != copy) { + v->copy = copy; + v->printGraph(); + } + e = e->next; + } while (e != edges); + } } #endif -btConvexHullInternal::Orientation btConvexHullInternal::getOrientation(const Edge *prev, const Edge *next, const Point32 &s, const Point32 &t) { - btAssert(prev->reverse->target == next->reverse->target); - if (prev->next == next) { - if (prev->prev == next) { - Point64 n = t.cross(s); - Point64 m = (*prev->target - *next->reverse->target).cross(*next->target - *next->reverse->target); - btAssert(!m.isZero()); - int64_t dot = n.dot(m); - btAssert(dot != 0); - return (dot > 0) ? COUNTER_CLOCKWISE : CLOCKWISE; - } - return COUNTER_CLOCKWISE; - } else if (prev->prev == next) { - return CLOCKWISE; - } else { - return NONE; - } +btConvexHullInternal::Orientation btConvexHullInternal::getOrientation(const Edge* prev, const Edge* next, const Point32& s, const Point32& t) +{ + btAssert(prev->reverse->target == next->reverse->target); + if (prev->next == next) { + if (prev->prev == next) { + Point64 n = t.cross(s); + Point64 m = (*prev->target - *next->reverse->target).cross(*next->target - *next->reverse->target); + btAssert(!m.isZero()); + int64_t dot = n.dot(m); + btAssert(dot != 0); + return (dot > 0) ? COUNTER_CLOCKWISE : CLOCKWISE; + } + return COUNTER_CLOCKWISE; + } + else if (prev->prev == next) { + return CLOCKWISE; + } + else { + return NONE; + } } -btConvexHullInternal::Edge *btConvexHullInternal::findMaxAngle(bool ccw, const Vertex *start, const Point32 &s, const Point64 &rxs, const Point64 &sxrxs, Rational64 &minCot) { - Edge *minEdge = NULL; +btConvexHullInternal::Edge* btConvexHullInternal::findMaxAngle(bool ccw, const Vertex* start, const Point32& s, const Point64& rxs, const Point64& sxrxs, Rational64& minCot) +{ + Edge* minEdge = NULL; #ifdef DEBUG_CONVEX_HULL - printf("find max edge for %d\n", start->point.index); + printf("find max edge for %d\n", start->point.index); #endif - Edge *e = start->edges; - if (e) { - do { - if (e->copy > mergeStamp) { - Point32 t = *e->target - *start; - Rational64 cot(t.dot(sxrxs), t.dot(rxs)); + Edge* e = start->edges; + if (e) { + do { + if (e->copy > mergeStamp) { + Point32 t = *e->target - *start; + Rational64 cot(t.dot(sxrxs), t.dot(rxs)); #ifdef DEBUG_CONVEX_HULL - printf(" Angle is %f (%d) for ", (float)btAtan(cot.toScalar()), (int32_t)cot.isNaN()); - e->print(); + printf(" Angle is %f (%d) for ", (float)btAtan(cot.toScalar()), (int32_t)cot.isNaN()); + e->print(); #endif - if (cot.isNaN()) { - btAssert(ccw ? (t.dot(s) < 0) : (t.dot(s) > 0)); - } else { - int32_t cmp; - if (minEdge == NULL) { - minCot = cot; - minEdge = e; - } else if ((cmp = cot.compare(minCot)) < 0) { - minCot = cot; - minEdge = e; - } else if ((cmp == 0) && (ccw == (getOrientation(minEdge, e, s, t) == COUNTER_CLOCKWISE))) { - minEdge = e; - } - } + if (cot.isNaN()) { + btAssert(ccw ? (t.dot(s) < 0) : (t.dot(s) > 0)); + } + else { + int32_t cmp; + if (minEdge == NULL) { + minCot = cot; + minEdge = e; + } + else if ((cmp = cot.compare(minCot)) < 0) { + minCot = cot; + minEdge = e; + } + else if ((cmp == 0) && (ccw == (getOrientation(minEdge, e, s, t) == COUNTER_CLOCKWISE))) { + minEdge = e; + } + } #ifdef DEBUG_CONVEX_HULL - printf("\n"); + printf("\n"); #endif - } - e = e->next; - } while (e != start->edges); - } - return minEdge; + } + e = e->next; + } while (e != start->edges); + } + return minEdge; } -void btConvexHullInternal::findEdgeForCoplanarFaces(Vertex *c0, Vertex *c1, Edge *&e0, Edge *&e1, Vertex *stop0, Vertex *stop1) { - Edge *start0 = e0; - Edge *start1 = e1; - Point32 et0 = start0 ? start0->target->point : c0->point; - Point32 et1 = start1 ? start1->target->point : c1->point; - Point32 s = c1->point - c0->point; - Point64 normal = ((start0 ? start0 : start1)->target->point - c0->point).cross(s); - int64_t dist = c0->point.dot(normal); - btAssert(!start1 || (start1->target->point.dot(normal) == dist)); - Point64 perp = s.cross(normal); - btAssert(!perp.isZero()); +void btConvexHullInternal::findEdgeForCoplanarFaces(Vertex* c0, Vertex* c1, Edge*& e0, Edge*& e1, Vertex* stop0, Vertex* stop1) +{ + Edge* start0 = e0; + Edge* start1 = e1; + Point32 et0 = start0 ? start0->target->point : c0->point; + Point32 et1 = start1 ? start1->target->point : c1->point; + Point32 s = c1->point - c0->point; + Point64 normal = ((start0 ? start0 : start1)->target->point - c0->point).cross(s); + int64_t dist = c0->point.dot(normal); + btAssert(!start1 || (start1->target->point.dot(normal) == dist)); + Point64 perp = s.cross(normal); + btAssert(!perp.isZero()); #ifdef DEBUG_CONVEX_HULL - printf(" Advancing %d %d (%p %p, %d %d)\n", c0->point.index, c1->point.index, start0, start1, start0 ? start0->target->point.index : -1, start1 ? start1->target->point.index : -1); + printf(" Advancing %d %d (%p %p, %d %d)\n", c0->point.index, c1->point.index, start0, start1, start0 ? start0->target->point.index : -1, start1 ? start1->target->point.index : -1); #endif - int64_t maxDot0 = et0.dot(perp); - if (e0) { - while (e0->target != stop0) { - Edge *e = e0->reverse->prev; - if (e->target->point.dot(normal) < dist) { - break; - } - btAssert(e->target->point.dot(normal) == dist); - if (e->copy == mergeStamp) { - break; - } - int64_t dot = e->target->point.dot(perp); - if (dot <= maxDot0) { - break; - } - maxDot0 = dot; - e0 = e; - et0 = e->target->point; - } - } - - int64_t maxDot1 = et1.dot(perp); - if (e1) { - while (e1->target != stop1) { - Edge *e = e1->reverse->next; - if (e->target->point.dot(normal) < dist) { - break; - } - btAssert(e->target->point.dot(normal) == dist); - if (e->copy == mergeStamp) { - break; - } - int64_t dot = e->target->point.dot(perp); - if (dot <= maxDot1) { - break; - } - maxDot1 = dot; - e1 = e; - et1 = e->target->point; - } - } + int64_t maxDot0 = et0.dot(perp); + if (e0) { + while (e0->target != stop0) { + Edge* e = e0->reverse->prev; + if (e->target->point.dot(normal) < dist) { + break; + } + btAssert(e->target->point.dot(normal) == dist); + if (e->copy == mergeStamp) { + break; + } + int64_t dot = e->target->point.dot(perp); + if (dot <= maxDot0) { + break; + } + maxDot0 = dot; + e0 = e; + et0 = e->target->point; + } + } + + int64_t maxDot1 = et1.dot(perp); + if (e1) { + while (e1->target != stop1) { + Edge* e = e1->reverse->next; + if (e->target->point.dot(normal) < dist) { + break; + } + btAssert(e->target->point.dot(normal) == dist); + if (e->copy == mergeStamp) { + break; + } + int64_t dot = e->target->point.dot(perp); + if (dot <= maxDot1) { + break; + } + maxDot1 = dot; + e1 = e; + et1 = e->target->point; + } + } #ifdef DEBUG_CONVEX_HULL - printf(" Starting at %d %d\n", et0.index, et1.index); + printf(" Starting at %d %d\n", et0.index, et1.index); #endif - int64_t dx = maxDot1 - maxDot0; - if (dx > 0) { - while (true) { - int64_t dy = (et1 - et0).dot(s); - - if (e0 && (e0->target != stop0)) { - Edge *f0 = e0->next->reverse; - if (f0->copy > mergeStamp) { - int64_t dx0 = (f0->target->point - et0).dot(perp); - int64_t dy0 = (f0->target->point - et0).dot(s); - if ((dx0 == 0) ? (dy0 < 0) : ((dx0 < 0) && (Rational64(dy0, dx0).compare(Rational64(dy, dx)) >= 0))) { - et0 = f0->target->point; - dx = (et1 - et0).dot(perp); - e0 = (e0 == start0) ? NULL : f0; - continue; - } - } - } - - if (e1 && (e1->target != stop1)) { - Edge *f1 = e1->reverse->next; - if (f1->copy > mergeStamp) { - Point32 d1 = f1->target->point - et1; - if (d1.dot(normal) == 0) { - int64_t dx1 = d1.dot(perp); - int64_t dy1 = d1.dot(s); - int64_t dxn = (f1->target->point - et0).dot(perp); - if ((dxn > 0) && ((dx1 == 0) ? (dy1 < 0) : ((dx1 < 0) && (Rational64(dy1, dx1).compare(Rational64(dy, dx)) > 0)))) { - e1 = f1; - et1 = e1->target->point; - dx = dxn; - continue; - } - } else { - btAssert((e1 == start1) && (d1.dot(normal) < 0)); - } - } - } - - break; - } - } else if (dx < 0) { - while (true) { - int64_t dy = (et1 - et0).dot(s); - - if (e1 && (e1->target != stop1)) { - Edge *f1 = e1->prev->reverse; - if (f1->copy > mergeStamp) { - int64_t dx1 = (f1->target->point - et1).dot(perp); - int64_t dy1 = (f1->target->point - et1).dot(s); - if ((dx1 == 0) ? (dy1 > 0) : ((dx1 < 0) && (Rational64(dy1, dx1).compare(Rational64(dy, dx)) <= 0))) { - et1 = f1->target->point; - dx = (et1 - et0).dot(perp); - e1 = (e1 == start1) ? NULL : f1; - continue; - } - } - } - - if (e0 && (e0->target != stop0)) { - Edge *f0 = e0->reverse->prev; - if (f0->copy > mergeStamp) { - Point32 d0 = f0->target->point - et0; - if (d0.dot(normal) == 0) { - int64_t dx0 = d0.dot(perp); - int64_t dy0 = d0.dot(s); - int64_t dxn = (et1 - f0->target->point).dot(perp); - if ((dxn < 0) && ((dx0 == 0) ? (dy0 > 0) : ((dx0 < 0) && (Rational64(dy0, dx0).compare(Rational64(dy, dx)) < 0)))) { - e0 = f0; - et0 = e0->target->point; - dx = dxn; - continue; - } - } else { - btAssert((e0 == start0) && (d0.dot(normal) < 0)); - } - } - } - - break; - } - } + int64_t dx = maxDot1 - maxDot0; + if (dx > 0) { + while (true) { + int64_t dy = (et1 - et0).dot(s); + + if (e0 && (e0->target != stop0)) { + Edge* f0 = e0->next->reverse; + if (f0->copy > mergeStamp) { + int64_t dx0 = (f0->target->point - et0).dot(perp); + int64_t dy0 = (f0->target->point - et0).dot(s); + if ((dx0 == 0) ? (dy0 < 0) : ((dx0 < 0) && (Rational64(dy0, dx0).compare(Rational64(dy, dx)) >= 0))) { + et0 = f0->target->point; + dx = (et1 - et0).dot(perp); + e0 = (e0 == start0) ? NULL : f0; + continue; + } + } + } + + if (e1 && (e1->target != stop1)) { + Edge* f1 = e1->reverse->next; + if (f1->copy > mergeStamp) { + Point32 d1 = f1->target->point - et1; + if (d1.dot(normal) == 0) { + int64_t dx1 = d1.dot(perp); + int64_t dy1 = d1.dot(s); + int64_t dxn = (f1->target->point - et0).dot(perp); + if ((dxn > 0) && ((dx1 == 0) ? (dy1 < 0) : ((dx1 < 0) && (Rational64(dy1, dx1).compare(Rational64(dy, dx)) > 0)))) { + e1 = f1; + et1 = e1->target->point; + dx = dxn; + continue; + } + } + else { + btAssert((e1 == start1) && (d1.dot(normal) < 0)); + } + } + } + + break; + } + } + else if (dx < 0) { + while (true) { + int64_t dy = (et1 - et0).dot(s); + + if (e1 && (e1->target != stop1)) { + Edge* f1 = e1->prev->reverse; + if (f1->copy > mergeStamp) { + int64_t dx1 = (f1->target->point - et1).dot(perp); + int64_t dy1 = (f1->target->point - et1).dot(s); + if ((dx1 == 0) ? (dy1 > 0) : ((dx1 < 0) && (Rational64(dy1, dx1).compare(Rational64(dy, dx)) <= 0))) { + et1 = f1->target->point; + dx = (et1 - et0).dot(perp); + e1 = (e1 == start1) ? NULL : f1; + continue; + } + } + } + + if (e0 && (e0->target != stop0)) { + Edge* f0 = e0->reverse->prev; + if (f0->copy > mergeStamp) { + Point32 d0 = f0->target->point - et0; + if (d0.dot(normal) == 0) { + int64_t dx0 = d0.dot(perp); + int64_t dy0 = d0.dot(s); + int64_t dxn = (et1 - f0->target->point).dot(perp); + if ((dxn < 0) && ((dx0 == 0) ? (dy0 > 0) : ((dx0 < 0) && (Rational64(dy0, dx0).compare(Rational64(dy, dx)) < 0)))) { + e0 = f0; + et0 = e0->target->point; + dx = dxn; + continue; + } + } + else { + btAssert((e0 == start0) && (d0.dot(normal) < 0)); + } + } + } + + break; + } + } #ifdef DEBUG_CONVEX_HULL - printf(" Advanced edges to %d %d\n", et0.index, et1.index); + printf(" Advanced edges to %d %d\n", et0.index, et1.index); #endif } -void btConvexHullInternal::merge(IntermediateHull &h0, IntermediateHull &h1) { - if (!h1.maxXy) { - return; - } - if (!h0.maxXy) { - h0 = h1; - return; - } - - mergeStamp--; - - Vertex *c0 = NULL; - Edge *toPrev0 = NULL; - Edge *firstNew0 = NULL; - Edge *pendingHead0 = NULL; - Edge *pendingTail0 = NULL; - Vertex *c1 = NULL; - Edge *toPrev1 = NULL; - Edge *firstNew1 = NULL; - Edge *pendingHead1 = NULL; - Edge *pendingTail1 = NULL; - Point32 prevPoint; - - if (mergeProjection(h0, h1, c0, c1)) { - Point32 s = *c1 - *c0; - Point64 normal = Point32(0, 0, -1).cross(s); - Point64 t = s.cross(normal); - btAssert(!t.isZero()); - - Edge *e = c0->edges; - Edge *start0 = NULL; - if (e) { - do { - int64_t dot = (*e->target - *c0).dot(normal); - btAssert(dot <= 0); - if ((dot == 0) && ((*e->target - *c0).dot(t) > 0)) { - if (!start0 || (getOrientation(start0, e, s, Point32(0, 0, -1)) == CLOCKWISE)) { - start0 = e; - } - } - e = e->next; - } while (e != c0->edges); - } - - e = c1->edges; - Edge *start1 = NULL; - if (e) { - do { - int64_t dot = (*e->target - *c1).dot(normal); - btAssert(dot <= 0); - if ((dot == 0) && ((*e->target - *c1).dot(t) > 0)) { - if (!start1 || (getOrientation(start1, e, s, Point32(0, 0, -1)) == COUNTER_CLOCKWISE)) { - start1 = e; - } - } - e = e->next; - } while (e != c1->edges); - } - - if (start0 || start1) { - findEdgeForCoplanarFaces(c0, c1, start0, start1, NULL, NULL); - if (start0) { - c0 = start0->target; - } - if (start1) { - c1 = start1->target; - } - } - - prevPoint = c1->point; - prevPoint.z++; - } else { - prevPoint = c1->point; - prevPoint.x++; - } - - Vertex *first0 = c0; - Vertex *first1 = c1; - bool firstRun = true; - - while (true) { - Point32 s = *c1 - *c0; - Point32 r = prevPoint - c0->point; - Point64 rxs = r.cross(s); - Point64 sxrxs = s.cross(rxs); +void btConvexHullInternal::merge(IntermediateHull& h0, IntermediateHull& h1) +{ + if (!h1.maxXy) { + return; + } + if (!h0.maxXy) { + h0 = h1; + return; + } + + mergeStamp--; + + Vertex* c0 = NULL; + Edge* toPrev0 = NULL; + Edge* firstNew0 = NULL; + Edge* pendingHead0 = NULL; + Edge* pendingTail0 = NULL; + Vertex* c1 = NULL; + Edge* toPrev1 = NULL; + Edge* firstNew1 = NULL; + Edge* pendingHead1 = NULL; + Edge* pendingTail1 = NULL; + Point32 prevPoint; + + if (mergeProjection(h0, h1, c0, c1)) { + Point32 s = *c1 - *c0; + Point64 normal = Point32(0, 0, -1).cross(s); + Point64 t = s.cross(normal); + btAssert(!t.isZero()); + + Edge* e = c0->edges; + Edge* start0 = NULL; + if (e) { + do { + int64_t dot = (*e->target - *c0).dot(normal); + btAssert(dot <= 0); + if ((dot == 0) && ((*e->target - *c0).dot(t) > 0)) { + if (!start0 || (getOrientation(start0, e, s, Point32(0, 0, -1)) == CLOCKWISE)) { + start0 = e; + } + } + e = e->next; + } while (e != c0->edges); + } + + e = c1->edges; + Edge* start1 = NULL; + if (e) { + do { + int64_t dot = (*e->target - *c1).dot(normal); + btAssert(dot <= 0); + if ((dot == 0) && ((*e->target - *c1).dot(t) > 0)) { + if (!start1 || (getOrientation(start1, e, s, Point32(0, 0, -1)) == COUNTER_CLOCKWISE)) { + start1 = e; + } + } + e = e->next; + } while (e != c1->edges); + } + + if (start0 || start1) { + findEdgeForCoplanarFaces(c0, c1, start0, start1, NULL, NULL); + if (start0) { + c0 = start0->target; + } + if (start1) { + c1 = start1->target; + } + } + + prevPoint = c1->point; + prevPoint.z++; + } + else { + prevPoint = c1->point; + prevPoint.x++; + } + + Vertex* first0 = c0; + Vertex* first1 = c1; + bool firstRun = true; + + while (true) { + Point32 s = *c1 - *c0; + Point32 r = prevPoint - c0->point; + Point64 rxs = r.cross(s); + Point64 sxrxs = s.cross(rxs); #ifdef DEBUG_CONVEX_HULL - printf("\n Checking %d %d\n", c0->point.index, c1->point.index); + printf("\n Checking %d %d\n", c0->point.index, c1->point.index); #endif - Rational64 minCot0(0, 0); - Edge *min0 = findMaxAngle(false, c0, s, rxs, sxrxs, minCot0); - Rational64 minCot1(0, 0); - Edge *min1 = findMaxAngle(true, c1, s, rxs, sxrxs, minCot1); - if (!min0 && !min1) { - Edge *e = newEdgePair(c0, c1); - e->link(e); - c0->edges = e; - - e = e->reverse; - e->link(e); - c1->edges = e; - return; - } else { - int32_t cmp = !min0 ? 1 : !min1 ? -1 : minCot0.compare(minCot1); + Rational64 minCot0(0, 0); + Edge* min0 = findMaxAngle(false, c0, s, rxs, sxrxs, minCot0); + Rational64 minCot1(0, 0); + Edge* min1 = findMaxAngle(true, c1, s, rxs, sxrxs, minCot1); + if (!min0 && !min1) { + Edge* e = newEdgePair(c0, c1); + e->link(e); + c0->edges = e; + + e = e->reverse; + e->link(e); + c1->edges = e; + return; + } + else { + int32_t cmp = !min0 ? 1 : !min1 ? -1 : minCot0.compare(minCot1); #ifdef DEBUG_CONVEX_HULL - printf(" -> Result %d\n", cmp); + printf(" -> Result %d\n", cmp); #endif - if (firstRun || ((cmp >= 0) ? !minCot1.isNegativeInfinity() : !minCot0.isNegativeInfinity())) { - Edge *e = newEdgePair(c0, c1); - if (pendingTail0) { - pendingTail0->prev = e; - } else { - pendingHead0 = e; - } - e->next = pendingTail0; - pendingTail0 = e; - - e = e->reverse; - if (pendingTail1) { - pendingTail1->next = e; - } else { - pendingHead1 = e; - } - e->prev = pendingTail1; - pendingTail1 = e; - } - - Edge *e0 = min0; - Edge *e1 = min1; + if (firstRun || ((cmp >= 0) ? !minCot1.isNegativeInfinity() : !minCot0.isNegativeInfinity())) { + Edge* e = newEdgePair(c0, c1); + if (pendingTail0) { + pendingTail0->prev = e; + } + else { + pendingHead0 = e; + } + e->next = pendingTail0; + pendingTail0 = e; + + e = e->reverse; + if (pendingTail1) { + pendingTail1->next = e; + } + else { + pendingHead1 = e; + } + e->prev = pendingTail1; + pendingTail1 = e; + } + + Edge* e0 = min0; + Edge* e1 = min1; #ifdef DEBUG_CONVEX_HULL - printf(" Found min edges to %d %d\n", e0 ? e0->target->point.index : -1, e1 ? e1->target->point.index : -1); + printf(" Found min edges to %d %d\n", e0 ? e0->target->point.index : -1, e1 ? e1->target->point.index : -1); #endif - if (cmp == 0) { - findEdgeForCoplanarFaces(c0, c1, e0, e1, NULL, NULL); - } - - if ((cmp >= 0) && e1) { - if (toPrev1) { - for (Edge *e = toPrev1->next, *n = NULL; e != min1; e = n) { - n = e->next; - removeEdgePair(e); - } - } - - if (pendingTail1) { - if (toPrev1) { - toPrev1->link(pendingHead1); - } else { - min1->prev->link(pendingHead1); - firstNew1 = pendingHead1; - } - pendingTail1->link(min1); - pendingHead1 = NULL; - pendingTail1 = NULL; - } else if (!toPrev1) { - firstNew1 = min1; - } - - prevPoint = c1->point; - c1 = e1->target; - toPrev1 = e1->reverse; - } - - if ((cmp <= 0) && e0) { - if (toPrev0) { - for (Edge *e = toPrev0->prev, *n = NULL; e != min0; e = n) { - n = e->prev; - removeEdgePair(e); - } - } - - if (pendingTail0) { - if (toPrev0) { - pendingHead0->link(toPrev0); - } else { - pendingHead0->link(min0->next); - firstNew0 = pendingHead0; - } - min0->link(pendingTail0); - pendingHead0 = NULL; - pendingTail0 = NULL; - } else if (!toPrev0) { - firstNew0 = min0; - } - - prevPoint = c0->point; - c0 = e0->target; - toPrev0 = e0->reverse; - } - } - - if ((c0 == first0) && (c1 == first1)) { - if (toPrev0 == NULL) { - pendingHead0->link(pendingTail0); - c0->edges = pendingTail0; - } else { - for (Edge *e = toPrev0->prev, *n = NULL; e != firstNew0; e = n) { - n = e->prev; - removeEdgePair(e); - } - if (pendingTail0) { - pendingHead0->link(toPrev0); - firstNew0->link(pendingTail0); - } - } - - if (toPrev1 == NULL) { - pendingTail1->link(pendingHead1); - c1->edges = pendingTail1; - } else { - for (Edge *e = toPrev1->next, *n = NULL; e != firstNew1; e = n) { - n = e->next; - removeEdgePair(e); - } - if (pendingTail1) { - toPrev1->link(pendingHead1); - pendingTail1->link(firstNew1); - } - } - - return; - } - - firstRun = false; - } + if (cmp == 0) { + findEdgeForCoplanarFaces(c0, c1, e0, e1, NULL, NULL); + } + + if ((cmp >= 0) && e1) { + if (toPrev1) { + for (Edge *e = toPrev1->next, *n = NULL; e != min1; e = n) { + n = e->next; + removeEdgePair(e); + } + } + + if (pendingTail1) { + if (toPrev1) { + toPrev1->link(pendingHead1); + } + else { + min1->prev->link(pendingHead1); + firstNew1 = pendingHead1; + } + pendingTail1->link(min1); + pendingHead1 = NULL; + pendingTail1 = NULL; + } + else if (!toPrev1) { + firstNew1 = min1; + } + + prevPoint = c1->point; + c1 = e1->target; + toPrev1 = e1->reverse; + } + + if ((cmp <= 0) && e0) { + if (toPrev0) { + for (Edge *e = toPrev0->prev, *n = NULL; e != min0; e = n) { + n = e->prev; + removeEdgePair(e); + } + } + + if (pendingTail0) { + if (toPrev0) { + pendingHead0->link(toPrev0); + } + else { + pendingHead0->link(min0->next); + firstNew0 = pendingHead0; + } + min0->link(pendingTail0); + pendingHead0 = NULL; + pendingTail0 = NULL; + } + else if (!toPrev0) { + firstNew0 = min0; + } + + prevPoint = c0->point; + c0 = e0->target; + toPrev0 = e0->reverse; + } + } + + if ((c0 == first0) && (c1 == first1)) { + if (toPrev0 == NULL) { + pendingHead0->link(pendingTail0); + c0->edges = pendingTail0; + } + else { + for (Edge *e = toPrev0->prev, *n = NULL; e != firstNew0; e = n) { + n = e->prev; + removeEdgePair(e); + } + if (pendingTail0) { + pendingHead0->link(toPrev0); + firstNew0->link(pendingTail0); + } + } + + if (toPrev1 == NULL) { + pendingTail1->link(pendingHead1); + c1->edges = pendingTail1; + } + else { + for (Edge *e = toPrev1->next, *n = NULL; e != firstNew1; e = n) { + n = e->next; + removeEdgePair(e); + } + if (pendingTail1) { + toPrev1->link(pendingHead1); + pendingTail1->link(firstNew1); + } + } + + return; + } + + firstRun = false; + } } -static bool pointCmp(const btConvexHullInternal::Point32 &p, const btConvexHullInternal::Point32 &q) { - return (p.y < q.y) || ((p.y == q.y) && ((p.x < q.x) || ((p.x == q.x) && (p.z < q.z)))); +static bool pointCmp(const btConvexHullInternal::Point32& p, const btConvexHullInternal::Point32& q) +{ + return (p.y < q.y) || ((p.y == q.y) && ((p.x < q.x) || ((p.x == q.x) && (p.z < q.z)))); } -void btConvexHullInternal::compute(const void *coords, bool doubleCoords, int32_t stride, int32_t count) { - btVector3 min(btScalar(1e30), btScalar(1e30), btScalar(1e30)), max(btScalar(-1e30), btScalar(-1e30), btScalar(-1e30)); - const char *ptr = (const char *)coords; - if (doubleCoords) { - for (int32_t i = 0; i < count; i++) { - const double *v = (const double *)ptr; - btVector3 p((btScalar)v[0], (btScalar)v[1], (btScalar)v[2]); - ptr += stride; - min.setMin(p); - max.setMax(p); - } - } else { - for (int32_t i = 0; i < count; i++) { - const float *v = (const float *)ptr; - btVector3 p(v[0], v[1], v[2]); - ptr += stride; - min.setMin(p); - max.setMax(p); - } - } - - btVector3 s = max - min; - maxAxis = s.maxAxis(); - minAxis = s.minAxis(); - if (minAxis == maxAxis) { - minAxis = (maxAxis + 1) % 3; - } - medAxis = 3 - maxAxis - minAxis; - - s /= btScalar(10216); - if (((medAxis + 1) % 3) != maxAxis) { - s *= -1; - } - scaling = s; - - if (s[0] != 0) { - s[0] = btScalar(1) / s[0]; - } - if (s[1] != 0) { - s[1] = btScalar(1) / s[1]; - } - if (s[2] != 0) { - s[2] = btScalar(1) / s[2]; - } - - center = (min + max) * btScalar(0.5); - - btAlignedObjectArray<Point32> points; - points.resize(count); - ptr = (const char *)coords; - if (doubleCoords) { - for (int32_t i = 0; i < count; i++) { - const double *v = (const double *)ptr; - btVector3 p((btScalar)v[0], (btScalar)v[1], (btScalar)v[2]); - ptr += stride; - p = (p - center) * s; - points[i].x = (int32_t)p[medAxis]; - points[i].y = (int32_t)p[maxAxis]; - points[i].z = (int32_t)p[minAxis]; - points[i].index = i; - } - } else { - for (int32_t i = 0; i < count; i++) { - const float *v = (const float *)ptr; - btVector3 p(v[0], v[1], v[2]); - ptr += stride; - p = (p - center) * s; - points[i].x = (int32_t)p[medAxis]; - points[i].y = (int32_t)p[maxAxis]; - points[i].z = (int32_t)p[minAxis]; - points[i].index = i; - } - } - points.quickSort(pointCmp); - - vertexPool.reset(); - vertexPool.setArraySize(count); - originalVertices.resize(count); - for (int32_t i = 0; i < count; i++) { - Vertex *v = vertexPool.newObject(); - v->edges = NULL; - v->point = points[i]; - v->copy = -1; - originalVertices[i] = v; - } - - points.clear(); - - edgePool.reset(); - edgePool.setArraySize(6 * count); - - usedEdgePairs = 0; - maxUsedEdgePairs = 0; - - mergeStamp = -3; - - IntermediateHull hull; - computeInternal(0, count, hull); - vertexList = hull.minXy; +void btConvexHullInternal::compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count) +{ + btVector3 min(btScalar(1e30), btScalar(1e30), btScalar(1e30)), max(btScalar(-1e30), btScalar(-1e30), btScalar(-1e30)); + const char* ptr = (const char*)coords; + if (doubleCoords) { + for (int32_t i = 0; i < count; i++) { + const double* v = (const double*)ptr; + btVector3 p((btScalar)v[0], (btScalar)v[1], (btScalar)v[2]); + ptr += stride; + min.setMin(p); + max.setMax(p); + } + } + else { + for (int32_t i = 0; i < count; i++) { + const float* v = (const float*)ptr; + btVector3 p(v[0], v[1], v[2]); + ptr += stride; + min.setMin(p); + max.setMax(p); + } + } + + btVector3 s = max - min; + maxAxis = s.maxAxis(); + minAxis = s.minAxis(); + if (minAxis == maxAxis) { + minAxis = (maxAxis + 1) % 3; + } + medAxis = 3 - maxAxis - minAxis; + + s /= btScalar(10216); + if (((medAxis + 1) % 3) != maxAxis) { + s *= -1; + } + scaling = s; + + if (s[0] != 0) { + s[0] = btScalar(1) / s[0]; + } + if (s[1] != 0) { + s[1] = btScalar(1) / s[1]; + } + if (s[2] != 0) { + s[2] = btScalar(1) / s[2]; + } + + center = (min + max) * btScalar(0.5); + + btAlignedObjectArray<Point32> points; + points.resize(count); + ptr = (const char*)coords; + if (doubleCoords) { + for (int32_t i = 0; i < count; i++) { + const double* v = (const double*)ptr; + btVector3 p((btScalar)v[0], (btScalar)v[1], (btScalar)v[2]); + ptr += stride; + p = (p - center) * s; + points[i].x = (int32_t)p[medAxis]; + points[i].y = (int32_t)p[maxAxis]; + points[i].z = (int32_t)p[minAxis]; + points[i].index = i; + } + } + else { + for (int32_t i = 0; i < count; i++) { + const float* v = (const float*)ptr; + btVector3 p(v[0], v[1], v[2]); + ptr += stride; + p = (p - center) * s; + points[i].x = (int32_t)p[medAxis]; + points[i].y = (int32_t)p[maxAxis]; + points[i].z = (int32_t)p[minAxis]; + points[i].index = i; + } + } + points.quickSort(pointCmp); + + vertexPool.reset(); + vertexPool.setArraySize(count); + originalVertices.resize(count); + for (int32_t i = 0; i < count; i++) { + Vertex* v = vertexPool.newObject(); + v->edges = NULL; + v->point = points[i]; + v->copy = -1; + originalVertices[i] = v; + } + + points.clear(); + + edgePool.reset(); + edgePool.setArraySize(6 * count); + + usedEdgePairs = 0; + maxUsedEdgePairs = 0; + + mergeStamp = -3; + + IntermediateHull hull; + computeInternal(0, count, hull); + vertexList = hull.minXy; #ifdef DEBUG_CONVEX_HULL - printf("max. edges %d (3v = %d)", maxUsedEdgePairs, 3 * count); + printf("max. edges %d (3v = %d)", maxUsedEdgePairs, 3 * count); #endif } -btVector3 btConvexHullInternal::toBtVector(const Point32 &v) { - btVector3 p; - p[medAxis] = btScalar(v.x); - p[maxAxis] = btScalar(v.y); - p[minAxis] = btScalar(v.z); - return p * scaling; +btVector3 btConvexHullInternal::toBtVector(const Point32& v) +{ + btVector3 p; + p[medAxis] = btScalar(v.x); + p[maxAxis] = btScalar(v.y); + p[minAxis] = btScalar(v.z); + return p * scaling; } -btVector3 btConvexHullInternal::getBtNormal(Face *face) { - return toBtVector(face->dir0).cross(toBtVector(face->dir1)).normalized(); +btVector3 btConvexHullInternal::getBtNormal(Face* face) +{ + return toBtVector(face->dir0).cross(toBtVector(face->dir1)).normalized(); } -btVector3 btConvexHullInternal::getCoordinates(const Vertex *v) { - btVector3 p; - p[medAxis] = v->xvalue(); - p[maxAxis] = v->yvalue(); - p[minAxis] = v->zvalue(); - return p * scaling + center; +btVector3 btConvexHullInternal::getCoordinates(const Vertex* v) +{ + btVector3 p; + p[medAxis] = v->xvalue(); + p[maxAxis] = v->yvalue(); + p[minAxis] = v->zvalue(); + return p * scaling + center; } -btScalar btConvexHullInternal::shrink(btScalar amount, btScalar clampAmount) { - if (!vertexList) { - return 0; - } - int32_t stamp = --mergeStamp; - btAlignedObjectArray<Vertex *> stack; - vertexList->copy = stamp; - stack.push_back(vertexList); - btAlignedObjectArray<Face *> faces; - - Point32 ref = vertexList->point; - Int128 hullCenterX(0, 0); - Int128 hullCenterY(0, 0); - Int128 hullCenterZ(0, 0); - Int128 volume(0, 0); - - while (stack.size() > 0) { - Vertex *v = stack[stack.size() - 1]; - stack.pop_back(); - Edge *e = v->edges; - if (e) { - do { - if (e->target->copy != stamp) { - e->target->copy = stamp; - stack.push_back(e->target); - } - if (e->copy != stamp) { - Face *face = facePool.newObject(); - face->init(e->target, e->reverse->prev->target, v); - faces.push_back(face); - Edge *f = e; - - Vertex *a = NULL; - Vertex *b = NULL; - do { - if (a && b) { - int64_t vol = (v->point - ref).dot((a->point - ref).cross(b->point - ref)); - btAssert(vol >= 0); - Point32 c = v->point + a->point + b->point + ref; - hullCenterX += vol * c.x; - hullCenterY += vol * c.y; - hullCenterZ += vol * c.z; - volume += vol; - } - - btAssert(f->copy != stamp); - f->copy = stamp; - f->face = face; - - a = b; - b = f->target; - - f = f->reverse->prev; - } while (f != e); - } - e = e->next; - } while (e != v->edges); - } - } - - if (volume.getSign() <= 0) { - return 0; - } - - btVector3 hullCenter; - hullCenter[medAxis] = hullCenterX.toScalar(); - hullCenter[maxAxis] = hullCenterY.toScalar(); - hullCenter[minAxis] = hullCenterZ.toScalar(); - hullCenter /= 4 * volume.toScalar(); - hullCenter *= scaling; - - int32_t faceCount = faces.size(); - - if (clampAmount > 0) { - btScalar minDist = SIMD_INFINITY; - for (int32_t i = 0; i < faceCount; i++) { - btVector3 normal = getBtNormal(faces[i]); - btScalar dist = normal.dot(toBtVector(faces[i]->origin) - hullCenter); - if (dist < minDist) { - minDist = dist; - } - } - - if (minDist <= 0) { - return 0; - } - - amount = btMin(amount, minDist * clampAmount); - } - - uint32_t seed = 243703; - for (int32_t i = 0; i < faceCount; i++, seed = 1664525 * seed + 1013904223) { - btSwap(faces[i], faces[seed % faceCount]); - } - - for (int32_t i = 0; i < faceCount; i++) { - if (!shiftFace(faces[i], amount, stack)) { - return -amount; - } - } - - return amount; +btScalar btConvexHullInternal::shrink(btScalar amount, btScalar clampAmount) +{ + if (!vertexList) { + return 0; + } + int32_t stamp = --mergeStamp; + btAlignedObjectArray<Vertex*> stack; + vertexList->copy = stamp; + stack.push_back(vertexList); + btAlignedObjectArray<Face*> faces; + + Point32 ref = vertexList->point; + Int128 hullCenterX(0, 0); + Int128 hullCenterY(0, 0); + Int128 hullCenterZ(0, 0); + Int128 volume(0, 0); + + while (stack.size() > 0) { + Vertex* v = stack[stack.size() - 1]; + stack.pop_back(); + Edge* e = v->edges; + if (e) { + do { + if (e->target->copy != stamp) { + e->target->copy = stamp; + stack.push_back(e->target); + } + if (e->copy != stamp) { + Face* face = facePool.newObject(); + face->init(e->target, e->reverse->prev->target, v); + faces.push_back(face); + Edge* f = e; + + Vertex* a = NULL; + Vertex* b = NULL; + do { + if (a && b) { + int64_t vol = (v->point - ref).dot((a->point - ref).cross(b->point - ref)); + btAssert(vol >= 0); + Point32 c = v->point + a->point + b->point + ref; + hullCenterX += vol * c.x; + hullCenterY += vol * c.y; + hullCenterZ += vol * c.z; + volume += vol; + } + + btAssert(f->copy != stamp); + f->copy = stamp; + f->face = face; + + a = b; + b = f->target; + + f = f->reverse->prev; + } while (f != e); + } + e = e->next; + } while (e != v->edges); + } + } + + if (volume.getSign() <= 0) { + return 0; + } + + btVector3 hullCenter; + hullCenter[medAxis] = hullCenterX.toScalar(); + hullCenter[maxAxis] = hullCenterY.toScalar(); + hullCenter[minAxis] = hullCenterZ.toScalar(); + hullCenter /= 4 * volume.toScalar(); + hullCenter *= scaling; + + int32_t faceCount = faces.size(); + + if (clampAmount > 0) { + btScalar minDist = SIMD_INFINITY; + for (int32_t i = 0; i < faceCount; i++) { + btVector3 normal = getBtNormal(faces[i]); + btScalar dist = normal.dot(toBtVector(faces[i]->origin) - hullCenter); + if (dist < minDist) { + minDist = dist; + } + } + + if (minDist <= 0) { + return 0; + } + + amount = btMin(amount, minDist * clampAmount); + } + + uint32_t seed = 243703; + for (int32_t i = 0; i < faceCount; i++, seed = 1664525 * seed + 1013904223) { + btSwap(faces[i], faces[seed % faceCount]); + } + + for (int32_t i = 0; i < faceCount; i++) { + if (!shiftFace(faces[i], amount, stack)) { + return -amount; + } + } + + return amount; } -bool btConvexHullInternal::shiftFace(Face *face, btScalar amount, btAlignedObjectArray<Vertex *> stack) { - btVector3 origShift = getBtNormal(face) * -amount; - if (scaling[0] != 0) { - origShift[0] /= scaling[0]; - } - if (scaling[1] != 0) { - origShift[1] /= scaling[1]; - } - if (scaling[2] != 0) { - origShift[2] /= scaling[2]; - } - Point32 shift((int32_t)origShift[medAxis], (int32_t)origShift[maxAxis], (int32_t)origShift[minAxis]); - if (shift.isZero()) { - return true; - } - Point64 normal = face->getNormal(); +bool btConvexHullInternal::shiftFace(Face* face, btScalar amount, btAlignedObjectArray<Vertex*> stack) +{ + btVector3 origShift = getBtNormal(face) * -amount; + if (scaling[0] != 0) { + origShift[0] /= scaling[0]; + } + if (scaling[1] != 0) { + origShift[1] /= scaling[1]; + } + if (scaling[2] != 0) { + origShift[2] /= scaling[2]; + } + Point32 shift((int32_t)origShift[medAxis], (int32_t)origShift[maxAxis], (int32_t)origShift[minAxis]); + if (shift.isZero()) { + return true; + } + Point64 normal = face->getNormal(); #ifdef DEBUG_CONVEX_HULL - printf("\nShrinking face (%d %d %d) (%d %d %d) (%d %d %d) by (%d %d %d)\n", - face->origin.x, face->origin.y, face->origin.z, face->dir0.x, face->dir0.y, face->dir0.z, face->dir1.x, face->dir1.y, face->dir1.z, shift.x, shift.y, shift.z); + printf("\nShrinking face (%d %d %d) (%d %d %d) (%d %d %d) by (%d %d %d)\n", + face->origin.x, face->origin.y, face->origin.z, face->dir0.x, face->dir0.y, face->dir0.z, face->dir1.x, face->dir1.y, face->dir1.z, shift.x, shift.y, shift.z); #endif - int64_t origDot = face->origin.dot(normal); - Point32 shiftedOrigin = face->origin + shift; - int64_t shiftedDot = shiftedOrigin.dot(normal); - btAssert(shiftedDot <= origDot); - if (shiftedDot >= origDot) { - return false; - } + int64_t origDot = face->origin.dot(normal); + Point32 shiftedOrigin = face->origin + shift; + int64_t shiftedDot = shiftedOrigin.dot(normal); + btAssert(shiftedDot <= origDot); + if (shiftedDot >= origDot) { + return false; + } - Edge *intersection = NULL; + Edge* intersection = NULL; - Edge *startEdge = face->nearbyVertex->edges; + Edge* startEdge = face->nearbyVertex->edges; #ifdef DEBUG_CONVEX_HULL - printf("Start edge is "); - startEdge->print(); - printf(", normal is (%lld %lld %lld), shifted dot is %lld\n", normal.x, normal.y, normal.z, shiftedDot); + printf("Start edge is "); + startEdge->print(); + printf(", normal is (%lld %lld %lld), shifted dot is %lld\n", normal.x, normal.y, normal.z, shiftedDot); #endif - Rational128 optDot = face->nearbyVertex->dot(normal); - int32_t cmp = optDot.compare(shiftedDot); + Rational128 optDot = face->nearbyVertex->dot(normal); + int32_t cmp = optDot.compare(shiftedDot); #ifdef SHOW_ITERATIONS - int32_t n = 0; + int32_t n = 0; #endif - if (cmp >= 0) { - Edge *e = startEdge; - do { + if (cmp >= 0) { + Edge* e = startEdge; + do { #ifdef SHOW_ITERATIONS - n++; + n++; #endif - Rational128 dot = e->target->dot(normal); - btAssert(dot.compare(origDot) <= 0); + Rational128 dot = e->target->dot(normal); + btAssert(dot.compare(origDot) <= 0); #ifdef DEBUG_CONVEX_HULL - printf("Moving downwards, edge is "); - e->print(); - printf(", dot is %f (%f %lld)\n", (float)dot.toScalar(), (float)optDot.toScalar(), shiftedDot); + printf("Moving downwards, edge is "); + e->print(); + printf(", dot is %f (%f %lld)\n", (float)dot.toScalar(), (float)optDot.toScalar(), shiftedDot); #endif - if (dot.compare(optDot) < 0) { - int32_t c = dot.compare(shiftedDot); - optDot = dot; - e = e->reverse; - startEdge = e; - if (c < 0) { - intersection = e; - break; - } - cmp = c; - } - e = e->prev; - } while (e != startEdge); - - if (!intersection) { - return false; - } - } else { - Edge *e = startEdge; - do { + if (dot.compare(optDot) < 0) { + int32_t c = dot.compare(shiftedDot); + optDot = dot; + e = e->reverse; + startEdge = e; + if (c < 0) { + intersection = e; + break; + } + cmp = c; + } + e = e->prev; + } while (e != startEdge); + + if (!intersection) { + return false; + } + } + else { + Edge* e = startEdge; + do { #ifdef SHOW_ITERATIONS - n++; + n++; #endif - Rational128 dot = e->target->dot(normal); - btAssert(dot.compare(origDot) <= 0); + Rational128 dot = e->target->dot(normal); + btAssert(dot.compare(origDot) <= 0); #ifdef DEBUG_CONVEX_HULL - printf("Moving upwards, edge is "); - e->print(); - printf(", dot is %f (%f %lld)\n", (float)dot.toScalar(), (float)optDot.toScalar(), shiftedDot); + printf("Moving upwards, edge is "); + e->print(); + printf(", dot is %f (%f %lld)\n", (float)dot.toScalar(), (float)optDot.toScalar(), shiftedDot); #endif - if (dot.compare(optDot) > 0) { - cmp = dot.compare(shiftedDot); - if (cmp >= 0) { - intersection = e; - break; - } - optDot = dot; - e = e->reverse; - startEdge = e; - } - e = e->prev; - } while (e != startEdge); - - if (!intersection) { - return true; - } - } + if (dot.compare(optDot) > 0) { + cmp = dot.compare(shiftedDot); + if (cmp >= 0) { + intersection = e; + break; + } + optDot = dot; + e = e->reverse; + startEdge = e; + } + e = e->prev; + } while (e != startEdge); + + if (!intersection) { + return true; + } + } #ifdef SHOW_ITERATIONS - printf("Needed %d iterations to find initial intersection\n", n); + printf("Needed %d iterations to find initial intersection\n", n); #endif - if (cmp == 0) { - Edge *e = intersection->reverse->next; + if (cmp == 0) { + Edge* e = intersection->reverse->next; #ifdef SHOW_ITERATIONS - n = 0; + n = 0; #endif - while (e->target->dot(normal).compare(shiftedDot) <= 0) { + while (e->target->dot(normal).compare(shiftedDot) <= 0) { #ifdef SHOW_ITERATIONS - n++; + n++; #endif - e = e->next; - if (e == intersection->reverse) { - return true; - } + e = e->next; + if (e == intersection->reverse) { + return true; + } #ifdef DEBUG_CONVEX_HULL - printf("Checking for outwards edge, current edge is "); - e->print(); - printf("\n"); + printf("Checking for outwards edge, current edge is "); + e->print(); + printf("\n"); #endif - } + } #ifdef SHOW_ITERATIONS - printf("Needed %d iterations to check for complete containment\n", n); + printf("Needed %d iterations to check for complete containment\n", n); #endif - } + } - Edge *firstIntersection = NULL; - Edge *faceEdge = NULL; - Edge *firstFaceEdge = NULL; + Edge* firstIntersection = NULL; + Edge* faceEdge = NULL; + Edge* firstFaceEdge = NULL; #ifdef SHOW_ITERATIONS - int32_t m = 0; + int32_t m = 0; #endif - while (true) { + while (true) { #ifdef SHOW_ITERATIONS - m++; + m++; #endif #ifdef DEBUG_CONVEX_HULL - printf("Intersecting edge is "); - intersection->print(); - printf("\n"); + printf("Intersecting edge is "); + intersection->print(); + printf("\n"); #endif - if (cmp == 0) { - Edge *e = intersection->reverse->next; - startEdge = e; + if (cmp == 0) { + Edge* e = intersection->reverse->next; + startEdge = e; #ifdef SHOW_ITERATIONS - n = 0; + n = 0; #endif - while (true) { + while (true) { #ifdef SHOW_ITERATIONS - n++; + n++; #endif - if (e->target->dot(normal).compare(shiftedDot) >= 0) { - break; - } - intersection = e->reverse; - e = e->next; - if (e == startEdge) { - return true; - } - } + if (e->target->dot(normal).compare(shiftedDot) >= 0) { + break; + } + intersection = e->reverse; + e = e->next; + if (e == startEdge) { + return true; + } + } #ifdef SHOW_ITERATIONS - printf("Needed %d iterations to advance intersection\n", n); + printf("Needed %d iterations to advance intersection\n", n); #endif - } + } #ifdef DEBUG_CONVEX_HULL - printf("Advanced intersecting edge to "); - intersection->print(); - printf(", cmp = %d\n", cmp); + printf("Advanced intersecting edge to "); + intersection->print(); + printf(", cmp = %d\n", cmp); #endif - if (!firstIntersection) { - firstIntersection = intersection; - } else if (intersection == firstIntersection) { - break; - } + if (!firstIntersection) { + firstIntersection = intersection; + } + else if (intersection == firstIntersection) { + break; + } - int32_t prevCmp = cmp; - Edge *prevIntersection = intersection; - Edge *prevFaceEdge = faceEdge; + int32_t prevCmp = cmp; + Edge* prevIntersection = intersection; + Edge* prevFaceEdge = faceEdge; - Edge *e = intersection->reverse; + Edge* e = intersection->reverse; #ifdef SHOW_ITERATIONS - n = 0; + n = 0; #endif - while (true) { + while (true) { #ifdef SHOW_ITERATIONS - n++; + n++; #endif - e = e->reverse->prev; - btAssert(e != intersection->reverse); - cmp = e->target->dot(normal).compare(shiftedDot); + e = e->reverse->prev; + btAssert(e != intersection->reverse); + cmp = e->target->dot(normal).compare(shiftedDot); #ifdef DEBUG_CONVEX_HULL - printf("Testing edge "); - e->print(); - printf(" -> cmp = %d\n", cmp); + printf("Testing edge "); + e->print(); + printf(" -> cmp = %d\n", cmp); #endif - if (cmp >= 0) { - intersection = e; - break; - } - } + if (cmp >= 0) { + intersection = e; + break; + } + } #ifdef SHOW_ITERATIONS - printf("Needed %d iterations to find other intersection of face\n", n); + printf("Needed %d iterations to find other intersection of face\n", n); #endif - if (cmp > 0) { - Vertex *removed = intersection->target; - e = intersection->reverse; - if (e->prev == e) { - removed->edges = NULL; - } else { - removed->edges = e->prev; - e->prev->link(e->next); - e->link(e); - } + if (cmp > 0) { + Vertex* removed = intersection->target; + e = intersection->reverse; + if (e->prev == e) { + removed->edges = NULL; + } + else { + removed->edges = e->prev; + e->prev->link(e->next); + e->link(e); + } #ifdef DEBUG_CONVEX_HULL - printf("1: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); + printf("1: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); #endif - Point64 n0 = intersection->face->getNormal(); - Point64 n1 = intersection->reverse->face->getNormal(); - int64_t m00 = face->dir0.dot(n0); - int64_t m01 = face->dir1.dot(n0); - int64_t m10 = face->dir0.dot(n1); - int64_t m11 = face->dir1.dot(n1); - int64_t r0 = (intersection->face->origin - shiftedOrigin).dot(n0); - int64_t r1 = (intersection->reverse->face->origin - shiftedOrigin).dot(n1); - Int128 det = Int128::mul(m00, m11) - Int128::mul(m01, m10); - btAssert(det.getSign() != 0); - Vertex *v = vertexPool.newObject(); - v->point.index = -1; - v->copy = -1; - v->point128 = PointR128(Int128::mul(face->dir0.x * r0, m11) - Int128::mul(face->dir0.x * r1, m01) + Int128::mul(face->dir1.x * r1, m00) - Int128::mul(face->dir1.x * r0, m10) + det * shiftedOrigin.x, - Int128::mul(face->dir0.y * r0, m11) - Int128::mul(face->dir0.y * r1, m01) + Int128::mul(face->dir1.y * r1, m00) - Int128::mul(face->dir1.y * r0, m10) + det * shiftedOrigin.y, - Int128::mul(face->dir0.z * r0, m11) - Int128::mul(face->dir0.z * r1, m01) + Int128::mul(face->dir1.z * r1, m00) - Int128::mul(face->dir1.z * r0, m10) + det * shiftedOrigin.z, - det); - v->point.x = (int32_t)v->point128.xvalue(); - v->point.y = (int32_t)v->point128.yvalue(); - v->point.z = (int32_t)v->point128.zvalue(); - intersection->target = v; - v->edges = e; - - stack.push_back(v); - stack.push_back(removed); - stack.push_back(NULL); - } - - if (cmp || prevCmp || (prevIntersection->reverse->next->target != intersection->target)) { - faceEdge = newEdgePair(prevIntersection->target, intersection->target); - if (prevCmp == 0) { - faceEdge->link(prevIntersection->reverse->next); - } - if ((prevCmp == 0) || prevFaceEdge) { - prevIntersection->reverse->link(faceEdge); - } - if (cmp == 0) { - intersection->reverse->prev->link(faceEdge->reverse); - } - faceEdge->reverse->link(intersection->reverse); - } else { - faceEdge = prevIntersection->reverse->next; - } - - if (prevFaceEdge) { - if (prevCmp > 0) { - faceEdge->link(prevFaceEdge->reverse); - } else if (faceEdge != prevFaceEdge->reverse) { - stack.push_back(prevFaceEdge->target); - while (faceEdge->next != prevFaceEdge->reverse) { - Vertex *removed = faceEdge->next->target; - removeEdgePair(faceEdge->next); - stack.push_back(removed); + Point64 n0 = intersection->face->getNormal(); + Point64 n1 = intersection->reverse->face->getNormal(); + int64_t m00 = face->dir0.dot(n0); + int64_t m01 = face->dir1.dot(n0); + int64_t m10 = face->dir0.dot(n1); + int64_t m11 = face->dir1.dot(n1); + int64_t r0 = (intersection->face->origin - shiftedOrigin).dot(n0); + int64_t r1 = (intersection->reverse->face->origin - shiftedOrigin).dot(n1); + Int128 det = Int128::mul(m00, m11) - Int128::mul(m01, m10); + btAssert(det.getSign() != 0); + Vertex* v = vertexPool.newObject(); + v->point.index = -1; + v->copy = -1; + v->point128 = PointR128(Int128::mul(face->dir0.x * r0, m11) - Int128::mul(face->dir0.x * r1, m01) + + Int128::mul(face->dir1.x * r1, m00) - Int128::mul(face->dir1.x * r0, m10) + det * shiftedOrigin.x, + Int128::mul(face->dir0.y * r0, m11) - Int128::mul(face->dir0.y * r1, m01) + + Int128::mul(face->dir1.y * r1, m00) - Int128::mul(face->dir1.y * r0, m10) + det * shiftedOrigin.y, + Int128::mul(face->dir0.z * r0, m11) - Int128::mul(face->dir0.z * r1, m01) + + Int128::mul(face->dir1.z * r1, m00) - Int128::mul(face->dir1.z * r0, m10) + det * shiftedOrigin.z, + det); + v->point.x = (int32_t)v->point128.xvalue(); + v->point.y = (int32_t)v->point128.yvalue(); + v->point.z = (int32_t)v->point128.zvalue(); + intersection->target = v; + v->edges = e; + + stack.push_back(v); + stack.push_back(removed); + stack.push_back(NULL); + } + + if (cmp || prevCmp || (prevIntersection->reverse->next->target != intersection->target)) { + faceEdge = newEdgePair(prevIntersection->target, intersection->target); + if (prevCmp == 0) { + faceEdge->link(prevIntersection->reverse->next); + } + if ((prevCmp == 0) || prevFaceEdge) { + prevIntersection->reverse->link(faceEdge); + } + if (cmp == 0) { + intersection->reverse->prev->link(faceEdge->reverse); + } + faceEdge->reverse->link(intersection->reverse); + } + else { + faceEdge = prevIntersection->reverse->next; + } + + if (prevFaceEdge) { + if (prevCmp > 0) { + faceEdge->link(prevFaceEdge->reverse); + } + else if (faceEdge != prevFaceEdge->reverse) { + stack.push_back(prevFaceEdge->target); + while (faceEdge->next != prevFaceEdge->reverse) { + Vertex* removed = faceEdge->next->target; + removeEdgePair(faceEdge->next); + stack.push_back(removed); #ifdef DEBUG_CONVEX_HULL - printf("2: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); + printf("2: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); #endif - } - stack.push_back(NULL); - } - } - faceEdge->face = face; - faceEdge->reverse->face = intersection->face; - - if (!firstFaceEdge) { - firstFaceEdge = faceEdge; - } - } + } + stack.push_back(NULL); + } + } + faceEdge->face = face; + faceEdge->reverse->face = intersection->face; + + if (!firstFaceEdge) { + firstFaceEdge = faceEdge; + } + } #ifdef SHOW_ITERATIONS - printf("Needed %d iterations to process all intersections\n", m); + printf("Needed %d iterations to process all intersections\n", m); #endif - if (cmp > 0) { - firstFaceEdge->reverse->target = faceEdge->target; - firstIntersection->reverse->link(firstFaceEdge); - firstFaceEdge->link(faceEdge->reverse); - } else if (firstFaceEdge != faceEdge->reverse) { - stack.push_back(faceEdge->target); - while (firstFaceEdge->next != faceEdge->reverse) { - Vertex *removed = firstFaceEdge->next->target; - removeEdgePair(firstFaceEdge->next); - stack.push_back(removed); + if (cmp > 0) { + firstFaceEdge->reverse->target = faceEdge->target; + firstIntersection->reverse->link(firstFaceEdge); + firstFaceEdge->link(faceEdge->reverse); + } + else if (firstFaceEdge != faceEdge->reverse) { + stack.push_back(faceEdge->target); + while (firstFaceEdge->next != faceEdge->reverse) { + Vertex* removed = firstFaceEdge->next->target; + removeEdgePair(firstFaceEdge->next); + stack.push_back(removed); #ifdef DEBUG_CONVEX_HULL - printf("3: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); + printf("3: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z); #endif - } - stack.push_back(NULL); - } + } + stack.push_back(NULL); + } - btAssert(stack.size() > 0); - vertexList = stack[0]; + btAssert(stack.size() > 0); + vertexList = stack[0]; #ifdef DEBUG_CONVEX_HULL - printf("Removing part\n"); + printf("Removing part\n"); #endif #ifdef SHOW_ITERATIONS - n = 0; + n = 0; #endif - int32_t pos = 0; - while (pos < stack.size()) { - int32_t end = stack.size(); - while (pos < end) { - Vertex *kept = stack[pos++]; + int32_t pos = 0; + while (pos < stack.size()) { + int32_t end = stack.size(); + while (pos < end) { + Vertex* kept = stack[pos++]; #ifdef DEBUG_CONVEX_HULL - kept->print(); + kept->print(); #endif - bool deeper = false; - Vertex *removed; - while ((removed = stack[pos++]) != NULL) { + bool deeper = false; + Vertex* removed; + while ((removed = stack[pos++]) != NULL) { #ifdef SHOW_ITERATIONS - n++; + n++; #endif - kept->receiveNearbyFaces(removed); - while (removed->edges) { - if (!deeper) { - deeper = true; - stack.push_back(kept); - } - stack.push_back(removed->edges->target); - removeEdgePair(removed->edges); - } - } - if (deeper) { - stack.push_back(NULL); - } - } - } + kept->receiveNearbyFaces(removed); + while (removed->edges) { + if (!deeper) { + deeper = true; + stack.push_back(kept); + } + stack.push_back(removed->edges->target); + removeEdgePair(removed->edges); + } + } + if (deeper) { + stack.push_back(NULL); + } + } + } #ifdef SHOW_ITERATIONS - printf("Needed %d iterations to remove part\n", n); + printf("Needed %d iterations to remove part\n", n); #endif - stack.resize(0); - face->origin = shiftedOrigin; + stack.resize(0); + face->origin = shiftedOrigin; - return true; + return true; } -static int32_t getVertexCopy(btConvexHullInternal::Vertex *vertex, btAlignedObjectArray<btConvexHullInternal::Vertex *> &vertices) { - int32_t index = vertex->copy; - if (index < 0) { - index = vertices.size(); - vertex->copy = index; - vertices.push_back(vertex); +static int32_t getVertexCopy(btConvexHullInternal::Vertex* vertex, btAlignedObjectArray<btConvexHullInternal::Vertex*>& vertices) +{ + int32_t index = vertex->copy; + if (index < 0) { + index = vertices.size(); + vertex->copy = index; + vertices.push_back(vertex); #ifdef DEBUG_CONVEX_HULL - printf("Vertex %d gets index *%d\n", vertex->point.index, index); + printf("Vertex %d gets index *%d\n", vertex->point.index, index); #endif - } - return index; + } + return index; } -btScalar btConvexHullComputer::compute(const void *coords, bool doubleCoords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp) { - if (count <= 0) { - vertices.clear(); - edges.clear(); - faces.clear(); - return 0; - } - - btConvexHullInternal hull; - hull.compute(coords, doubleCoords, stride, count); - - btScalar shift = 0; - if ((shrink > 0) && ((shift = hull.shrink(shrink, shrinkClamp)) < 0)) { - vertices.clear(); - edges.clear(); - faces.clear(); - return shift; - } - - vertices.resize(0); - edges.resize(0); - faces.resize(0); - - btAlignedObjectArray<btConvexHullInternal::Vertex *> oldVertices; - getVertexCopy(hull.vertexList, oldVertices); - int32_t copied = 0; - while (copied < oldVertices.size()) { - btConvexHullInternal::Vertex *v = oldVertices[copied]; - vertices.push_back(hull.getCoordinates(v)); - btConvexHullInternal::Edge *firstEdge = v->edges; - if (firstEdge) { - int32_t firstCopy = -1; - int32_t prevCopy = -1; - btConvexHullInternal::Edge *e = firstEdge; - do { - if (e->copy < 0) { - int32_t s = edges.size(); - edges.push_back(Edge()); - edges.push_back(Edge()); - Edge *c = &edges[s]; - Edge *r = &edges[s + 1]; - e->copy = s; - e->reverse->copy = s + 1; - c->reverse = 1; - r->reverse = -1; - c->targetVertex = getVertexCopy(e->target, oldVertices); - r->targetVertex = copied; +btScalar btConvexHullComputer::compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp) +{ + if (count <= 0) { + vertices.clear(); + edges.clear(); + faces.clear(); + return 0; + } + + btConvexHullInternal hull; + hull.compute(coords, doubleCoords, stride, count); + + btScalar shift = 0; + if ((shrink > 0) && ((shift = hull.shrink(shrink, shrinkClamp)) < 0)) { + vertices.clear(); + edges.clear(); + faces.clear(); + return shift; + } + + vertices.resize(0); + edges.resize(0); + faces.resize(0); + + btAlignedObjectArray<btConvexHullInternal::Vertex*> oldVertices; + getVertexCopy(hull.vertexList, oldVertices); + int32_t copied = 0; + while (copied < oldVertices.size()) { + btConvexHullInternal::Vertex* v = oldVertices[copied]; + vertices.push_back(hull.getCoordinates(v)); + btConvexHullInternal::Edge* firstEdge = v->edges; + if (firstEdge) { + int32_t firstCopy = -1; + int32_t prevCopy = -1; + btConvexHullInternal::Edge* e = firstEdge; + do { + if (e->copy < 0) { + int32_t s = edges.size(); + edges.push_back(Edge()); + edges.push_back(Edge()); + Edge* c = &edges[s]; + Edge* r = &edges[s + 1]; + e->copy = s; + e->reverse->copy = s + 1; + c->reverse = 1; + r->reverse = -1; + c->targetVertex = getVertexCopy(e->target, oldVertices); + r->targetVertex = copied; #ifdef DEBUG_CONVEX_HULL - printf(" CREATE: Vertex *%d has edge to *%d\n", copied, c->getTargetVertex()); + printf(" CREATE: Vertex *%d has edge to *%d\n", copied, c->getTargetVertex()); #endif - } - if (prevCopy >= 0) { - edges[e->copy].next = prevCopy - e->copy; - } else { - firstCopy = e->copy; - } - prevCopy = e->copy; - e = e->next; - } while (e != firstEdge); - edges[firstCopy].next = prevCopy - firstCopy; - } - copied++; - } - - for (int32_t i = 0; i < copied; i++) { - btConvexHullInternal::Vertex *v = oldVertices[i]; - btConvexHullInternal::Edge *firstEdge = v->edges; - if (firstEdge) { - btConvexHullInternal::Edge *e = firstEdge; - do { - if (e->copy >= 0) { + } + if (prevCopy >= 0) { + edges[e->copy].next = prevCopy - e->copy; + } + else { + firstCopy = e->copy; + } + prevCopy = e->copy; + e = e->next; + } while (e != firstEdge); + edges[firstCopy].next = prevCopy - firstCopy; + } + copied++; + } + + for (int32_t i = 0; i < copied; i++) { + btConvexHullInternal::Vertex* v = oldVertices[i]; + btConvexHullInternal::Edge* firstEdge = v->edges; + if (firstEdge) { + btConvexHullInternal::Edge* e = firstEdge; + do { + if (e->copy >= 0) { #ifdef DEBUG_CONVEX_HULL - printf("Vertex *%d has edge to *%d\n", i, edges[e->copy].getTargetVertex()); + printf("Vertex *%d has edge to *%d\n", i, edges[e->copy].getTargetVertex()); #endif - faces.push_back(e->copy); - btConvexHullInternal::Edge *f = e; - do { + faces.push_back(e->copy); + btConvexHullInternal::Edge* f = e; + do { #ifdef DEBUG_CONVEX_HULL - printf(" Face *%d\n", edges[f->copy].getTargetVertex()); + printf(" Face *%d\n", edges[f->copy].getTargetVertex()); #endif - f->copy = -1; - f = f->reverse->prev; - } while (f != e); - } - e = e->next; - } while (e != firstEdge); - } - } - - return shift; + f->copy = -1; + f = f->reverse->prev; + } while (f != e); + } + e = e->next; + } while (e != firstEdge); + } + } + + return shift; } -//GODOT ADDITION -}; -// +// -- GODOT start -- +}; // namespace VHACD +// -- GODOT end -- |