diff options
55 files changed, 1622 insertions, 756 deletions
diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index 0512cdd798..572a6c5239 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -265,76 +265,28 @@ void CameraMatrix::get_viewport_size(real_t &r_width, real_t &r_height) const { bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8points) const { - const real_t *matrix = (const real_t *)this->matrix; - - ///////--- Near Plane ---/////// - Plane near_plane = Plane(matrix[3] + matrix[2], - matrix[7] + matrix[6], - matrix[11] + matrix[10], - -matrix[15] - matrix[14]); - near_plane.normalize(); - - ///////--- Far Plane ---/////// - Plane far_plane = Plane(matrix[2] - matrix[3], - matrix[6] - matrix[7], - matrix[10] - matrix[11], - matrix[15] - matrix[14]); - far_plane.normalize(); - - ///////--- Right Plane ---/////// - Plane right_plane = Plane(matrix[0] - matrix[3], - matrix[4] - matrix[7], - matrix[8] - matrix[11], - -matrix[15] + matrix[12]); - right_plane.normalize(); - - ///////--- Top Plane ---/////// - Plane top_plane = Plane(matrix[1] - matrix[3], - matrix[5] - matrix[7], - matrix[9] - matrix[11], - -matrix[15] + matrix[13]); - top_plane.normalize(); - - Vector3 near_endpoint_left, near_endpoint_right; - Vector3 far_endpoint_left, far_endpoint_right; - - bool res = near_plane.intersect_3(right_plane, top_plane, &near_endpoint_right); - ERR_FAIL_COND_V(!res, false); - - res = far_plane.intersect_3(right_plane, top_plane, &far_endpoint_right); - ERR_FAIL_COND_V(!res, false); - - if ((matrix[8] == 0) && (matrix[9] == 0)) { - near_endpoint_left = near_endpoint_right; - near_endpoint_left.x = -near_endpoint_left.x; - - far_endpoint_left = far_endpoint_right; - far_endpoint_left.x = -far_endpoint_left.x; - } else { - ///////--- Left Plane ---/////// - Plane left_plane = Plane(matrix[0] + matrix[3], - matrix[4] + matrix[7], - matrix[8] + matrix[11], - -matrix[15] - matrix[12]); - left_plane.normalize(); + Vector<Plane> planes = get_projection_planes(Transform()); + const Planes intersections[8][3]={ + {PLANE_FAR,PLANE_LEFT,PLANE_TOP}, + {PLANE_FAR,PLANE_LEFT,PLANE_BOTTOM}, + {PLANE_FAR,PLANE_RIGHT,PLANE_TOP}, + {PLANE_FAR,PLANE_RIGHT,PLANE_BOTTOM}, + {PLANE_NEAR,PLANE_LEFT,PLANE_TOP}, + {PLANE_NEAR,PLANE_LEFT,PLANE_BOTTOM}, + {PLANE_NEAR,PLANE_RIGHT,PLANE_TOP}, + {PLANE_NEAR,PLANE_RIGHT,PLANE_BOTTOM}, + }; - res = near_plane.intersect_3(left_plane, top_plane, &near_endpoint_left); - ERR_FAIL_COND_V(!res, false); + for(int i=0;i<8;i++) { - res = far_plane.intersect_3(left_plane, top_plane, &far_endpoint_left); + Vector3 point; + bool res = planes[intersections[i][0]].intersect_3(planes[intersections[i][1]],planes[intersections[i][2]], &point); ERR_FAIL_COND_V(!res, false); + p_8points[i]=p_transform.xform(point); } - p_8points[0] = p_transform.xform(Vector3(near_endpoint_right.x, near_endpoint_right.y, near_endpoint_right.z)); - p_8points[1] = p_transform.xform(Vector3(near_endpoint_right.x, -near_endpoint_right.y, near_endpoint_right.z)); - p_8points[2] = p_transform.xform(Vector3(near_endpoint_left.x, near_endpoint_left.y, near_endpoint_left.z)); - p_8points[3] = p_transform.xform(Vector3(near_endpoint_left.x, -near_endpoint_left.y, near_endpoint_left.z)); - p_8points[4] = p_transform.xform(Vector3(far_endpoint_right.x, far_endpoint_right.y, far_endpoint_right.z)); - p_8points[5] = p_transform.xform(Vector3(far_endpoint_right.x, -far_endpoint_right.y, far_endpoint_right.z)); - p_8points[6] = p_transform.xform(Vector3(far_endpoint_left.x, far_endpoint_left.y, far_endpoint_left.z)); - p_8points[7] = p_transform.xform(Vector3(far_endpoint_left.x, -far_endpoint_left.y, far_endpoint_left.z)); - return true; + } Vector<Plane> CameraMatrix::get_projection_planes(const Transform &p_transform) const { @@ -610,6 +562,12 @@ int CameraMatrix::get_pixels_per_meter(int p_for_pixel_width) const { return int((result.x * 0.5 + 0.5) * p_for_pixel_width); } +bool CameraMatrix::is_orthogonal() const { + + return matrix[3][3]==1.0; +} + + real_t CameraMatrix::get_fov() const { const real_t *matrix = (const real_t *)this->matrix; diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h index 175d0cdb1b..87cc4b95b8 100644 --- a/core/math/camera_matrix.h +++ b/core/math/camera_matrix.h @@ -69,6 +69,7 @@ struct CameraMatrix { real_t get_z_near() const; real_t get_aspect() const; real_t get_fov() const; + bool is_orthogonal() const; Vector<Plane> get_projection_planes(const Transform &p_transform) const; @@ -83,6 +84,7 @@ struct CameraMatrix { Plane xform4(const Plane &p_vec4) const; _FORCE_INLINE_ Vector3 xform(const Vector3 &p_vec3) const; + operator String() const; void scale_translate_to_fit(const Rect3 &p_aabb); diff --git a/core/project_settings.cpp b/core/project_settings.cpp index a74917162b..23e4961138 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -114,7 +114,15 @@ String ProjectSettings::globalize_path(const String &p_path) const { return p_path.replace("res:/", resource_path); }; return p_path.replace("res://", ""); - }; + } else if (p_path.begins_with("user://")) { + + String data_dir = OS::get_singleton()->get_data_dir(); + if (data_dir != "") { + + return p_path.replace("user:/", data_dir); + }; + return p_path.replace("user://", ""); + } return p_path; } diff --git a/doc/base/classes.xml b/doc/base/classes.xml index cd96b2da32..d9153b1855 100644 --- a/doc/base/classes.xml +++ b/doc/base/classes.xml @@ -31,6 +31,7 @@ <argument index="1" name="alpha" type="float"> </argument> <description> + Make a color from color name (color_names.inc) and alpha ranging from 0 to 1. </description> </method> <method name="abs"> @@ -113,6 +114,7 @@ <argument index="0" name="ascii" type="int"> </argument> <description> + Returns a character as String of the given ASCII code. </description> </method> <method name="clamp"> @@ -309,6 +311,10 @@ <argument index="2" name="value" type="float"> </argument> <description> + Returns a normalized value considering the given range. + [codeblock] + inverse_lerp(3, 5, 4) # return 0.5 + [/codeblock] </description> </method> <method name="is_inf"> @@ -335,6 +341,7 @@ <argument index="0" name="var" type="Variant"> </argument> <description> + Returns the length of the given Variant if applicable. It will return character count of a String, element count of an Array, etc. </description> </method> <method name="lerp"> @@ -551,6 +558,10 @@ <argument index="4" name="ostop" type="float"> </argument> <description> + Maps a value from range [istart, istop] to [ostart, ostop]. + [codeblock] + range_lerp(75, 0, 100, -1, 1) # returns 0.5 + [/codeblock] </description> </method> <method name="round"> @@ -667,6 +678,11 @@ <argument index="0" name="type" type="String"> </argument> <description> + Returns whether the given class is exist in [ClassDB]. + [codeblock] + type_exists("Sprite") # returns true + type_exists("Variant") # returns false + [/codeblock] </description> </method> <method name="typeof"> @@ -753,6 +769,7 @@ </methods> <members> <member name="ARVRServer" type="ARVRServer" setter="" getter="" brief=""> + [ARVRServer] singleton </member> <member name="AudioServer" type="AudioServer" setter="" getter="" brief=""> [AudioServer] singleton @@ -1944,18 +1961,25 @@ Variable is of type [Array]. </constant> <constant name="TYPE_RAW_ARRAY" value="20"> + Variable is of type [PoolByteArray]. </constant> <constant name="TYPE_INT_ARRAY" value="21"> + Variable is of type [PoolIntArray]. </constant> <constant name="TYPE_REAL_ARRAY" value="22"> + Variable is of type [PoolRealArray]. </constant> <constant name="TYPE_STRING_ARRAY" value="23"> + Variable is of type [PoolStringArray]. </constant> <constant name="TYPE_VECTOR2_ARRAY" value="24"> + Variable is of type [PoolVector2Array]. </constant> <constant name="TYPE_VECTOR3_ARRAY" value="25"> + Variable is of type [PoolVector3Array]. </constant> <constant name="TYPE_COLOR_ARRAY" value="26"> + Variable is of type [PoolColorArray]. </constant> <constant name="TYPE_MAX" value="27"> </constant> @@ -3477,6 +3501,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> + Return the update mode of a value track. </description> </method> <method name="value_track_set_update_mode"> @@ -3487,6 +3512,7 @@ <argument index="1" name="mode" type="int" enum="Animation.UpdateMode"> </argument> <description> + Set the update mode (UPDATE_*) of a value track. </description> </method> </methods> @@ -3510,10 +3536,13 @@ Cubic interpolation. </constant> <constant name="UPDATE_CONTINUOUS" value="0"> + Update between keyframes. </constant> <constant name="UPDATE_DISCRETE" value="1"> + Update at the keyframes and hold the value. </constant> <constant name="UPDATE_TRIGGER" value="2"> + Update at the keyframes. </constant> </constants> </class> @@ -3551,6 +3580,7 @@ <argument index="0" name="anim_from" type="String"> </argument> <description> + Return the name of the next animation in the queue. </description> </method> <method name="animation_set_next"> @@ -3561,6 +3591,7 @@ <argument index="1" name="anim_to" type="String"> </argument> <description> + Set the name of an animation that will be played after. </description> </method> <method name="clear_caches"> @@ -3933,6 +3964,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns an animation given its name. </description> </method> <method name="animation_node_get_master_animation" qualifiers="const"> @@ -3986,6 +4018,7 @@ <argument index="2" name="dst_input_idx" type="int"> </argument> <description> + Returns whether node [code]id[/code] and [code]dst_id[/code] are connected at the specified slot. </description> </method> <method name="blend2_node_get_amount" qualifiers="const"> @@ -3994,6 +4027,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns the blend amount of a Blend2 node given its name. </description> </method> <method name="blend2_node_set_amount"> @@ -4004,6 +4038,7 @@ <argument index="1" name="blend" type="float"> </argument> <description> + Sets the blend amount of a Blend2 node given its name and value. </description> </method> <method name="blend2_node_set_filter_path"> @@ -4024,6 +4059,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns the blend amount of a Blend3 node given its name. </description> </method> <method name="blend3_node_set_amount"> @@ -4034,6 +4070,7 @@ <argument index="1" name="blend" type="float"> </argument> <description> + Sets the blend amount of a Blend3 node given its name and value. </description> </method> <method name="blend4_node_get_amount" qualifiers="const"> @@ -4042,6 +4079,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns the blend amount of a Blend4 node given its name. </description> </method> <method name="blend4_node_set_amount"> @@ -4052,6 +4090,7 @@ <argument index="1" name="blend" type="Vector2"> </argument> <description> + Sets the blend amount of a Blend4 node given its name and value. </description> </method> <method name="connect_nodes"> @@ -4064,6 +4103,7 @@ <argument index="2" name="dst_input_idx" type="int"> </argument> <description> + Connects node [code]id[/code] to [code]dst_id[/code] at the specified input slot. </description> </method> <method name="disconnect_nodes"> @@ -4074,12 +4114,14 @@ <argument index="1" name="dst_input_idx" type="int"> </argument> <description> + Disconnects nodes connected to [code]id[/code] at the specified input slot. </description> </method> <method name="get_animation_process_mode" qualifiers="const"> <return type="int" enum="AnimationTreePlayer.AnimationProcessMode"> </return> <description> + Returns playback process mode of this AnimationTreePlayer. </description> </method> <method name="get_base_path" qualifiers="const"> @@ -4098,12 +4140,14 @@ <return type="PoolStringArray"> </return> <description> + Returns a PoolStringArray containing the name of all nodes. </description> </method> <method name="is_active" qualifiers="const"> <return type="bool"> </return> <description> + Returns whether this AnimationTreePlayer is active. </description> </method> <method name="mix_node_get_amount" qualifiers="const"> @@ -4112,6 +4156,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns mix amount of a Mix node given its name. </description> </method> <method name="mix_node_set_amount"> @@ -4122,6 +4167,7 @@ <argument index="1" name="ratio" type="float"> </argument> <description> + Sets mix amount of a Mix node given its name and value. </description> </method> <method name="node_exists" qualifiers="const"> @@ -4159,6 +4205,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns position of a node in the graph given its name. </description> </method> <method name="node_get_type" qualifiers="const"> @@ -4189,6 +4236,7 @@ <argument index="1" name="screen_pos" type="Vector2"> </argument> <description> + Sets position of a node in the graph given its name and position. </description> </method> <method name="oneshot_node_get_autorestart_delay" qualifiers="const"> @@ -4197,6 +4245,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns autostart delay of a OneShot node given its name. </description> </method> <method name="oneshot_node_get_autorestart_random_delay" qualifiers="const"> @@ -4205,6 +4254,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns autostart random delay of a OneShot node given its name. </description> </method> <method name="oneshot_node_get_fadein_time" qualifiers="const"> @@ -4213,6 +4263,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns fade in time of a OneShot node given its name. </description> </method> <method name="oneshot_node_get_fadeout_time" qualifiers="const"> @@ -4221,6 +4272,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns fade out time of a OneShot node given its name. </description> </method> <method name="oneshot_node_has_autorestart" qualifiers="const"> @@ -4229,6 +4281,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns whether a OneShot node will auto restart given its name. </description> </method> <method name="oneshot_node_is_active" qualifiers="const"> @@ -4237,6 +4290,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns whether a OneShot node is active given its name. </description> </method> <method name="oneshot_node_set_autorestart"> @@ -4247,6 +4301,7 @@ <argument index="1" name="enable" type="bool"> </argument> <description> + Sets autorestart property of a OneShot node given its name and value. </description> </method> <method name="oneshot_node_set_autorestart_delay"> @@ -4257,6 +4312,7 @@ <argument index="1" name="delay_sec" type="float"> </argument> <description> + Sets autorestart delay of a OneShot node given its name and value in seconds. </description> </method> <method name="oneshot_node_set_autorestart_random_delay"> @@ -4267,6 +4323,7 @@ <argument index="1" name="rand_sec" type="float"> </argument> <description> + Sets autorestart random delay of a OneShot node given its name and value in seconds. </description> </method> <method name="oneshot_node_set_fadein_time"> @@ -4277,6 +4334,7 @@ <argument index="1" name="time_sec" type="float"> </argument> <description> + Sets fade in time of a OneShot node given its name and value in seconds. </description> </method> <method name="oneshot_node_set_fadeout_time"> @@ -4287,6 +4345,7 @@ <argument index="1" name="time_sec" type="float"> </argument> <description> + Sets fade out time of a OneShot node given its name and value in seconds. </description> </method> <method name="oneshot_node_set_filter_path"> @@ -4307,6 +4366,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Starts a OneShot node given its name. </description> </method> <method name="oneshot_node_stop"> @@ -4315,6 +4375,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Stops a OneShot node given its name. </description> </method> <method name="recompute_caches"> @@ -4335,6 +4396,7 @@ <return type="void"> </return> <description> + Resets this AnimationTreePlayer. </description> </method> <method name="set_active"> @@ -4343,6 +4405,7 @@ <argument index="0" name="enabled" type="bool"> </argument> <description> + Sets whether this AnimationTreePlayer is active. AnimationTreePlayer will start processing if set to active. </description> </method> <method name="set_animation_process_mode"> @@ -4351,6 +4414,7 @@ <argument index="0" name="mode" type="int" enum="AnimationTreePlayer.AnimationProcessMode"> </argument> <description> + Sets process mode (ANIMATION_PROCESS_*) of this AnimationTreePlayer. </description> </method> <method name="set_base_path"> @@ -4359,6 +4423,7 @@ <argument index="0" name="path" type="NodePath"> </argument> <description> + Sets base path of this AnimationTreePlayer. </description> </method> <method name="set_master_player"> @@ -4375,6 +4440,7 @@ <argument index="0" name="id" type="String"> </argument> <description> + Returns time scale value of a TimeScale node given its name. </description> </method> <method name="timescale_node_set_scale"> @@ -4385,6 +4451,7 @@ <argument index="1" name="scale" type="float"> </argument> <description> + Sets time scale value of a TimeScale node given its name and value. </description> </method> <method name="timeseek_node_seek"> @@ -4395,6 +4462,7 @@ <argument index="1" name="pos_sec" type="float"> </argument> <description> + Sets time seek value of a TimeSeek node given its name and value. </description> </method> <method name="transition_node_delete_input"> @@ -4490,24 +4558,34 @@ </members> <constants> <constant name="NODE_OUTPUT" value="0"> + Output node. </constant> <constant name="NODE_ANIMATION" value="1"> + Animation node. </constant> <constant name="NODE_ONESHOT" value="2"> + OneShot node. </constant> <constant name="NODE_MIX" value="3"> + Mix node. </constant> <constant name="NODE_BLEND2" value="4"> + Blend2 node. </constant> <constant name="NODE_BLEND3" value="5"> + Blend3 node. </constant> <constant name="NODE_BLEND4" value="6"> + Blend4 node. </constant> <constant name="NODE_TIMESCALE" value="7"> + TimeScale node. </constant> <constant name="NODE_TIMESEEK" value="8"> + TimeSeek node. </constant> <constant name="NODE_TRANSITION" value="9"> + Transition node. </constant> </constants> </class> @@ -8947,8 +9025,10 @@ </class> <class name="BitMap" inherits="Resource" category="Core"> <brief_description> + Boolean matrix. </brief_description> <description> + A two-dimensional array of boolean values, can be used to efficiently store a binary matrix (every matrix element takes only one bit) and query the values using natural cartesian coordinates. </description> <methods> <method name="create"> @@ -8957,6 +9037,7 @@ <argument index="0" name="size" type="Vector2"> </argument> <description> + Creates a bitmap with the specified size, filled with false. </description> </method> <method name="create_from_image_alpha"> @@ -8965,6 +9046,7 @@ <argument index="0" name="image" type="Image"> </argument> <description> + Creates a bitmap that matches the given image dimensions, every element of the bitmap is set to false if the alpha value of the image at that position is 0, and true in other case. </description> </method> <method name="get_bit" qualifiers="const"> @@ -8973,18 +9055,21 @@ <argument index="0" name="pos" type="Vector2"> </argument> <description> + Returns bitmap's value at the specified position. </description> </method> <method name="get_size" qualifiers="const"> <return type="Vector2"> </return> <description> + Returns bitmap's dimensions. </description> </method> <method name="get_true_bit_count" qualifiers="const"> <return type="int"> </return> <description> + Returns the amount of bitmap elements that are set to true. </description> </method> <method name="set_bit"> @@ -8995,6 +9080,7 @@ <argument index="1" name="bit" type="bool"> </argument> <description> + Sets the bitmap's element at the specified position, to the specified value. </description> </method> <method name="set_bit_rect"> @@ -9005,6 +9091,7 @@ <argument index="1" name="bit" type="bool"> </argument> <description> + Sets a rectangular portion of the bitmap to the specified value. </description> </method> </methods> @@ -9292,35 +9379,30 @@ <return type="Texture"> </return> <description> - Return the button icon. </description> </method> <method name="get_clip_text" qualifiers="const"> <return type="bool"> </return> <description> - Return the state of the [i]clip_text[/i] property (see [method set_clip_text]) </description> </method> <method name="get_text" qualifiers="const"> <return type="String"> </return> <description> - Return the button text. </description> </method> <method name="get_text_align" qualifiers="const"> <return type="int" enum="Button.TextAlign"> </return> <description> - Return the text alignment policy. </description> </method> <method name="is_flat" qualifiers="const"> <return type="bool"> </return> <description> - Return the state of the [i]flat[/i] property (see [method set_flat]). </description> </method> <method name="set_button_icon"> @@ -9329,7 +9411,6 @@ <argument index="0" name="texture" type="Texture"> </argument> <description> - Set the icon that will be displayed next to the text inside the button area. </description> </method> <method name="set_clip_text"> @@ -9338,7 +9419,6 @@ <argument index="0" name="enabled" type="bool"> </argument> <description> - Set the [i]clip_text[/i] property of a Button. When this property is enabled, text that is too large to fit the button is clipped, when disabled (default) the Button will always be wide enough to hold the text. </description> </method> <method name="set_flat"> @@ -9347,7 +9427,6 @@ <argument index="0" name="enabled" type="bool"> </argument> <description> - Set the [i]flat[/i] property of a Button. Flat buttons don't display decoration unless hovered or pressed. </description> </method> <method name="set_text"> @@ -9356,7 +9435,6 @@ <argument index="0" name="text" type="String"> </argument> <description> - Set the button text, which will be displayed inside the button area. </description> </method> <method name="set_text_align"> @@ -9365,20 +9443,24 @@ <argument index="0" name="align" type="int" enum="Button.TextAlign"> </argument> <description> - Set the text alignment policy, using one of the ALIGN_* constants. </description> </method> </methods> <members> <member name="align" type="int" setter="set_text_align" getter="get_text_align" brief="" enum="Button.TextAlign"> + Text alignment policy for the button's text, use one of the ALIGN_* constants. </member> <member name="clip_text" type="bool" setter="set_clip_text" getter="get_clip_text" brief=""> + When this property is enabled, text that is too large to fit the button is clipped, when disabled the Button will always be wide enough to hold the text. This property is disabled by default. </member> - <member name="flat" type="bool" setter="set_flat" getter="is_flat" brief=""> + <member name="flat" type="bool" setter="set_flat" getter="is_flat" brief="button decoration mode"> + Flat buttons don't display decoration. </member> <member name="icon" type="Texture" setter="set_button_icon" getter="get_button_icon" brief=""> + Button's icon, if text is present the icon will be placed before the text. </member> <member name="text" type="String" setter="set_text" getter="get_text" brief=""> + The button's text that will be displayed inside the button's area. </member> </members> <constants> @@ -9386,7 +9468,7 @@ Align the text to the left. </constant> <constant name="ALIGN_CENTER" value="1"> - Center the text. + Align the text to the center. </constant> <constant name="ALIGN_RIGHT" value="2"> Align the text to the right. @@ -10955,8 +11037,10 @@ </class> <class name="CapsuleMesh" inherits="PrimitiveMesh" category="Core"> <brief_description> + Class representing a capsule-shaped [PrimitiveMesh]. </brief_description> <description> + Class representing a capsule-shaped [PrimitiveMesh]. </description> <methods> <method name="get_mid_height" qualifiers="const"> @@ -11018,12 +11102,16 @@ </methods> <members> <member name="mid_height" type="float" setter="set_mid_height" getter="get_mid_height" brief=""> + Height of the capsule mesh from the center point. Defaults to 1.0. </member> <member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments" brief=""> + Number of radial segments on the capsule mesh. Defaults to 64. </member> <member name="radius" type="float" setter="set_radius" getter="get_radius" brief=""> + Radius of the capsule mesh. Defaults to 1.0. </member> <member name="rings" type="int" setter="set_rings" getter="get_rings" brief=""> + Number of rings along the height of the capsule. Defaults to 8. </member> </members> <constants> @@ -12077,8 +12165,10 @@ </class> <class name="CollisionShape" inherits="Spatial" category="Core"> <brief_description> + Node that represents collision shape data in 3D space. </brief_description> <description> + Editor facility for creating and editing collision shapes in 3D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area] to give it a detection shape, or add it to a [PhysicsBody] to give create solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape. </description> <methods> <method name="get_shape" qualifiers="const"> @@ -12097,6 +12187,7 @@ <return type="void"> </return> <description> + Sets the collision shape's shape to the addition of all its convexed [MeshInstance] siblings geometry. </description> </method> <method name="resource_changed"> @@ -12105,6 +12196,7 @@ <argument index="0" name="resource" type="Resource"> </argument> <description> + If this method exists within a script it will be called whenever the shape resource has been modified. </description> </method> <method name="set_disabled"> @@ -12126,8 +12218,10 @@ </methods> <members> <member name="disabled" type="bool" setter="set_disabled" getter="is_disabled" brief=""> + A disabled collision shape has no effect in the world. </member> <member name="shape" type="Shape" setter="set_shape" getter="get_shape" brief=""> + The actual shape owned by this collision shape. </member> </members> <constants> @@ -12135,17 +12229,16 @@ </class> <class name="CollisionShape2D" inherits="Node2D" category="Core"> <brief_description> - Editor-only class for easy editing of shapes. + Node that represents collision shape data in 2D space. </brief_description> <description> - Editor-only class. This is not present when running the game. It's used in the editor to properly edit and position collision shapes in [CollisionObject2D]. This is not accessible from regular code. + Editor facility for creating and editing collision shapes in 2D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area2D] to give it a detection shape, or add it to a [PhysicsBody2D] to give create solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape. </description> <methods> <method name="get_shape" qualifiers="const"> <return type="Shape2D"> </return> <description> - Return this shape's [Shape2D]. </description> </method> <method name="is_disabled" qualifiers="const"> @@ -12182,16 +12275,18 @@ <argument index="0" name="shape" type="Shape2D"> </argument> <description> - Set this shape's [Shape2D]. This will not appear as a node, but can be directly edited as a property. </description> </method> </methods> <members> <member name="disabled" type="bool" setter="set_disabled" getter="is_disabled" brief=""> + A disabled collision shape has no effect in the world. </member> <member name="one_way_collision" type="bool" setter="set_one_way_collision" getter="is_one_way_collision_enabled" brief=""> + Sets whether this collision shape should only detect collision on one side (top or bottom). </member> <member name="shape" type="Shape2D" setter="set_shape" getter="get_shape" brief=""> + The actual shape owned by this collision shape. </member> </members> <constants> @@ -12856,23 +12951,23 @@ </class> <class name="Control" inherits="CanvasItem" category="Core"> <brief_description> - Control is the base node for all the GUI components. + Base node for all User Interface components. </brief_description> <description> - Control is the base class Node for all the GUI components. Every GUI component inherits from it, directly or indirectly. In this way, sections of the scene tree made of contiguous control nodes, become user interfaces. - Controls are relative to the parent position and size by using anchors and margins. This ensures that they can adapt easily in most situation to changing dialog and screen sizes. When more flexibility is desired, [Container] derived nodes can be used. + The base class Node for all User Interface components. Every UI node inherits from it. Any scene or portion of a scene tree composed of Control nodes is a User Interface. + Controls use anchors and margins to place themselves relative to their parent. They adapt automatically when their parent or the screen size changes. To build flexible UIs, use built-in [Container] nodes or create your own. Anchors work by defining which margin do they follow, and a value relative to it. Allowed anchoring modes are ANCHOR_BEGIN, where the margin is relative to the top or left margins of the parent (in pixels), ANCHOR_END for the right and bottom margins of the parent and ANCHOR_RATIO, which is a ratio from 0 to 1 in the parent range. - Input device events are first sent to the root controls via the [method Node._input], which distribute it through the tree, then delivers them to the adequate one (under cursor or keyboard focus based) by calling [method MainLoop._input_event]. There is no need to enable input processing on controls to receive such events. To ensure that no one else will receive the event (not even [method Node._unhandled_input]), the control can accept it by calling [method accept_event]. - Only one control can hold the keyboard focus (receiving keyboard events), for that the control must define the focus mode with [method set_focus_mode]. Focus is lost when another control gains it, or the current focus owner is hidden. - It is sometimes desired for a control to ignore mouse/pointer events. This is often the case when placing other controls on top of a button, in such cases. Calling [method set_ignore_mouse] enables this function. - Finally, controls are skinned according to a [Theme]. Setting a [Theme] on a control will propagate all the skinning down the tree. Optionally, skinning can be overridden per each control by calling the add_*_override functions, or from the editor. + Godot sends Input events to the root node first, via [method Node._input]. The method distributes it through the node tree and delivers the input events to the node under the mouse cursor or on focus with the keyboard. To do so, it calls [method MainLoop._input_event]. No need to enable [method Node.set_process_input] on Controls to receive input events. Call [method accept_event] to ensure no other node receives the event, not even [method Node._unhandled_input]. + Only the one Control node in focus receives keyboard events. To do so, the Control must get the focus mode with [method set_focus_mode]. It loses focus when another Control gets it, or if the current Control in focus is hidden. + You'll sometimes want Controls to ignore mouse or touch events. For example, if you place an icon on top of a button. Call [method set_ignore_mouse] for that. + [Theme] resources change the Control's appearance. If you change the [Theme] on a parent Control node, it will propagate to all of its children. You can override parts of the theme on each Control with the add_*_override methods, like [method add_font_override]. You can also override the theme from the editor. </description> <methods> <method name="_get_minimum_size" qualifiers="virtual"> <return type="Vector2"> </return> <description> - Return the minimum size this Control can shrink to. A control will never be displayed or resized smaller than its minimum size. + Returns the minimum size this Control can shrink to. A control will never be displayed or resized smaller than its minimum size. </description> </method> <method name="_gui_input" qualifiers="virtual"> @@ -12887,7 +12982,7 @@ <return type="void"> </return> <description> - Handles the event, no other control will receive it and it will not be sent to nodes waiting on [method Node._unhandled_input] or [method Node._unhandled_key_input]. + Marks the input event as handled. No other Control will receive it, and the input event will not propagate. Not even to nodes listening to [method Node._unhandled_input] or [method Node._unhandled_key_input]. </description> </method> <method name="add_color_override"> @@ -13742,12 +13837,12 @@ <signals> <signal name="focus_entered"> <description> - Emitted when keyboard focus is gained. + Emitted when the node gains keyboard focus. </description> </signal> <signal name="focus_exited"> <description> - Emitted when the keyboard focus is lost. + Emitted when the node loses keyboard focus. </description> </signal> <signal name="gui_input"> @@ -13758,7 +13853,7 @@ </signal> <signal name="minimum_size_changed"> <description> - Emitted when the minimum size of the control changed. + Emitted when the node's minimum size changes. </description> </signal> <signal name="modal_closed"> @@ -13767,22 +13862,22 @@ </signal> <signal name="mouse_entered"> <description> - Emitted when the mouse enters the control area. + Emitted when the mouse enters the control's area. </description> </signal> <signal name="mouse_exited"> <description> - Emitted when the mouse left the control area. + Emitted when the mouse leaves the control's area. </description> </signal> <signal name="resized"> <description> - Emitted when the control changed size. + Emitted when the control changes size. </description> </signal> <signal name="size_flags_changed"> <description> - Emitted when the size flags changed. + Emitted when the size flags change. </description> </signal> </signals> @@ -14093,8 +14188,10 @@ </class> <class name="CubeMesh" inherits="PrimitiveMesh" category="Core"> <brief_description> + Generate an axis-aligned cuboid [PrimitiveMesh]. </brief_description> <description> + Generate an axis-aligned cuboid [PrimitiveMesh]. </description> <methods> <method name="get_size" qualifiers="const"> @@ -14156,12 +14253,16 @@ </methods> <members> <member name="size" type="Vector3" setter="set_size" getter="get_size" brief=""> + Size of the cuboid mesh. Defaults to (2, 2, 2). </member> <member name="subdivide_depth" type="int" setter="set_subdivide_depth" getter="get_subdivide_depth" brief=""> + Number of extra edge loops inserted along the z-axis. Defaults to 0. </member> <member name="subdivide_height" type="int" setter="set_subdivide_height" getter="get_subdivide_height" brief=""> + Number of extra edge loops inserted along the y-axis. Defaults to 0. </member> <member name="subdivide_width" type="int" setter="set_subdivide_width" getter="get_subdivide_width" brief=""> + Number of extra edge loops inserted along the x-axis. Defaults to 0. </member> </members> <constants> @@ -14851,8 +14952,10 @@ </class> <class name="CylinderMesh" inherits="PrimitiveMesh" category="Core"> <brief_description> + Class representing a cylindrical [PrimitiveMesh]. </brief_description> <description> + Class representing a cylindrical [PrimitiveMesh]. </description> <methods> <method name="get_bottom_radius" qualifiers="const"> @@ -14928,14 +15031,19 @@ </methods> <members> <member name="bottom_radius" type="float" setter="set_bottom_radius" getter="get_bottom_radius" brief=""> + Bottom radius of the cylinder. Defaults to 1.0. </member> <member name="height" type="float" setter="set_height" getter="get_height" brief=""> + Full height of the cylinder. Defaults to 2.0. </member> <member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments" brief=""> + Number of radial segments on the cylinder. Defaults to 64. </member> <member name="rings" type="int" setter="set_rings" getter="get_rings" brief=""> + Number of edge rings along the height of the cylinder. Defaults to 4. </member> <member name="top_radius" type="float" setter="set_top_radius" getter="get_top_radius" brief=""> + Top radius of the cylinder. Defaults to 1.0. </member> </members> <constants> @@ -19171,20 +19279,31 @@ </class> <class name="GDScript" inherits="Script" category="Core"> <brief_description> + A script implemented in the GDScript programming language. </brief_description> <description> + A script implemented in the GDScript programming language. The script exends the functionality of all objects that instance it. + [method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <methods> <method name="get_as_byte_code" qualifiers="const"> <return type="PoolByteArray"> </return> <description> + Returns byte code for the script source code. </description> </method> <method name="new" qualifiers="vararg"> <return type="Object"> </return> <description> + Returns a new instance of the script. + For example: + [codeblock] + var MyClass = load("myclass.gd") + var instance = MyClass.new() + assert(instance.get_script() == MyClass) + [/codeblock] </description> </method> </methods> @@ -20383,8 +20502,10 @@ </class> <class name="GradientTexture" inherits="Texture" category="Core"> <brief_description> + Gradient filled texture. </brief_description> <description> + Uses a [Gradient] to fill the texture data, the gradient will be filled from left to right using colors obtained from the gradient, this means that the texture does not necessarily represent an exact copy of the gradient, but instead an interpolation of samples obtained from the gradient at fixed steps (see [method set_width]). </description> <methods> <method name="get_gradient" qualifiers="const"> @@ -20412,8 +20533,10 @@ </methods> <members> <member name="gradient" type="Gradient" setter="set_gradient" getter="get_gradient" brief=""> + The [Gradient] that will be used to fill the texture. </member> <member name="width" type="int" setter="set_width" getter="get_width" brief=""> + The number of color samples that will be obtained from the [Gradient]. </member> </members> <constants> @@ -23171,6 +23294,7 @@ </class> <class name="InputEvent" inherits="Resource" category="Core"> <brief_description> + Generic input event </brief_description> <description> </description> @@ -23181,24 +23305,28 @@ <argument index="0" name="event" type="InputEvent"> </argument> <description> + Returns true if this input event matches the event passed. </description> </method> <method name="as_text" qualifiers="const"> <return type="String"> </return> <description> + Returns a [String] representation of the event. </description> </method> <method name="get_device" qualifiers="const"> <return type="int"> </return> <description> + Returns the id of the device that generated the event. </description> </method> <method name="get_id" qualifiers="const"> <return type="int"> </return> <description> + Returns the id of the event. </description> </method> <method name="is_action" qualifiers="const"> @@ -23207,7 +23335,7 @@ <argument index="0" name="action" type="String"> </argument> <description> - Return if this input event matches a pre-defined action, no matter the type. + Returns true if this input event matches a pre-defined action, no matter the type. </description> </method> <method name="is_action_pressed" qualifiers="const"> @@ -23216,7 +23344,7 @@ <argument index="0" name="action" type="String"> </argument> <description> - Return whether the given action is being pressed (and is not an echo event for KEY events). Not relevant for the event types MOUSE_MOTION, SCREEN_DRAG and NONE. + Returns true if the given action is being pressed (and is not an echo event for KEY events). Not relevant for the event types MOUSE_MOTION, SCREEN_DRAG and NONE. </description> </method> <method name="is_action_released" qualifiers="const"> @@ -23225,7 +23353,7 @@ <argument index="0" name="action" type="String"> </argument> <description> - Return whether the given action is released (i.e. not pressed). Not relevant for the event types MOUSE_MOTION, SCREEN_DRAG and NONE. + Returns true if the given action is released (i.e. not pressed). Not relevant for the event types MOUSE_MOTION, SCREEN_DRAG and NONE. </description> </method> <method name="is_action_type" qualifiers="const"> @@ -23238,14 +23366,14 @@ <return type="bool"> </return> <description> - Return if this input event is an echo event (only for events of type KEY, it will return false for other types). + Returns true if this input event is an echo event (only for events of type KEY, it will return false for other types). </description> </method> <method name="is_pressed" qualifiers="const"> <return type="bool"> </return> <description> - Return if this input event is pressed. Not relevant for the event types MOUSE_MOTION, SCREEN_DRAG and NONE. + Returns true if this input event is pressed. Not relevant for the event types MOUSE_MOTION, SCREEN_DRAG and NONE. </description> </method> <method name="set_device"> @@ -26182,10 +26310,10 @@ </class> <class name="LightOccluder2D" inherits="Node2D" category="Core"> <brief_description> - Occludes light cast by a Light2D, thus casting shadows. + Occludes light cast by a Light2D, casting shadows. </brief_description> <description> - Occludes light cast by a Light2D, thus casting shadows. The LightOccluder2D must be provided with a shape (see OccluderPolygon2D) that allows the shadow to be computed. This shape affects the resulting shadow, while the shape of the representating asset shadowed does not actually affect shadows. + Occludes light cast by a Light2D, casting shadows. The LightOccluder2D must be provided with an [OccluderPolygon2D] in order for the shadow to be computed. </description> <methods> <method name="get_occluder_light_mask" qualifiers="const"> @@ -26223,8 +26351,10 @@ </methods> <members> <member name="light_mask" type="int" setter="set_occluder_light_mask" getter="get_occluder_light_mask" brief=""> + The LightOccluder2D's light mask. The LightOccluder2D will cast shadows only from Light2D(s) that have the same light mask(s). </member> <member name="occluder" type="OccluderPolygon2D" setter="set_occluder_polygon" getter="get_occluder_polygon" brief=""> + The [OccluderPolygon2D] used to compute the shadow. </member> </members> <constants> @@ -26232,8 +26362,10 @@ </class> <class name="Line2D" inherits="Node2D" category="Core"> <brief_description> + A 2D line. </brief_description> <description> + A line through several points in 2D space. </description> <methods> <method name="add_point"> @@ -26242,6 +26374,7 @@ <argument index="0" name="pos" type="Vector2"> </argument> <description> + Add a point at the x/y position in the supplied [Vector2] </description> </method> <method name="get_begin_cap_mode" qualifiers="const"> @@ -26330,6 +26463,7 @@ <argument index="0" name="i" type="int"> </argument> <description> + Remove the point at index 'i' from the line. </description> </method> <method name="set_begin_cap_mode"> @@ -26534,6 +26668,7 @@ <return type="int" enum="LineEdit.Align"> </return> <description> + Return the align mode of the [LineEdit]. </description> </method> <method name="get_cursor_pos" qualifiers="const"> @@ -26560,18 +26695,21 @@ <return type="PopupMenu"> </return> <description> + Return the [PopupMenu] of this [LineEdit]. </description> </method> <method name="get_placeholder" qualifiers="const"> <return type="String"> </return> <description> + Return the placeholder text. </description> </method> <method name="get_placeholder_alpha" qualifiers="const"> <return type="float"> </return> <description> + Return transparency of the placeholder text. </description> </method> <method name="get_text" qualifiers="const"> @@ -26601,6 +26739,7 @@ <argument index="0" name="option" type="int"> </argument> <description> + Execute a given action as defined in the MENU_* enum. </description> </method> <method name="select"> @@ -26611,6 +26750,12 @@ <argument index="1" name="to" type="int" default="-1"> </argument> <description> + Select the text inside [LineEdit] by the given character positions. [code]from[/code] is default to the beginning. [code]to[/code] is default to the end. + [codeblock] + select() # select all + select(5) # select from the fifth character to the end. + select(2, 5) # select from the second to the fifth character. + [/codeblock] </description> </method> <method name="select_all"> @@ -26626,6 +26771,7 @@ <argument index="0" name="align" type="int" enum="LineEdit.Align"> </argument> <description> + Set text alignment of the [LineEdit]. </description> </method> <method name="set_cursor_pos"> @@ -26669,6 +26815,7 @@ <argument index="0" name="text" type="String"> </argument> <description> + Set the placeholder text. </description> </method> <method name="set_placeholder_alpha"> @@ -26677,6 +26824,7 @@ <argument index="0" name="alpha" type="float"> </argument> <description> + Set transparency of the placeholder text. </description> </method> <method name="set_secret"> @@ -26740,24 +26888,34 @@ </signals> <constants> <constant name="ALIGN_LEFT" value="0"> + Align left. </constant> <constant name="ALIGN_CENTER" value="1"> + Align center. </constant> <constant name="ALIGN_RIGHT" value="2"> + Align right. </constant> <constant name="ALIGN_FILL" value="3"> + Align fill. </constant> <constant name="MENU_CUT" value="0"> + Cut (Copy and clear). </constant> <constant name="MENU_COPY" value="1"> + Copy the selected text. </constant> <constant name="MENU_PASTE" value="2"> + Paste the clipboard text over the selected text. </constant> <constant name="MENU_CLEAR" value="3"> + Clear the text. </constant> <constant name="MENU_SELECT_ALL" value="4"> + Select all text. </constant> <constant name="MENU_UNDO" value="5"> + Undo an action. </constant> <constant name="MENU_MAX" value="6"> </constant> @@ -26960,6 +27118,7 @@ <return type="void"> </return> <description> + Called before the program exits. </description> </method> <method name="_idle" qualifiers="virtual"> @@ -26968,12 +27127,14 @@ <argument index="0" name="delta" type="float"> </argument> <description> + Called each idle frame with time since last call as an only argument. </description> </method> <method name="_initialize" qualifiers="virtual"> <return type="void"> </return> <description> + Called once during initialization. </description> </method> <method name="_input_event" qualifiers="virtual"> @@ -27100,6 +27261,7 @@ <argument index="0" name="base64_str" type="String"> </argument> <description> + Return [PoolByteArray] of a given base64 encoded String. </description> </method> <method name="base64_to_utf8"> @@ -27108,6 +27270,7 @@ <argument index="0" name="base64_str" type="String"> </argument> <description> + Return utf8 String of a given base64 encoded String. </description> </method> <method name="base64_to_variant"> @@ -27116,6 +27279,7 @@ <argument index="0" name="base64_str" type="String"> </argument> <description> + Return [Variant] of a given base64 encoded String. </description> </method> <method name="raw_to_base64"> @@ -27124,6 +27288,7 @@ <argument index="0" name="array" type="PoolByteArray"> </argument> <description> + Return base64 encoded String of a given [PoolByteArray]. </description> </method> <method name="utf8_to_base64"> @@ -27132,6 +27297,7 @@ <argument index="0" name="utf8_str" type="String"> </argument> <description> + Return base64 encoded String of a given utf8 String. </description> </method> <method name="variant_to_base64"> @@ -27140,6 +27306,7 @@ <argument index="0" name="variant" type="Variant"> </argument> <description> + Return base64 encoded String of a given [Variant]. </description> </method> </methods> @@ -27219,6 +27386,7 @@ <signals> <signal name="about_to_show"> <description> + Emitted when [PopupMenu] of this MenuButton is about to show. </description> </signal> </signals> @@ -28621,6 +28789,7 @@ <argument index="3" name="out_bandwidth" type="int" default="0"> </argument> <description> + Create client that connects to a server at address [code]ip[/code] using specified [code]port[/code]. </description> </method> <method name="create_server"> @@ -28635,6 +28804,7 @@ <argument index="3" name="out_bandwidth" type="int" default="0"> </argument> <description> + Create server that listens to connections via [code]port[/code]. </description> </method> <method name="get_compression_mode" qualifiers="const"> @@ -28701,6 +28871,7 @@ <return type="bool"> </return> <description> + Return whether this [NetworkedMultiplayerPeer] is refusing new connections. </description> </method> <method name="poll"> @@ -28715,6 +28886,7 @@ <argument index="0" name="enable" type="bool"> </argument> <description> + If [code]endable[/code] is true, this [NetworkedMultiplayerPeer] will refuse new connections. </description> </method> <method name="set_target_peer"> @@ -28737,26 +28909,31 @@ <signals> <signal name="connection_failed"> <description> + Emitted when failed to connect to server. </description> </signal> <signal name="connection_succeeded"> <description> + Emitted when successfully connected to server. </description> </signal> <signal name="peer_connected"> <argument index="0" name="id" type="int"> </argument> <description> + Emitted by the server when a client is connected. </description> </signal> <signal name="peer_disconnected"> <argument index="0" name="id" type="int"> </argument> <description> + Emitted by the server when a client is disconnected. </description> </signal> <signal name="server_disconnected"> <description> + Emitted by clients when server is disconnected. </description> </signal> </signals> @@ -29125,6 +29302,7 @@ <argument index="0" name="event" type="InputEvent"> </argument> <description> + Called when there is a change to input devices. Propagated through the node tree until a Node consumes it. </description> </method> <method name="_process" qualifiers="virtual"> @@ -29152,6 +29330,7 @@ <argument index="0" name="event" type="InputEvent"> </argument> <description> + Propagated to all nodes when the previous InputEvent is not consumed by any nodes. </description> </method> <method name="_unhandled_key_input" qualifiers="virtual"> @@ -29349,12 +29528,14 @@ <return type="int" enum="Node.PauseMode"> </return> <description> + Return the pause mode (PAUSE_MODE_*) of this Node. </description> </method> <method name="get_position_in_parent" qualifiers="const"> <return type="int"> </return> <description> + Return the order in the node tree branch, i.e. if called by the first child Node, return 0. </description> </method> <method name="get_process_delta_time" qualifiers="const"> @@ -29374,6 +29555,7 @@ <return type="SceneTree"> </return> <description> + Return a [SceneTree] that this node is inside. </description> </method> <method name="get_viewport" qualifiers="const"> @@ -29388,6 +29570,7 @@ <argument index="0" name="path" type="NodePath"> </argument> <description> + Return whether the node that a given [NodePath] points too exists. </description> </method> <method name="has_node_and_resource" qualifiers="const"> @@ -29441,12 +29624,14 @@ <argument index="0" name="group" type="String"> </argument> <description> + Return whether this Node is in the specified group. </description> </method> <method name="is_inside_tree" qualifiers="const"> <return type="bool"> </return> <description> + Return whether this Node is inside a [SceneTree]. </description> </method> <method name="is_network_master" qualifiers="const"> @@ -29538,6 +29723,7 @@ <return type="void"> </return> <description> + Queues a node for deletion at the end of the current frame. When deleted, all of its children nodes will be deleted as well. This method ensures it's safe to delete the node, contrary to [method Object.free]. Use [method Object.is_queued_for_deletion] to know whether a node will be deleted at the end of the frame. </description> </method> <method name="raise"> @@ -29587,6 +29773,7 @@ <return type="void"> </return> <description> + Request that [code]_ready[/code] be called again. </description> </method> <method name="rpc" qualifiers="vararg"> @@ -29767,6 +29954,7 @@ <argument index="0" name="mode" type="int" enum="Node.PauseMode"> </argument> <description> + Set pause mode (PAUSE_MODE_*) of this Node. </description> </method> <method name="set_process"> @@ -29837,10 +30025,12 @@ </signal> <signal name="tree_entered"> <description> + Emitted when Node enters the tree. </description> </signal> <signal name="tree_exited"> <description> + Emitted when Node exits the tree. </description> </signal> </signals> @@ -29885,18 +30075,25 @@ <constant name="RPC_MODE_DISABLED" value="0"> </constant> <constant name="RPC_MODE_REMOTE" value="1"> + Call a method remotely. </constant> <constant name="RPC_MODE_SYNC" value="2"> + Call a method both remotely and locally. </constant> <constant name="RPC_MODE_MASTER" value="3"> + Call a method if the Node is Master. </constant> <constant name="RPC_MODE_SLAVE" value="4"> + Call a method if the Node is Slave. </constant> <constant name="PAUSE_MODE_INHERIT" value="0"> + Inherits pause mode from parent. For root node, it is equivalent to PAUSE_MODE_STOP. </constant> <constant name="PAUSE_MODE_STOP" value="1"> + Stop processing when SceneTree is paused. </constant> <constant name="PAUSE_MODE_PROCESS" value="2"> + Continue to process regardless of SceneTree pause state. </constant> <constant name="DUPLICATE_SIGNALS" value="1"> </constant> @@ -37878,8 +38075,10 @@ </class> <class name="PlaneMesh" inherits="PrimitiveMesh" category="Core"> <brief_description> + Class representing a planar [PrimitiveMesh]. </brief_description> <description> + Class representing a planar [PrimitiveMesh]. This flat mesh does not have a thickness. </description> <methods> <method name="get_size" qualifiers="const"> @@ -37927,10 +38126,13 @@ </methods> <members> <member name="size" type="Vector2" setter="set_size" getter="get_size" brief=""> + Size of the generated plane. Defaults to (2.0, 2.0). </member> <member name="subdivide_depth" type="int" setter="set_subdivide_depth" getter="get_subdivide_depth" brief=""> + Number of subdivision along the z-axis. Defaults to 0. </member> <member name="subdivide_width" type="int" setter="set_subdivide_width" getter="get_subdivide_width" brief=""> + Number of subdivision along the x-axis. Defaults to 0. </member> </members> <constants> @@ -39588,8 +39790,10 @@ </class> <class name="PrimitiveMesh" inherits="Mesh" category="Core"> <brief_description> + Base class for all primitive meshes. Handles applying a [Material] to a primitive mesh. </brief_description> <description> + Base class for all primitive meshes. Handles applying a [Material] to a primitive mesh. </description> <methods> <method name="get_material" qualifiers="const"> @@ -39609,6 +39813,7 @@ </methods> <members> <member name="material" type="Material" setter="set_material" getter="get_material" brief=""> + The current [Material] of the primitive mesh. </member> </members> <constants> @@ -39616,8 +39821,10 @@ </class> <class name="PrismMesh" inherits="PrimitiveMesh" category="Core"> <brief_description> + Class representing a prism-shaped [PrimitiveMesh]. </brief_description> <description> + Class representing a prism-shaped [PrimitiveMesh]. </description> <methods> <method name="get_left_to_right" qualifiers="const"> @@ -39693,14 +39900,19 @@ </methods> <members> <member name="left_to_right" type="float" setter="set_left_to_right" getter="get_left_to_right" brief=""> + Displacement of of the upper edge along the x-axis. 0.0 positions edge straight above the bottome left edge. Defaults to 0.5 (positioned on the midpoint). </member> <member name="size" type="Vector3" setter="set_size" getter="get_size" brief=""> + Size of the prism. Defaults to (2.0, 2.0, 2.0). </member> <member name="subdivide_depth" type="int" setter="set_subdivide_depth" getter="get_subdivide_depth" brief=""> + Number of added edge loops along the z-axis. Defaults to 0. </member> <member name="subdivide_height" type="int" setter="set_subdivide_height" getter="get_subdivide_height" brief=""> + Number of added edge loops along the y-axis. Defaults to 0. </member> <member name="subdivide_width" type="int" setter="set_subdivide_width" getter="get_subdivide_width" brief=""> + Number of added edge loops along the x-axis. Defaults to 0. </member> </members> <constants> @@ -40250,8 +40462,10 @@ </class> <class name="QuadMesh" inherits="PrimitiveMesh" category="Core"> <brief_description> + Class representing a square mesh. </brief_description> <description> + Class representing a square mesh with size (2,2,0). Consider using a [PlaneMesh] if you require a differently sized plane. </description> <methods> </methods> @@ -43128,38 +43342,55 @@ </methods> <members> <member name="angular_damp" type="float" setter="set_angular_damp" getter="get_angular_damp" brief=""> + Dampens rotational forces of the Rigid body by the 'angular_damp' rate. </member> <member name="angular_velocity" type="Vector3" setter="set_angular_velocity" getter="get_angular_velocity" brief=""> + The current rotational velocity of the Rigid body </member> <member name="axis_lock" type="int" setter="set_axis_lock" getter="get_axis_lock" brief="" enum="RigidBody.AxisLock"> + Locks the rotational forces to a particular axis, preventing rotations on other axes. </member> <member name="bounce" type="float" setter="set_bounce" getter="get_bounce" brief=""> + Bounciness of the Rigid body. </member> <member name="can_sleep" type="bool" setter="set_can_sleep" getter="is_able_to_sleep" brief=""> + If true, the Rigid body will no longer calculate forces when there is no movement and will act as a static body. It will wake up when other forces are applied through other collisions or when the 'apply_impulse' method is used. </member> <member name="contact_monitor" type="bool" setter="set_contact_monitor" getter="is_contact_monitor_enabled" brief=""> + If true, the Rigid body will emit signals when it collides with another Rigid body. </member> <member name="contacts_reported" type="int" setter="set_max_contacts_reported" getter="get_max_contacts_reported" brief=""> + The maximum contacts to report. Bodies can keep a log of the contacts with other bodies, this is enabled by setting the maximum amount of contacts reported to a number greater than 0. </member> <member name="continuous_cd" type="bool" setter="set_use_continuous_collision_detection" getter="is_using_continuous_collision_detection" brief=""> + Continuous collision detection tries to predict where a moving body will collide, instead of moving it and correcting its movement if it collided. The first is more precise, and misses less impacts by small, fast-moving objects. The second is faster to compute, but can miss small, fast-moving objects. </member> <member name="custom_integrator" type="bool" setter="set_use_custom_integrator" getter="is_using_custom_integrator" brief=""> + If true, internal force integration will be disabled (like gravity or air friction) for this body. Other than collision response, the body will only move as determined by the [method _integrate_forces] function, if defined. </member> <member name="friction" type="float" setter="set_friction" getter="get_friction" brief=""> + The body friction, from 0 (frictionless) to 1 (max friction). </member> <member name="gravity_scale" type="float" setter="set_gravity_scale" getter="get_gravity_scale" brief=""> + The 'gravity_scale' for this Rigid body will be multiplied by the global 3d gravity setting found in "Project > Project Settings > Physics > 3d". A value of 1 will be normal gravity, 2 will apply double gravity, and 0.5 will apply half gravity to this object. </member> <member name="linear_damp" type="float" setter="set_linear_damp" getter="get_linear_damp" brief=""> + The linear damp for this body. Default of -1, cannot be less than -1. If this value is different from -1, any linear damp derived from the world or areas will be overridden. </member> <member name="linear_velocity" type="Vector3" setter="set_linear_velocity" getter="get_linear_velocity" brief=""> + The body linear velocity. Can be used sporadically, but [b]DON'T SET THIS IN EVERY FRAME[/b], because physics may run in another thread and runs at a different granularity. Use [method _integrate_forces] as your process loop for precise control of the body state. </member> <member name="mass" type="float" setter="set_mass" getter="get_mass" brief=""> + The body mass. </member> <member name="mode" type="int" setter="set_mode" getter="get_mode" brief="" enum="RigidBody.Mode"> + The body mode from the MODE_* enum. Modes include: MODE_STATIC, MODE_KINEMATIC, MODE_RIGID, and MODE_CHARACTER. </member> <member name="sleeping" type="bool" setter="set_sleeping" getter="is_sleeping" brief=""> + The current 'sleeping' state of the Rigid body. </member> <member name="weight" type="float" setter="set_weight" getter="get_weight" brief=""> + The body weight given standard earth-weight (gravity 9.8). </member> </members> <signals> @@ -44315,17 +44546,18 @@ </class> <class name="Script" inherits="Resource" category="Core"> <brief_description> - Base class for scripts. + A class stored as a resource. </brief_description> <description> - Base class for scripts. Any script that is loaded becomes one of these resources, which can then create instances. + A class stored as a resource. The script exends the functionality of all objects that instance it. + The 'new' method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <methods> <method name="can_instance" qualifiers="const"> <return type="bool"> </return> <description> - Return true if this script can be instance (ie not a library). + Returns true if the script can be instanced. </description> </method> <method name="get_node_type" qualifiers="const"> @@ -44338,7 +44570,7 @@ <return type="String"> </return> <description> - Return the script source code (if available). + Returns the script source code, or an empty string if source code is not available. </description> </method> <method name="has_script_signal" qualifiers="const"> @@ -44347,13 +44579,14 @@ <argument index="0" name="signal_name" type="String"> </argument> <description> + Returns true if the script, or a base class, defines a signal with the given name. </description> </method> <method name="has_source_code" qualifiers="const"> <return type="bool"> </return> <description> - Return true if the script contains source code. + Returns true if the script contains non-empty source code. </description> </method> <method name="instance_has" qualifiers="const"> @@ -44362,21 +44595,24 @@ <argument index="0" name="base_object" type="Object"> </argument> <description> - Return true if a given object uses an instance of this script. + Returns true if 'base_object' is an instance of this script. </description> </method> <method name="is_tool" qualifiers="const"> <return type="bool"> </return> <description> + Returns true if the script is a tool script. A tool script can run in the editor. </description> </method> <method name="reload"> <return type="int" enum="Error"> </return> <argument index="0" name="keep_state" type="bool" default="false"> + If true, preserve existing script instances and subclasses. </argument> <description> + Reloads the script's class implementation. Returns an error code. </description> </method> <method name="set_source_code"> @@ -44385,7 +44621,7 @@ <argument index="0" name="source" type="String"> </argument> <description> - Set the script source code. + Sets the script source code. Does not reload the class implementation. </description> </method> </methods> @@ -45353,16 +45589,17 @@ </class> <class name="Spatial" inherits="Node" category="Core"> <brief_description> - Base class for all 3D nodes. + Most basic 3D game object, parent of all 3D related nodes. </brief_description> <description> - Spatial is the base for every type of 3D [Node]. It contains a 3D [Transform] which can be set or get as local or global. If a Spatial [Node] has Spatial children, their transforms will be relative to the parent. + Most basic 3D game object, with a 3D [Transform] and visibility settings. All 3D physics nodes and sprites inherit from Spatial. Use Spatial as a parent node to move, scale, rotate and show/hide children in a 3D project. </description> <methods> <method name="get_gizmo" qualifiers="const"> <return type="SpatialGizmo"> </return> <description> + Return the SpatialGizmo for this node. Used for example in [EditorSpatialGizmo] as custom visualization and editing handles in Editor. </description> </method> <method name="get_global_transform" qualifiers="const"> @@ -45416,6 +45653,7 @@ <return type="World"> </return> <description> + Return current [World] resource this Spatial node is registered to. </description> </method> <method name="global_rotate"> @@ -45426,6 +45664,7 @@ <argument index="1" name="radians" type="float"> </argument> <description> + Rotate current node along normal [Vector3] by angle in radians in Global space. </description> </method> <method name="global_translate"> @@ -45434,42 +45673,49 @@ <argument index="0" name="offset" type="Vector3"> </argument> <description> + Move current node by [Vector3] offset in Global space. </description> </method> <method name="hide"> <return type="void"> </return> <description> + Disable rendering of this node. Change Spatial Visible property to false. </description> </method> <method name="is_local_transform_notification_enabled" qualifiers="const"> <return type="bool"> </return> <description> + Returns whether node sends notification that its local transformation changed. Spatial will not propagate this by default. </description> </method> <method name="is_set_as_toplevel" qualifiers="const"> <return type="bool"> </return> <description> + Returns whether this node is set as Toplevel, ignoring its parent node transformations. </description> </method> <method name="is_transform_notification_enabled" qualifiers="const"> <return type="bool"> </return> <description> + Returns whether node sends notification that its transformation changed. Spatial will not propagate this by default. </description> </method> <method name="is_visible" qualifiers="const"> <return type="bool"> </return> <description> + Returns whether this node is set to be visible. </description> </method> <method name="is_visible_in_tree" qualifiers="const"> <return type="bool"> </return> <description> + Returns whether this node is visible, taking into consideration that its parents visibility. </description> </method> <method name="look_at"> @@ -45480,6 +45726,7 @@ <argument index="1" name="up" type="Vector3"> </argument> <description> + Rotates itself to point into direction of target position. Operations take place in global space. </description> </method> <method name="look_at_from_pos"> @@ -45492,12 +45739,14 @@ <argument index="2" name="up" type="Vector3"> </argument> <description> + Moves itself to specified position and then rotates itself to point into direction of target position. Operations take place in global space. </description> </method> <method name="orthonormalize"> <return type="void"> </return> <description> + Reset this node transformations (like scale, skew and taper) preserving its rotation and translation. Performs orthonormalization on this node [Transform3D]. </description> </method> <method name="rotate"> @@ -45508,6 +45757,7 @@ <argument index="1" name="radians" type="float"> </argument> <description> + Rotates node in local space on given normal [Vector3] by angle in radians. </description> </method> <method name="rotate_x"> @@ -45516,6 +45766,7 @@ <argument index="0" name="radians" type="float"> </argument> <description> + Rotates node in local space on X axis by angle in radians. </description> </method> <method name="rotate_y"> @@ -45524,6 +45775,7 @@ <argument index="0" name="radians" type="float"> </argument> <description> + Rotates node in local space on Y axis by angle in radians. </description> </method> <method name="rotate_z"> @@ -45532,6 +45784,7 @@ <argument index="0" name="radians" type="float"> </argument> <description> + Rotates node in local space on Z axis by angle in radians. </description> </method> <method name="set_as_toplevel"> @@ -45540,6 +45793,7 @@ <argument index="0" name="enable" type="bool"> </argument> <description> + Makes this node ignore its parents tranformations. Node tranformations are only in global space. </description> </method> <method name="set_gizmo"> @@ -45548,6 +45802,7 @@ <argument index="0" name="gizmo" type="SpatialGizmo"> </argument> <description> + Set [SpatialGizmo] for this node. Used for example in [EditorSpatialGizmo] as custom visualization and editing handles in Editor. </description> </method> <method name="set_global_transform"> @@ -45556,13 +45811,14 @@ <argument index="0" name="global" type="Transform"> </argument> <description> - Set the transform globally, relative to worldspace. + Set the transform globally, relative to world space. </description> </method> <method name="set_identity"> <return type="void"> </return> <description> + Reset all tranformations for this node. Set its [Transform3D] to identity matrix. </description> </method> <method name="set_ignore_transform_notification"> @@ -45571,6 +45827,7 @@ <argument index="0" name="enabled" type="bool"> </argument> <description> + Set whether this node ignores notification that its transformation changed. </description> </method> <method name="set_notify_local_transform"> @@ -45579,6 +45836,7 @@ <argument index="0" name="enable" type="bool"> </argument> <description> + Set whether this node sends notification that its local transformation changed. Spatial will not propagate this by default. </description> </method> <method name="set_notify_transform"> @@ -45587,6 +45845,7 @@ <argument index="0" name="enable" type="bool"> </argument> <description> + Set whether this node sends notification that its transformation changed. Spatial will not propagate this by default. </description> </method> <method name="set_rotation"> @@ -45613,6 +45872,7 @@ <argument index="0" name="scale" type="Vector3"> </argument> <description> + Set the scale. </description> </method> <method name="set_transform"> @@ -45644,6 +45904,7 @@ <return type="void"> </return> <description> + Enable rendering of this node. Change Spatial Visible property to false. </description> </method> <method name="to_global" qualifiers="const"> @@ -45652,6 +45913,7 @@ <argument index="0" name="local_point" type="Vector3"> </argument> <description> + Tranform [Vector3] from this node local space to world space. </description> </method> <method name="to_local" qualifiers="const"> @@ -45660,6 +45922,7 @@ <argument index="0" name="global_point" type="Vector3"> </argument> <description> + Tranform [Vector3] from world space to this node local space. </description> </method> <method name="translate"> @@ -45668,46 +45931,60 @@ <argument index="0" name="offset" type="Vector3"> </argument> <description> + Change node position by given offset [Vector3]. </description> </method> <method name="update_gizmo"> <return type="void"> </return> <description> + Update [SpatialGizmo] of this node. </description> </method> </methods> <members> <member name="global_transform" type="Transform" setter="set_global_transform" getter="get_global_transform" brief=""> + World space (global) [Transform] of this node. </member> <member name="rotation" type="Vector3" setter="set_rotation" getter="get_rotation" brief=""> + Local euler rotation in radians of this node. </member> <member name="rotation_deg" type="Vector3" setter="set_rotation_deg" getter="get_rotation_deg" brief=""> + Local euler rotation in degrees of this node. </member> <member name="scale" type="Vector3" setter="set_scale" getter="get_scale" brief=""> + Local scale of this node. </member> <member name="transform" type="Transform" setter="set_transform" getter="get_transform" brief=""> + Local space [Transform] of this node. </member> <member name="translation" type="Vector3" setter="set_translation" getter="get_translation" brief=""> + Local translation of this node. </member> <member name="visible" type="bool" setter="set_visible" getter="is_visible" brief=""> + Visibility of this node. Toggles if this node is rendered. </member> </members> <signals> <signal name="visibility_changed"> <description> + Emitted when node visibility changed. </description> </signal> </signals> <constants> <constant name="NOTIFICATION_TRANSFORM_CHANGED" value="29" enum=""> - Spatial nodes receive this notification with their global transform changes. This means that either the current or a parent node changed its transform. + Spatial nodes receive this notification when their global transform changes. This means that either the current or a parent node changed its transform. + In order for NOTIFICATION_TRANSFORM_CHANGED to work user first needs to ask for it, with set_notify_transform(true). </constant> <constant name="NOTIFICATION_ENTER_WORLD" value="41" enum=""> + Spatial nodes receive this notification when they are registered to new [World] resource. </constant> <constant name="NOTIFICATION_EXIT_WORLD" value="42" enum=""> + Spatial nodes receive this notification when they are unregistered from current [World] resource. </constant> <constant name="NOTIFICATION_VISIBILITY_CHANGED" value="43" enum=""> + Spatial nodes receive this notification when their visibility changes. </constant> </constants> </class> @@ -46785,8 +47062,10 @@ </class> <class name="SphereMesh" inherits="PrimitiveMesh" category="Core"> <brief_description> + Class representing a spherical [PrimitiveMesh]. </brief_description> <description> + Class representing a spherical [PrimitiveMesh]. </description> <methods> <method name="get_height" qualifiers="const"> @@ -46862,14 +47141,19 @@ </methods> <members> <member name="height" type="float" setter="set_height" getter="get_height" brief=""> + Full height of the sphere. Defaults to 2.0. </member> <member name="is_hemisphere" type="bool" setter="set_is_hemisphere" getter="get_is_hemisphere" brief=""> + Determines whether a full sphere or a hemisphere is created. Attention: To get a regular hemisphere the height and radius of the sphere have to equal. Defaults to false. </member> <member name="radial_segments" type="int" setter="set_radial_segments" getter="get_radial_segments" brief=""> + Number of radial segments on the sphere. Defaults to 64. </member> <member name="radius" type="float" setter="set_radius" getter="get_radius" brief=""> + Radius of sphere. Defaults to 1.0. </member> <member name="rings" type="int" setter="set_rings" getter="get_rings" brief=""> + Number of segments along the height of the sphere. Defaults to 32. </member> </members> <constants> @@ -47869,12 +48153,16 @@ </methods> <members> <member name="bounce" type="float" setter="set_bounce" getter="get_bounce" brief=""> + The body bounciness. </member> <member name="constant_angular_velocity" type="Vector3" setter="set_constant_angular_velocity" getter="get_constant_angular_velocity" brief=""> + The constant angular velocity for the body. This does not rotate the body, but affects other bodies that touch it, as if it was in a state of rotation. </member> <member name="constant_linear_velocity" type="Vector3" setter="set_constant_linear_velocity" getter="get_constant_linear_velocity" brief=""> + The constant linear velocity for the body. This does not move the body, but affects other bodies that touch it, as if it was in a state of movement. </member> <member name="friction" type="float" setter="set_friction" getter="get_friction" brief=""> + The body friction, from 0 (frictionless) to 1 (full friction). </member> </members> <constants> @@ -53077,8 +53365,15 @@ </class> <class name="ToolButton" inherits="Button" category="Core"> <brief_description> + Flat button helper class. </brief_description> <description> + This is a helper class to generate a flat [Button] (see [method Button.set_flat]), creating a ToolButton is equivalent to: + + [codeblock] + var btn = Button.new() + btn.set_flat(true) + [/codeblock] </description> <methods> </methods> @@ -53283,10 +53578,10 @@ </class> <class name="Transform" category="Built-In Types"> <brief_description> - 3D Transformation. + 3D Transformation. 3x4 matrix. </brief_description> <description> - Transform is used to store translation, rotation and scaling transformations. It consists of a Basis "basis" and Vector3 "origin". Transform is used to represent transformations of objects in space, and as such, determine their position, orientation and scale. It is similar to a 3x4 matrix. + Represents one or many transformations in 3D space such as translation, rotation, or scaling. It consists of a [Basis] "basis" and an [Vector3] "origin". It is similar to a 3x4 matrix. </description> <methods> <method name="Transform"> @@ -53301,7 +53596,7 @@ <argument index="3" name="origin" type="Vector3"> </argument> <description> - Construct the Transform from four Vector3. Each axis corresponds to local basis vectors (some of which may be scaled). + Construct the Transform from four [Vector3]. Each axis corresponds to local basis vectors (some of which may be scaled). </description> </method> <method name="Transform"> @@ -53312,7 +53607,7 @@ <argument index="1" name="origin" type="Vector3"> </argument> <description> - Construct the Transform from a Basis and Vector3. + Construct the Transform from a [Basis] and [Vector3]. </description> </method> <method name="Transform"> @@ -53321,7 +53616,7 @@ <argument index="0" name="from" type="Transform2D"> </argument> <description> - Construct the Transform from a Transform2D. + Construct the Transform from a [Transform2D]. </description> </method> <method name="Transform"> @@ -53330,7 +53625,7 @@ <argument index="0" name="from" type="Quat"> </argument> <description> - Construct the Transform from a Quat. The origin will be Vector3(0, 0, 0). + Construct the Transform from a [Quat]. The origin will be Vector3(0, 0, 0). </description> </method> <method name="Transform"> @@ -53339,7 +53634,7 @@ <argument index="0" name="from" type="Basis"> </argument> <description> - Construct the Transform from a Basis. The origin will be Vector3(0, 0, 0). + Construct the Transform from a [Basis]. The origin will be Vector3(0, 0, 0). </description> </method> <method name="affine_inverse"> @@ -53357,13 +53652,14 @@ <argument index="1" name="weight" type="float"> </argument> <description> + Interpolate to other Transform by weight amount (0-1). </description> </method> <method name="inverse"> <return type="Transform"> </return> <description> - Returns the inverse of the transform, under the assumption that the transformation is composed of rotation and translation (no scaling). + Returns the inverse of the transform, under the assumption that the transformation is composed of rotation and translation (no scaling, use affine_inverse for transforms with scaling). </description> </method> <method name="looking_at"> @@ -53410,7 +53706,7 @@ <argument index="0" name="ofs" type="Vector3"> </argument> <description> - Translate the transform by the specified displacement. + Translate the transform by the specified offset. </description> </method> <method name="xform"> @@ -53434,10 +53730,10 @@ </methods> <members> <member name="basis" type="Basis" setter="" getter="" brief=""> - The basis is a matrix containing 3 [Vector3] as its columns: X axis, Y axis, and Z axis. These vectors can be interpreted as the basis vectors of local coordinate system travelling with the object. + The basis is a matrix containing 3 [Vector3] as its columns: X axis, Y axis, and Z axis. These vectors can be interpreted as the basis vectors of local coordinate system traveling with the object. </member> <member name="origin" type="Vector3" setter="" getter="" brief=""> - The origin of the transform. Which is the translation offset. + The translation offset of the transform. </member> </members> <constants> @@ -53445,10 +53741,10 @@ </class> <class name="Transform2D" category="Built-In Types"> <brief_description> - 3x2 Matrix for 2D transforms. + 2D Transformation. 3x2 matrix. </brief_description> <description> - 3x2 Matrix for 2D transforms. + Represents one or many transformations in 3D space such as translation, rotation, or scaling. It consists of a two [Vector2] x, y and [Vector2] "origin". It is similar to a 3x2 matrix. </description> <methods> <method name="Transform2D"> @@ -53457,6 +53753,7 @@ <argument index="0" name="from" type="Transform"> </argument> <description> + Constructs the [Transform2D] from a 3D [Transform]. </description> </method> <method name="Transform2D"> @@ -53469,6 +53766,7 @@ <argument index="2" name="origin" type="Vector2"> </argument> <description> + Constructs the [Transform2D] from 3 [Vector2] consisting of rows x, y and origin. </description> </method> <method name="Transform2D"> @@ -53479,13 +53777,14 @@ <argument index="1" name="pos" type="Vector2"> </argument> <description> + Constructs the [Transform2D] from rotation angle in radians and position [Vector2]. </description> </method> <method name="affine_inverse"> <return type="Transform2D"> </return> <description> - Return the inverse of the matrix. + Returns the inverse of the matrix. </description> </method> <method name="basis_xform"> @@ -53494,6 +53793,7 @@ <argument index="0" name="v" type="var"> </argument> <description> + Transforms the given vector "v" by this transform basis (no translation). </description> </method> <method name="basis_xform_inv"> @@ -53502,12 +53802,14 @@ <argument index="0" name="v" type="var"> </argument> <description> + Inverse-transforms vector "v" by this transform basis (no translation). </description> </method> <method name="get_origin"> <return type="Vector2"> </return> <description> + Return the origin [Vector2] (translation). </description> </method> <method name="get_rotation"> @@ -53521,6 +53823,7 @@ <return type="Vector2"> </return> <description> + Return the scale. </description> </method> <method name="interpolate_with"> @@ -53531,18 +53834,21 @@ <argument index="1" name="weight" type="float"> </argument> <description> + Interpolate to other Transform2D by weight amount (0-1). </description> </method> <method name="inverse"> <return type="Transform2D"> </return> <description> + Returns the inverse of the transform, under the assumption that the transformation is composed of rotation and translation (no scaling, use affine_inverse for transforms with scaling). </description> </method> <method name="orthonormalized"> <return type="Transform2D"> </return> <description> + Returns a transfrom with the basis orthogonal (90 degrees), and normalized axis vectors. </description> </method> <method name="rotated"> @@ -53551,6 +53857,7 @@ <argument index="0" name="phi" type="float"> </argument> <description> + Rotate the transform by phi. </description> </method> <method name="scaled"> @@ -53559,6 +53866,7 @@ <argument index="0" name="scale" type="Vector2"> </argument> <description> + Scale the transform by the specified 2D scaling factors. </description> </method> <method name="translated"> @@ -53567,6 +53875,7 @@ <argument index="0" name="offset" type="Vector2"> </argument> <description> + Translate the transform by the specified offset. </description> </method> <method name="xform"> @@ -53575,6 +53884,7 @@ <argument index="0" name="v" type="var"> </argument> <description> + Transforms the given vector "v" by this transform. </description> </method> <method name="xform_inv"> @@ -53583,15 +53893,19 @@ <argument index="0" name="v" type="var"> </argument> <description> + Inverse-transforms the given vector "v" by this transform. </description> </method> </methods> <members> <member name="origin" type="Vector2" setter="" getter="" brief=""> + The translation offset of the transform. </member> <member name="x" type="Vector2" setter="" getter="" brief=""> + The X axis of 2x2 basis matrix containing 2 [Vector2] as its columns: X axis and Y axis. These vectors can be interpreted as the basis vectors of local coordinate system traveling with the object. </member> <member name="y" type="Vector2" setter="" getter="" brief=""> + The Y axis of 2x2 basis matrix containing 2 [Vector2] as its columns: X axis and Y axis. These vectors can be interpreted as the basis vectors of local coordinate system traveling with the object. </member> </members> <constants> @@ -55464,8 +55778,10 @@ </class> <class name="Variant" category="Core"> <brief_description> + The most important data type in Godot. </brief_description> <description> + A Variant takes up only 20 bytes and can store almost any engine datatype inside of it. Variants are rarely used to hold information for long periods of time, instead they are used mainly for communication, editing, serialization and moving data around. </description> <methods> </methods> diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index b695b7e704..146a2359b6 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -2599,7 +2599,7 @@ void RasterizerSceneGLES3::_setup_directional_light(int p_index, const Transform } } - ubo_data.shadow_split_offsets[j] = 1.0 / li->shadow_transform[j].split; + ubo_data.shadow_split_offsets[j] = li->shadow_transform[j].split; Transform modelview = (p_camera_inverse_transform * li->shadow_transform[j].transform).inverse(); @@ -3174,6 +3174,7 @@ void RasterizerSceneGLES3::_render_mrts(Environment *env, const CameraMatrix &p_ for (int i = 0; i < storage->frame.current_rt->effects.ssao.depth_mipmap_fbos.size(); i++) { state.ssao_minify_shader.set_conditional(SsaoMinifyShaderGLES3::MINIFY_START, i == 0); + state.ssao_minify_shader.set_conditional(SsaoMinifyShaderGLES3::USE_ORTHOGONAL_PROJECTION, p_cam_projection.is_orthogonal()); state.ssao_minify_shader.bind(); state.ssao_minify_shader.set_uniform(SsaoMinifyShaderGLES3::CAMERA_Z_FAR, p_cam_projection.get_z_far()); state.ssao_minify_shader.set_uniform(SsaoMinifyShaderGLES3::CAMERA_Z_NEAR, p_cam_projection.get_z_near()); @@ -3203,6 +3204,7 @@ void RasterizerSceneGLES3::_render_mrts(Environment *env, const CameraMatrix &p_ glDepthFunc(GL_GREATER); // do SSAO! state.ssao_shader.set_conditional(SsaoShaderGLES3::ENABLE_RADIUS2, env->ssao_radius2 > 0.001); + state.ssao_shader.set_conditional(SsaoShaderGLES3::USE_ORTHOGONAL_PROJECTION, p_cam_projection.is_orthogonal()); state.ssao_shader.bind(); state.ssao_shader.set_uniform(SsaoShaderGLES3::CAMERA_Z_FAR, p_cam_projection.get_z_far()); state.ssao_shader.set_uniform(SsaoShaderGLES3::CAMERA_Z_NEAR, p_cam_projection.get_z_near()); @@ -3312,6 +3314,7 @@ void RasterizerSceneGLES3::_render_mrts(Environment *env, const CameraMatrix &p_ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, storage->frame.current_rt->effects.ssao.blur_fbo[0]); glBlitFramebuffer(0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height, 0, 0, storage->frame.current_rt->width, storage->frame.current_rt->height, GL_COLOR_BUFFER_BIT, GL_LINEAR); + state.sss_shader.set_conditional(SubsurfScatteringShaderGLES3::USE_ORTHOGONAL_PROJECTION, p_cam_projection.is_orthogonal()); state.sss_shader.set_conditional(SubsurfScatteringShaderGLES3::USE_11_SAMPLES, subsurface_scatter_quality == SSS_QUALITY_LOW); state.sss_shader.set_conditional(SubsurfScatteringShaderGLES3::USE_17_SAMPLES, subsurface_scatter_quality == SSS_QUALITY_MEDIUM); state.sss_shader.set_conditional(SubsurfScatteringShaderGLES3::USE_25_SAMPLES, subsurface_scatter_quality == SSS_QUALITY_HIGH); @@ -3366,6 +3369,7 @@ void RasterizerSceneGLES3::_render_mrts(Environment *env, const CameraMatrix &p_ //perform SSR state.ssr_shader.set_conditional(ScreenSpaceReflectionShaderGLES3::REFLECT_ROUGHNESS, env->ssr_roughness); + state.ssr_shader.set_conditional(ScreenSpaceReflectionShaderGLES3::USE_ORTHOGONAL_PROJECTION, p_cam_projection.is_orthogonal()); state.ssr_shader.bind(); @@ -3519,6 +3523,7 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p int vp_h = storage->frame.current_rt->height; int vp_w = storage->frame.current_rt->width; + state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::USE_ORTHOGONAL_PROJECTION, p_cam_projection.is_orthogonal()); state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_FAR_BLUR, true); state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_LOW, env->dof_blur_far_quality == VS::ENV_DOF_BLUR_QUALITY_LOW); state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_MEDIUM, env->dof_blur_far_quality == VS::ENV_DOF_BLUR_QUALITY_MEDIUM); @@ -3561,6 +3566,7 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_LOW, false); state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_MEDIUM, false); state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_HIGH, false); + state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::USE_ORTHOGONAL_PROJECTION, false); composite_from = storage->frame.current_rt->effects.mip_maps[0].color; } @@ -3573,6 +3579,7 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p int vp_h = storage->frame.current_rt->height; int vp_w = storage->frame.current_rt->width; + state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::USE_ORTHOGONAL_PROJECTION, p_cam_projection.is_orthogonal()); state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_NEAR_BLUR, true); state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_NEAR_FIRST_TAP, true); @@ -3648,6 +3655,7 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_LOW, false); state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_MEDIUM, false); state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::DOF_QUALITY_HIGH, false); + state.effect_blur_shader.set_conditional(EffectBlurShaderGLES3::USE_ORTHOGONAL_PROJECTION, false); composite_from = storage->frame.current_rt->effects.mip_maps[0].color; } @@ -4157,7 +4165,7 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const clear_color = env->bg_color.to_linear(); storage->frame.clear_request = false; - } else if (env->bg_mode == VS::ENV_BG_SKY) { + } else if (env->bg_mode == VS::ENV_BG_SKY || env->bg_mode == VS::ENV_BG_COLOR_SKY) { sky = storage->sky_owner.getornull(env->sky); @@ -4165,6 +4173,9 @@ void RasterizerSceneGLES3::render_scene(const Transform &p_cam_transform, const env_radiance_tex = sky->radiance; } storage->frame.clear_request = false; + if (env->bg_mode == VS::ENV_BG_COLOR_SKY) { + clear_color = env->bg_color.to_linear(); + } } else { storage->frame.clear_request = false; diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index a744abdfa8..3cacfac578 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -4473,6 +4473,7 @@ RID RasterizerStorageGLES3::light_create(VS::LightType p_type) { light->omni_shadow_mode = VS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID; light->omni_shadow_detail = VS::LIGHT_OMNI_SHADOW_DETAIL_VERTICAL; light->directional_blend_splits = false; + light->directional_range_mode = VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE; light->reverse_cull = false; light->version = 0; @@ -4625,6 +4626,22 @@ VS::LightDirectionalShadowMode RasterizerStorageGLES3::light_directional_get_sha return light->directional_shadow_mode; } +void RasterizerStorageGLES3::light_directional_set_shadow_depth_range_mode(RID p_light, VS::LightDirectionalShadowDepthRangeMode p_range_mode) { + + Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND(!light); + + light->directional_range_mode=p_range_mode; +} + +VS::LightDirectionalShadowDepthRangeMode RasterizerStorageGLES3::light_directional_get_shadow_depth_range_mode(RID p_light) const { + + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE); + + return light->directional_range_mode; +} + VS::LightType RasterizerStorageGLES3::light_get_type(RID p_light) const { const Light *light = light_owner.getornull(p_light); diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index b26032dbc4..6abc22b643 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -879,6 +879,7 @@ public: VS::LightOmniShadowMode omni_shadow_mode; VS::LightOmniShadowDetail omni_shadow_detail; VS::LightDirectionalShadowMode directional_shadow_mode; + VS::LightDirectionalShadowDepthRangeMode directional_range_mode; bool directional_blend_splits; uint64_t version; }; @@ -906,6 +907,9 @@ public: virtual VS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light); virtual VS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light); + virtual void light_directional_set_shadow_depth_range_mode(RID p_light, VS::LightDirectionalShadowDepthRangeMode p_range_mode); + virtual VS::LightDirectionalShadowDepthRangeMode light_directional_get_shadow_depth_range_mode(RID p_light) const; + virtual bool light_has_shadow(RID p_light) const; virtual VS::LightType light_get_type(RID p_light) const; diff --git a/drivers/gles3/shaders/effect_blur.glsl b/drivers/gles3/shaders/effect_blur.glsl index 09e522866c..b5f98a1244 100644 --- a/drivers/gles3/shaders/effect_blur.glsl +++ b/drivers/gles3/shaders/effect_blur.glsl @@ -168,7 +168,11 @@ void main() { float depth = textureLod( dof_source_depth, uv_interp, 0.0).r; depth = depth * 2.0 - 1.0; +#ifdef USE_ORTHOGONAL_PROJECTION + depth = ((depth + (camera_z_far + camera_z_near)/(camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near))/2.0; +#else depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - depth * (camera_z_far - camera_z_near)); +#endif float amount = smoothstep(dof_begin,dof_end,depth); float k_accum=0.0; @@ -182,8 +186,11 @@ void main() { float tap_depth = texture( dof_source_depth, tap_uv, 0.0).r; tap_depth = tap_depth * 2.0 - 1.0; +#ifdef USE_ORTHOGONAL_PROJECTION + tap_depth = ((tap_depth + (camera_z_far + camera_z_near)/(camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near))/2.0; +#else tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near)); - +#endif float tap_amount = mix(smoothstep(dof_begin,dof_end,tap_depth),1.0,int_ofs==0); tap_amount*=tap_amount*tap_amount; //prevent undesired glow effect @@ -221,7 +228,11 @@ void main() { float tap_depth = texture( dof_source_depth, tap_uv, 0.0).r; tap_depth = tap_depth * 2.0 - 1.0; +#ifdef USE_ORTHOGONAL_PROJECTION + tap_depth = ((tap_depth + (camera_z_far + camera_z_near)/(camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near))/2.0; +#else tap_depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - tap_depth * (camera_z_far - camera_z_near)); +#endif float tap_amount = 1.0-smoothstep(dof_end,dof_begin,tap_depth); tap_amount*=tap_amount*tap_amount; //prevent undesired glow effect diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 977cee5fcb..73268c1914 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -484,7 +484,7 @@ VERTEX_SHADER_CODE vec3 directional_diffuse = vec3(0.0); vec3 directional_specular = vec3(0.0); - light_compute(normal_interp,-light_direction_attenuation.xyz,-normalize( vertex_interp ),light_color_energy.rgb,roughness,directional_diffuse,directional_specular); + light_compute(normal_interp,-light_direction_attenuation.xyz,-normalize( vertex_interp ),light_color_energy.rgb,roughness,directional_diffuse,directional_specular); float diff_avg = dot(diffuse_light_interp.rgb,vec3(0.33333)); float diff_dir_avg = dot(directional_diffuse,vec3(0.33333)); @@ -1697,9 +1697,16 @@ FRAGMENT_SHADER_CODE vec3 light_attenuation=vec3(1.0); + float depth_z = -vertex.z; #ifdef LIGHT_DIRECTIONAL_SHADOW - if (gl_FragCoord.w > shadow_split_offsets.w) { +#ifdef LIGHT_USE_PSSM4 + if (depth_z < shadow_split_offsets.w) { +#elif defined(LIGHT_USE_PSSM2) + if (depth_z < shadow_split_offsets.y) { +#else + if (depth_z < shadow_split_offsets.x) { +#endif //LIGHT_USE_PSSM4 vec3 pssm_coord; float pssm_fade=0.0; @@ -1708,17 +1715,15 @@ FRAGMENT_SHADER_CODE float pssm_blend; vec3 pssm_coord2; bool use_blend=true; - vec3 light_pssm_split_inv = 1.0/shadow_split_offsets.xyz; - float w_inv = 1.0/gl_FragCoord.w; #endif #ifdef LIGHT_USE_PSSM4 - if (gl_FragCoord.w > shadow_split_offsets.y) { + if (depth_z < shadow_split_offsets.y) { - if (gl_FragCoord.w > shadow_split_offsets.x) { + if (depth_z < shadow_split_offsets.x) { highp vec4 splane=(shadow_matrix1 * vec4(vertex,1.0)); pssm_coord=splane.xyz/splane.w; @@ -1728,7 +1733,7 @@ FRAGMENT_SHADER_CODE splane=(shadow_matrix2 * vec4(vertex,1.0)); pssm_coord2=splane.xyz/splane.w; - pssm_blend=smoothstep(0.0,light_pssm_split_inv.x,w_inv); + pssm_blend=smoothstep(0.0,shadow_split_offsets.x,depth_z); #endif } else { @@ -1739,14 +1744,14 @@ FRAGMENT_SHADER_CODE #if defined(LIGHT_USE_PSSM_BLEND) splane=(shadow_matrix3 * vec4(vertex,1.0)); pssm_coord2=splane.xyz/splane.w; - pssm_blend=smoothstep(light_pssm_split_inv.x,light_pssm_split_inv.y,w_inv); + pssm_blend=smoothstep(shadow_split_offsets.x,shadow_split_offsets.y,depth_z); #endif } } else { - if (gl_FragCoord.w > shadow_split_offsets.z) { + if (depth_z < shadow_split_offsets.z) { highp vec4 splane=(shadow_matrix3 * vec4(vertex,1.0)); pssm_coord=splane.xyz/splane.w; @@ -1754,13 +1759,14 @@ FRAGMENT_SHADER_CODE #if defined(LIGHT_USE_PSSM_BLEND) splane=(shadow_matrix4 * vec4(vertex,1.0)); pssm_coord2=splane.xyz/splane.w; - pssm_blend=smoothstep(light_pssm_split_inv.y,light_pssm_split_inv.z,w_inv); + pssm_blend=smoothstep(shadow_split_offsets.y,shadow_split_offsets.z,depth_z); #endif } else { + highp vec4 splane=(shadow_matrix4 * vec4(vertex,1.0)); pssm_coord=splane.xyz/splane.w; - pssm_fade = smoothstep(shadow_split_offsets.z,shadow_split_offsets.w,gl_FragCoord.w); + pssm_fade = smoothstep(shadow_split_offsets.z,shadow_split_offsets.w,depth_z); #if defined(LIGHT_USE_PSSM_BLEND) use_blend=false; @@ -1776,7 +1782,7 @@ FRAGMENT_SHADER_CODE #ifdef LIGHT_USE_PSSM2 - if (gl_FragCoord.w > shadow_split_offsets.x) { + if (depth_z < shadow_split_offsets.x) { highp vec4 splane=(shadow_matrix1 * vec4(vertex,1.0)); pssm_coord=splane.xyz/splane.w; @@ -1786,13 +1792,13 @@ FRAGMENT_SHADER_CODE splane=(shadow_matrix2 * vec4(vertex,1.0)); pssm_coord2=splane.xyz/splane.w; - pssm_blend=smoothstep(0.0,light_pssm_split_inv.x,w_inv); + pssm_blend=smoothstep(0.0,shadow_split_offsets.x,depth_z); #endif } else { highp vec4 splane=(shadow_matrix2 * vec4(vertex,1.0)); pssm_coord=splane.xyz/splane.w; - pssm_fade = smoothstep(shadow_split_offsets.x,shadow_split_offsets.y,gl_FragCoord.w); + pssm_fade = smoothstep(shadow_split_offsets.x,shadow_split_offsets.y,depth_z); #if defined(LIGHT_USE_PSSM_BLEND) use_blend=false; @@ -1834,6 +1840,7 @@ FRAGMENT_SHADER_CODE } + #endif //LIGHT_DIRECTIONAL_SHADOW #ifdef USE_VERTEX_LIGHTING diff --git a/drivers/gles3/shaders/screen_space_reflection.glsl b/drivers/gles3/shaders/screen_space_reflection.glsl index cc41d36c37..b2e6f7a736 100644 --- a/drivers/gles3/shaders/screen_space_reflection.glsl +++ b/drivers/gles3/shaders/screen_space_reflection.glsl @@ -56,7 +56,6 @@ vec2 view_to_screen(vec3 view_pos,out float w) { #define M_PI 3.14159265359 - void main() { @@ -158,8 +157,13 @@ void main() { w+=w_advance; //convert to linear depth + depth = texture(source_depth, pos*pixel_size).r * 2.0 - 1.0; +#ifdef USE_ORTHOGONAL_PROJECTION + depth = ((depth + (camera_z_far + camera_z_near)/(camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near))/2.0; +#else depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - depth * (camera_z_far - camera_z_near)); +#endif depth=-depth; z_from = z_to; diff --git a/drivers/gles3/shaders/ssao.glsl b/drivers/gles3/shaders/ssao.glsl index 0e8fc89d6c..c668e63745 100644 --- a/drivers/gles3/shaders/ssao.glsl +++ b/drivers/gles3/shaders/ssao.glsl @@ -65,7 +65,12 @@ layout(location = 0) out float visibility; uniform vec4 proj_info; vec3 reconstructCSPosition(vec2 S, float z) { - return vec3((S.xy * proj_info.xy + proj_info.zw) * z, z); +#ifdef USE_ORTHOGONAL_PROJECTION + return vec3((S.xy * proj_info.xy + proj_info.zw), z); +#else + return vec3((S.xy * proj_info.xy + proj_info.zw) * z, z); + +#endif } vec3 getPosition(ivec2 ssP) { @@ -73,7 +78,11 @@ vec3 getPosition(ivec2 ssP) { P.z = texelFetch(source_depth, ssP, 0).r; P.z = P.z * 2.0 - 1.0; +#ifdef USE_ORTHOGONAL_PROJECTION + P.z = ((P.z + (camera_z_far + camera_z_near)/(camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near))/2.0; +#else P.z = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - P.z * (camera_z_far - camera_z_near)); +#endif P.z = -P.z; // Offset to pixel center @@ -118,7 +127,12 @@ vec3 getOffsetPosition(ivec2 ssC, vec2 unitOffset, float ssR) { //read from depth buffer P.z = texelFetch(source_depth, mipP, 0).r; P.z = P.z * 2.0 - 1.0; +#ifdef USE_ORTHOGONAL_PROJECTION + P.z = ((P.z + (camera_z_far + camera_z_near)/(camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near))/2.0; +#else P.z = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - P.z * (camera_z_far - camera_z_near)); + +#endif P.z = -P.z; } else { @@ -214,8 +228,11 @@ void main() { // Choose the screen-space sample radius // proportional to the projected area of the sphere +#ifdef USE_ORTHOGONAL_PROJECTION + float ssDiskRadius = -proj_scale * radius; +#else float ssDiskRadius = -proj_scale * radius / C.z; - +#endif float sum = 0.0; for (int i = 0; i < NUM_SAMPLES; ++i) { sum += sampleAO(ssC, C, n_C, ssDiskRadius, radius,i, randomPatternRotationAngle); diff --git a/drivers/gles3/shaders/ssao_minify.glsl b/drivers/gles3/shaders/ssao_minify.glsl index 6e46a1842c..647c762438 100644 --- a/drivers/gles3/shaders/ssao_minify.glsl +++ b/drivers/gles3/shaders/ssao_minify.glsl @@ -41,7 +41,11 @@ void main() { #ifdef MINIFY_START float fdepth = texelFetch(source_depth, clamp(ssP * 2 + ivec2(ssP.y & 1, ssP.x & 1), ivec2(0), from_size - ivec2(1)), source_mipmap).r; fdepth = fdepth * 2.0 - 1.0; +#ifdef USE_ORTHOGONAL_PROJECTION + fdepth = ((fdepth + (camera_z_far + camera_z_near)/(camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near))/2.0; +#else fdepth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - fdepth * (camera_z_far - camera_z_near)); +#endif fdepth /= camera_z_far; depth = uint(clamp(fdepth*65535.0,0.0,65535.0)); diff --git a/drivers/gles3/shaders/subsurf_scattering.glsl b/drivers/gles3/shaders/subsurf_scattering.glsl index ad39c61990..20c3b7473f 100644 --- a/drivers/gles3/shaders/subsurf_scattering.glsl +++ b/drivers/gles3/shaders/subsurf_scattering.glsl @@ -127,12 +127,16 @@ void main() { // Fetch linear depth of current pixel: float depth = texture(source_depth, uv_interp).r * 2.0 - 1.0; +#ifdef USE_ORTHOGONAL_PROJECTION + depth = ((depth + (camera_z_far + camera_z_near)/(camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near))/2.0; + float scale = unit_size; //remember depth is negative by default in OpenGL +#else depth = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - depth * (camera_z_far - camera_z_near)); + float scale = unit_size / depth; //remember depth is negative by default in OpenGL +#endif - float scale = unit_size / depth; //remember depth is negative by default in OpenGL - // Calculate the final step to fetch the surrounding pixels: vec2 step = max_radius * scale * dir; step *= strength; // Modulate it using the alpha channel. @@ -154,7 +158,12 @@ void main() { #ifdef ENABLE_FOLLOW_SURFACE // If the difference in depth is huge, we lerp color back to "colorM": float depth_cmp = texture(source_depth, offset).r *2.0 - 1.0; + +#ifdef USE_ORTHOGONAL_PROJECTION + depth_cmp = ((depth_cmp + (camera_z_far + camera_z_near)/(camera_z_far - camera_z_near)) * (camera_z_far - camera_z_near))/2.0; +#else depth_cmp = 2.0 * camera_z_near * camera_z_far / (camera_z_far + camera_z_near - depth_cmp * (camera_z_far - camera_z_near)); +#endif float s = clamp(300.0f * scale * max_radius * abs(depth - depth_cmp),0.0,1.0); diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp index b85a63a44a..6b8038c3ef 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -221,37 +221,24 @@ Error DirAccessUnix::change_dir(String p_dir) { if (prev_dir.parse_utf8(real_current_dir_name)) prev_dir = real_current_dir_name; //no utf8, maybe latin? - //print_line("directory we are changing out of (prev_dir): " + prev_dir); - // try_dir is the directory we are trying to change into String try_dir = ""; if (p_dir.is_rel_path()) { String next_dir = current_dir + "/" + p_dir; - //print_line("p_dir is relative: " + p_dir + " about to simplfy: " + next_dir); next_dir = next_dir.simplify_path(); try_dir = next_dir; } else { try_dir = p_dir; - //print_line("p_dir is absolute: " + p_dir); - } - - // if try_dir is nothing, it is not changing directory so change it to a "." otherwise chdir will fail - if (try_dir == "") { - try_dir = "."; } - //print_line("directory we are changing in to (try_dir): " + try_dir); - bool worked = (chdir(try_dir.utf8().get_data()) == 0); // we can only give this utf8 if (!worked) { - //print_line("directory does not exist"); return ERR_INVALID_PARAMETER; } // the directory exists, so set current_dir to try_dir current_dir = try_dir; chdir(prev_dir.utf8().get_data()); - //print_line("directory exists, setting current_dir to: " + current_dir); return OK; } @@ -319,11 +306,16 @@ size_t DirAccessUnix::get_space_left() { DirAccessUnix::DirAccessUnix() { dir_stream = 0; - current_dir = "."; _cisdir = false; /* determine drive count */ + // set current directory to an absolute path of the current directory + char real_current_dir_name[2048]; + getcwd(real_current_dir_name, 2048); + if (current_dir.parse_utf8(real_current_dir_name)) + current_dir = real_current_dir_name; + change_dir(current_dir); } diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 890c3d8091..54bf31cd62 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -489,6 +489,7 @@ DependencyErrorDialog::DependencyErrorDialog() { vb->add_margin_child(TTR("Scene failed to load due to missing dependencies:"), files, true); files->set_v_size_flags(SIZE_EXPAND_FILL); get_ok()->set_text(TTR("Open Anyway")); + get_cancel()->set_text(TTR("Done")); text = memnew(Label); vb->add_child(text); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index df4598793c..3d5732c749 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -2269,6 +2269,9 @@ void CanvasItemEditor::_notification(int p_what) { if (p_what == NOTIFICATION_FIXED_PROCESS) { + + EditorNode::get_singleton()->get_scene_root()->set_snap_controls_to_pixels(GLOBAL_GET("gui/common/snap_controls_to_pixels")); + List<Node *> &selection = editor_selection->get_selected_node_list(); bool all_control = true; diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 323a36154e..da53b06516 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -449,13 +449,15 @@ struct ProjectItem { String conf; uint64_t last_modified; bool favorite; + bool grayed; ProjectItem() {} - ProjectItem(const String &p_project, const String &p_path, const String &p_conf, uint64_t p_last_modified, bool p_favorite = false) { + ProjectItem(const String &p_project, const String &p_path, const String &p_conf, uint64_t p_last_modified, bool p_favorite = false, bool p_grayed = false) { project = p_project; path = p_path; conf = p_conf; last_modified = p_last_modified; favorite = p_favorite; + grayed = p_grayed; } _FORCE_INLINE_ bool operator<(const ProjectItem &l) const { return last_modified > l.last_modified; } _FORCE_INLINE_ bool operator==(const ProjectItem &l) const { return project == l.project; } @@ -737,6 +739,7 @@ void ProjectManager::_load_recent_projects() { String project = _name.get_slice("/", 1); String conf = path.plus_file("project.godot"); bool favorite = (_name.begins_with("favorite_projects/")) ? true : false; + bool grayed = false; uint64_t last_modified = 0; if (FileAccess::exists(conf)) { @@ -748,16 +751,15 @@ void ProjectManager::_load_recent_projects() { if (cache_modified > last_modified) last_modified = cache_modified; } - - ProjectItem item(project, path, conf, last_modified, favorite); - if (favorite) - favorite_projects.push_back(item); - else - projects.push_back(item); } else { - //project doesn't exist on disk but it's in the XML settings file - EditorSettings::get_singleton()->erase(_name); //remove it + grayed = true; } + + ProjectItem item(project, path, conf, last_modified, favorite, grayed); + if (favorite) + favorite_projects.push_back(item); + else + projects.push_back(item); } projects.sort(); @@ -782,14 +784,14 @@ void ProjectManager::_load_recent_projects() { String path = item.path; String conf = item.conf; bool is_favorite = item.favorite; + bool is_grayed = item.grayed; Ref<ConfigFile> cf = memnew(ConfigFile); - Error err = cf->load(conf); - ERR_CONTINUE(err != OK); + Error cf_err = cf->load(conf); String project_name = TTR("Unnamed Project"); - if (cf->has_section_key("application", "config/name")) { + if (cf_err == OK && cf->has_section_key("application", "config/name")) { project_name = static_cast<String>(cf->get_value("application", "config/name")).xml_unescape(); } @@ -797,7 +799,7 @@ void ProjectManager::_load_recent_projects() { continue; Ref<Texture> icon; - if (cf->has_section_key("application", "config/icon")) { + if (cf_err == OK && cf->has_section_key("application", "config/icon")) { String appicon = cf->get_value("application", "config/icon"); if (appicon != "") { Ref<Image> img; @@ -819,8 +821,10 @@ void ProjectManager::_load_recent_projects() { } String main_scene; - if (cf->has_section_key("application", "run/main_scene")) { + if (cf_err == OK && cf->has_section_key("application", "run/main_scene")) { main_scene = cf->get_value("application", "run/main_scene"); + } else { + main_scene = ""; } selected_list_copy.erase(project); @@ -848,6 +852,8 @@ void ProjectManager::_load_recent_projects() { hb->add_child(tf); VBoxContainer *vb = memnew(VBoxContainer); + if (is_grayed) + vb->set_modulate(Color(0.5, 0.5, 0.5)); vb->set_name("project"); vb->set_h_size_flags(SIZE_EXPAND_FILL); hb->add_child(vb); @@ -926,6 +932,13 @@ void ProjectManager::_open_project_confirm() { for (Map<String, String>::Element *E = selected_list.front(); E; E = E->next()) { const String &selected = E->key(); String path = EditorSettings::get_singleton()->get("projects/" + selected); + String conf = path + "/project.godot"; + if (!FileAccess::exists(conf)) { + dialog_error->set_text(TTR("Can't open project")); + dialog_error->popup_centered_minsize(); + return; + } + print_line("OPENING: " + path + " (" + selected + ")"); List<String> args; @@ -1397,6 +1410,9 @@ ProjectManager::ProjectManager() { run_error_diag = memnew(AcceptDialog); gui_base->add_child(run_error_diag); run_error_diag->set_title(TTR("Can't run project")); + + dialog_error = memnew(AcceptDialog); + gui_base->add_child(dialog_error); } ProjectManager::~ProjectManager() { diff --git a/editor/project_manager.h b/editor/project_manager.h index ecc01955ba..9fcf10a0d9 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -58,6 +58,7 @@ class ProjectManager : public Control { ConfirmationDialog *multi_run_ask; ConfirmationDialog *multi_scan_ask; AcceptDialog *run_error_diag; + AcceptDialog *dialog_error; NewProjectDialog *npdialog; ScrollContainer *scroll; VBoxContainer *scroll_childs; diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index afdf48b314..4fe24a2eff 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -823,7 +823,14 @@ Node *SceneTreeDock::_duplicate(Node *p_node, Map<Node *, Node *> &duplimap) { if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) continue; String name = E->get().name; - node->set(name, p_node->get(name)); + Variant value = p_node->get(name); + // Duplicate dictionaries and arrays, mainly needed for __meta__ + if (value.get_type() == Variant::DICTIONARY) { + value = Dictionary(value).copy(); + } else if (value.get_type() == Variant::ARRAY) { + value = Array(value).duplicate(); + } + node->set(name, value); } List<Node::GroupInfo> group_info; diff --git a/main/main.cpp b/main/main.cpp index 7b2a890a8e..fe07ba484d 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1325,6 +1325,7 @@ bool Main::start() { int shadow_atlas_q2_subdiv = GLOBAL_GET("rendering/quality/shadow_atlas/quadrant_2_subdiv"); int shadow_atlas_q3_subdiv = GLOBAL_GET("rendering/quality/shadow_atlas/quadrant_3_subdiv"); + sml->get_root()->set_shadow_atlas_size(shadow_atlas_size); sml->get_root()->set_shadow_atlas_quadrant_subdiv(0, Viewport::ShadowAtlasQuadrantSubdiv(shadow_atlas_q0_subdiv)); sml->get_root()->set_shadow_atlas_quadrant_subdiv(1, Viewport::ShadowAtlasQuadrantSubdiv(shadow_atlas_q1_subdiv)); @@ -1333,6 +1334,9 @@ bool Main::start() { Viewport::Usage usage = Viewport::Usage(int(GLOBAL_GET("rendering/quality/intended_usage/framebuffer_allocation"))); sml->get_root()->set_usage(usage); + bool snap_controls = GLOBAL_DEF("gui/common/snap_controls_to_pixels", true); + sml->get_root()->set_snap_controls_to_pixels(snap_controls); + } else { GLOBAL_DEF("display/window/stretch/mode", "disabled"); ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/mode", PropertyInfo(Variant::STRING, "display/window/stretch/mode", PROPERTY_HINT_ENUM, "disabled,2d,viewport")); @@ -1342,6 +1346,8 @@ bool Main::start() { ProjectSettings::get_singleton()->set_custom_property_info("display/window/stretch/shrink", PropertyInfo(Variant::STRING, "display/window/stretch/shrink", PROPERTY_HINT_RANGE, "1,8,1")); sml->set_auto_accept_quit(GLOBAL_DEF("application/config/auto_accept_quit", true)); sml->set_quit_on_go_back(GLOBAL_DEF("application/config/quit_on_go_back", true)); + GLOBAL_DEF("gui/common/snap_controls_to_pixels", true); + } String local_game_path; diff --git a/misc/dist/html/default.html b/misc/dist/html/default.html new file mode 100644 index 0000000000..9fae34f97e --- /dev/null +++ b/misc/dist/html/default.html @@ -0,0 +1,386 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang=""> +<head> + <meta charset="utf-8" /> + <title></title> + <style type="text/css"> + + body { + margin: 0; + border: 0 none; + padding: 0; + text-align: center; + background-color: #222226; + font-family: 'Noto Sans', Arial, sans-serif; + } + + + /* Godot Engine default theme style + * ================================ */ + + .godot { + color: #e0e0e0; + background-color: #3b3943; + background-image: linear-gradient(to bottom, #403e48, #35333c); + border: 1px solid #45434e; + box-shadow: 0 0 1px 1px #2f2d35; + } + + button.godot { + font-family: 'Droid Sans', Arial, sans-serif; /* override user agent style */ + padding: 1px 5px; + background-color: #37353f; + background-image: linear-gradient(to bottom, #413e49, #3a3842); + border: 1px solid #514f5d; + border-radius: 1px; + box-shadow: 0 0 1px 1px #2a2930; + } + + button.godot:hover { + color: #f0f0f0; + background-color: #44414e; + background-image: linear-gradient(to bottom, #494652, #423f4c); + border: 1px solid #5a5667; + box-shadow: 0 0 1px 1px #26252b; + } + + button.godot:active { + color: #fff; + background-color: #3e3b46; + background-image: linear-gradient(to bottom, #36343d, #413e49); + border: 1px solid #4f4c59; + box-shadow: 0 0 1px 1px #26252b; + } + + button.godot:disabled { + color: rgba(230, 230, 230, 0.2); + background-color: #3d3d3d; + background-image: linear-gradient(to bottom, #434343, #393939); + border: 1px solid #474747; + box-shadow: 0 0 1px 1px #2d2b33; + } + + + /* Canvas / wrapper + * ================ */ + + #container { + display: inline-block; /* scale with canvas */ + vertical-align: top; /* prevent extra height */ + position: relative; /* root for absolutely positioned overlay */ + margin: 0; + border: 0 none; + padding: 0; + background-color: #0c0c0c; + } + + #canvas { + display: block; + margin: 0 auto; + color: white; + } + + #canvas:focus { + outline: none; + } + + + /* Status display + * ============== */ + + #status { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + display: flex; + justify-content: center; + align-items: center; + /* don't consume click events - make children visible explicitly */ + visibility: hidden; + } + + #status-progress { + width: 244px; + height: 7px; + background-color: #38363A; + border: 1px solid #444246; + padding: 1px; + box-shadow: 0 0 2px 1px #1B1C22; + border-radius: 2px; + visibility: visible; + } + + #status-progress-inner { + height: 100%; + width: 0; + box-sizing: border-box; + transition: width 0.5s linear; + background-color: #202020; + border: 1px solid #222223; + box-shadow: 0 0 1px 1px #27282E; + border-radius: 3px; + } + + #status-indeterminate { + visibility: visible; + position: relative; + } + + #status-indeterminate > div { + width: 3px; + height: 0; + border-style: solid; + border-width: 6px 2px 0 2px; + border-color: #2b2b2b transparent transparent transparent; + transform-origin: center 14px; + position: absolute; + } + + #status-indeterminate > div:nth-child(1) { transform: rotate( 22.5deg); } + #status-indeterminate > div:nth-child(2) { transform: rotate( 67.5deg); } + #status-indeterminate > div:nth-child(3) { transform: rotate(112.5deg); } + #status-indeterminate > div:nth-child(4) { transform: rotate(157.5deg); } + #status-indeterminate > div:nth-child(5) { transform: rotate(202.5deg); } + #status-indeterminate > div:nth-child(6) { transform: rotate(247.5deg); } + #status-indeterminate > div:nth-child(7) { transform: rotate(292.5deg); } + #status-indeterminate > div:nth-child(8) { transform: rotate(337.5deg); } + + #status-notice { + margin: 0 100px; + line-height: 1.3; + visibility: visible; + padding: 4px 6px; + visibility: visible; + } + + + /* Debug output + * ============ */ + + #output-panel { + display: none; + max-width: 700px; + font-size: small; + margin: 6px auto 0; + padding: 0 4px 4px; + text-align: left; + line-height: 2.2; + } + + #output-header { + display: flex; + justify-content: space-between; + align-items: center; + } + + #output-container { + padding: 6px; + background-color: #2c2a32; + box-shadow: inset 0 0 1px 1px #232127; + color: #bbb; + } + + #output-scroll { + line-height: 1; + height: 12em; + overflow-y: scroll; + white-space: pre-wrap; + font-size: small; + font-family: "Lucida Console", Monaco, monospace; + } + </style> +$GODOT_HEAD_INCLUDE +</head> +<body> + <div id="container"> + <canvas id="canvas" oncontextmenu="event.preventDefault();" width="640" height="480"> + HTML5 canvas appears to be unsupported in the current browser.<br /> + Please try updating or use a different browser. + </canvas> + <div id="status"> + <div id='status-progress' style='display: none;' oncontextmenu="event.preventDefault();"><div id ='status-progress-inner'></div></div> + <div id='status-indeterminate' style='display: none;' oncontextmenu="event.preventDefault();"> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + <div></div> + </div> + <div id="status-notice" class="godot" style='display: none;'></div> + </div> + </div> + <div id="output-panel" class="godot"> + <div id="output-header"> + Output: + <button id='output-clear' class='godot' type='button' autocomplete='off'>Clear</button> + </div> + <div id="output-container"><div id="output-scroll"></div></div> + </div> + + <script type="text/javascript" src="$GODOT_BASENAME.js"></script> + <script type="text/javascript">//<![CDATA[ + + var game = new Engine; + + (function() { + + const BASENAME = '$GODOT_BASENAME'; + const MEMORY_SIZE = $GODOT_TOTAL_MEMORY; + const DEBUG_ENABLED = $GODOT_DEBUG_ENABLED; + const INDETERMINATE_STATUS_STEP_MS = 100; + + var container = document.getElementById('container'); + var canvas = document.getElementById('canvas'); + var statusProgress = document.getElementById('status-progress'); + var statusProgressInner = document.getElementById('status-progress-inner'); + var statusIndeterminate = document.getElementById('status-indeterminate'); + var statusNotice = document.getElementById('status-notice'); + + var initializing = true; + var statusMode = 'hidden'; + var indeterminiateStatusAnimationId = 0; + + setStatusMode('indeterminate'); + game.setCanvas(canvas); + game.setAsmjsMemorySize(MEMORY_SIZE); + + function setStatusMode(mode) { + + if (statusMode === mode || !initializing) + return; + [statusProgress, statusIndeterminate, statusNotice].forEach(elem => { + elem.style.display = 'none'; + }); + if (indeterminiateStatusAnimationId !== 0) { + cancelAnimationFrame(indeterminiateStatusAnimationId); + indeterminiateStatusAnimationId = 0; + } + switch (mode) { + case 'progress': + statusProgress.style.display = 'block'; + break; + case 'indeterminate': + statusIndeterminate.style.display = 'block'; + indeterminiateStatusAnimationId = requestAnimationFrame(animateStatusIndeterminate); + break; + case 'notice': + statusNotice.style.display = 'block'; + break; + case 'hidden': + break; + default: + throw new Error("Invalid status mode"); + } + statusMode = mode; + } + + function animateStatusIndeterminate(ms) { + var i = Math.floor(ms / INDETERMINATE_STATUS_STEP_MS % 8); + if (statusIndeterminate.children[i].style.borderTopColor == '') { + Array.prototype.slice.call(statusIndeterminate.children).forEach(child => { + child.style.borderTopColor = ''; + }); + statusIndeterminate.children[i].style.borderTopColor = '#dfdfdf'; + } + requestAnimationFrame(animateStatusIndeterminate); + } + + function setStatusNotice(text) { + + while (statusNotice.lastChild) { + statusNotice.removeChild(statusNotice.lastChild); + } + var lines = text.split('\n'); + lines.forEach((line, index) => { + statusNotice.appendChild(document.createTextNode(line)); + statusNotice.appendChild(document.createElement('br')); + }); + }; + + game.setProgressFunc((current, total) => { + + if (total > 0) { + statusProgressInner.style.width = current/total * 100 + '%'; + setStatusMode('progress'); + if (current === total) { + // wait for progress bar animation + setTimeout(() => { + setStatusMode('indeterminate'); + }, 500); + } + } else { + setStatusMode('indeterminate'); + } + }); + + if (DEBUG_ENABLED) { + var outputRoot = document.getElementById("output-panel"); + var outputScroll = document.getElementById("output-scroll"); + var OUTPUT_MSG_COUNT_MAX = 400; + + document.getElementById('output-clear').addEventListener('click', () => { + while (outputScroll.firstChild) { + outputScroll.firstChild.remove(); + } + }); + + outputRoot.style.display = 'block'; + + function print(text) { + if (arguments.length > 1) { + text = Array.prototype.slice.call(arguments).join(" "); + } + if (text.length <= 0) return; + while (outputScroll.childElementCount >= OUTPUT_MSG_COUNT_MAX) { + outputScroll.firstChild.remove(); + } + var msg = document.createElement("div"); + if (String.prototype.trim.call(text).startsWith("**ERROR**")) { + msg.style.color = "#d44"; + } else if (String.prototype.trim.call(text).startsWith("**WARNING**")) { + msg.style.color = "#ccc000"; + } else if (String.prototype.trim.call(text).startsWith("**SCRIPT ERROR**")) { + msg.style.color = "#c6d"; + } + msg.textContent = text; + var scrollToBottom = outputScroll.scrollHeight - (outputScroll.clientHeight + outputScroll.scrollTop) < 10; + outputScroll.appendChild(msg); + if (scrollToBottom) { + outputScroll.scrollTop = outputScroll.scrollHeight; + } + }; + + function printError(text) { + print('**ERROR**' + ":", text); + } + + game.setStdoutFunc(text => { + print(text); + console.log(text); + }); + + game.setStderrFunc(text => { + printError(text); + console.warn(text); + }); + } + + game.start(BASENAME + '.pck').then(() => { + setStatusMode('hidden'); + initializing = false; + }, err => { + if (DEBUG_ENABLED) + printError(err.message); + setStatusNotice(err.message); + setStatusMode('notice'); + initializing = false; + }); + })(); + //]]></script> +</body> +</html> diff --git a/misc/dist/html_fs/godotfs.js b/misc/dist/html_fs/godotfs.js deleted file mode 100644 index 676ee689fb..0000000000 --- a/misc/dist/html_fs/godotfs.js +++ /dev/null @@ -1,151 +0,0 @@ - -var Module; -if (typeof Module === 'undefined') Module = eval('(function() { try { return Module || {} } catch(e) { return {} } })()'); -if (!Module.expectedDataFileDownloads) { - Module.expectedDataFileDownloads = 0; - Module.finishedDataFileDownloads = 0; -} -Module.expectedDataFileDownloads++; -(function() { - - const PACK_FILE_NAME = '$GODOT_PACK_NAME'; - const PACK_FILE_SIZE = $GODOT_PACK_SIZE; - function fetchRemotePackage(packageName, callback, errback) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', packageName, true); - xhr.responseType = 'arraybuffer'; - xhr.onprogress = function(event) { - var url = packageName; - if (event.loaded && event.total) { - if (!xhr.addedTotal) { - xhr.addedTotal = true; - if (!Module.dataFileDownloads) Module.dataFileDownloads = {}; - Module.dataFileDownloads[url] = { - loaded: event.loaded, - total: event.total - }; - } else { - Module.dataFileDownloads[url].loaded = event.loaded; - } - var total = 0; - var loaded = 0; - var num = 0; - for (var download in Module.dataFileDownloads) { - var data = Module.dataFileDownloads[download]; - total += data.total; - loaded += data.loaded; - num++; - } - total = Math.ceil(total * Module.expectedDataFileDownloads/num); - if (Module['setStatus']) Module['setStatus']('Downloading data... (' + loaded + '/' + total + ')'); - } else if (!Module.dataFileDownloads) { - if (Module['setStatus']) Module['setStatus']('Downloading data...'); - } - }; - xhr.onload = function(event) { - var packageData = xhr.response; - callback(packageData); - }; - xhr.send(null); - }; - - function handleError(error) { - console.error('package error:', error); - }; - - var fetched = null, fetchedCallback = null; - fetchRemotePackage(PACK_FILE_NAME, function(data) { - if (fetchedCallback) { - fetchedCallback(data); - fetchedCallback = null; - } else { - fetched = data; - } - }, handleError); - - function runWithFS() { - -function assert(check, msg) { - if (!check) throw msg + new Error().stack; -} - - function DataRequest(start, end, crunched, audio) { - this.start = start; - this.end = end; - this.crunched = crunched; - this.audio = audio; - } - DataRequest.prototype = { - requests: {}, - open: function(mode, name) { - this.name = name; - this.requests[name] = this; - Module['addRunDependency']('fp ' + this.name); - }, - send: function() {}, - onload: function() { - var byteArray = this.byteArray.subarray(this.start, this.end); - - this.finish(byteArray); - - }, - finish: function(byteArray) { - var that = this; - Module['FS_createPreloadedFile'](this.name, null, byteArray, true, true, function() { - Module['removeRunDependency']('fp ' + that.name); - }, function() { - if (that.audio) { - Module['removeRunDependency']('fp ' + that.name); // workaround for chromium bug 124926 (still no audio with this, but at least we don't hang) - } else { - Module.printErr('Preloading file ' + that.name + ' failed'); - } - }, false, true); // canOwn this data in the filesystem, it is a slide into the heap that will never change - this.requests[this.name] = null; - }, - }; - new DataRequest(0, PACK_FILE_SIZE, 0, 0).open('GET', '/' + PACK_FILE_NAME); - - var PACKAGE_PATH; - if (typeof window === 'object') { - PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.toString().substring(0, window.location.pathname.toString().lastIndexOf('/')) + '/'); - } else { - // worker - PACKAGE_PATH = encodeURIComponent(location.pathname.toString().substring(0, location.pathname.toString().lastIndexOf('/')) + '/'); - } - var PACKAGE_NAME = PACK_FILE_NAME; - var REMOTE_PACKAGE_NAME = PACK_FILE_NAME; - var PACKAGE_UUID = 'b39761ce-0348-4959-9b16-302ed8e1592e'; - - function processPackageData(arrayBuffer) { - Module.finishedDataFileDownloads++; - assert(arrayBuffer, 'Loading data file failed.'); - var byteArray = new Uint8Array(arrayBuffer); - var curr; - - // Reuse the bytearray from the XHR as the source for file reads. - DataRequest.prototype.byteArray = byteArray; - DataRequest.prototype.requests['/' + PACK_FILE_NAME].onload(); - Module['removeRunDependency']('datafile_datapack'); - - }; - Module['addRunDependency']('datafile_datapack'); - - if (!Module.preloadResults) Module.preloadResults = {}; - - Module.preloadResults[PACKAGE_NAME] = {fromCache: false}; - if (fetched) { - processPackageData(fetched); - fetched = null; - } else { - fetchedCallback = processPackageData; - } - - } - if (Module['calledRun']) { - runWithFS(); - } else { - if (!Module['preRun']) Module['preRun'] = []; - Module["preRun"].push(runWithFS); // FS is not initialized yet, wait for it - } - -})(); diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index d809109987..559e9fa455 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -248,7 +248,7 @@ void unregister_gdnative_types() { memdelete(GDNativeCallRegistry::singleton); #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (Engine::get_singleton()->is_editor_hint() && discoverer != NULL) { memdelete(discoverer); } #endif diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp index f0cfdd6258..04752dc71a 100644 --- a/modules/gdscript/gd_functions.cpp +++ b/modules/gdscript/gd_functions.cpp @@ -1177,20 +1177,28 @@ void GDFunctions::call(Function p_func, const Variant **p_args, int p_arg_count, VALIDATE_ARG_COUNT(1); switch (p_args[0]->get_type()) { + case Variant::STRING: { + + String d = *p_args[0]; + r_ret = d.length(); + } break; case Variant::DICTIONARY: { + Dictionary d = *p_args[0]; r_ret = d.size(); } break; case Variant::ARRAY: { + Array d = *p_args[0]; r_ret = d.size(); } break; case Variant::POOL_BYTE_ARRAY: { + PoolVector<uint8_t> d = *p_args[0]; r_ret = d.size(); - } break; case Variant::POOL_INT_ARRAY: { + PoolVector<int> d = *p_args[0]; r_ret = d.size(); } break; @@ -1200,14 +1208,14 @@ void GDFunctions::call(Function p_func, const Variant **p_args, int p_arg_count, r_ret = d.size(); } break; case Variant::POOL_STRING_ARRAY: { + PoolVector<String> d = *p_args[0]; r_ret = d.size(); - } break; case Variant::POOL_VECTOR2_ARRAY: { + PoolVector<Vector2> d = *p_args[0]; r_ret = d.size(); - } break; case Variant::POOL_VECTOR3_ARRAY: { diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index b804863ee1..e282041745 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -20,33 +20,32 @@ for x in javascript_files: javascript_objects.append(env_javascript.Object(x)) env.Append(LINKFLAGS=["-s", "EXPORTED_FUNCTIONS=\"['_main','_audio_server_mix_function','_main_after_fs_sync','_send_notification']\""]) -env.Append(LINKFLAGS=["--shell-file", '"platform/javascript/godot_shell.html"']) # output file name without file extension basename = "godot" + env["PROGSUFFIX"] target_dir = env.Dir("#bin") -js_file = target_dir.File(basename + ".js") -implicit_targets = [js_file] zip_dir = target_dir.Dir('.javascript_zip') -zip_files = env.InstallAs([zip_dir.File("godot.js"), zip_dir.File("godotfs.js")], [js_file, "#misc/dist/html_fs/godotfs.js"]) +zip_files = env.InstallAs(zip_dir.File('godot.html'), '#misc/dist/html/default.html') +implicit_targets = [] if env['wasm'] == 'yes': - wasm_file = target_dir.File(basename+'.wasm') - implicit_targets.append(wasm_file) - zip_files.append(InstallAs(zip_dir.File('godot.wasm'), wasm_file)) + wasm = target_dir.File(basename + '.wasm') + implicit_targets.append(wasm) + zip_files.append(InstallAs(zip_dir.File('godot.wasm'), wasm)) + prejs = env.File('pre_wasm.js') else: - asmjs_files = [target_dir.File(basename+'.asm.js'), target_dir.File(basename+'.html.mem')] - zip_files.append(InstallAs([zip_dir.File('godot.asm.js'), zip_dir.File('godot.mem')], asmjs_files)) + asmjs_files = [target_dir.File(basename + '.asm.js'), target_dir.File(basename + '.js.mem')] implicit_targets.extend(asmjs_files) + zip_files.append(InstallAs([zip_dir.File('godot.asm.js'), zip_dir.File('godot.mem')], asmjs_files)) + prejs = env.File('pre_asmjs.js') -# HTML file must be the first target in the list -html_file = env.Program(["#bin/godot"] + implicit_targets, javascript_objects, PROGSUFFIX=env["PROGSUFFIX"]+".html")[0] -Depends(html_file, "godot_shell.html") +js = env.Program(['#bin/godot'] + implicit_targets, javascript_objects, PROGSUFFIX=env['PROGSUFFIX'] + '.js')[0]; +zip_files.append(InstallAs(zip_dir.File('godot.js'), js)) -# Emscripten hardcodes file names, so replace common base name with -# placeholder while leaving extension; also change `.html.mem` to just `.mem` -fixup_html = env.Substfile(html_file, SUBST_DICT=[(basename, '$$GODOT_BASE'), ('.html.mem', '.mem')], SUBSTFILESUFFIX='.fixup.html') +postjs = env.File('engine.js') +env.Depends(js, [prejs, postjs]) +env.Append(LINKFLAGS=['--pre-js', prejs.path]) +env.Append(LINKFLAGS=['--post-js', postjs.path]) -zip_files.append(InstallAs(zip_dir.File('godot.html'), fixup_html)) -Zip('#bin/godot', zip_files, ZIPSUFFIX=env['PROGSUFFIX']+env['ZIPSUFFIX'], ZIPROOT=zip_dir, ZIPCOMSTR="Archving $SOURCES as $TARGET") +Zip('#bin/godot', zip_files, ZIPSUFFIX=env['PROGSUFFIX'] + env['ZIPSUFFIX'], ZIPROOT=zip_dir, ZIPCOMSTR="Archving $SOURCES as $TARGET") diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 5f066f1b15..bea8f156af 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -100,6 +100,7 @@ def configure(env): ## Link flags + env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="[\'FS\']"']) env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1']) if (env['wasm'] == 'yes'): @@ -112,6 +113,7 @@ def configure(env): else: env.Append(LINKFLAGS=['-s', 'ASM_JS=1']) env.Append(LINKFLAGS=['--separate-asm']) + env.Append(LINKFLAGS=['--memory-init-file', '1']) # TODO: Move that to opus module's config if("module_opus_enabled" in env and env["module_opus_enabled"] != "no"): diff --git a/platform/javascript/engine.js b/platform/javascript/engine.js new file mode 100644 index 0000000000..552f5a7e02 --- /dev/null +++ b/platform/javascript/engine.js @@ -0,0 +1,366 @@ + return Module; + }, +}; + +(function() { + var engine = Engine; + + var USING_WASM = engine.USING_WASM; + var DOWNLOAD_ATTEMPTS_MAX = 4; + + var basePath = null; + var engineLoadPromise = null; + + var loadingFiles = {}; + + function getBasePath(path) { + + if (path.endsWith('/')) + path = path.slice(0, -1); + if (path.lastIndexOf('.') > path.lastIndexOf('/')) + path = path.slice(0, path.lastIndexOf('.')); + return path; + } + + function getBaseName(path) { + + path = getBasePath(path); + return path.slice(path.lastIndexOf('/') + 1); + } + + Engine = function Engine() { + + this.rtenv = null; + + var gameInitPromise = null; + var unloadAfterInit = true; + var memorySize = 268435456; + + var progressFunc = null; + var pckProgressTracker = {}; + var lastProgress = { loaded: 0, total: 0 }; + + var canvas = null; + var stdout = null; + var stderr = null; + + this.initGame = function(mainPack) { + + if (!gameInitPromise) { + + if (mainPack === undefined) { + if (basePath !== null) { + mainPack = basePath + '.pck'; + } else { + return Promise.reject(new Error("No main pack to load specified")); + } + } + if (basePath === null) + basePath = getBasePath(mainPack); + + gameInitPromise = Engine.initEngine().then( + instantiate.bind(this) + ); + var gameLoadPromise = loadPromise(mainPack, pckProgressTracker).then(function(xhr) { return xhr.response; }); + gameInitPromise = Promise.all([gameLoadPromise, gameInitPromise]).then(function(values) { + // resolve with pck + return new Uint8Array(values[0]); + }); + if (unloadAfterInit) + gameInitPromise.then(Engine.unloadEngine); + requestAnimationFrame(animateProgress); + } + return gameInitPromise; + }; + + function instantiate(initializer) { + + var rtenvOpts = { + noInitialRun: true, + thisProgram: getBaseName(basePath), + engine: this, + }; + if (typeof stdout === 'function') + rtenvOpts.print = stdout; + if (typeof stderr === 'function') + rtenvOpts.printErr = stderr; + if (typeof WebAssembly === 'object' && initializer instanceof WebAssembly.Module) { + rtenvOpts.instantiateWasm = function(imports, onSuccess) { + WebAssembly.instantiate(initializer, imports).then(function(result) { + onSuccess(result); + }); + return {}; + }; + } else if (initializer.asm && initializer.mem) { + rtenvOpts.asm = initializer.asm; + rtenvOpts.memoryInitializerRequest = initializer.mem; + rtenvOpts.TOTAL_MEMORY = memorySize; + } else { + throw new Error("Invalid initializer"); + } + + return new Promise(function(resolve, reject) { + rtenvOpts.onRuntimeInitialized = resolve; + rtenvOpts.onAbort = reject; + rtenvOpts.engine.rtenv = Engine.RuntimeEnvironment(rtenvOpts); + }); + } + + this.start = function(mainPack) { + + return this.initGame(mainPack).then(synchronousStart.bind(this)); + }; + + function synchronousStart(pckView) { + // TODO don't expect canvas when runninng as cli tool + if (canvas instanceof HTMLCanvasElement) { + this.rtenv.canvas = canvas; + } else { + var firstCanvas = document.getElementsByTagName('canvas')[0]; + if (firstCanvas instanceof HTMLCanvasElement) { + this.rtenv.canvas = firstCanvas; + } else { + throw new Error("No canvas found"); + } + } + + var actualCanvas = this.rtenv.canvas; + var context = false; + try { + context = actualCanvas.getContext('webgl2') || actualCanvas.getContext('experimental-webgl2'); + } catch (e) {} + if (!context) { + throw new Error("WebGL 2 not available"); + } + + // canvas can grab focus on click + if (actualCanvas.tabIndex < 0) { + actualCanvas.tabIndex = 0; + } + // necessary to calculate cursor coordinates correctly + actualCanvas.style.padding = 0; + actualCanvas.style.borderWidth = 0; + actualCanvas.style.borderStyle = 'none'; + // until context restoration is implemented + actualCanvas.addEventListener('webglcontextlost', function(ev) { + alert("WebGL context lost, please reload the page"); + ev.preventDefault(); + }, false); + + this.rtenv.FS.createDataFile('/', this.rtenv.thisProgram + '.pck', pckView, true, true, true); + gameInitPromise = null; + this.rtenv.callMain(); + } + + this.setProgressFunc = function(func) { + progressFunc = func; + }; + + function animateProgress() { + + var loaded = 0; + var total = 0; + var totalIsValid = true; + var progressIsFinal = true; + + [loadingFiles, pckProgressTracker].forEach(function(tracker) { + Object.keys(tracker).forEach(function(file) { + if (!tracker[file].final) + progressIsFinal = false; + if (!totalIsValid || tracker[file].total === 0) { + totalIsValid = false; + total = 0; + } else { + total += tracker[file].total; + } + loaded += tracker[file].loaded; + }); + }); + if (loaded !== lastProgress.loaded || total !== lastProgress.total) { + lastProgress.loaded = loaded; + lastProgress.total = total; + if (typeof progressFunc === 'function') + progressFunc(loaded, total); + } + if (!progressIsFinal) + requestAnimationFrame(animateProgress); + } + + this.setCanvas = function(elem) { + canvas = elem; + }; + + this.setAsmjsMemorySize = function(size) { + memorySize = size; + }; + + this.setUnloadAfterInit = function(enabled) { + + if (enabled && !unloadAfterInit && gameInitPromise) { + gameInitPromise.then(Engine.unloadEngine); + } + unloadAfterInit = enabled; + }; + + this.setStdoutFunc = function(func) { + + var print = function(text) { + if (arguments.length > 1) { + text = Array.prototype.slice.call(arguments).join(" "); + } + func(text); + }; + if (this.rtenv) + this.rtenv.print = print; + stdout = print; + }; + + this.setStderrFunc = function(func) { + + var printErr = function(text) { + if (arguments.length > 1) + text = Array.prototype.slice.call(arguments).join(" "); + func(text); + }; + if (this.rtenv) + this.rtenv.printErr = printErr; + stderr = printErr; + }; + + + }; // Engine() + + Engine.RuntimeEnvironment = engine.RuntimeEnvironment; + + Engine.initEngine = function(newBasePath) { + + if (newBasePath !== undefined) basePath = getBasePath(newBasePath); + if (engineLoadPromise === null) { + if (USING_WASM) { + if (typeof WebAssembly !== 'object') + return Promise.reject(new Error("Browser doesn't support WebAssembly")); + // TODO cache/retrieve module to/from idb + engineLoadPromise = loadPromise(basePath + '.wasm').then(function(xhr) { + return WebAssembly.compile(xhr.response); + }); + } else { + var asmjsPromise = loadPromise(basePath + '.asm.js').then(function(xhr) { + return asmjsModulePromise(xhr.response); + }); + var memPromise = loadPromise(basePath + '.mem'); + engineLoadPromise = Promise.all([asmjsPromise, memPromise]).then(function(values) { + return { asm: values[0], mem: values[1] }; + }); + } + engineLoadPromise = engineLoadPromise.catch(function(err) { + engineLoadPromise = null; + throw err; + }); + } + return engineLoadPromise; + }; + + function asmjsModulePromise(module) { + var elem = document.createElement('script'); + var script = new Blob([ + 'Engine.asm = (function() { var Module = {};', + module, + 'return Module.asm; })();' + ]); + var url = URL.createObjectURL(script); + elem.src = url; + return new Promise(function(resolve, reject) { + elem.addEventListener('load', function() { + URL.revokeObjectURL(url); + var asm = Engine.asm; + Engine.asm = undefined; + setTimeout(function() { + // delay to reclaim compilation memory + resolve(asm); + }, 1); + }); + elem.addEventListener('error', function() { + URL.revokeObjectURL(url); + reject("asm.js faiilure"); + }); + document.body.appendChild(elem); + }); + } + + Engine.unloadEngine = function() { + engineLoadPromise = null; + }; + + function loadPromise(file, tracker) { + if (tracker === undefined) + tracker = loadingFiles; + return new Promise(function(resolve, reject) { + loadXHR(resolve, reject, file, tracker); + }); + } + + function loadXHR(resolve, reject, file, tracker) { + + var xhr = new XMLHttpRequest; + xhr.open('GET', file); + if (!file.endsWith('.js')) { + xhr.responseType = 'arraybuffer'; + } + ['loadstart', 'progress', 'load', 'error', 'timeout', 'abort'].forEach(function(ev) { + xhr.addEventListener(ev, onXHREvent.bind(xhr, resolve, reject, file, tracker)); + }); + xhr.send(); + } + + function onXHREvent(resolve, reject, file, tracker, ev) { + + if (this.status >= 400) { + + if (this.status < 500 || ++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) { + reject(new Error("Failed loading file '" + file + "': " + this.statusText)); + this.abort(); + return; + } else { + loadXHR(resolve, reject, file); + } + } + + switch (ev.type) { + case 'loadstart': + if (tracker[file] === undefined) { + tracker[file] = { + total: ev.total, + loaded: ev.loaded, + attempts: 0, + final: false, + }; + } + break; + + case 'progress': + tracker[file].loaded = ev.loaded; + tracker[file].total = ev.total; + break; + + case 'load': + tracker[file].final = true; + resolve(this); + break; + + case 'error': + case 'timeout': + if (++tracker[file].attempts >= DOWNLOAD_ATTEMPTS_MAX) { + tracker[file].final = true; + reject(new Error("Failed loading file '" + file + "'")); + } else { + loadXHR(resolve, reject, file); + } + break; + + case 'abort': + tracker[file].final = true; + reject(new Error("Loading file '" + file + "' was aborted.")); + break; + } + } +})(); diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index 5a161dd0ab..4a97bf4c32 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -100,8 +100,8 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re for (int i = 0; i < lines.size(); i++) { String current_line = lines[i]; - current_line = current_line.replace("$GODOT_TMEM", itos(memory_mb * 1024 * 1024)); - current_line = current_line.replace("$GODOT_BASE", p_name); + current_line = current_line.replace("$GODOT_TOTAL_MEMORY", itos(memory_mb * 1024 * 1024)); + current_line = current_line.replace("$GODOT_BASENAME", p_name); current_line = current_line.replace("$GODOT_HEAD_INCLUDE", p_preset->get("html/head_include")); current_line = current_line.replace("$GODOT_DEBUG_ENABLED", p_debug ? "true" : "false"); str_export += current_line + "\n"; @@ -114,28 +114,6 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re } } -void EditorExportPlatformJavaScript::_fix_fsloader_js(Vector<uint8_t> &p_js, const String &p_pack_name, uint64_t p_pack_size) { - - String str_template = String::utf8(reinterpret_cast<const char *>(p_js.ptr()), p_js.size()); - String str_export; - Vector<String> lines = str_template.split("\n"); - for (int i = 0; i < lines.size(); i++) { - if (lines[i].find("$GODOT_PACK_NAME") != -1) { - str_export += lines[i].replace("$GODOT_PACK_NAME", p_pack_name); - } else if (lines[i].find("$GODOT_PACK_SIZE") != -1) { - str_export += lines[i].replace("$GODOT_PACK_SIZE", itos(p_pack_size)); - } else { - str_export += lines[i] + "\n"; - } - } - - CharString cs = str_export.utf8(); - p_js.resize(cs.length()); - for (int i = 0; i < cs.length(); i++) { - p_js[i] = cs[i]; - } -} - void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { if (p_preset->get("texture_format/s3tc")) { @@ -286,10 +264,6 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese _fix_html(data, p_preset, p_path.get_file().get_basename(), p_debug); file = p_path.get_file(); - } else if (file == "godotfs.js") { - - _fix_fsloader_js(data, pck_path.get_file(), pack_size); - file = p_path.get_file().get_basename() + "fs.js"; } else if (file == "godot.js") { file = p_path.get_file().get_basename() + ".js"; diff --git a/platform/javascript/godot_shell.html b/platform/javascript/godot_shell.html deleted file mode 100644 index ee7399a129..0000000000 --- a/platform/javascript/godot_shell.html +++ /dev/null @@ -1,347 +0,0 @@ -<!DOCTYPE html> -<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang=""> -<head> - <meta charset="utf-8" /> - <title></title> - <style type="text/css"> - body { - margin: 0; - border: 0 none; - padding: 0; - text-align: center; - background-color: #222226; - font-family: 'Droid Sans', Arial, sans-serif; - } - - - /* Godot Engine default theme style - * ================================ */ - - .godot { - color: #e0e0e0; - background-color: #3b3943; - background-image: linear-gradient(to bottom, #403e48, #35333c); - border: 1px solid #45434e; - box-shadow: 0 0 1px 1px #2f2d35; - } - - button.godot { - font-family: 'Droid Sans', Arial, sans-serif; /* override user agent style */ - padding: 1px 5px; - background-color: #37353f; - background-image: linear-gradient(to bottom, #413e49, #3a3842); - border: 1px solid #514f5d; - border-radius: 1px; - box-shadow: 0 0 1px 1px #2a2930; - } - - button.godot:hover { - color: #f0f0f0; - background-color: #44414e; - background-image: linear-gradient(to bottom, #494652, #423f4c); - border: 1px solid #5a5667; - box-shadow: 0 0 1px 1px #26252b; - } - - button.godot:active { - color: #fff; - background-color: #3e3b46; - background-image: linear-gradient(to bottom, #36343d, #413e49); - border: 1px solid #4f4c59; - box-shadow: 0 0 1px 1px #26252b; - } - - button.godot:disabled { - color: rgba(230, 230, 230, 0.2); - background-color: #3d3d3d; - background-image: linear-gradient(to bottom, #434343, #393939); - border: 1px solid #474747; - box-shadow: 0 0 1px 1px #2d2b33; - } - - - /* Canvas / wrapper - * ================ */ - - #container { - display: inline-block; /* scale with canvas */ - vertical-align: top; /* prevent extra height */ - position: relative; /* root for absolutely positioned overlay */ - margin: 0; - border: 0 none; - padding: 0; - background-color: #0c0c0c; - } - - #canvas { - display: block; - margin: 0 auto; - /* canvas must have border and padding set to zero to - * calculate cursor coordinates correctly */ - border: 0 none; - padding: 0; - color: white; - } - - #canvas:focus { - outline: none; - } - - - /* Status display - * ============== */ - - #status-container { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - display: flex; - justify-content: center; - align-items: center; - /* don't consume click events - make children visible explicitly */ - visibility: hidden; - } - - #status { - line-height: 1.3; - cursor: pointer; - visibility: visible; - padding: 4px 6px; - } - - - /* Debug output - * ============ */ - - #output-panel { - display: none; - max-width: 700px; - font-size: small; - margin: 6px auto 0; - padding: 0 4px 4px; - text-align: left; - line-height: 2.2; - } - - #output-header { - display: flex; - justify-content: space-between; - align-items: center; - } - - #output-container { - padding: 6px; - background-color: #2c2a32; - box-shadow: inset 0 0 1px 1px #232127; - color: #bbb; - } - - #output-scroll { - line-height: 1; - height: 12em; - overflow-y: scroll; - white-space: pre-wrap; - font-size: small; - font-family: "Lucida Console", Monaco, monospace; - } - </style> -$GODOT_HEAD_INCLUDE -</head> -<body> - <div id="container"> - <canvas id="canvas" width="640" height="480" tabindex="0" oncontextmenu="event.preventDefault();"> - HTML5 canvas appears to be unsupported in the current browser.<br /> - Please try updating or use a different browser. - </canvas> - <div id="status-container"> - <span id="status" class="godot" onclick="this.style.visibility='hidden';">Downloading page...</span> - </div> - </div> - <div id="output-panel" class="godot"> - <div id="output-header"> - Output: - <button class="godot" type="button" autocomplete="off" onclick="Presentation.clearOutput();">Clear</button> - </div> - <div id="output-container"><div id="output-scroll"></div></div> - </div> - - <!-- Scripts --> - <script type="text/javascript">//<![CDATA[ - var Presentation = (function() { - var statusElement = document.getElementById("status"); - var canvasElement = document.getElementById("canvas"); - - var presentation = { - postRun: [], - setStatusVisible: function setStatusVisible(visible) { - statusElement.style.visibility = (visible ? "visible" : "hidden"); - }, - setStatus: function setStatus(text) { - if (text.length === 0) { - // emscripten sets empty string as status after "Running..." - // per timeout, but another status may have been set by then - if (Presentation.setStatus.lastText === "Running...") - Presentation.setStatusVisible(false); - return; - } - Presentation.setStatus.lastText = text; - while (statusElement.lastChild) { - statusElement.removeChild(statusElement.lastChild); - } - var lines = text.split("\n"); - lines.forEach(function(line, index) { - statusElement.appendChild(document.createTextNode(line)); - statusElement.appendChild(document.createElement("br")); - }); - var closeNote = document.createElement("span"); - closeNote.style.fontSize = "small"; - closeNote.textContent = "click to close"; - statusElement.appendChild(closeNote); - Presentation.setStatusVisible(true); - }, - isWebGL2Available: function isWebGL2Available() { - var context; - try { - context = canvasElement.getContext("webgl2") || canvasElement.getContext("experimental-webgl2"); - } catch (e) {} - return !!context; - }, - }; - - window.onerror = function(event) { presentation.setStatus("Failure during start-up\nSee JavaScript console") }; - - if ($GODOT_DEBUG_ENABLED) { // debugging enabled - var outputRoot = document.getElementById("output-panel"); - var outputElement = document.getElementById("output-scroll"); - const maxOutputMessages = 400; - - presentation.setOutputVisible = function setOutputVisible(visible) { - outputRoot.style.display = (visible ? "block" : "none"); - }; - presentation.clearOutput = function clearOutput() { - while (outputElement.firstChild) { - outputElement.firstChild.remove(); - } - }; - - presentation.setOutputVisible(true); - - presentation.print = function print(text) { - if (arguments.length > 1) { - text = Array.prototype.slice.call(arguments).join(" "); - } - if (text.length <= 0) return; - while (outputElement.childElementCount >= maxOutputMessages) { - outputElement.firstChild.remove(); - } - var msg = document.createElement("div"); - if (String.prototype.trim.call(text).startsWith("**ERROR**") - || String.prototype.trim.call(text).startsWith("**EXCEPTION**")) { - msg.style.color = "#d44"; - } else if (String.prototype.trim.call(text).startsWith("**WARNING**")) { - msg.style.color = "#ccc000"; - } else if (String.prototype.trim.call(text).startsWith("**SCRIPT ERROR**")) { - msg.style.color = "#c6d"; - } - msg.textContent = text; - var scrollToBottom = outputElement.scrollHeight - (outputElement.clientHeight + outputElement.scrollTop) < 10; - outputElement.appendChild(msg); - if (scrollToBottom) { - outputElement.scrollTop = outputElement.scrollHeight; - } - }; - - presentation.postRun.push(function() { - window.onerror = function(event) { presentation.print("**EXCEPTION**:", event) }; - }); - - } else { - presentation.postRun.push(function() { window.onerror = null; }); - } - - return presentation; - })(); - - // Emscripten interface - var Module = (function() { - const BASE_NAME = '$GODOT_BASE'; - var module = { - thisProgram: BASE_NAME, - wasmBinaryFile: BASE_NAME + '.wasm', - TOTAL_MEMORY: $GODOT_TMEM, - print: function print(text) { - if (arguments.length > 1) { - text = Array.prototype.slice.call(arguments).join(" "); - } - console.log(text); - if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") { - Presentation.print(text); - } - }, - printErr: function printErr(text) { - if (arguments.length > 1) { - text = Array.prototype.slice.call(arguments).join(" "); - } - console.error(text); - if (typeof Presentation !== "undefined" && typeof Presentation.print === "function") { - Presentation.print("**ERROR**:", text) - } - }, - canvas: document.getElementById("canvas"), - setStatus: function setStatus(text) { - var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); - var now = Date.now(); - if (m) { - if (now - Date.now() < 30) // if this is a progress update, skip it if too soon - return; - text = m[1]; - } - if (typeof Presentation !== "undefined" && typeof Presentation.setStatus == "function") { - Presentation.setStatus(text); - } - } - }; - - // As a default initial behavior, pop up an alert when WebGL context is lost. To make your - // application robust, you may want to override this behavior before shipping! - // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2 - module.canvas.addEventListener("webglcontextlost", function(e) { alert("WebGL context lost. Plase reload the page."); e.preventDefault(); }, false); - - if (typeof Presentation !== "undefined" && Presentation.postRun instanceof Array) { - module.postRun = Presentation.postRun; - } - - return module; - })(); - - if (!Presentation.isWebGL2Available()) { - Presentation.setStatus("WebGL 2 appears to be unsupported.\nPlease update browser and drivers."); - Presentation.preventLoading = true; - } else { - Presentation.setStatus("Downloading..."); - } - - if (Presentation.preventLoading) { - // prevent *fs.js and Emscripten's SCRIPT placeholder from loading any files - Presentation._XHR_send = XMLHttpRequest.prototype.send; - XMLHttpRequest.prototype.send = function() {}; - Presentation._Node_appendChild = Node.prototype.appendChild; - Node.prototype.appendChild = function(node) { - if (!(node instanceof HTMLScriptElement)) { - return Presentation._Node_appendChild.call(this, node); - } - } - } - //]]></script> - <script type="text/javascript" src="$GODOT_BASEfs.js"></script> -{{{ SCRIPT }}} - <script type="text/javascript"> - if (Presentation.preventLoading) { - XMLHttpRequest.prototype.send = Presentation._XHR_send; - Node.prototype.appendChild = Presentation._Node_appendChild; - } - </script> -</body> -</html> diff --git a/platform/javascript/pre_asmjs.js b/platform/javascript/pre_asmjs.js new file mode 100644 index 0000000000..3c497721b6 --- /dev/null +++ b/platform/javascript/pre_asmjs.js @@ -0,0 +1,3 @@ +var Engine = { + USING_WASM: false, + RuntimeEnvironment: function(Module) { diff --git a/platform/javascript/pre_wasm.js b/platform/javascript/pre_wasm.js new file mode 100644 index 0000000000..be4383c8c9 --- /dev/null +++ b/platform/javascript/pre_wasm.js @@ -0,0 +1,3 @@ +var Engine = { + USING_WASM: true, + RuntimeEnvironment: function(Module) { diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp index caf313190b..966dd88a2c 100644 --- a/scene/3d/arvr_nodes.cpp +++ b/scene/3d/arvr_nodes.cpp @@ -31,7 +31,6 @@ #include "arvr_nodes.h" #include "core/os/input.h" #include "servers/arvr/arvr_interface.h" -#include "servers/arvr/arvr_positional_tracker.h" #include "servers/arvr_server.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -142,6 +141,7 @@ void ARVRController::_bind_methods() { ClassDB::bind_method(D_METHOD("get_joystick_axis", "axis"), &ARVRController::get_joystick_axis); ClassDB::bind_method(D_METHOD("get_is_active"), &ARVRController::get_is_active); + ClassDB::bind_method(D_METHOD("get_hand"), &ARVRController::get_hand); ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::INT, "button"))); ADD_SIGNAL(MethodInfo("button_release", PropertyInfo(Variant::INT, "button"))); @@ -204,6 +204,19 @@ bool ARVRController::get_is_active() const { return is_active; }; +ARVRPositionalTracker::TrackerHand ARVRController::get_hand() const { + // get our ARVRServer + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, ARVRPositionalTracker::TRACKER_HAND_UNKNOWN); + + ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, controller_id); + if (tracker == NULL) { + return ARVRPositionalTracker::TRACKER_HAND_UNKNOWN; + }; + + return tracker->get_hand(); +}; + String ARVRController::get_configuration_warning() const { if (!is_visible() || !is_inside_tree()) return String(); diff --git a/scene/3d/arvr_nodes.h b/scene/3d/arvr_nodes.h index 4c14be71b5..5269ec0248 100644 --- a/scene/3d/arvr_nodes.h +++ b/scene/3d/arvr_nodes.h @@ -33,6 +33,7 @@ #include "scene/3d/camera.h" #include "scene/3d/spatial.h" +#include "servers/arvr/arvr_positional_tracker.h" /** @author Bastiaan Olij <mux213@gmail.com> @@ -84,6 +85,7 @@ public: float get_joystick_axis(int p_axis) const; bool get_is_active() const; + ARVRPositionalTracker::TrackerHand get_hand() const; String get_configuration_warning() const; diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp index 09b253b309..d410ba1e2a 100644 --- a/scene/3d/light.cpp +++ b/scene/3d/light.cpp @@ -230,7 +230,6 @@ void Light::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_shadow_color", "get_shadow_color"); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "shadow_bias", PROPERTY_HINT_RANGE, "-16,16,0.01"), "set_param", "get_param", PARAM_SHADOW_BIAS); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "shadow_contact", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_CONTACT_SHADOW_SIZE); - ADD_PROPERTYI(PropertyInfo(Variant::REAL, "shadow_max_distance", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_reverse_cull_face"), "set_shadow_reverse_cull_face", "get_shadow_reverse_cull_face"); ADD_GROUP("Editor", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only"); @@ -308,6 +307,18 @@ DirectionalLight::ShadowMode DirectionalLight::get_shadow_mode() const { return shadow_mode; } +void DirectionalLight::set_shadow_depth_range(ShadowDepthRange p_range) { + shadow_depth_range=p_range; + VS::get_singleton()->light_directional_set_shadow_depth_range_mode(light, VS::LightDirectionalShadowDepthRangeMode(p_range)); + +} + +DirectionalLight::ShadowDepthRange DirectionalLight::get_shadow_depth_range() const { + + return shadow_depth_range; +} + + void DirectionalLight::set_blend_splits(bool p_enable) { blend_splits = p_enable; @@ -324,6 +335,9 @@ void DirectionalLight::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shadow_mode", "mode"), &DirectionalLight::set_shadow_mode); ClassDB::bind_method(D_METHOD("get_shadow_mode"), &DirectionalLight::get_shadow_mode); + ClassDB::bind_method(D_METHOD("set_shadow_depth_range", "mode"), &DirectionalLight::set_shadow_depth_range); + ClassDB::bind_method(D_METHOD("get_shadow_depth_range"), &DirectionalLight::get_shadow_depth_range); + ClassDB::bind_method(D_METHOD("set_blend_splits", "enabled"), &DirectionalLight::set_blend_splits); ClassDB::bind_method(D_METHOD("is_blend_splits_enabled"), &DirectionalLight::is_blend_splits_enabled); @@ -335,10 +349,15 @@ void DirectionalLight::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "directional_shadow_blend_splits"), "set_blend_splits", "is_blend_splits_enabled"); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_normal_bias", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_bias_split_scale", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_BIAS_SPLIT_SCALE); + ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_depth_range", PROPERTY_HINT_ENUM, "Stable,Optimized"), "set_shadow_depth_range", "get_shadow_depth_range"); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_max_distance", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE); BIND_ENUM_CONSTANT(SHADOW_ORTHOGONAL); BIND_ENUM_CONSTANT(SHADOW_PARALLEL_2_SPLITS); BIND_ENUM_CONSTANT(SHADOW_PARALLEL_4_SPLITS); + + BIND_ENUM_CONSTANT( SHADOW_DEPTH_RANGE_STABLE ); + BIND_ENUM_CONSTANT( SHADOW_DEPTH_RANGE_OPTIMIZED ); } DirectionalLight::DirectionalLight() @@ -349,6 +368,8 @@ DirectionalLight::DirectionalLight() set_param(PARAM_SHADOW_MAX_DISTANCE, 200); set_param(PARAM_SHADOW_BIAS_SPLIT_SCALE, 0.25); set_shadow_mode(SHADOW_PARALLEL_4_SPLITS); + set_shadow_depth_range(SHADOW_DEPTH_RANGE_STABLE); + blend_splits = false; } diff --git a/scene/3d/light.h b/scene/3d/light.h index 5d589d33e5..6aa0220265 100644 --- a/scene/3d/light.h +++ b/scene/3d/light.h @@ -133,9 +133,15 @@ public: SHADOW_PARALLEL_4_SPLITS }; + enum ShadowDepthRange { + SHADOW_DEPTH_RANGE_STABLE = VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE, + SHADOW_DEPTH_RANGE_OPTIMIZED = VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_OPTIMIZED, + }; + private: bool blend_splits; ShadowMode shadow_mode; + ShadowDepthRange shadow_depth_range; protected: static void _bind_methods(); @@ -144,6 +150,9 @@ public: void set_shadow_mode(ShadowMode p_mode); ShadowMode get_shadow_mode() const; + void set_shadow_depth_range(ShadowDepthRange p_mode); + ShadowDepthRange get_shadow_depth_range() const; + void set_blend_splits(bool p_enable); bool is_blend_splits_enabled() const; @@ -151,6 +160,7 @@ public: }; VARIANT_ENUM_CAST(DirectionalLight::ShadowMode) +VARIANT_ENUM_CAST(DirectionalLight::ShadowDepthRange) class OmniLight : public Light { diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 961fccc804..7bf11e6712 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1249,6 +1249,10 @@ void Control::_size_changed() { new_size_cache.height = MAX(minimum_size.height, new_size_cache.height); } + if (get_viewport()->is_snap_controls_to_pixels_enabled()) { + new_size_cache =new_size_cache.floor(); + new_pos_cache = new_pos_cache.floor(); + } bool pos_changed = new_pos_cache != data.pos_cache; bool size_changed = new_size_cache != data.size_cache; diff --git a/scene/main/node.cpp b/scene/main/node.cpp index c3d9d97c5a..a543dba9cb 100755 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2117,7 +2117,15 @@ Node *Node::_duplicate(int p_flags) const { if (!(p_flags & DUPLICATE_SCRIPTS) && name == "script/script") continue; - node->set(name, get(name)); + Variant value = get(name); + // Duplicate dictionaries and arrays, mainly needed for __meta__ + if (value.get_type() == Variant::DICTIONARY) { + value = Dictionary(value).copy(); + } else if (value.get_type() == Variant::ARRAY) { + value = Array(value).duplicate(); + } + + node->set(name, value); } node->set_name(get_name()); @@ -2199,7 +2207,16 @@ void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) continue; String name = E->get().name; - node->set(name, get(name)); + + Variant value = get(name); + // Duplicate dictionaries and arrays, mainly needed for __meta__ + if (value.get_type() == Variant::DICTIONARY) { + value = Dictionary(value).copy(); + } else if (value.get_type() == Variant::ARRAY) { + value = Array(value).duplicate(); + } + + node->set(name, value); } node->set_name(get_name()); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index c71a280755..434d594bbb 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2578,6 +2578,17 @@ int Viewport::get_render_info(RenderInfo p_info) { return VS::get_singleton()->viewport_get_render_info(viewport, VS::ViewportRenderInfo(p_info)); } +void Viewport::set_snap_controls_to_pixels(bool p_enable) { + + snap_controls_to_pixels=p_enable; +} + +bool Viewport::is_snap_controls_to_pixels_enabled() const { + + return snap_controls_to_pixels; +} + + void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_arvr", "use"), &Viewport::set_use_arvr); @@ -2680,6 +2691,9 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shadow_atlas_size", "size"), &Viewport::set_shadow_atlas_size); ClassDB::bind_method(D_METHOD("get_shadow_atlas_size"), &Viewport::get_shadow_atlas_size); + ClassDB::bind_method(D_METHOD("set_snap_controls_to_pixels", "enabled"), &Viewport::set_snap_controls_to_pixels); + ClassDB::bind_method(D_METHOD("is_snap_controls_to_pixels_enabled"), &Viewport::is_snap_controls_to_pixels_enabled); + ClassDB::bind_method(D_METHOD("set_shadow_atlas_quadrant_subdiv", "quadrant", "subdiv"), &Viewport::set_shadow_atlas_quadrant_subdiv); ClassDB::bind_method(D_METHOD("get_shadow_atlas_quadrant_subdiv", "quadrant"), &Viewport::get_shadow_atlas_quadrant_subdiv); @@ -2707,6 +2721,7 @@ void Viewport::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking"), "set_physics_object_picking", "get_physics_object_picking"); ADD_GROUP("GUI", "gui_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_disable_input"), "set_disable_input", "is_input_disabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_snap_controls_to_pixels"), "set_snap_controls_to_pixels", "is_snap_controls_to_pixels_enabled"); ADD_GROUP("Shadow Atlas", "shadow_atlas_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_atlas_size"), "set_shadow_atlas_size", "get_shadow_atlas_size"); ADD_PROPERTYI(PropertyInfo(Variant::INT, "shadow_atlas_quad_0", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_shadow_atlas_quadrant_subdiv", "get_shadow_atlas_quadrant_subdiv", 0); @@ -2822,6 +2837,8 @@ Viewport::Viewport() { usage = USAGE_3D; debug_draw = DEBUG_DRAW_DISABLED; clear_mode = CLEAR_MODE_ALWAYS; + + snap_controls_to_pixels = true; } Viewport::~Viewport() { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index ce2bc991f5..6bbd4b26b5 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -1,3 +1,4 @@ + /*************************************************************************/ /* viewport.h */ /*************************************************************************/ @@ -193,6 +194,8 @@ private: bool filter; bool gen_mipmaps; + bool snap_controls_to_pixels; + bool physics_object_picking; List<Ref<InputEvent> > physics_picking_events; ObjectID physics_object_capture; @@ -463,6 +466,9 @@ public: int get_render_info(RenderInfo p_info); + void set_snap_controls_to_pixels(bool p_enable); + bool is_snap_controls_to_pixels_enabled() const; + Viewport(); ~Viewport(); }; diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 14225d945d..da3bc6a95b 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -269,13 +269,13 @@ Ref<Texture> Environment::get_adjustment_color_correction() const { void Environment::_validate_property(PropertyInfo &property) const { if (property.name == "background_sky" || property.name == "background_sky_scale" || property.name == "ambient_light/sky_contribution") { - if (bg_mode != BG_SKY) { + if (bg_mode != BG_SKY && bg_mode != BG_COLOR_SKY) { property.usage = PROPERTY_USAGE_NOEDITOR; } } if (property.name == "background_color") { - if (bg_mode != BG_COLOR) { + if (bg_mode != BG_COLOR && bg_mode != BG_COLOR_SKY) { property.usage = PROPERTY_USAGE_NOEDITOR; } } @@ -839,7 +839,7 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_ambient_light_sky_contribution"), &Environment::get_ambient_light_sky_contribution); ADD_GROUP("Background", "background_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "background_mode", PROPERTY_HINT_ENUM, "Clear Color,Custom Color,Sky,Canvas,Keep"), "set_background", "get_background"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "background_mode", PROPERTY_HINT_ENUM, "Clear Color,Custom Color,Sky,Color+Sky,Canvas,Keep"), "set_background", "get_background"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "background_sky", PROPERTY_HINT_RESOURCE_TYPE, "Sky"), "set_sky", "get_sky"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "background_sky_scale", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_sky_scale", "get_sky_scale"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "background_color"), "set_bg_color", "get_bg_color"); @@ -1118,6 +1118,7 @@ void Environment::_bind_methods() { BIND_ENUM_CONSTANT(BG_CLEAR_COLOR); BIND_ENUM_CONSTANT(BG_COLOR); BIND_ENUM_CONSTANT(BG_SKY); + BIND_ENUM_CONSTANT(BG_COLOR_SKY); BIND_ENUM_CONSTANT(BG_CANVAS); BIND_ENUM_CONSTANT(BG_MAX); diff --git a/scene/resources/environment.h b/scene/resources/environment.h index 6337981b95..9046ec1e49 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -45,6 +45,7 @@ public: BG_CLEAR_COLOR, BG_COLOR, BG_SKY, + BG_COLOR_SKY, BG_CANVAS, BG_KEEP, BG_MAX diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index a187692bcb..abe9a00c3f 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -67,8 +67,8 @@ RID Material::get_rid() const { } void Material::_validate_property(PropertyInfo &property) const { - if (!_can_do_next_pass() && property.name=="next_pass") { - property.usage=0; + if (!_can_do_next_pass() && property.name == "next_pass") { + property.usage = 0; } } @@ -80,7 +80,7 @@ void Material::_bind_methods() { ClassDB::bind_method(D_METHOD("set_render_priority", "priority"), &Material::set_render_priority); ClassDB::bind_method(D_METHOD("get_render_priority"), &Material::get_render_priority); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "render_priority", PROPERTY_HINT_RANGE, itos(RENDER_PRIORITY_MIN) + "," + itos(RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RENDER_PRIORITY_MIN) + "," + itos(RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "next_pass", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_next_pass", "get_next_pass"); BIND_CONSTANT(RENDER_PRIORITY_MAX); @@ -212,7 +212,7 @@ void ShaderMaterial::get_argument_options(const StringName &p_function, int p_id bool ShaderMaterial::_can_do_next_pass() const { - return shader.is_valid() && shader->get_mode()==Shader::MODE_SPATIAL; + return shader.is_valid() && shader->get_mode() == Shader::MODE_SPATIAL; } ShaderMaterial::ShaderMaterial() { diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index aa7827a61a..04efe88102 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -761,7 +761,7 @@ Array ArrayMesh::surface_get_arrays(int p_surface) const { Array ArrayMesh::surface_get_blend_shape_arrays(int p_surface) const { ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Array()); - return Array(); + return VisualServer::get_singleton()->mesh_surface_get_blend_shape_arrays(mesh, p_surface); } int ArrayMesh::get_surface_count() const { @@ -1010,6 +1010,8 @@ void ArrayMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("surface_get_material", "surf_idx"), &ArrayMesh::surface_get_material); ClassDB::bind_method(D_METHOD("surface_set_name", "surf_idx", "name"), &ArrayMesh::surface_set_name); ClassDB::bind_method(D_METHOD("surface_get_name", "surf_idx"), &ArrayMesh::surface_get_name); + ClassDB::bind_method(D_METHOD("surface_get_arrays", "surf_idx"), &ArrayMesh::surface_get_arrays); + ClassDB::bind_method(D_METHOD("surface_get_blend_shape_arrays", "surf_idx"), &ArrayMesh::surface_get_blend_shape_arrays); ClassDB::bind_method(D_METHOD("create_trimesh_shape"), &ArrayMesh::create_trimesh_shape); ClassDB::bind_method(D_METHOD("create_convex_shape"), &ArrayMesh::create_convex_shape); ClassDB::bind_method(D_METHOD("create_outline", "margin"), &ArrayMesh::create_outline); diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 53c6eb2d89..f4edb258b6 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -120,6 +120,7 @@ public: virtual int surface_get_array_len(int p_idx) const = 0; virtual int surface_get_array_index_len(int p_idx) const = 0; virtual Array surface_get_arrays(int p_surface) const = 0; + virtual Array surface_get_blend_shape_arrays(int p_surface) const = 0; virtual uint32_t surface_get_format(int p_idx) const = 0; virtual PrimitiveType surface_get_primitive_type(int p_idx) const = 0; virtual Ref<Material> surface_get_material(int p_idx) const = 0; @@ -174,7 +175,7 @@ public: void add_surface(uint32_t p_format, PrimitiveType p_primitive, const PoolVector<uint8_t> &p_array, int p_vertex_count, const PoolVector<uint8_t> &p_index_array, int p_index_count, const Rect3 &p_aabb, const Vector<PoolVector<uint8_t> > &p_blend_shapes = Vector<PoolVector<uint8_t> >(), const Vector<Rect3> &p_bone_aabbs = Vector<Rect3>()); Array surface_get_arrays(int p_surface) const; - virtual Array surface_get_blend_shape_arrays(int p_surface) const; + Array surface_get_blend_shape_arrays(int p_surface) const; void add_blend_shape(const StringName &p_name); int get_blend_shape_count() const; diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index cfc1468533..52e080bdc8 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -105,6 +105,15 @@ Array PrimitiveMesh::surface_get_arrays(int p_surface) const { return VisualServer::get_singleton()->mesh_surface_get_arrays(mesh, 0); } +Array PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, 1, Array()); + if (pending_request) { + _update(); + } + + return Array(); +} + uint32_t PrimitiveMesh::surface_get_format(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, 1, 0); if (pending_request) { @@ -119,6 +128,8 @@ Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const { } Ref<Material> PrimitiveMesh::surface_get_material(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, 1, NULL); + return material; } @@ -151,6 +162,8 @@ void PrimitiveMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_material", "material"), &PrimitiveMesh::set_material); ClassDB::bind_method(D_METHOD("get_material"), &PrimitiveMesh::get_material); + ClassDB::bind_method(D_METHOD("get_mesh_arrays"), &PrimitiveMesh::get_mesh_arrays); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_material", "get_material"); } @@ -168,6 +181,10 @@ Ref<Material> PrimitiveMesh::get_material() const { return material; } +Array PrimitiveMesh::get_mesh_arrays() const { + return surface_get_arrays(0); +} + PrimitiveMesh::PrimitiveMesh() { // defaults mesh = VisualServer::get_singleton()->mesh_create(); diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 34fb75a196..38a5695883 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -67,6 +67,7 @@ public: virtual int surface_get_array_len(int p_idx) const; virtual int surface_get_array_index_len(int p_idx) const; virtual Array surface_get_arrays(int p_surface) const; + virtual Array surface_get_blend_shape_arrays(int p_surface) const; virtual uint32_t surface_get_format(int p_idx) const; virtual Mesh::PrimitiveType surface_get_primitive_type(int p_idx) const; virtual Ref<Material> surface_get_material(int p_idx) const; @@ -78,6 +79,8 @@ public: void set_material(const Ref<Material> &p_material); Ref<Material> get_material() const; + Array get_mesh_arrays() const; + PrimitiveMesh(); ~PrimitiveMesh(); }; diff --git a/servers/arvr/arvr_positional_tracker.cpp b/servers/arvr/arvr_positional_tracker.cpp index 539bac6703..4ecd7a3898 100644 --- a/servers/arvr/arvr_positional_tracker.cpp +++ b/servers/arvr/arvr_positional_tracker.cpp @@ -31,6 +31,10 @@ #include "core/os/input.h" void ARVRPositionalTracker::_bind_methods() { + BIND_ENUM_CONSTANT(TRACKER_HAND_UNKNOWN); + BIND_ENUM_CONSTANT(TRACKER_LEFT_HAND); + BIND_ENUM_CONSTANT(TRACKER_RIGHT_HAND); + // this class is read only from GDScript, so we only have access to getters.. ClassDB::bind_method(D_METHOD("get_type"), &ARVRPositionalTracker::get_type); ClassDB::bind_method(D_METHOD("get_name"), &ARVRPositionalTracker::get_name); @@ -39,6 +43,7 @@ void ARVRPositionalTracker::_bind_methods() { ClassDB::bind_method(D_METHOD("get_orientation"), &ARVRPositionalTracker::get_orientation); ClassDB::bind_method(D_METHOD("get_tracks_position"), &ARVRPositionalTracker::get_tracks_position); ClassDB::bind_method(D_METHOD("get_position"), &ARVRPositionalTracker::get_position); + ClassDB::bind_method(D_METHOD("get_hand"), &ARVRPositionalTracker::get_hand); ClassDB::bind_method(D_METHOD("get_transform", "adjust_by_reference_frame"), &ARVRPositionalTracker::get_transform); // these functions we don't want to expose to normal users but do need to be callable from GDNative @@ -141,6 +146,14 @@ Vector3 ARVRPositionalTracker::get_rw_position() const { return rw_position; }; +ARVRPositionalTracker::TrackerHand ARVRPositionalTracker::get_hand() const { + return hand; +}; + +void ARVRPositionalTracker::set_hand(const ARVRPositionalTracker::TrackerHand p_hand) { + hand = p_hand; +}; + Transform ARVRPositionalTracker::get_transform(bool p_adjust_by_reference_frame) const { Transform new_transform; @@ -164,6 +177,7 @@ ARVRPositionalTracker::ARVRPositionalTracker() { tracker_id = 0; tracks_orientation = false; tracks_position = false; + hand = TRACKER_HAND_UNKNOWN; }; ARVRPositionalTracker::~ARVRPositionalTracker(){ diff --git a/servers/arvr/arvr_positional_tracker.h b/servers/arvr/arvr_positional_tracker.h index f91f862ba3..ff0c150f89 100644 --- a/servers/arvr/arvr_positional_tracker.h +++ b/servers/arvr/arvr_positional_tracker.h @@ -48,6 +48,13 @@ class ARVRPositionalTracker : public Object { GDCLASS(ARVRPositionalTracker, Object); _THREAD_SAFE_CLASS_ +public: + enum TrackerHand { + TRACKER_HAND_UNKNOWN, /* unknown or not applicable */ + TRACKER_LEFT_HAND, /* controller is the left hand controller */ + TRACKER_RIGHT_HAND /* controller is the right hand controller */ + }; + private: ARVRServer::TrackerType type; // type of tracker StringName name; // (unique) name of the tracker @@ -57,6 +64,7 @@ private: Basis orientation; // our orientation bool tracks_position; // do we track position? Vector3 rw_position; // our position "in the real world, so without world_scale applied" + TrackerHand hand; // if known, the hand this tracker is held in protected: static void _bind_methods(); @@ -77,6 +85,8 @@ public: Vector3 get_position() const; // get position with world_scale applied void set_rw_position(const Vector3 &p_rw_position); Vector3 get_rw_position() const; + ARVRPositionalTracker::TrackerHand get_hand() const; + void set_hand(const ARVRPositionalTracker::TrackerHand p_hand); Transform get_transform(bool p_adjust_by_reference_frame) const; @@ -84,4 +94,6 @@ public: ~ARVRPositionalTracker(); }; +VARIANT_ENUM_CAST(ARVRPositionalTracker::TrackerHand); + #endif diff --git a/servers/arvr_server.cpp b/servers/arvr_server.cpp index bac24f6438..3308e1cc07 100644 --- a/servers/arvr_server.cpp +++ b/servers/arvr_server.cpp @@ -68,8 +68,8 @@ void ARVRServer::_bind_methods() { ADD_SIGNAL(MethodInfo("interface_added", PropertyInfo(Variant::STRING, "name"))); ADD_SIGNAL(MethodInfo("interface_removed", PropertyInfo(Variant::STRING, "name"))); - ADD_SIGNAL(MethodInfo("tracker_added", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::INT, "type"))); - ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING, "name"))); + ADD_SIGNAL(MethodInfo("tracker_added", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::INT, "type"), PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::INT, "type"), PropertyInfo(Variant::INT, "id"))); }; real_t ARVRServer::get_world_scale() const { @@ -232,7 +232,7 @@ void ARVRServer::add_tracker(ARVRPositionalTracker *p_tracker) { ERR_FAIL_NULL(p_tracker); trackers.push_back(p_tracker); - emit_signal("tracker_added", p_tracker->get_name(), p_tracker->get_type()); + emit_signal("tracker_added", p_tracker->get_name(), p_tracker->get_type(), p_tracker->get_tracker_id()); }; void ARVRServer::remove_tracker(ARVRPositionalTracker *p_tracker) { @@ -250,7 +250,7 @@ void ARVRServer::remove_tracker(ARVRPositionalTracker *p_tracker) { ERR_FAIL_COND(idx == -1); - emit_signal("tracker_removed", p_tracker->get_name()); + emit_signal("tracker_removed", p_tracker->get_name(), p_tracker->get_type(), p_tracker->get_tracker_id()); trackers.remove(idx); }; diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 187a0b180b..0784fb1049 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -335,6 +335,9 @@ public: virtual void light_directional_set_shadow_mode(RID p_light, VS::LightDirectionalShadowMode p_mode) = 0; virtual void light_directional_set_blend_splits(RID p_light, bool p_enable) = 0; virtual bool light_directional_get_blend_splits(RID p_light) const = 0; + virtual void light_directional_set_shadow_depth_range_mode(RID p_light, VS::LightDirectionalShadowDepthRangeMode p_range_mode) = 0; + virtual VS::LightDirectionalShadowDepthRangeMode light_directional_get_shadow_depth_range_mode(RID p_light) const = 0; + virtual VS::LightDirectionalShadowMode light_directional_get_shadow_mode(RID p_light) = 0; virtual VS::LightOmniShadowMode light_omni_get_shadow_mode(RID p_light) = 0; diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index 3953bc5f48..7c7ce46268 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -795,6 +795,7 @@ public: BIND2(light_directional_set_shadow_mode, RID, LightDirectionalShadowMode) BIND2(light_directional_set_blend_splits, RID, bool) + BIND2(light_directional_set_shadow_depth_range_mode, RID, LightDirectionalShadowDepthRangeMode) /* PROBE API */ diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index 0d70b7fc0e..e791f7b443 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -886,12 +886,55 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons float max_distance = p_cam_projection.get_z_far(); float shadow_max = VSG::storage->light_get_param(p_instance->base, VS::LIGHT_PARAM_SHADOW_MAX_DISTANCE); - if (shadow_max > 0) { + if (shadow_max > 0 && !p_cam_orthogonal) { //its impractical (and leads to unwanted behaviors) to set max distance in orthogonal camera max_distance = MIN(shadow_max, max_distance); } max_distance = MAX(max_distance, p_cam_projection.get_z_near() + 0.001); + float min_distance = MIN(p_cam_projection.get_z_near(),max_distance); - float range = max_distance - p_cam_projection.get_z_near(); + VS::LightDirectionalShadowDepthRangeMode depth_range_mode = VSG::storage->light_directional_get_shadow_depth_range_mode(p_instance->base); + + if (depth_range_mode==VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_OPTIMIZED) { + //optimize min/max + Vector<Plane> planes = p_cam_projection.get_projection_planes(p_cam_transform); + int cull_count = p_scenario->octree.cull_convex(planes, instance_shadow_cull_result, MAX_INSTANCE_CULL, VS::INSTANCE_GEOMETRY_MASK); + Plane base(p_cam_transform.origin,-p_cam_transform.basis.get_axis(2)); + //check distance max and min + + bool found_items=false; + float z_max=-1e20; + float z_min=1e20; + + for(int i=0;i<cull_count;i++) { + + Instance *instance = instance_shadow_cull_result[i]; + if (!instance->visible || !((1 << instance->base_type) & VS::INSTANCE_GEOMETRY_MASK) || !static_cast<InstanceGeometryData *>(instance->base_data)->can_cast_shadows) { + continue; + } + + float max,min; + instance->transformed_aabb.project_range_in_plane(base, min, max); + + if (max>z_max) { + z_max=max; + } + + if (min<z_min) { + z_min=min; + } + + found_items=true; + } + + if (found_items) { + min_distance=MAX(min_distance,z_min); + max_distance=MIN(max_distance,z_max); + } + + } + + + float range = max_distance - min_distance; int splits = 0; switch (VSG::storage->light_directional_get_shadow_mode(p_instance->base)) { @@ -902,9 +945,9 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons float distances[5]; - distances[0] = p_cam_projection.get_z_near(); + distances[0] = min_distance; for (int i = 0; i < splits; i++) { - distances[i + 1] = p_cam_projection.get_z_near() + VSG::storage->light_get_param(p_instance->base, VS::LightParam(VS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET + i)) * range; + distances[i + 1] = min_distance + VSG::storage->light_get_param(p_instance->base, VS::LightParam(VS::LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET + i)) * range; }; distances[splits] = max_distance; @@ -984,8 +1027,6 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons { //camera viewport stuff - //this trick here is what stabilizes the shadow (make potential jaggies to not move) - //at the cost of some wasted resolution. Still the quality increase is very well worth it Vector3 center; @@ -1006,7 +1047,7 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons radius = d; } - radius *= texture_size / (texture_size - 2.0); //add a texel by each side, so stepified texture will always fit + radius *= texture_size / (texture_size - 2.0); //add a texel by each side if (i == 0) { first_radius = radius; @@ -1021,12 +1062,19 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons z_max_cam = z_vec.dot(center) + radius; z_min_cam = z_vec.dot(center) - radius; - float unit = radius * 2.0 / texture_size; + if (depth_range_mode==VS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE) { + //this trick here is what stabilizes the shadow (make potential jaggies to not move) + //at the cost of some wasted resolution. Still the quality increase is very well worth it + + float unit = radius * 2.0 / texture_size; + + x_max_cam = Math::stepify(x_max_cam, unit); + x_min_cam = Math::stepify(x_min_cam, unit); + y_max_cam = Math::stepify(y_max_cam, unit); + y_min_cam = Math::stepify(y_min_cam, unit); + } + - x_max_cam = Math::stepify(x_max_cam, unit); - x_min_cam = Math::stepify(x_min_cam, unit); - y_max_cam = Math::stepify(y_max_cam, unit); - y_min_cam = Math::stepify(y_min_cam, unit); } //now that we now all ranges, we can proceed to make the light frustum planes, for culling octree @@ -1069,6 +1117,8 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons } { + + CameraMatrix ortho_camera; real_t half_x = (x_max_cam - x_min_cam) * 0.5; real_t half_y = (y_max_cam - y_min_cam) * 0.5; diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index f24049be92..5cf941b93d 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -233,6 +233,7 @@ public: FUNC2(light_directional_set_shadow_mode, RID, LightDirectionalShadowMode) FUNC2(light_directional_set_blend_splits, RID, bool) + FUNC2(light_directional_set_shadow_depth_range_mode, RID, LightDirectionalShadowDepthRangeMode) /* PROBE API */ diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index 67b847d127..47a5f4c7f3 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -1420,6 +1420,29 @@ Array VisualServer::mesh_surface_get_arrays(RID p_mesh, int p_surface) const { return _get_array_from_surface(format, vertex_data, vertex_len, index_data, index_len); } +Array VisualServer::mesh_surface_get_blend_shape_arrays(RID p_mesh, int p_surface) const { + + Vector<PoolVector<uint8_t> > blend_shape_data = mesh_surface_get_blend_shapes(p_mesh, p_surface); + if (blend_shape_data.size() > 0) { + int vertex_len = mesh_surface_get_array_len(p_mesh, p_surface); + + PoolVector<uint8_t> index_data = mesh_surface_get_index_array(p_mesh, p_surface); + int index_len = mesh_surface_get_array_index_len(p_mesh, p_surface); + + uint32_t format = mesh_surface_get_format(p_mesh, p_surface); + + Array blend_shape_array; + blend_shape_array.resize(blend_shape_data.size()); + for (int i = 0; i < blend_shape_data.size(); i++) { + blend_shape_array.set(i, _get_array_from_surface(format, blend_shape_data[i], vertex_len, index_data, index_len)); + } + + return blend_shape_array; + } else { + return Array(); + } +} + void VisualServer::_bind_methods() { ClassDB::bind_method(D_METHOD("force_draw"), &VisualServer::draw); diff --git a/servers/visual_server.h b/servers/visual_server.h index d516013ee2..72f36f6b65 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -267,6 +267,7 @@ public: virtual PoolVector<uint8_t> mesh_surface_get_index_array(RID p_mesh, int p_surface) const = 0; virtual Array mesh_surface_get_arrays(RID p_mesh, int p_surface) const; + virtual Array mesh_surface_get_blend_shape_arrays(RID p_mesh, int p_surface) const; virtual uint32_t mesh_surface_get_format(RID p_mesh, int p_surface) const = 0; virtual PrimitiveType mesh_surface_get_primitive_type(RID p_mesh, int p_surface) const = 0; @@ -406,6 +407,14 @@ public: virtual void light_directional_set_shadow_mode(RID p_light, LightDirectionalShadowMode p_mode) = 0; virtual void light_directional_set_blend_splits(RID p_light, bool p_enable) = 0; + enum LightDirectionalShadowDepthRangeMode { + LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE, + LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_OPTIMIZED, + + }; + + virtual void light_directional_set_shadow_depth_range_mode(RID p_light, LightDirectionalShadowDepthRangeMode p_range_mode) = 0; + /* PROBE API */ virtual RID reflection_probe_create() = 0; @@ -620,6 +629,7 @@ public: ENV_BG_CLEAR_COLOR, ENV_BG_COLOR, ENV_BG_SKY, + ENV_BG_COLOR_SKY, ENV_BG_CANVAS, ENV_BG_KEEP, ENV_BG_MAX |