diff options
66 files changed, 645 insertions, 419 deletions
diff --git a/doc/class.xsd b/doc/class.xsd index 7681ddad3d..d29d053006 100644 --- a/doc/class.xsd +++ b/doc/class.xsd @@ -95,6 +95,8 @@ </xs:sequence> <xs:attribute type="xs:string" name="name" use="optional" /> <xs:attribute type="xs:string" name="qualifiers" use="optional" /> + <xs:attribute type="xs:boolean" name="is_deprecated" use="optional" /> + <xs:attribute type="xs:boolean" name="is_experimental" use="optional" /> </xs:complexType> </xs:element> </xs:sequence> @@ -114,6 +116,8 @@ <xs:attribute type="xs:string" name="overrides" use="optional" /> <xs:attribute type="xs:string" name="enum" use="optional" /> <xs:attribute type="xs:string" name="default" use="optional" /> + <xs:attribute type="xs:boolean" name="is_deprecated" use="optional" /> + <xs:attribute type="xs:boolean" name="is_experimental" use="optional" /> </xs:extension> </xs:simpleContent> </xs:complexType> @@ -156,6 +160,8 @@ <xs:attribute type="xs:string" name="value" /> <xs:attribute type="xs:string" name="enum" use="optional" /> <xs:attribute type="xs:boolean" name="is_bitfield" use="optional" /> + <xs:attribute type="xs:boolean" name="is_deprecated" use="optional" /> + <xs:attribute type="xs:boolean" name="is_experimental" use="optional" /> </xs:extension> </xs:simpleContent> </xs:complexType> @@ -256,6 +262,8 @@ </xs:sequence> <xs:attribute type="xs:string" name="name" /> <xs:attribute type="xs:string" name="inherits" /> + <xs:attribute type="xs:boolean" name="is_deprecated" use="optional" /> + <xs:attribute type="xs:boolean" name="is_experimental" use="optional" /> <xs:attribute type="xs:float" name="version" /> </xs:complexType> </xs:element> diff --git a/doc/classes/BoneAttachment3D.xml b/doc/classes/BoneAttachment3D.xml index dc3d448621..f29525038e 100644 --- a/doc/classes/BoneAttachment3D.xml +++ b/doc/classes/BoneAttachment3D.xml @@ -16,10 +16,11 @@ Returns the [NodePath] to the external [Skeleton3D] node, if one has been set. </description> </method> - <method name="get_override_mode" qualifiers="const"> + <method name="get_override_mode" qualifiers="const" is_deprecated="true"> <return type="int" /> <description> - Returns the override mode for the BoneAttachment3D node. + Deprecated. Local pose overrides will be removed. + Returns the override mode for the BoneAttachment3D node (0=global / 1=local). </description> </method> <method name="get_override_pose" qualifiers="const"> @@ -48,11 +49,12 @@ Sets the [NodePath] to the external skeleton that the BoneAttachment3D node should use. The external [Skeleton3D] node is only used when [code]use_external_skeleton[/code] is set to [code]true[/code]. </description> </method> - <method name="set_override_mode"> + <method name="set_override_mode" is_deprecated="true"> <return type="void" /> <param index="0" name="override_mode" type="int" /> <description> - Sets the override mode for the BoneAttachment3D node. The override mode defines which of the bone poses the BoneAttachment3D node will override. + Deprecated. Local pose overrides will be removed. + Sets the override mode for the BoneAttachment3D node (0=global / 1=local). The override mode defines which of the bone poses the BoneAttachment3D node will override. </description> </method> <method name="set_override_pose"> diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml index bae2335cfb..ce2b5f2584 100644 --- a/doc/classes/MultiMesh.xml +++ b/doc/classes/MultiMesh.xml @@ -1,13 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="MultiMesh" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Provides high-performance mesh instancing. + Provides high-performance drawing of a mesh multiple times using GPU instancing. </brief_description> <description> MultiMesh provides low-level mesh instancing. Drawing thousands of [MeshInstance3D] nodes can be slow, since each object is submitted to the GPU then drawn individually. MultiMesh is much faster as it can draw thousands of instances with a single draw call, resulting in less API overhead. As a drawback, if the instances are too far away from each other, performance may be reduced as every single instance will always render (they are spatially indexed as one, for the whole object). Since instances may have any behavior, the AABB used for visibility must be provided by the user. + [b]Note:[/b] A MultiMesh is a single object, therefore the same maximum lights per object restriction applies. This means, that once the maximum lights are consumed by one or more instances, the rest of the MultiMesh instances will [b]not[/b] receive any lighting. + [b]Note:[/b] Blend Shapes will be ignored if used in a MultiMesh. </description> <tutorials> <link title="Animating thousands of fish with MultiMeshInstance">$DOCS_URL/tutorials/performance/vertex_animation/animating_thousands_of_fish.html</link> @@ -24,7 +26,7 @@ <return type="Color" /> <param index="0" name="instance" type="int" /> <description> - Gets a specific instance's color. + Gets a specific instance's color multiplier. </description> </method> <method name="get_instance_custom_data" qualifiers="const"> @@ -53,8 +55,8 @@ <param index="0" name="instance" type="int" /> <param index="1" name="color" type="Color" /> <description> - Sets the color of a specific instance by [i]multiplying[/i] the mesh's existing vertex colors. - For the color to take effect, ensure that [member use_colors] is [code]true[/code] on the [MultiMesh] and [member BaseMaterial3D.vertex_color_use_as_albedo] is [code]true[/code] on the material. If the color doesn't look as expected, make sure the material's albedo color is set to pure white ([code]Color(1, 1, 1)[/code]). + Sets the color of a specific instance by [i]multiplying[/i] the mesh's existing vertex colors. This allows for different color tinting per instance. + For the color to take effect, ensure that [member use_colors] is [code]true[/code] on the [MultiMesh] and [member BaseMaterial3D.vertex_color_use_as_albedo] is [code]true[/code] on the material. If you intend to set an absolute color instead of tinting, make sure the material's albedo color is set to pure white ([code]Color(1, 1, 1)[/code]). </description> </method> <method name="set_instance_custom_data"> @@ -64,6 +66,7 @@ <description> Sets custom data for a specific instance. Although [Color] is used, it is just a container for 4 floating point numbers. For the custom data to be used, ensure that [member use_custom_data] is [code]true[/code]. + This custom instance data has to be manually accessed in your custom shader using [code]INSTANCE_CUSTOM[/code]. </description> </method> <method name="set_instance_transform"> @@ -87,28 +90,33 @@ <member name="buffer" type="PackedFloat32Array" setter="set_buffer" getter="get_buffer" default="PackedFloat32Array()"> </member> <member name="color_array" type="PackedColorArray" setter="_set_color_array" getter="_get_color_array"> + See [method set_instance_color]. </member> <member name="custom_data_array" type="PackedColorArray" setter="_set_custom_data_array" getter="_get_custom_data_array"> + See [method set_instance_custom_data]. </member> <member name="instance_count" type="int" setter="set_instance_count" getter="get_instance_count" default="0"> Number of instances that will get drawn. This clears and (re)sizes the buffers. Setting data format or flags afterwards will have no effect. By default, all instances are drawn but you can limit this with [member visible_instance_count]. </member> <member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh"> - Mesh to be drawn. + [Mesh] resource to be instanced. + The looks of the individual instances can be modified using [method set_instance_color] and [method set_instance_custom_data]. </member> <member name="transform_2d_array" type="PackedVector2Array" setter="_set_transform_2d_array" getter="_get_transform_2d_array"> + See [method set_instance_transform_2d]. </member> <member name="transform_array" type="PackedVector3Array" setter="_set_transform_array" getter="_get_transform_array"> + See [method set_instance_transform]. </member> <member name="transform_format" type="int" setter="set_transform_format" getter="get_transform_format" enum="MultiMesh.TransformFormat" default="0"> Format of transform used to transform mesh, either 2D or 3D. </member> <member name="use_colors" type="bool" setter="set_use_colors" getter="is_using_colors" default="false"> - If [code]true[/code], the [MultiMesh] will use color data (see [member color_array]). + If [code]true[/code], the [MultiMesh] will use color data (see [method set_instance_color]). Can only be set when [member instance_count] is [code]0[/code] or less. This means that you need to call this method before setting the instance count, or temporarily reset it to [code]0[/code]. </member> <member name="use_custom_data" type="bool" setter="set_use_custom_data" getter="is_using_custom_data" default="false"> - If [code]true[/code], the [MultiMesh] will use custom data (see [member custom_data_array]). + If [code]true[/code], the [MultiMesh] will use custom data (see [method set_instance_custom_data]). Can only be set when [member instance_count] is [code]0[/code] or less. This means that you need to call this method before setting the instance count, or temporarily reset it to [code]0[/code]. </member> <member name="visible_instance_count" type="int" setter="set_visible_instance_count" getter="get_visible_instance_count" default="-1"> Limits the number of instances drawn, -1 draws all instances. Changing this does not change the sizes of the buffers. diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml index 5a0766263a..3bd0e04b92 100644 --- a/doc/classes/Skeleton3D.xml +++ b/doc/classes/Skeleton3D.xml @@ -32,9 +32,10 @@ Removes the global pose override on all bones in the skeleton. </description> </method> - <method name="clear_bones_local_pose_override"> + <method name="clear_bones_local_pose_override" is_deprecated="true"> <return type="void" /> <description> + Deprecated. Local pose overrides will be removed. Removes the local pose override on all bones in the skeleton. </description> </method> @@ -43,7 +44,7 @@ <description> </description> </method> - <method name="execute_modifications"> + <method name="execute_modifications" is_deprecated="true"> <return type="void" /> <param index="0" name="delta" type="float" /> <param index="1" name="execution_mode" type="int" /> @@ -58,7 +59,7 @@ Returns the bone index that matches [param name] as its name. </description> </method> - <method name="force_update_all_bone_transforms"> + <method name="force_update_all_bone_transforms" is_deprecated="true"> <return type="void" /> <description> Force updates the bone transforms/poses for all bones in the skeleton. @@ -166,7 +167,7 @@ Returns the rest transform for a bone [param bone_idx]. </description> </method> - <method name="get_modification_stack"> + <method name="get_modification_stack" is_deprecated="true"> <return type="SkeletonModificationStack3D" /> <description> Returns the modification stack attached to this skeleton, if one exists. @@ -178,7 +179,7 @@ Returns an array with all of the bones that are parentless. Another way to look at this is that it returns the indexes of all the bones that are not dependent or modified by other bones in the Skeleton. </description> </method> - <method name="global_pose_to_local_pose"> + <method name="global_pose_to_local_pose" is_deprecated="true"> <return type="Transform3D" /> <param index="0" name="bone_idx" type="int" /> <param index="1" name="global_pose" type="Transform3D" /> @@ -187,15 +188,16 @@ This can be used to easily convert a global pose from [method get_bone_global_pose] to a global transform in [method set_bone_local_pose_override]. </description> </method> - <method name="global_pose_to_world_transform"> + <method name="global_pose_to_world_transform" is_deprecated="true"> <return type="Transform3D" /> <param index="0" name="global_pose" type="Transform3D" /> <description> + Deprecated. Use [Node3D] apis instead. Takes the passed-in global pose and converts it to a world transform. This can be used to easily convert a global pose from [method get_bone_global_pose] to a global transform usable with a node's transform, like [member Node3D.global_transform] for example. </description> </method> - <method name="global_pose_z_forward_to_bone_forward"> + <method name="global_pose_z_forward_to_bone_forward" is_deprecated="true"> <return type="Basis" /> <param index="0" name="bone_idx" type="int" /> <param index="1" name="basis" type="Basis" /> @@ -211,7 +213,7 @@ Returns whether the bone pose for the bone at [param bone_idx] is enabled. </description> </method> - <method name="local_pose_to_global_pose"> + <method name="local_pose_to_global_pose" is_deprecated="true"> <return type="Transform3D" /> <param index="0" name="bone_idx" type="int" /> <param index="1" name="local_pose" type="Transform3D" /> @@ -293,16 +295,17 @@ <description> Sets the global pose transform, [param pose], for the bone at [param bone_idx]. [param amount] is the interpolation strength that will be used when applying the pose, and [param persistent] determines if the applied pose will remain. - [b]Note:[/b] The pose transform needs to be a global pose! Use [method world_transform_to_global_pose] to convert a world transform, like one you can get from a [Node3D], to a global pose. + [b]Note:[/b] The pose transform needs to be a global pose! To convert a world transform from a [Node3D] to a global bone pose, multiply the [method Transform3D.affine_inverse] of the node's [member Node3D.global_transform] by the desired world transform </description> </method> - <method name="set_bone_local_pose_override"> + <method name="set_bone_local_pose_override" is_deprecated="true"> <return type="void" /> <param index="0" name="bone_idx" type="int" /> <param index="1" name="pose" type="Transform3D" /> <param index="2" name="amount" type="float" /> <param index="3" name="persistent" type="bool" default="false" /> <description> + Deprecated. Local pose overrides will be removed. Sets the local pose transform, [param pose], for the bone at [param bone_idx]. [param amount] is the interpolation strength that will be used when applying the pose, and [param persistent] determines if the applied pose will remain. [b]Note:[/b] The pose transform needs to be a local pose! Use [method global_pose_to_local_pose] to convert a global pose to a local pose. @@ -353,7 +356,7 @@ Sets the rest transform for bone [param bone_idx]. </description> </method> - <method name="set_modification_stack"> + <method name="set_modification_stack" is_deprecated="true"> <return type="void" /> <param index="0" name="modification_stack" type="SkeletonModificationStack3D" /> <description> @@ -367,10 +370,11 @@ Unparents the bone at [param bone_idx] and sets its rest position to that of its parent prior to being reset. </description> </method> - <method name="world_transform_to_global_pose"> + <method name="world_transform_to_global_pose" is_deprecated="true"> <return type="Transform3D" /> <param index="0" name="world_transform" type="Transform3D" /> <description> + Deprecated. Use [Node3D] apis instead. Takes the passed-in global transform and converts it to a global pose. This can be used to easily convert a global transform from [member Node3D.global_transform] to a global pose usable with [method set_bone_global_pose_override], for example. </description> diff --git a/doc/classes/SkeletonIK3D.xml b/doc/classes/SkeletonIK3D.xml index 788ba3e248..1d50a97954 100644 --- a/doc/classes/SkeletonIK3D.xml +++ b/doc/classes/SkeletonIK3D.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonIK3D" inherits="Node" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonIK3D" inherits="Node" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + SkeletonIK3D is used to place the end bone of a [Skeleton3D] bone chain at a certain point in 3D by rotating all bones in the chain accordingly. </brief_description> <description> + SkeletonIK3D is used to place the end bone of a [Skeleton3D] bone chain at a certain point in 3D by rotating all bones in the chain accordingly. A typical scenario for IK in games is to place a characters feet on the ground or a characters hands on a currently hold object. SkeletonIK uses FabrikInverseKinematic internally to solve the bone chain and applies the results to the [Skeleton3D] [code]bones_global_pose_override[/code] property for all affected bones in the chain. If fully applied this overwrites any bone transform from [Animation]s or bone custom poses set by users. The applied amount can be controlled with the [code]interpolation[/code] property. + [codeblock] + # Apply IK effect automatically on every new frame (not the current) + skeleton_ik_node.start() + + # Apply IK effect only on the current frame + skeleton_ik_node.start(true) + + # Stop IK effect and reset bones_global_pose_override on Skeleton + skeleton_ik_node.stop() + + # Apply full IK effect + skeleton_ik_node.set_interpolation(1.0) + + # Apply half IK effect + skeleton_ik_node.set_interpolation(0.5) + + # Apply zero IK effect (a value at or below 0.01 also removes bones_global_pose_override on Skeleton) + skeleton_ik_node.set_interpolation(0.0) + [/codeblock] </description> <tutorials> <link title="3D Inverse Kinematics Demo">https://godotengine.org/asset-library/asset/523</link> @@ -11,45 +32,59 @@ <method name="get_parent_skeleton" qualifiers="const"> <return type="Skeleton3D" /> <description> + Returns the parent [Skeleton3D] Node that was present when SkeletonIK entered the [SceneTree]. Returns null if the parent node was not a [Skeleton3D] Node when SkeletonIK3D entered the [SceneTree]. </description> </method> <method name="is_running"> <return type="bool" /> <description> + Returns [code]true[/code] if SkeletonIK is applying IK effects on continues frames to the [Skeleton3D] bones. Returns [code]false[/code] if SkeletonIK is stopped or [method start] was used with the [code]one_time[/code] parameter set to [code]true[/code]. </description> </method> <method name="start"> <return type="void" /> <param index="0" name="one_time" type="bool" default="false" /> <description> + Starts applying IK effects on each frame to the [Skeleton3D] bones but will only take effect starting on the next frame. If [code]one_time[/code] is [code]true[/code], this will take effect immediately but also reset on the next frame. </description> </method> <method name="stop"> <return type="void" /> <description> + Stops applying IK effects on each frame to the [Skeleton3D] bones and also calls [method Skeleton3D.clear_bones_global_pose_override] to remove existing overrides on all bones. </description> </method> </methods> <members> <member name="interpolation" type="float" setter="set_interpolation" getter="get_interpolation" default="1.0"> + Interpolation value for how much the IK results are applied to the current skeleton bone chain. A value of [code]1.0[/code] will overwrite all skeleton bone transforms completely while a value of [code]0.0[/code] will visually disable the SkeletonIK. A value at or below [code]0.01[/code] also calls [method Skeleton3D.clear_bones_global_pose_override]. </member> <member name="magnet" type="Vector3" setter="set_magnet_position" getter="get_magnet_position" default="Vector3(0, 0, 0)"> + Secondary target position (first is [member target] property or [member target_node]) for the IK chain. Use magnet position (pole target) to control the bending of the IK chain. Only works if the bone chain has more than 2 bones. The middle chain bone position will be linearly interpolated with the magnet position. </member> <member name="max_iterations" type="int" setter="set_max_iterations" getter="get_max_iterations" default="10"> + Number of iteration loops used by the IK solver to produce more accurate (and elegant) bone chain results. </member> <member name="min_distance" type="float" setter="set_min_distance" getter="get_min_distance" default="0.01"> + The minimum distance between bone and goal target. If the distance is below this value, the IK solver stops further iterations. </member> <member name="override_tip_basis" type="bool" setter="set_override_tip_basis" getter="is_override_tip_basis" default="true"> + If [code]true[/code] overwrites the rotation of the tip bone with the rotation of the [member target] (or [member target_node] if defined). </member> <member name="root_bone" type="StringName" setter="set_root_bone" getter="get_root_bone" default="&"""> + The name of the current root bone, the first bone in the IK chain. </member> <member name="target" type="Transform3D" setter="set_target_transform" getter="get_target_transform" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)"> + First target of the IK chain where the tip bone is placed and, if [member override_tip_basis] is [code]true[/code], how the tip bone is rotated. If a [member target_node] path is available the nodes transform is used instead and this property is ignored. </member> <member name="target_node" type="NodePath" setter="set_target_node" getter="get_target_node" default="NodePath("")"> + Target node [NodePath] for the IK chain. If available, the node's current [Transform3D] is used instead of the [member target] property. </member> <member name="tip_bone" type="StringName" setter="set_tip_bone" getter="get_tip_bone" default="&"""> + The name of the current tip bone, the last bone in the IK chain placed at the [member target] transform (or [member target_node] if defined). </member> <member name="use_magnet" type="bool" setter="set_use_magnet" getter="is_using_magnet" default="false"> + If [code]true[/code], instructs the IK solver to consider the secondary magnet target (pole target) when calculating the bone chain. Use the magnet position (pole target) to control the bending of the IK chain. </member> </members> </class> diff --git a/doc/classes/SkeletonModification2D.xml b/doc/classes/SkeletonModification2D.xml index 46d32aef41..77aaf0213b 100644 --- a/doc/classes/SkeletonModification2D.xml +++ b/doc/classes/SkeletonModification2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification2D" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification2D" inherits="Resource" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A resource that operates on [Bone2D] nodes in a [Skeleton2D]. </brief_description> diff --git a/doc/classes/SkeletonModification2DCCDIK.xml b/doc/classes/SkeletonModification2DCCDIK.xml index c8fee3f94d..0d80b1b914 100644 --- a/doc/classes/SkeletonModification2DCCDIK.xml +++ b/doc/classes/SkeletonModification2DCCDIK.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification2DCCDIK" inherits="SkeletonModification2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification2DCCDIK" inherits="SkeletonModification2D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that uses CCDIK to manipulate a series of bones to reach a target in 2D. </brief_description> diff --git a/doc/classes/SkeletonModification2DFABRIK.xml b/doc/classes/SkeletonModification2DFABRIK.xml index ff3a65fe1a..8d1cd4b4d4 100644 --- a/doc/classes/SkeletonModification2DFABRIK.xml +++ b/doc/classes/SkeletonModification2DFABRIK.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification2DFABRIK" inherits="SkeletonModification2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification2DFABRIK" inherits="SkeletonModification2D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that uses FABRIK to manipulate a series of [Bone2D] nodes to reach a target. </brief_description> diff --git a/doc/classes/SkeletonModification2DJiggle.xml b/doc/classes/SkeletonModification2DJiggle.xml index 7329b2d865..3321fffe1b 100644 --- a/doc/classes/SkeletonModification2DJiggle.xml +++ b/doc/classes/SkeletonModification2DJiggle.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification2DJiggle" inherits="SkeletonModification2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification2DJiggle" inherits="SkeletonModification2D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that jiggles [Bone2D] nodes as they move towards a target. </brief_description> diff --git a/doc/classes/SkeletonModification2DLookAt.xml b/doc/classes/SkeletonModification2DLookAt.xml index 4747b06056..136fed37de 100644 --- a/doc/classes/SkeletonModification2DLookAt.xml +++ b/doc/classes/SkeletonModification2DLookAt.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification2DLookAt" inherits="SkeletonModification2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification2DLookAt" inherits="SkeletonModification2D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that rotates a [Bone2D] node to look at a target. </brief_description> diff --git a/doc/classes/SkeletonModification2DPhysicalBones.xml b/doc/classes/SkeletonModification2DPhysicalBones.xml index d5f46b2ea0..209602e974 100644 --- a/doc/classes/SkeletonModification2DPhysicalBones.xml +++ b/doc/classes/SkeletonModification2DPhysicalBones.xml @@ -1,10 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification2DPhysicalBones" inherits="SkeletonModification2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification2DPhysicalBones" inherits="SkeletonModification2D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that applies the transforms of [PhysicalBone2D] nodes to [Bone2D] nodes. </brief_description> <description> This modification takes the transforms of [PhysicalBone2D] nodes and applies them to [Bone2D] nodes. This allows the [Bone2D] nodes to react to physics thanks to the linked [PhysicalBone2D] nodes. + Experimental. Physical bones may be changed in the future to perform the position update of [Bone2D] on their own. </description> <tutorials> </tutorials> diff --git a/doc/classes/SkeletonModification2DStackHolder.xml b/doc/classes/SkeletonModification2DStackHolder.xml index 791dea2fb1..85df111c0d 100644 --- a/doc/classes/SkeletonModification2DStackHolder.xml +++ b/doc/classes/SkeletonModification2DStackHolder.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification2DStackHolder" inherits="SkeletonModification2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification2DStackHolder" inherits="SkeletonModification2D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that holds and executes a [SkeletonModificationStack2D]. </brief_description> diff --git a/doc/classes/SkeletonModification2DTwoBoneIK.xml b/doc/classes/SkeletonModification2DTwoBoneIK.xml index edd5431a0c..c476d71d44 100644 --- a/doc/classes/SkeletonModification2DTwoBoneIK.xml +++ b/doc/classes/SkeletonModification2DTwoBoneIK.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification2DTwoBoneIK" inherits="SkeletonModification2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification2DTwoBoneIK" inherits="SkeletonModification2D" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that rotates two bones using the law of cosigns to reach the target. </brief_description> diff --git a/doc/classes/SkeletonModification3D.xml b/doc/classes/SkeletonModification3D.xml index 8457179651..25431ea96f 100644 --- a/doc/classes/SkeletonModification3D.xml +++ b/doc/classes/SkeletonModification3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification3D" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification3D" inherits="Resource" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A resource that operates on bones in a [Skeleton3D]. </brief_description> diff --git a/doc/classes/SkeletonModification3DCCDIK.xml b/doc/classes/SkeletonModification3DCCDIK.xml index dec0fbe99f..90b2e78449 100644 --- a/doc/classes/SkeletonModification3DCCDIK.xml +++ b/doc/classes/SkeletonModification3DCCDIK.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification3DCCDIK" inherits="SkeletonModification3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification3DCCDIK" inherits="SkeletonModification3D" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that uses CCDIK to manipulate a series of bones to reach a target. </brief_description> diff --git a/doc/classes/SkeletonModification3DFABRIK.xml b/doc/classes/SkeletonModification3DFABRIK.xml index 325cc2a12e..a2bec2b559 100644 --- a/doc/classes/SkeletonModification3DFABRIK.xml +++ b/doc/classes/SkeletonModification3DFABRIK.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification3DFABRIK" inherits="SkeletonModification3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification3DFABRIK" inherits="SkeletonModification3D" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that uses FABRIK to manipulate a series of bones to reach a target. </brief_description> diff --git a/doc/classes/SkeletonModification3DJiggle.xml b/doc/classes/SkeletonModification3DJiggle.xml index ef469d42ea..304f08bb20 100644 --- a/doc/classes/SkeletonModification3DJiggle.xml +++ b/doc/classes/SkeletonModification3DJiggle.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification3DJiggle" inherits="SkeletonModification3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification3DJiggle" inherits="SkeletonModification3D" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that jiggles bones as they move towards a target. </brief_description> diff --git a/doc/classes/SkeletonModification3DLookAt.xml b/doc/classes/SkeletonModification3DLookAt.xml index 3602cfad95..aeed953ca9 100644 --- a/doc/classes/SkeletonModification3DLookAt.xml +++ b/doc/classes/SkeletonModification3DLookAt.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification3DLookAt" inherits="SkeletonModification3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification3DLookAt" inherits="SkeletonModification3D" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that rotates a bone to look at a target. </brief_description> diff --git a/doc/classes/SkeletonModification3DStackHolder.xml b/doc/classes/SkeletonModification3DStackHolder.xml index 24240236a4..9448e2c783 100644 --- a/doc/classes/SkeletonModification3DStackHolder.xml +++ b/doc/classes/SkeletonModification3DStackHolder.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification3DStackHolder" inherits="SkeletonModification3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification3DStackHolder" inherits="SkeletonModification3D" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that holds and executes a [SkeletonModificationStack3D]. </brief_description> diff --git a/doc/classes/SkeletonModification3DTwoBoneIK.xml b/doc/classes/SkeletonModification3DTwoBoneIK.xml index 6618ebbcfb..0e7ffd5c80 100644 --- a/doc/classes/SkeletonModification3DTwoBoneIK.xml +++ b/doc/classes/SkeletonModification3DTwoBoneIK.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModification3DTwoBoneIK" inherits="SkeletonModification3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModification3DTwoBoneIK" inherits="SkeletonModification3D" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A modification that moves two bones to reach the target. </brief_description> diff --git a/doc/classes/SkeletonModificationStack2D.xml b/doc/classes/SkeletonModificationStack2D.xml index 950e52e622..d96833e335 100644 --- a/doc/classes/SkeletonModificationStack2D.xml +++ b/doc/classes/SkeletonModificationStack2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModificationStack2D" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModificationStack2D" inherits="Resource" is_experimental="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A resource that holds a stack of [SkeletonModification2D]s. </brief_description> diff --git a/doc/classes/SkeletonModificationStack3D.xml b/doc/classes/SkeletonModificationStack3D.xml index 34c7099bca..9eaeeefd8e 100644 --- a/doc/classes/SkeletonModificationStack3D.xml +++ b/doc/classes/SkeletonModificationStack3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SkeletonModificationStack3D" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +<class name="SkeletonModificationStack3D" inherits="Resource" is_deprecated="true" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> A resource that holds a stack of [SkeletonModification3D]s. </brief_description> diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 0db16b491d..2e67c61e54 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -1720,6 +1720,7 @@ </constant> <constant name="HINTING_NORMAL" value="2" enum="Hinting"> Use the default font hinting mode (crisper but less smooth). + [b]Note:[/b] This hinting mode changes both horizontal and vertical glyph metrics. If applied to monospace font, some glyphs might have different width. </constant> <constant name="SUBPIXEL_POSITIONING_DISABLED" value="0" enum="SubpixelPositioning"> Glyph horizontal position is rounded to the whole pixel size, each glyph is rasterized once. diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index dae26b1e5f..1952efff0e 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -683,6 +683,7 @@ void RasterizerSceneGLES3::_setup_sky(const RenderDataGLES3 *p_render_data, cons light_data_dirty = true; for (uint32_t i = sky_globals.directional_light_count; i < sky_globals.max_directional_lights; i++) { sky_globals.directional_lights[i].enabled = false; + sky_globals.last_frame_directional_lights[i].enabled = false; } } diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 523c9dd8e6..687e98ba58 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -1379,9 +1379,9 @@ MaterialStorage::MaterialStorage() { actions.renames["POINT_SIZE"] = "gl_PointSize"; actions.renames["MODEL_MATRIX"] = "model_matrix"; - actions.renames["CANVAS_MATRIX"] = "canvas_data.canvas_transform"; - actions.renames["SCREEN_MATRIX"] = "canvas_data.screen_transform"; - actions.renames["TIME"] = "canvas_data.time"; + actions.renames["CANVAS_MATRIX"] = "canvas_transform"; + actions.renames["SCREEN_MATRIX"] = "screen_transform"; + actions.renames["TIME"] = "time"; actions.renames["PI"] = _MKSTR(Math_PI); actions.renames["TAU"] = _MKSTR(Math_TAU); actions.renames["E"] = _MKSTR(Math_E); @@ -1399,7 +1399,7 @@ MaterialStorage::MaterialStorage() { actions.renames["SPECULAR_SHININESS"] = "specular_shininess"; actions.renames["SCREEN_UV"] = "screen_uv"; actions.renames["SCREEN_TEXTURE"] = "screen_texture"; - actions.renames["SCREEN_PIXEL_SIZE"] = "canvas_data.screen_pixel_size"; + actions.renames["SCREEN_PIXEL_SIZE"] = "screen_pixel_size"; actions.renames["FRAGCOORD"] = "gl_FragCoord"; actions.renames["POINT_COORD"] = "gl_PointCoord"; actions.renames["INSTANCE_ID"] = "gl_InstanceIndex"; diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index 9f655ab00a..7d6eb186dc 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -1162,13 +1162,11 @@ Error DocTools::_load(Ref<XMLParser> parser) { } if (parser->has_attribute("is_deprecated")) { - String result = parser->get_attribute_value("is_deprecated"); - c.is_deprecated = (result == "true" || result == "True" || result == "TRUE" || result == "1"); + c.is_deprecated = parser->get_attribute_value("is_deprecated").to_lower() == "true"; } if (parser->has_attribute("is_experimental")) { - String result = parser->get_attribute_value("is_experimental"); - c.is_experimental = (result == "true" || result == "True" || result == "TRUE" || result == "1"); + c.is_experimental = parser->get_attribute_value("is_experimental").to_lower() == "true"; } while (parser->read() == OK) { @@ -1315,12 +1313,10 @@ Error DocTools::_load(Ref<XMLParser> parser) { constant2.is_bitfield = parser->get_attribute_value("is_bitfield").to_lower() == "true"; } if (parser->has_attribute("is_deprecated")) { - String result = parser->get_attribute_value("is_deprecated"); - constant2.is_deprecated = (result == "true" || result == "True" || result == "TRUE" || result == "1"); + constant2.is_deprecated = parser->get_attribute_value("is_deprecated").to_lower() == "true"; } if (parser->has_attribute("is_experimental")) { - String result = parser->get_attribute_value("is_experimental"); - constant2.is_experimental = (result == "true" || result == "True" || result == "TRUE" || result == "1"); + constant2.is_experimental = parser->get_attribute_value("is_experimental").to_lower() == "true"; } if (!parser->is_empty()) { parser->read(); @@ -1376,10 +1372,10 @@ static void _write_method_doc(Ref<FileAccess> f, const String &p_name, Vector<Do String additional_attributes; if (m.is_deprecated) { - additional_attributes += " is_deprecated=\"True\""; + additional_attributes += " is_deprecated=\"true\""; } if (m.is_experimental) { - additional_attributes += " is_experimental=\"True\""; + additional_attributes += " is_experimental=\"true\""; } _write_string(f, 2, "<" + p_name + " name=\"" + m.name.xml_escape() + "\"" + qualifiers + additional_attributes + ">"); @@ -1446,10 +1442,10 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String, if (!c.inherits.is_empty()) { header += " inherits=\"" + c.inherits + "\""; if (c.is_deprecated) { - header += " is_deprecated=\"True\""; + header += " is_deprecated=\"true\""; } if (c.is_experimental) { - header += " is_experimental=\"True\""; + header += " is_experimental=\"true\""; } } header += String(" version=\"") + VERSION_BRANCH + "\""; @@ -1495,10 +1491,10 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String, additional_attributes += " default=\"" + c.properties[i].default_value.xml_escape(true) + "\""; } if (c.properties[i].is_deprecated) { - additional_attributes += " is_deprecated=\"True\""; + additional_attributes += " is_deprecated=\"true\""; } if (c.properties[i].is_experimental) { - additional_attributes += " is_experimental=\"True\""; + additional_attributes += " is_experimental=\"true\""; } const DocData::PropertyDoc &p = c.properties[i]; @@ -1523,10 +1519,10 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String, String additional_attributes; if (c.constants[i].is_deprecated) { - additional_attributes += " is_deprecated=\"True\""; + additional_attributes += " is_deprecated=\"true\""; } if (c.constants[i].is_experimental) { - additional_attributes += " is_experimental=\"True\""; + additional_attributes += " is_experimental=\"true\""; } if (k.is_value_valid) { diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index fffe77f1c4..c31d13d122 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -96,6 +96,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)EditorSettings::get_singleton()->get("interface/editor/font_subpixel_positioning"); TextServer::Hinting font_hinting; + TextServer::Hinting font_mono_hinting; switch (font_hinting_setting) { case 0: // The "Auto" setting uses the setting that best matches the OS' font rendering: @@ -104,18 +105,23 @@ void editor_register_fonts(Ref<Theme> p_theme) { // - Linux has configurable font hinting, but most distributions including Ubuntu default to "Light". #ifdef MACOS_ENABLED font_hinting = TextServer::HINTING_NONE; + font_mono_hinting = TextServer::HINTING_NONE; #else font_hinting = TextServer::HINTING_LIGHT; + font_mono_hinting = TextServer::HINTING_LIGHT; #endif break; case 1: font_hinting = TextServer::HINTING_NONE; + font_mono_hinting = TextServer::HINTING_NONE; break; case 2: font_hinting = TextServer::HINTING_LIGHT; + font_mono_hinting = TextServer::HINTING_LIGHT; break; default: font_hinting = TextServer::HINTING_NORMAL; + font_mono_hinting = TextServer::HINTING_LIGHT; break; } @@ -163,7 +169,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { default_font_bold->set_fallbacks(fallbacks_bold); default_font_bold_msdf->set_fallbacks(fallbacks_bold); - Ref<FontFile> default_font_mono = load_internal_font(_font_JetBrainsMono_Regular, _font_JetBrainsMono_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning); + Ref<FontFile> default_font_mono = load_internal_font(_font_JetBrainsMono_Regular, _font_JetBrainsMono_Regular_size, font_mono_hinting, font_antialiasing, true, font_subpixel_positioning); default_font_mono->set_fallbacks(fallbacks); // Init base font configs and load custom fonts. @@ -260,7 +266,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { Ref<FontVariation> mono_fc; mono_fc.instantiate(); if (custom_font_path_source.length() > 0 && dir->file_exists(custom_font_path_source)) { - Ref<FontFile> custom_font = load_external_font(custom_font_path_source, font_hinting, font_antialiasing, true, font_subpixel_positioning); + Ref<FontFile> custom_font = load_external_font(custom_font_path_source, font_mono_hinting, font_antialiasing, true, font_subpixel_positioning); { TypedArray<Font> fallback_custom; fallback_custom.push_back(default_font_mono); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index f001887ef9..9e7314bf50 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1651,6 +1651,10 @@ void EditorInspectorArray::_panel_draw(int p_index) { void EditorInspectorArray::_panel_gui_input(Ref<InputEvent> p_event, int p_index) { ERR_FAIL_INDEX(p_index, (int)array_elements.size()); + if (read_only) { + return; + } + Ref<InputEventKey> key_ref = p_event; if (key_ref.is_valid()) { const InputEventKey &key = **key_ref; @@ -2151,7 +2155,7 @@ void EditorInspectorArray::drop_data_fw(const Point2 &p_point, const Variant &p_ } bool EditorInspectorArray::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { - if (!movable) { + if (!movable || read_only) { return false; } // First, update drawing. @@ -2271,7 +2275,9 @@ VBoxContainer *EditorInspectorArray::get_vbox(int p_index) { } } -EditorInspectorArray::EditorInspectorArray() { +EditorInspectorArray::EditorInspectorArray(bool p_read_only) { + read_only = p_read_only; + set_mouse_filter(Control::MOUSE_FILTER_STOP); odd_style.instantiate(); @@ -2297,6 +2303,7 @@ EditorInspectorArray::EditorInspectorArray() { add_button = EditorInspector::create_inspector_action_button(TTR("Add Element")); add_button->connect("pressed", callable_mp(this, &EditorInspectorArray::_add_button_pressed)); + add_button->set_disabled(read_only); vbox->add_child(add_button); control_dropping = memnew(Control); @@ -2317,6 +2324,7 @@ EditorInspectorArray::EditorInspectorArray() { new_size_spin_box->set_max(16384); new_size_spin_box->connect("value_changed", callable_mp(this, &EditorInspectorArray::_new_size_spin_box_value_changed)); new_size_spin_box->get_line_edit()->connect("text_submitted", callable_mp(this, &EditorInspectorArray::_new_size_spin_box_text_submitted)); + new_size_spin_box->set_editable(!read_only); resize_dialog_vbox->add_margin_child(TTRC("New Size:"), new_size_spin_box); vbox->connect("visibility_changed", callable_mp(this, &EditorInspectorArray::_vbox_visibility_changed)); @@ -3051,7 +3059,7 @@ void EditorInspector::update_tree() { if (p.type == Variant::NIL) { // Setup the array to use a method to create/move/delete elements. array_element_prefix = class_name_components[0]; - editor_inspector_array = memnew(EditorInspectorArray); + editor_inspector_array = memnew(EditorInspectorArray(all_read_only)); String array_label = path.contains("/") ? path.substr(path.rfind("/") + 1) : path; array_label = EditorPropertyNameProcessor::get_singleton()->process_name(property_label_string, property_name_style); @@ -3063,7 +3071,7 @@ void EditorInspector::update_tree() { // Setup the array to use the count property and built-in functions to create/move/delete elements. if (class_name_components.size() >= 2) { array_element_prefix = class_name_components[1]; - editor_inspector_array = memnew(EditorInspectorArray); + editor_inspector_array = memnew(EditorInspectorArray(all_read_only)); int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0; editor_inspector_array->setup_with_count_property(object, class_name_components[0], p.name, array_element_prefix, page, c, foldable, movable, numbered, page_size, add_button_text, swap_method); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index d634eae23f..70378d8cf2 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -333,6 +333,7 @@ class EditorInspectorArray : public EditorInspectorSection { int begin_array_index = 0; int end_array_index = 0; + bool read_only = false; bool movable = true; bool numbered = false; @@ -404,7 +405,7 @@ public: void setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "", const String &p_swap_method = ""); VBoxContainer *get_vbox(int p_index); - EditorInspectorArray(); + EditorInspectorArray(bool p_read_only); }; class EditorPaginator : public HBoxContainer { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 804722299c..8eaddcb7e1 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3932,7 +3932,7 @@ void EditorNode::edit_foreign_resource(Ref<Resource> p_resource) { InspectorDock::get_singleton()->call_deferred("edit_resource", p_resource); } -bool EditorNode::is_resource_read_only(Ref<Resource> p_resource) { +bool EditorNode::is_resource_read_only(Ref<Resource> p_resource, bool p_foreign_resources_are_writable) { ERR_FAIL_COND_V(p_resource.is_null(), false); String path = p_resource->get_path(); @@ -3944,7 +3944,11 @@ bool EditorNode::is_resource_read_only(Ref<Resource> p_resource) { // If the base resource is a packed scene, we treat it as read-only if it is not the currently edited scene. if (ResourceLoader::get_resource_type(base) == "PackedScene") { if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) { - return true; + // If we have not flagged foreign resources as writable or the base scene the resource is + // part was imported, it can be considered read-only. + if (!p_foreign_resources_are_writable || FileAccess::exists(base + ".import")) { + return true; + } } } else { // If a corresponding .import file exists for the base file, we assume it to be imported and should therefore treated as read-only. diff --git a/editor/editor_node.h b/editor/editor_node.h index df3d2ae0f8..200d68908c 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -784,7 +784,7 @@ public: void open_request(const String &p_path); void edit_foreign_resource(Ref<Resource> p_resource); - bool is_resource_read_only(Ref<Resource> p_resource); + bool is_resource_read_only(Ref<Resource> p_resource, bool p_foreign_resources_are_writable = false); bool is_changing_scene() const; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index ad84b30689..728a3b0f80 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -268,6 +268,7 @@ void EditorPropertyArray::update_property() { size_slider->set_step(1); size_slider->set_max(1000000); size_slider->set_h_size_flags(SIZE_EXPAND_FILL); + size_slider->set_read_only(is_read_only()); size_slider->connect("value_changed", callable_mp(this, &EditorPropertyArray::_length_changed)); hbox->add_child(size_slider); @@ -278,6 +279,7 @@ void EditorPropertyArray::update_property() { button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Element")); button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); button_add_item->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyArray::_add_element)); + button_add_item->set_disabled(is_read_only()); vbox->add_child(button_add_item); paginator = memnew(EditorPaginator); @@ -328,6 +330,7 @@ void EditorPropertyArray::update_property() { Button *reorder_button = memnew(Button); reorder_button->set_icon(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons"))); reorder_button->set_default_cursor_shape(Control::CURSOR_MOVE); + reorder_button->set_disabled(is_read_only()); reorder_button->connect("gui_input", callable_mp(this, &EditorPropertyArray::_reorder_button_gui_input)); reorder_button->connect("button_down", callable_mp(this, &EditorPropertyArray::_reorder_button_down).bind(i + offset)); reorder_button->connect("button_up", callable_mp(this, &EditorPropertyArray::_reorder_button_up)); @@ -358,6 +361,7 @@ void EditorPropertyArray::update_property() { prop->connect("property_changed", callable_mp(this, &EditorPropertyArray::_property_changed)); prop->connect("object_id_selected", callable_mp(this, &EditorPropertyArray::_object_id_selected)); prop->set_h_size_flags(SIZE_EXPAND_FILL); + prop->set_read_only(is_read_only()); hbox->add_child(prop); bool is_untyped_array = array.get_type() == Variant::ARRAY && subtype == Variant::NIL; @@ -366,10 +370,12 @@ void EditorPropertyArray::update_property() { Button *edit = memnew(Button); edit->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); hbox->add_child(edit); + edit->set_disabled(is_read_only()); edit->connect("pressed", callable_mp(this, &EditorPropertyArray::_change_type).bind(edit, i + offset)); } else { Button *remove = memnew(Button); remove->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); + remove->set_disabled(is_read_only()); remove->connect("pressed", callable_mp(this, &EditorPropertyArray::_remove_pressed).bind(i + offset)); hbox->add_child(remove); } @@ -409,6 +415,10 @@ void EditorPropertyArray::_button_draw() { } bool EditorPropertyArray::_is_drop_valid(const Dictionary &p_drag_data) const { + if (is_read_only()) { + return false; + } + String allowed_type = Variant::get_type_name(subtype); // When the subtype is of type Object, an additional subtype may be specified in the hint string @@ -609,7 +619,7 @@ void EditorPropertyArray::setup(Variant::Type p_array_type, const String &p_hint } void EditorPropertyArray::_reorder_button_gui_input(const Ref<InputEvent> &p_event) { - if (reorder_from_index < 0) { + if (reorder_from_index < 0 || is_read_only()) { return; } @@ -646,6 +656,10 @@ void EditorPropertyArray::_reorder_button_gui_input(const Ref<InputEvent> &p_eve } void EditorPropertyArray::_reorder_button_down(int p_index) { + if (is_read_only()) { + return; + } + reorder_from_index = p_index; reorder_to_index = p_index; reorder_selected_element_hbox = Object::cast_to<HBoxContainer>(property_vbox->get_child(p_index % page_length)); @@ -656,6 +670,10 @@ void EditorPropertyArray::_reorder_button_down(int p_index) { } void EditorPropertyArray::_reorder_button_up() { + if (is_read_only()) { + return; + } + if (reorder_from_index != reorder_to_index) { // Move the element. Variant array = object->get_array(); @@ -1097,6 +1115,10 @@ void EditorPropertyDictionary::update_property() { } } + ERR_FAIL_COND(!prop); + + prop->set_read_only(is_read_only()); + if (i == amount) { PanelContainer *pc = memnew(PanelContainer); property_vbox->add_child(pc); @@ -1135,6 +1157,7 @@ void EditorPropertyDictionary::update_property() { prop->set_h_size_flags(SIZE_EXPAND_FILL); Button *edit = memnew(Button); edit->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); + edit->set_disabled(is_read_only()); hbox->add_child(edit); edit->connect("pressed", callable_mp(this, &EditorPropertyDictionary::_change_type).bind(edit, change_index)); @@ -1143,6 +1166,7 @@ void EditorPropertyDictionary::update_property() { if (i == amount + 1) { button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Key/Value Pair")); button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); + button_add_item->set_disabled(is_read_only()); button_add_item->connect("pressed", callable_mp(this, &EditorPropertyDictionary::_add_key_value)); add_vbox->add_child(button_add_item); } diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 5346052f4d..84cb085551 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -186,7 +186,7 @@ void EditorResourcePicker::_update_menu_items() { // Add options for changing existing value of the resource. if (edited_resource.is_valid()) { // Determine if the edited resource is part of another scene (foreign) which was imported - bool is_edited_resource_foreign_import = EditorNode::get_singleton()->is_resource_read_only(edited_resource); + bool is_edited_resource_foreign_import = EditorNode::get_singleton()->is_resource_read_only(edited_resource, true); // If the resource is determined to be foreign and imported, change the menu entry's description to 'inspect' rather than 'edit' // since will only be able to view its properties in read-only mode. diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index ae21aad337..d0a1ddafa1 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -984,11 +984,17 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) { } void SpriteFramesEditor::edit(SpriteFrames *p_frames) { - if (frames == p_frames) { + bool new_read_only_state = false; + if (p_frames) { + new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_frames); + } + + if (frames == p_frames && new_read_only_state == read_only) { return; } frames = p_frames; + read_only = new_read_only_state; if (p_frames) { if (!p_frames->has_animation(edited_anim)) { @@ -1009,6 +1015,20 @@ void SpriteFramesEditor::edit(SpriteFrames *p_frames) { } else { hide(); } + + new_anim->set_disabled(read_only); + remove_anim->set_disabled(read_only); + anim_speed->set_editable(!read_only); + anim_loop->set_disabled(read_only); + load->set_disabled(read_only); + load_sheet->set_disabled(read_only); + copy->set_disabled(read_only); + paste->set_disabled(read_only); + empty->set_disabled(read_only); + empty2->set_disabled(read_only); + move_up->set_disabled(read_only); + move_down->set_disabled(read_only); + _delete->set_disabled(read_only); } void SpriteFramesEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { @@ -1016,6 +1036,10 @@ void SpriteFramesEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { } Variant SpriteFramesEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + if (read_only) { + return false; + } + if (!frames->has_animation(edited_anim)) { return false; } @@ -1038,6 +1062,10 @@ Variant SpriteFramesEditor::get_drag_data_fw(const Point2 &p_point, Control *p_f } bool SpriteFramesEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + if (read_only) { + return false; + } + Dictionary d = p_data; if (!d.has("type")) { @@ -1169,6 +1197,7 @@ SpriteFramesEditor::SpriteFramesEditor() { remove_anim->set_flat(true); remove_anim->set_tooltip_text(TTR("Remove Animation")); hbc_animlist->add_child(remove_anim); + remove_anim->set_disabled(true); remove_anim->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_remove)); anim_search_box = memnew(LineEdit); diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index f2530b732f..092f556c63 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -57,6 +57,8 @@ class SpriteFramesEditor : public HSplitContainer { }; int dominant_param = PARAM_FRAME_COUNT; + bool read_only = false; + Button *load = nullptr; Button *load_sheet = nullptr; Button *_delete = nullptr; diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index f11874a994..69a2670418 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -2164,9 +2164,11 @@ void ProjectManager::_open_selected_projects_ask() { return; } + const Size2i popup_min_width = Size2i(600.0 * EDSCALE, 0); + if (selected_list.size() > 1) { - multi_open_ask->set_text(TTR("Are you sure to open more than one project?")); - multi_open_ask->popup_centered(); + multi_open_ask->set_text(vformat(TTR("You requested to open %d projects in parallel. Do you confirm?\nNote that usual checks for engine version compatibility will be bypassed."), selected_list.size())); + multi_open_ask->popup_centered(popup_min_width); return; } @@ -2175,8 +2177,7 @@ void ProjectManager::_open_selected_projects_ask() { return; } - // Update the project settings or don't open - const String conf = project.path.path_join("project.godot"); + // Update the project settings or don't open. const int config_version = project.version; PackedStringArray unsupported_features = project.unsupported_features; @@ -2186,26 +2187,30 @@ void ProjectManager::_open_selected_projects_ask() { ask_update_settings->get_ok_button()->set_text("OK"); - // Check if the config_version property was empty or 0 + // Check if the config_version property was empty or 0. if (config_version == 0) { - ask_update_settings->set_text(vformat(TTR("The following project settings file does not specify the version of Godot through which it was created.\n\n%s\n\nIf you proceed with opening it, it will be converted to Godot's current configuration file format.\nWarning: You won't be able to open the project with previous versions of the engine anymore."), conf)); - ask_update_settings->popup_centered(); + ask_update_settings->set_text(vformat(TTR("The selected project \"%s\" does not specify its supported Godot version in its configuration file (\"project.godot\").\n\nProject path: %s\n\nIf you proceed with opening it, it will be converted to Godot's current configuration file format.\n\nWarning: You won't be able to open the project with previous versions of the engine anymore."), project.project_name, project.path)); + ask_update_settings->popup_centered(popup_min_width); return; } - // Check if we need to convert project settings from an earlier engine version + // Check if we need to convert project settings from an earlier engine version. if (config_version < ProjectSettings::CONFIG_VERSION) { if (config_version == GODOT4_CONFIG_VERSION - 1 && ProjectSettings::CONFIG_VERSION == GODOT4_CONFIG_VERSION) { // Conversion from Godot 3 to 4. full_convert_button->show(); + ask_update_settings->set_text(vformat(TTR("The selected project \"%s\" was generated by Godot 3.x, and needs to be converted for Godot 4.x.\n\nProject path: %s\n\nYou have three options:\n- Convert only the configuration file (\"project.godot\"). Use this to open the project without attempting to convert its scenes, resources and scripts.\n- Convert the entire project including its scenes, resources and scripts (recommended if you are upgrading).\n- Do nothing and go back.\n\nWarning: If you select a conversion option, you won't be able to open the project with previous versions of the engine anymore."), project.project_name, project.path)); + ask_update_settings->get_ok_button()->set_text(TTR("Convert project.godot Only")); + } else { + ask_update_settings->set_text(vformat(TTR("The selected project \"%s\" was generated by an older engine version, and needs to be converted for this version.\n\nProject path: %s\n\nDo you want to convert it?\n\nWarning: You won't be able to open the project with previous versions of the engine anymore."), project.project_name, project.path)); + ask_update_settings->get_ok_button()->set_text(TTR("Convert project.godot")); } - ask_update_settings->set_text(vformat(TTR("The following project settings file was generated by an older engine version, and needs to be converted for this version:\n\n%s\n\nDo you want to convert it? You can also convert the entire project (recommended if you are upgrading).\nWarning: You won't be able to open the project with previous versions of the engine anymore."), conf)); - ask_update_settings->get_ok_button()->set_text(TTR("Convert project.godot")); - ask_update_settings->popup_centered(); + ask_update_settings->popup_centered(popup_min_width); + ask_update_settings->get_cancel_button()->grab_focus(); // To prevent accidents. return; } - // Check if the file was generated by a newer, incompatible engine version + // Check if the file was generated by a newer, incompatible engine version. if (config_version > ProjectSettings::CONFIG_VERSION) { - dialog_error->set_text(vformat(TTR("Can't open project at '%s'.") + "\n" + TTR("The project settings were created by a newer engine version, whose settings are not compatible with this version."), project.path)); - dialog_error->popup_centered(); + dialog_error->set_text(vformat(TTR("Can't open project \"%s\" at the following path:\n\n%s\n\nThe project settings were created by a newer engine version, whose settings are not compatible with this version."), project.project_name, project.path)); + dialog_error->popup_centered(popup_min_width); return; } // Check if the project is using features not supported by this build of Godot. @@ -2234,14 +2239,20 @@ void ProjectManager::_open_selected_projects_ask() { warning_message += TTR("Open anyway? Project will be modified."); ask_update_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); ask_update_settings->set_text(warning_message); - ask_update_settings->popup_centered(); + ask_update_settings->popup_centered(popup_min_width); return; } - // Open if the project is up-to-date + // Open if the project is up-to-date. _open_selected_projects(); } +void ProjectManager::_full_convert_button_pressed() { + ask_update_settings->hide(); + ask_full_convert_dialog->popup_centered(Size2i(600.0 * EDSCALE, 0)); + ask_full_convert_dialog->get_cancel_button()->grab_focus(); +} + void ProjectManager::_perform_full_project_conversion() { Vector<ProjectList::Item> selected_list = _project_list->get_selected_projects(); if (selected_list.is_empty()) { @@ -2869,21 +2880,18 @@ ProjectManager::ProjectManager() { add_child(multi_scan_ask); ask_update_settings = memnew(ConfirmationDialog); + ask_update_settings->set_autowrap(true); ask_update_settings->get_ok_button()->connect("pressed", callable_mp(this, &ProjectManager::_confirm_update_settings)); - full_convert_button = ask_update_settings->add_button("Perform Full Project Conversion", false); + full_convert_button = ask_update_settings->add_button("Convert Full Project", !GLOBAL_GET("gui/common/swap_cancel_ok")); + full_convert_button->connect("pressed", callable_mp(this, &ProjectManager::_full_convert_button_pressed)); add_child(ask_update_settings); ask_full_convert_dialog = memnew(ConfirmationDialog); ask_full_convert_dialog->set_autowrap(true); - ask_full_convert_dialog->set_text(TTR(R"(This option will perform full project conversion, updating scenes and scripts from Godot 3.x to work in Godot 4.0. Note that this is a best-effort conversion, i.e. it makes upgrading the project easier, but it will not open out-of-the-box and will still require manual adjustments. - -IMPORTANT: Make sure to backup your project before converting, as this operation makes it impossible to open in older versions of Godot.)")); + ask_full_convert_dialog->set_text(TTR("This option will perform full project conversion, updating scenes, resources and scripts from Godot 3.x to work in Godot 4.0.\n\nNote that this is a best-effort conversion, i.e. it makes upgrading the project easier, but it will not open out-of-the-box and will still require manual adjustments.\n\nIMPORTANT: Make sure to backup your project before converting, as this operation makes it impossible to open it in older versions of Godot.")); ask_full_convert_dialog->connect("confirmed", callable_mp(this, &ProjectManager::_perform_full_project_conversion)); add_child(ask_full_convert_dialog); - full_convert_button->connect("pressed", callable_mp((Window *)ask_update_settings, &ConfirmationDialog::hide)); - full_convert_button->connect("pressed", callable_mp((Window *)ask_full_convert_dialog, &ConfirmationDialog::popup_centered_ratio).bind(0.2)); - npdialog = memnew(ProjectDialog); npdialog->connect("projects_updated", callable_mp(this, &ProjectManager::_on_projects_updated)); npdialog->connect("project_created", callable_mp(this, &ProjectManager::_on_project_created)); diff --git a/editor/project_manager.h b/editor/project_manager.h index 0831a63e68..7c05429dde 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -108,6 +108,7 @@ class ProjectManager : public Control { void _run_project_confirm(); void _open_selected_projects(); void _open_selected_projects_ask(); + void _full_convert_button_pressed(); void _perform_full_project_conversion(); void _import_project(); void _new_project(); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets index bff9760b32..859ea52c93 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets @@ -17,7 +17,7 @@ <!-- C# source generators --> <ItemGroup Condition=" '$(DisableImplicitGodotGeneratorReferences)' != 'true' "> - <PackageReference Include="Godot.SourceGenerators" Version="$(PackageFloatingVersion_Godot)" /> + <PackageReference Include="Godot.SourceGenerators" Version="$(PackageVersion_Godot_SourceGenerators)" /> </ItemGroup> <!-- Godot API references --> diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets index 4baae77b34..37bd4a0be0 100644 --- a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets +++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets @@ -21,10 +21,13 @@ Outputs="$(GeneratedGodotNupkgsVersionsFile)"> <PropertyGroup> <GenerateGodotNupkgsVersionsCode><![CDATA[ -namespace $(RootNamespace) { - public class GeneratedGodotNupkgsVersions { +namespace $(RootNamespace) +{ + public class GeneratedGodotNupkgsVersions + { public const string GodotNETSdk = "$(PackageVersion_Godot_NET_Sdk)"%3b public const string GodotSourceGenerators = "$(PackageVersion_Godot_SourceGenerators)"%3b + public const string GodotSharp = "$(PackageVersion_GodotSharp)"%3b } } ]]></GenerateGodotNupkgsVersionsCode> diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs index 7bce53308c..b437c7e742 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/DotNetFinder.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Runtime.InteropServices; using JetBrains.Annotations; using OS = GodotTools.Utils.OS; @@ -16,6 +17,23 @@ namespace GodotTools.Build // In the future, this method may do more than just search in PATH. We could look in // known locations or use Godot's linked nethost to search from the hostfxr location. + if (OS.IsMacOS) + { + if (RuntimeInformation.OSArchitecture == Architecture.X64) + { + string dotnet_x64 = "/usr/local/share/dotnet/x64/dotnet"; // Look for x64 version, when running under Rosetta 2. + if (File.Exists(dotnet_x64)) + { + return dotnet_x64; + } + } + string dotnet = "/usr/local/share/dotnet/dotnet"; // Look for native version. + if (File.Exists(dotnet)) + { + return dotnet; + } + } + return OS.PathWhich("dotnet"); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs index d2e0e128b5..fe309b8102 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs @@ -22,71 +22,13 @@ namespace GodotTools.Build public static string GodotFallbackFolderPath => Path.Combine(GodotSharpDirs.MonoUserDir, "GodotNuGetFallbackFolder"); - private static void AddFallbackFolderToNuGetConfig(string nuGetConfigPath, string name, string path) - { - var xmlDoc = new XmlDocument(); - xmlDoc.Load(nuGetConfigPath); - - const string nuGetConfigRootName = "configuration"; - - var rootNode = xmlDoc.DocumentElement; - - if (rootNode == null) - { - // No root node, create it - rootNode = xmlDoc.CreateElement(nuGetConfigRootName); - xmlDoc.AppendChild(rootNode); - - // Since this can be considered pretty much a new NuGet.Config, add the default nuget.org source as well - XmlElement nugetOrgSourceEntry = xmlDoc.CreateElement("add"); - nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = "nuget.org"; - nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = - "https://api.nuget.org/v3/index.json"; - nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("protocolVersion")).Value = "3"; - rootNode.AppendChild(xmlDoc.CreateElement("packageSources")).AppendChild(nugetOrgSourceEntry); - } - else - { - // Check that the root node is the expected one - if (rootNode.Name != nuGetConfigRootName) - throw new FormatException("Invalid root Xml node for NuGet.Config. " + - $"Expected '{nuGetConfigRootName}' got '{rootNode.Name}'."); - } - - var fallbackFoldersNode = rootNode["fallbackPackageFolders"] ?? - rootNode.AppendChild(xmlDoc.CreateElement("fallbackPackageFolders")); - - // Check if it already has our fallback package folder - for (var xmlNode = fallbackFoldersNode.FirstChild; xmlNode != null; xmlNode = xmlNode.NextSibling) - { - if (xmlNode.NodeType != XmlNodeType.Element) - continue; - - var xmlElement = (XmlElement)xmlNode; - if (xmlElement.Name == "add" && - xmlElement.Attributes["key"]?.Value == name && - xmlElement.Attributes["value"]?.Value == path) - { - return; - } - } - - XmlElement newEntry = xmlDoc.CreateElement("add"); - newEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = name; - newEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = path; - - fallbackFoldersNode.AppendChild(newEntry); - - xmlDoc.Save(nuGetConfigPath); - } - /// <summary> - /// Returns all the paths where the user NuGet.Config files can be found. + /// Returns all the paths where the Godot.Offline.Config files can be found. /// Does not determine whether the returned files exist or not. /// </summary> - private static string[] GetAllUserNuGetConfigFilePaths() + private static string[] GetAllGodotNuGetConfigFilePaths() { - // Where to find 'NuGet/NuGet.Config': + // Where to find 'NuGet/config/Godot.Offline.Config': // // - Mono/.NETFramework (standalone NuGet): // Uses Environment.SpecialFolder.ApplicationData @@ -98,10 +40,12 @@ namespace GodotTools.Build string applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + const string configFileName = "Godot.Offline.Config"; + if (Utils.OS.IsWindows) { // %APPDATA% for both - return new[] { Path.Combine(applicationData, "NuGet", "NuGet.Config") }; + return new[] { Path.Combine(applicationData, "NuGet", "config", configFileName) }; } var paths = new string[2]; @@ -111,20 +55,20 @@ namespace GodotTools.Build string dotnetCliHome = Environment.GetEnvironmentVariable("DOTNET_CLI_HOME"); if (!string.IsNullOrEmpty(dotnetCliHome)) { - paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "NuGet.Config"); + paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "config", configFileName); } else { string home = Environment.GetEnvironmentVariable("HOME"); if (string.IsNullOrEmpty(home)) throw new InvalidOperationException("Required environment variable 'HOME' is not set."); - paths[0] = Path.Combine(home, ".nuget", "NuGet", "NuGet.Config"); + paths[0] = Path.Combine(home, ".nuget", "NuGet", "config", configFileName); } // Mono/.NETFramework (standalone NuGet) // ApplicationData is $HOME/.config on Linux/macOS - paths[1] = Path.Combine(applicationData, "NuGet", "NuGet.Config"); + paths[1] = Path.Combine(applicationData, "NuGet", "config", configFileName); return paths; } @@ -141,28 +85,26 @@ namespace GodotTools.Build // The nuspec is not lower case inside the nupkg but must be made lower case when extracted. /// <summary> - /// Adds the specified fallback folder to the user NuGet.Config files, + /// Adds the specified fallback folder to the Godot.Offline.Config files, /// for both standalone NuGet (Mono/.NETFramework) and dotnet CLI NuGet. /// </summary> - public static void AddFallbackFolderToUserNuGetConfigs(string name, string path) + public static void AddFallbackFolderToGodotNuGetConfigs(string name, string path) { - foreach (string nuGetConfigPath in GetAllUserNuGetConfigFilePaths()) + // Make sure the fallback folder exists to avoid error: + // MSB4018: The "ResolvePackageAssets" task failed unexpectedly. + System.IO.Directory.CreateDirectory(path); + + foreach (string nuGetConfigPath in GetAllGodotNuGetConfigFilePaths()) { - if (!System.IO.File.Exists(nuGetConfigPath)) - { - // It doesn't exist, so we create a default one - const string defaultConfig = @"<?xml version=""1.0"" encoding=""utf-8""?> + string defaultConfig = @$"<?xml version=""1.0"" encoding=""utf-8""?> <configuration> - <packageSources> - <add key=""nuget.org"" value=""https://api.nuget.org/v3/index.json"" protocolVersion=""3"" /> - </packageSources> + <fallbackPackageFolders> + <add key=""{name}"" value=""{path}"" /> + </fallbackPackageFolders> </configuration> "; - System.IO.Directory.CreateDirectory(Path.GetDirectoryName(nuGetConfigPath)); - System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM - } - - AddFallbackFolderToNuGetConfig(nuGetConfigPath, name, path); + System.IO.Directory.CreateDirectory(Path.GetDirectoryName(nuGetConfigPath)); + System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM } } @@ -189,6 +131,7 @@ namespace GodotTools.Build string destDir = Path.Combine(fallbackFolder, packageIdLower, packageVersionLower); string nupkgDestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg"); string nupkgSha512DestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg.sha512"); + string nupkgMetadataDestPath = Path.Combine(destDir, ".nupkg.metadata"); if (File.Exists(nupkgDestPath) && File.Exists(nupkgSha512DestPath)) return; // Already added (for speed we don't check if every file is properly extracted) @@ -197,12 +140,18 @@ namespace GodotTools.Build // Generate .nupkg.sha512 file - using (var alg = SHA512.Create()) - { - alg.ComputeHash(File.ReadAllBytes(nupkgPath)); - string base64Hash = Convert.ToBase64String(alg.Hash); - File.WriteAllText(nupkgSha512DestPath, base64Hash); - } + byte[] hash = SHA512.HashData(File.ReadAllBytes(nupkgPath)); + string base64Hash = Convert.ToBase64String(hash); + File.WriteAllText(nupkgSha512DestPath, base64Hash); + + // Generate .nupkg.metadata file + // Spec: https://github.com/NuGet/Home/wiki/Nupkg-Metadata-File + + File.WriteAllText(nupkgMetadataDestPath, @$"{{ + ""version"": 2, + ""contentHash"": ""{base64Hash}"", + ""source"": null +}}"); // Extract nupkg ExtractNupkg(destDir, nupkgPath, packageId, packageVersion); @@ -251,7 +200,7 @@ namespace GodotTools.Build entryFullName.EndsWith(".nupkg.sha512", StringComparison.OrdinalIgnoreCase) || entryFullName.EndsWith(".nupkg.metadata", StringComparison.OrdinalIgnoreCase) || // Nuspec at root level. We already extracted it previously but in lower case. - entryFullName.IndexOf('/') == -1 && entryFullName.EndsWith(".nuspec")) + !entryFullName.Contains('/') && entryFullName.EndsWith(".nuspec")) { continue; } @@ -297,6 +246,8 @@ namespace GodotTools.Build { ("Godot.NET.Sdk", GeneratedGodotNupkgsVersions.GodotNETSdk), ("Godot.SourceGenerators", GeneratedGodotNupkgsVersions.GodotSourceGenerators), + ("GodotSharp", GeneratedGodotNupkgsVersions.GodotSharp), + ("GodotSharpEditor", GeneratedGodotNupkgsVersions.GodotSharp), }; } } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 1cfaea3ec9..89364d1c02 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -123,7 +123,7 @@ namespace GodotTools try { string fallbackFolder = NuGetUtils.GodotFallbackFolderPath; - NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, + NuGetUtils.AddFallbackFolderToGodotNuGetConfigs(NuGetUtils.GodotFallbackFolderName, fallbackFolder); NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder); } @@ -497,7 +497,7 @@ namespace GodotTools try { // At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included - NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, + NuGetUtils.AddFallbackFolderToGodotNuGetConfigs(NuGetUtils.GodotFallbackFolderName, NuGetUtils.GodotFallbackFolderPath); } catch (Exception e) diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index c27bb959fe..ce4ac9b796 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -2229,6 +2229,26 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf // Generate signal { + if (p_isignal.is_deprecated) { + if (p_isignal.deprecation_message.is_empty()) { + WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'."); + } + + p_output.append(MEMBER_BEGIN "[Obsolete(\""); + p_output.append(p_isignal.deprecation_message); + p_output.append("\")]"); + } + + String delegate_name = p_isignal.proxy_name; + delegate_name += "EventHandler"; // Delegate name is [SignalName]EventHandler + + // Generate delegate + p_output.append(MEMBER_BEGIN "public delegate void "); + p_output.append(delegate_name); + p_output.append("("); + p_output.append(arguments_sig); + p_output.append(");\n"); + if (p_isignal.method_doc && p_isignal.method_doc->description.size()) { String xml_summary = bbcode_to_xml(fix_doc_description(p_isignal.method_doc->description), &p_itype); Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>(); @@ -2247,25 +2267,11 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf } if (p_isignal.is_deprecated) { - if (p_isignal.deprecation_message.is_empty()) { - WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'."); - } - p_output.append(MEMBER_BEGIN "[Obsolete(\""); p_output.append(p_isignal.deprecation_message); p_output.append("\")]"); } - String delegate_name = p_isignal.proxy_name; - delegate_name += "EventHandler"; // Delegate name is [SignalName]EventHandler - - // Generate delegate - p_output.append(MEMBER_BEGIN "public delegate void "); - p_output.append(delegate_name); - p_output.append("("); - p_output.append(arguments_sig); - p_output.append(");\n"); - // TODO: // Could we assume the StringName instance of signal name will never be freed (it's stored in ClassDB) before the managed world is unloaded? // If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here. diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp index 9d0864e4d5..d46972ffb6 100644 --- a/modules/multiplayer/multiplayer_spawner.cpp +++ b/modules/multiplayer/multiplayer_spawner.cpp @@ -91,9 +91,6 @@ void MultiplayerSpawner::add_spawnable_scene(const String &p_path) { sc.path = p_path; if (Engine::get_singleton()->is_editor_hint()) { ERR_FAIL_COND(!FileAccess::exists(p_path)); - } else { - sc.cache = ResourceLoader::load(p_path); - ERR_FAIL_COND_MSG(sc.cache.is_null(), "Invalid spawnable scene: " + p_path); } spawnable_scenes.push_back(sc); } @@ -271,9 +268,12 @@ const Variant MultiplayerSpawner::get_spawn_argument(const ObjectID &p_id) const Node *MultiplayerSpawner::instantiate_scene(int p_id) { ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!"); ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_id, spawnable_scenes.size(), nullptr); - Ref<PackedScene> scene = spawnable_scenes[p_id].cache; - ERR_FAIL_COND_V(scene.is_null(), nullptr); - return scene->instantiate(); + SpawnableScene &sc = spawnable_scenes[p_id]; + if (sc.cache.is_null()) { + sc.cache = ResourceLoader::load(sc.path); + } + ERR_FAIL_COND_V_MSG(sc.cache.is_null(), nullptr, "Invalid spawnable scene: " + sc.path); + return sc.cache->instantiate(); } Node *MultiplayerSpawner::instantiate_custom(const Variant &p_data) { diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp index cfb8e0cd42..f989fc45a5 100644 --- a/modules/navigation/navigation_mesh_generator.cpp +++ b/modules/navigation/navigation_mesh_generator.cpp @@ -209,6 +209,9 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans for (uint32_t shape_owner : shape_owners) { const int shape_count = static_body->shape_owner_get_shape_count(shape_owner); for (int i = 0; i < shape_count; i++) { + if (static_body->is_shape_owner_disabled(i)) { + continue; + } Ref<Shape3D> s = static_body->shape_owner_get_shape(shape_owner, i); if (s.is_null()) { continue; diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index a14170525d..237215c198 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1496,7 +1496,7 @@ void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) { IDC_HELP }; - if (cursors[p_shape] != nullptr) { + if (cursors_cache.has(p_shape)) { SetCursor(cursors[p_shape]); } else { SetCursor(LoadCursor(hInstance, win_cursors[p_shape])); @@ -1509,55 +1509,6 @@ DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const { return cursor_shape; } -void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) { - // Get the system display DC. - HDC hDC = GetDC(nullptr); - - // Create helper DC. - HDC hMainDC = CreateCompatibleDC(hDC); - HDC hAndMaskDC = CreateCompatibleDC(hDC); - HDC hXorMaskDC = CreateCompatibleDC(hDC); - - // Get the dimensions of the source bitmap. - BITMAP bm; - GetObject(hSourceBitmap, sizeof(BITMAP), &bm); - - // Create the mask bitmaps. - hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // Color. - hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // Color. - - // Release the system display DC. - ReleaseDC(nullptr, hDC); - - // Select the bitmaps to helper DC. - HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap); - HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap); - HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap); - - // Assign the monochrome AND mask bitmap pixels so that the pixels of the source bitmap - // with 'clrTransparent' will be white pixels of the monochrome bitmap. - SetBkColor(hMainDC, clrTransparent); - BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY); - - // Assign the color XOR mask bitmap pixels so that the pixels of the source bitmap - // with 'clrTransparent' will be black and rest the pixels same as corresponding - // pixels of the source bitmap. - SetBkColor(hXorMaskDC, RGB(0, 0, 0)); - SetTextColor(hXorMaskDC, RGB(255, 255, 255)); - BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY); - BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND); - - // Deselect bitmaps from the helper DC. - SelectObject(hMainDC, hOldMainBitmap); - SelectObject(hAndMaskDC, hOldAndMaskBitmap); - SelectObject(hXorMaskDC, hOldXorMaskBitmap); - - // Delete the helper DC. - DeleteDC(hXorMaskDC); - DeleteDC(hAndMaskDC); - DeleteDC(hMainDC); -} - void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { _THREAD_SAFE_METHOD_ @@ -1610,8 +1561,26 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor UINT image_size = texture_size.width * texture_size.height; // Create the BITMAP with alpha channel. - COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size); - + COLORREF *buffer = nullptr; + + BITMAPV5HEADER bi; + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = texture_size.width; + bi.bV5Height = -texture_size.height; + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5RedMask = 0x00ff0000; + bi.bV5GreenMask = 0x0000ff00; + bi.bV5BlueMask = 0x000000ff; + bi.bV5AlphaMask = 0xff000000; + + HDC dc = GetDC(nullptr); + HBITMAP bitmap = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS, reinterpret_cast<void **>(&buffer), nullptr, 0); + HBITMAP mask = CreateBitmap(texture_size.width, texture_size.height, 1, 1, nullptr); + + bool fully_transparent = true; for (UINT index = 0; index < image_size; index++) { int row_index = floor(index / texture_size.width) + atlas_rect.position.y; int column_index = (index % int(texture_size.width)) + atlas_rect.position.x; @@ -1620,39 +1589,28 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor column_index = MIN(column_index, atlas_rect.size.width - 1); row_index = MIN(row_index, atlas_rect.size.height - 1); } + const Color &c = image->get_pixel(column_index, row_index); + fully_transparent = fully_transparent && (c.a == 0.f); - *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32(); - } - - // Using 4 channels, so 4 * 8 bits. - HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer); - COLORREF clrTransparent = -1; - - // Create the AND and XOR masks for the bitmap. - HBITMAP hAndMask = nullptr; - HBITMAP hXorMask = nullptr; - - GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask); - - if (nullptr == hAndMask || nullptr == hXorMask) { - memfree(buffer); - DeleteObject(bitmap); - return; + *(buffer + index) = c.to_argb32(); } // Finally, create the icon. - ICONINFO iconinfo; - iconinfo.fIcon = FALSE; - iconinfo.xHotspot = p_hotspot.x; - iconinfo.yHotspot = p_hotspot.y; - iconinfo.hbmMask = hAndMask; - iconinfo.hbmColor = hXorMask; - if (cursors[p_shape]) { DestroyIcon(cursors[p_shape]); } - cursors[p_shape] = CreateIconIndirect(&iconinfo); + if (fully_transparent) { + cursors[p_shape] = nullptr; + } else { + ICONINFO iconinfo; + iconinfo.fIcon = FALSE; + iconinfo.xHotspot = p_hotspot.x; + iconinfo.yHotspot = p_hotspot.y; + iconinfo.hbmMask = mask; + iconinfo.hbmColor = bitmap; + cursors[p_shape] = CreateIconIndirect(&iconinfo); + } Vector<Variant> params; params.push_back(p_cursor); @@ -1665,17 +1623,15 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor } } - DeleteObject(hAndMask); - DeleteObject(hXorMask); - - memfree(buffer); + DeleteObject(mask); DeleteObject(bitmap); + ReleaseDC(nullptr, dc); } else { // Reset to default system cursor. if (cursors[p_shape]) { DestroyIcon(cursors[p_shape]); - cursors[p_shape] = nullptr; } + cursors[p_shape] = nullptr; CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index b0ad7e36c0..fd64a02020 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -311,8 +311,6 @@ class DisplayServerWindows : public DisplayServer { String tablet_driver; Vector<String> tablet_drivers; - void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap); - enum { KEY_EVENT_BUFFER_SIZE = 512 }; diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 69512903b4..5a0236268b 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -190,6 +190,14 @@ void GraphEditMinimap::_adjust_graph_scroll(const Vector2 &p_offset) { ge->set_scroll_ofs(p_offset + graph_offset - camera_size / 2); } +TypedArray<String> GraphEdit::get_configuration_warnings() const { + TypedArray<String> warnings = Control::get_configuration_warnings(); + + warnings.push_back(RTR("Please be aware that GraphEdit and GraphNode will undergo extensive refactoring in a future beta version involving compatibility-breaking API changes.")); + + return warnings; +} + Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) { if (is_node_connected(p_from, p_from_port, p_to, p_to_port)) { return OK; diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 9371ed3df4..0fe9e7c555 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -287,6 +287,8 @@ protected: GDVIRTUAL4R(bool, _is_node_hover_valid, StringName, int, StringName, int); public: + TypedArray<String> get_configuration_warnings() const override; + Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); bool is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 448ff74a53..e47bfef576 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -156,17 +156,7 @@ Material::~Material() { bool ShaderMaterial::_set(const StringName &p_name, const Variant &p_value) { if (shader.is_valid()) { - StringName pr = shader->remap_uniform(p_name); - if (!pr) { - String n = p_name; - if (n.find("shader_parameter/") == 0) { //backwards compatibility - pr = n.replace_first("shader_parameter/", ""); - } else if (n.find("shader_uniform/") == 0) { //backwards compatibility - pr = n.replace_first("shader_uniform/", ""); - } else if (n.find("param/") == 0) { //backwards compatibility - pr = n.substr(6, n.length()); - } - } + StringName pr = shader->remap_parameter(p_name); if (pr) { set_shader_parameter(pr, p_value); return true; @@ -178,25 +168,9 @@ bool ShaderMaterial::_set(const StringName &p_name, const Variant &p_value) { bool ShaderMaterial::_get(const StringName &p_name, Variant &r_ret) const { if (shader.is_valid()) { - StringName pr = shader->remap_uniform(p_name); - if (!pr) { - String n = p_name; - if (n.find("shader_parameter/") == 0) { //backwards compatibility - pr = n.replace_first("shader_parameter/", ""); - } else if (n.find("shader_uniform/") == 0) { //backwards compatibility - pr = n.replace_first("shader_uniform/", ""); - } else if (n.find("param/") == 0) { //backwards compatibility - pr = n.substr(6, n.length()); - } - } - + StringName pr = shader->remap_parameter(p_name); if (pr) { - HashMap<StringName, Variant>::ConstIterator E = param_cache.find(pr); - if (E) { - r_ret = E->value; - } else { - r_ret = Variant(); - } + r_ret = get_shader_parameter(pr); return true; } } @@ -238,6 +212,7 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { PropertyInfo info; info.usage = PROPERTY_USAGE_GROUP; info.name = last_group.capitalize(); + info.hint_string = "shader_parameter/"; List<PropertyInfo> none_subgroup; none_subgroup.push_back(info); @@ -252,6 +227,7 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { PropertyInfo info; info.usage = PROPERTY_USAGE_SUBGROUP; info.name = last_subgroup.capitalize(); + info.hint_string = "shader_parameter/"; List<PropertyInfo> subgroup; subgroup.push_back(info); @@ -271,12 +247,13 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { PropertyInfo info; info.usage = PROPERTY_USAGE_GROUP; - info.name = "Shader Param"; + info.name = "Shader Parameters"; + info.hint_string = "shader_parameter/"; groups["<None>"]["<None>"].push_back(info); } PropertyInfo info = E->get(); - info.name = info.name; + info.name = "shader_parameter/" + info.name; groups[last_group][last_subgroup].push_back(info); } @@ -303,7 +280,7 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { bool ShaderMaterial::_property_can_revert(const StringName &p_name) const { if (shader.is_valid()) { - StringName pr = shader->remap_uniform(p_name); + StringName pr = shader->remap_parameter(p_name); if (pr) { Variant default_value = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), pr); Variant current_value; @@ -316,7 +293,7 @@ bool ShaderMaterial::_property_can_revert(const StringName &p_name) const { bool ShaderMaterial::_property_get_revert(const StringName &p_name, Variant &r_property) const { if (shader.is_valid()) { - StringName pr = shader->remap_uniform(p_name); + StringName pr = shader->remap_parameter(p_name); if (pr) { r_property = RenderingServer::get_singleton()->shader_get_parameter_default(shader->get_rid(), pr); return true; @@ -924,6 +901,7 @@ void BaseMaterial3D::_update_shader() { if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, length(MODEL_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(MODEL_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; } + code += " MODELVIEW_NORMAL_MATRIX = mat3(MODELVIEW_MATRIX);\n"; } break; case BILLBOARD_FIXED_Y: { code += " MODELVIEW_MATRIX = VIEW_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), INV_VIEW_MATRIX[2].xyz)), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(normalize(cross(INV_VIEW_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))), 0.0), MODEL_MATRIX[3]);\n"; @@ -931,6 +909,7 @@ void BaseMaterial3D::_update_shader() { if (flags[FLAG_BILLBOARD_KEEP_SCALE]) { code += " MODELVIEW_MATRIX = MODELVIEW_MATRIX * mat4(vec4(length(MODEL_MATRIX[0].xyz), 0.0, 0.0, 0.0),vec4(0.0, length(MODEL_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(MODEL_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; } + code += " MODELVIEW_NORMAL_MATRIX = mat3(MODELVIEW_MATRIX);\n"; } break; case BILLBOARD_PARTICLES: { //make billboard @@ -939,6 +918,8 @@ void BaseMaterial3D::_update_shader() { code += " mat_world = mat_world * mat4(vec4(cos(INSTANCE_CUSTOM.x), -sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n"; //set modelview code += " MODELVIEW_MATRIX = VIEW_MATRIX * mat_world;\n"; + //set modelview normal + code += " MODELVIEW_NORMAL_MATRIX = mat3(MODELVIEW_MATRIX);\n"; //handle animation code += " float h_frames = float(particles_anim_h_frames);\n"; @@ -949,7 +930,7 @@ void BaseMaterial3D::_update_shader() { code += " particle_frame = clamp(particle_frame, 0.0, particle_total_frames - 1.0);\n"; code += " } else {\n"; code += " particle_frame = mod(particle_frame, particle_total_frames);\n"; - code += " }"; + code += " }\n"; code += " UV /= vec2(h_frames, v_frames);\n"; code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n"; } break; diff --git a/scene/resources/shader.h b/scene/resources/shader.h index d267e6520e..57be142a95 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -87,15 +87,44 @@ public: virtual bool is_text_shader() const; - _FORCE_INLINE_ StringName remap_uniform(const StringName &p_uniform) const { + // Finds the shader parameter name for the given property name, which should start with "shader_parameter/". + _FORCE_INLINE_ StringName remap_parameter(const StringName &p_property) const { if (params_cache_dirty) { get_shader_uniform_list(nullptr); } - const HashMap<StringName, StringName>::Iterator E = params_cache.find(p_uniform); - if (E) { - return E->value; + String n = p_property; + + // Backwards compatibility with old shader parameter names. + // Note: The if statements are important to make sure we are only replacing text exactly at index 0. + if (n.find("param/") == 0) { + n = n.replace_first("param/", "shader_parameter/"); + } + if (n.find("shader_param/") == 0) { + n = n.replace_first("shader_param/", "shader_parameter/"); + } + if (n.find("shader_uniform/") == 0) { + n = n.replace_first("shader_uniform/", "shader_parameter/"); + } + + { + // Additional backwards compatibility for projects between #62972 and #64092 (about a month of v4.0 development). + // These projects did not have any prefix for shader uniforms due to a bug. + // This code should be removed during beta or rc of 4.0. + const HashMap<StringName, StringName>::Iterator E = params_cache.find(n); + if (E) { + return E->value; + } + } + + if (n.begins_with("shader_parameter/")) { + n = n.replace_first("shader_parameter/", ""); + const HashMap<StringName, StringName>::Iterator E = params_cache.find(n); + if (E) { + return E->value; + } } + return StringName(); } diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp index 068c756849..56234a8a14 100644 --- a/scene/resources/skeleton_modification_stack_2d.cpp +++ b/scene/resources/skeleton_modification_stack_2d.cpp @@ -182,11 +182,11 @@ void SkeletonModificationStack2D::delete_modification(int p_mod_idx) { void SkeletonModificationStack2D::set_modification(int p_mod_idx, Ref<SkeletonModification2D> p_mod) { ERR_FAIL_INDEX(p_mod_idx, modifications.size()); - if (p_mod == nullptr) { - modifications.insert(p_mod_idx, nullptr); + if (p_mod.is_null()) { + modifications.write[p_mod_idx] = Ref<SkeletonModification2D>(); } else { + modifications.write[p_mod_idx] = p_mod; p_mod->_setup_modification(this); - modifications.insert(p_mod_idx, p_mod); } #ifdef TOOLS_ENABLED diff --git a/servers/physics_2d/godot_collision_solver_2d.cpp b/servers/physics_2d/godot_collision_solver_2d.cpp index 0d7b42b80d..761a1c33e2 100644 --- a/servers/physics_2d/godot_collision_solver_2d.cpp +++ b/servers/physics_2d/godot_collision_solver_2d.cpp @@ -34,7 +34,7 @@ #define collision_solver sat_2d_calculate_penetration //#define collision_solver gjk_epa_calculate_penetration -bool GodotCollisionSolver2D::solve_static_world_boundary(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result) { +bool GodotCollisionSolver2D::solve_static_world_boundary(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, real_t p_margin) { const GodotWorldBoundaryShape2D *world_boundary = static_cast<const GodotWorldBoundaryShape2D *>(p_shape_A); if (p_shape_B->get_type() == PhysicsServer2D::SHAPE_WORLD_BOUNDARY) { return false; @@ -52,7 +52,9 @@ bool GodotCollisionSolver2D::solve_static_world_boundary(const GodotShape2D *p_s bool found = false; for (int i = 0; i < support_count; i++) { + supports[i] += p_margin * supports[i].normalized(); supports[i] = p_transform_B.xform(supports[i]); + supports[i] += p_motion_B; real_t pd = n.dot(supports[i]); if (pd >= d) { continue; @@ -231,9 +233,9 @@ bool GodotCollisionSolver2D::solve(const GodotShape2D *p_shape_A, const Transfor } if (swap) { - return solve_static_world_boundary(p_shape_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true); + return solve_static_world_boundary(p_shape_B, p_transform_B, p_shape_A, p_transform_A, p_motion_A, p_result_callback, p_userdata, true, p_margin_A); } else { - return solve_static_world_boundary(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false); + return solve_static_world_boundary(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_motion_B, p_result_callback, p_userdata, false, p_margin_B); } } else if (type_A == PhysicsServer2D::SHAPE_SEPARATION_RAY) { diff --git a/servers/physics_2d/godot_collision_solver_2d.h b/servers/physics_2d/godot_collision_solver_2d.h index bd90641f04..3aac2751e0 100644 --- a/servers/physics_2d/godot_collision_solver_2d.h +++ b/servers/physics_2d/godot_collision_solver_2d.h @@ -38,7 +38,7 @@ public: typedef void (*CallbackResult)(const Vector2 &p_point_A, const Vector2 &p_point_B, void *p_userdata); private: - static bool solve_static_world_boundary(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result); + static bool solve_static_world_boundary(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, real_t p_margin = 0); static bool concave_callback(void *p_userdata, GodotShape2D *p_convex); static bool solve_concave(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0); static bool solve_separation_ray(const GodotShape2D *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis = nullptr, real_t p_margin = 0); diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index ceba43ae8f..307af99e91 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -1261,6 +1261,7 @@ void SkyRD::setup(RID p_env, Ref<RenderSceneBuffersRD> p_render_buffers, const P light_data_dirty = true; for (uint32_t i = sky_scene_state.ubo.directional_light_count; i < sky_scene_state.max_directional_lights; i++) { sky_scene_state.directional_lights[i].enabled = false; + sky_scene_state.last_frame_directional_lights[i].enabled = false; } } diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index dec5e23558..33d7ddd6e8 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -477,6 +477,10 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p prev_material_uniform_set = material_uniform_set; } + if (surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH) { + mesh_storage->_multimesh_get_motion_vectors_offsets(surf->owner->data->base, push_constant.multimesh_motion_vectors_current_offset, push_constant.multimesh_motion_vectors_previous_offset); + } + RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(SceneState::PushConstant)); uint32_t instance_count = surf->owner->instance_count > 1 ? surf->owner->instance_count : element_info.repeat; @@ -924,7 +928,7 @@ _FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primit static const uint32_t subtractor[RS::PRIMITIVE_MAX] = { 0, 0, 1, 0, 1 }; return (p_indices - subtractor[p_primitive]) / divisor[p_primitive]; } -void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_using_sdfgi, bool p_using_opaque_gi, bool p_append) { +void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, uint32_t p_color_pass_flags = 0, bool p_using_sdfgi, bool p_using_opaque_gi, bool p_append) { RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton(); if (p_render_list == RENDER_LIST_OPAQUE) { @@ -1149,6 +1153,12 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con scene_state.used_depth_texture = true; } + if (p_color_pass_flags & COLOR_PASS_FLAG_MOTION_VECTORS && flags & INSTANCE_DATA_FLAG_MULTIMESH) { + if (RendererRD::MeshStorage::get_singleton()->_multimesh_enable_motion_vectors(inst->data->base)) { + inst->transforms_uniform_set = mesh_storage->multimesh_get_3d_uniform_set(inst->data->base, scene_shader.default_shader_rd, TRANSFORMS_UNIFORM_SET); + } + } + } else if (p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) { if (surf->flags & GeometryInstanceSurfaceDataCache::FLAG_PASS_SHADOW) { rl->add_element(surf); @@ -1322,7 +1332,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co _update_render_base_uniform_set(); //may have changed due to the above (light buffer enlarged, as an example) - _fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR, using_sdfgi, using_sdfgi || using_voxelgi); + _fill_render_list(RENDER_LIST_OPAQUE, p_render_data, PASS_MODE_COLOR, color_pass_flags, using_sdfgi, using_sdfgi || using_voxelgi); render_list[RENDER_LIST_OPAQUE].sort_by_key(); render_list[RENDER_LIST_ALPHA].sort_by_reverse_depth_and_priority(); _fill_instance_data(RENDER_LIST_OPAQUE, p_render_data->render_info ? p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE] : (int *)nullptr); @@ -1723,7 +1733,7 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page PassMode pass_mode = p_use_dp ? PASS_MODE_SHADOW_DP : PASS_MODE_SHADOW; uint32_t render_list_from = render_list[RENDER_LIST_SECONDARY].elements.size(); - _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode, false, false, true); + _fill_render_list(RENDER_LIST_SECONDARY, &render_data, pass_mode, 0, false, false, true); uint32_t render_list_size = render_list[RENDER_LIST_SECONDARY].elements.size() - render_list_from; render_list[RENDER_LIST_SECONDARY].sort_by_key_range(render_list_from, render_list_size); _fill_instance_data(RENDER_LIST_SECONDARY, p_render_info ? p_render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_SHADOW] : (int *)nullptr, render_list_from, render_list_size, false); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 35379cd69b..9e2586542a 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -337,7 +337,8 @@ class RenderForwardClustered : public RendererSceneRenderRD { struct PushConstant { uint32_t base_index; // uint32_t uv_offset; //packed - uint32_t pad[2]; + uint32_t multimesh_motion_vectors_current_offset; + uint32_t multimesh_motion_vectors_previous_offset; }; struct InstanceData { @@ -429,7 +430,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { void _update_instance_data_buffer(RenderListType p_render_list); void _fill_instance_data(RenderListType p_render_list, int *p_render_info = nullptr, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true); - void _fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, bool p_using_sdfgi = false, bool p_using_opaque_gi = false, bool p_append = false); + void _fill_render_list(RenderListType p_render_list, const RenderDataRD *p_render_data, PassMode p_pass_mode, uint32_t p_color_pass_flags, bool p_using_sdfgi = false, bool p_using_opaque_gi = false, bool p_append = false); HashMap<Size2i, RID> sdfgi_framebuffer_size_cache; diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl index bdf84bb03a..fe770ac065 100644 --- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl @@ -186,6 +186,7 @@ void main() { uv += pixel_size * 0.5; //half pixel to read centers vec4 color = texture(color_texture, uv); + float initial_blur = color.a; float accum = 1.0; float radius = params.blur_scale; @@ -193,8 +194,8 @@ void main() { vec2 suv = uv + vec2(cos(ang), sin(ang)) * pixel_size * radius; vec4 sample_color = texture(color_texture, suv); float sample_size = abs(sample_color.a); - if (sample_color.a > color.a) { - sample_size = clamp(sample_size, 0.0, abs(color.a) * 2.0); + if (sample_color.a > initial_blur) { + sample_size = clamp(sample_size, 0.0, abs(initial_blur) * 2.0); } float m = smoothstep(radius - 0.5, radius + 0.5, sample_size); diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl index a2bdc2e90e..1b487835d2 100644 --- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl @@ -221,12 +221,9 @@ void main() { vec4 sample_color = texture(source_color, uv_adj); sample_color.a = texture(source_weight, uv_adj).r; - float limit; - - if (sample_color.a < color.a) { - limit = abs(sample_color.a); - } else { - limit = abs(color.a); + float limit = abs(sample_color.a); + if (sample_color.a > color.a) { + limit = clamp(limit, 0.0, abs(color.a) * 2.0); } limit -= DEPTH_GAP; diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl index 26b96b358f..8e8ec6883a 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl @@ -129,7 +129,7 @@ invariant gl_Position; #GLOBALS -void vertex_shader(in uint instance_index, in bool is_multimesh, in SceneData scene_data, in mat4 model_matrix, out vec4 screen_pos) { +void vertex_shader(in uint instance_index, in bool is_multimesh, in uint multimesh_offset, in SceneData scene_data, in mat4 model_matrix, out vec4 screen_pos) { vec4 instance_custom = vec4(0.0); #if defined(COLOR_USED) color_interp = color_attrib; @@ -208,7 +208,7 @@ void vertex_shader(in uint instance_index, in bool is_multimesh, in SceneData sc } } - uint offset = stride * gl_InstanceIndex; + uint offset = stride * (gl_InstanceIndex + multimesh_offset); if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_FORMAT_2D)) { matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); @@ -397,13 +397,13 @@ void main() { mat4 model_matrix = instances.data[instance_index].transform; #if defined(MOTION_VECTORS) global_time = scene_data_block.prev_data.time; - vertex_shader(instance_index, is_multimesh, scene_data_block.prev_data, instances.data[instance_index].prev_transform, prev_screen_position); + vertex_shader(instance_index, is_multimesh, draw_call.multimesh_motion_vectors_previous_offset, scene_data_block.prev_data, instances.data[instance_index].prev_transform, prev_screen_position); global_time = scene_data_block.data.time; - vertex_shader(instance_index, is_multimesh, scene_data_block.data, model_matrix, screen_position); + vertex_shader(instance_index, is_multimesh, draw_call.multimesh_motion_vectors_current_offset, scene_data_block.data, model_matrix, screen_position); #else global_time = scene_data_block.data.time; vec4 screen_position; - vertex_shader(instance_index, is_multimesh, scene_data_block.data, model_matrix, screen_position); + vertex_shader(instance_index, is_multimesh, draw_call.multimesh_motion_vectors_current_offset, scene_data_block.data, model_matrix, screen_position); #endif } diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl index 45484b8c47..d97da0c571 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl @@ -29,8 +29,8 @@ layout(push_constant, std430) uniform DrawCall { uint instance_index; uint uv_offset; - uint pad0; - uint pad1; + uint multimesh_motion_vectors_current_offset; + uint multimesh_motion_vectors_previous_offset; } draw_call; diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 49e3543ba5..1827b73507 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "mesh_storage.h" +#include "../../rendering_server_globals.h" using namespace RendererRD; @@ -1211,7 +1212,13 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS:: if (multimesh->data_cache_dirty_regions) { memdelete_arr(multimesh->data_cache_dirty_regions); multimesh->data_cache_dirty_regions = nullptr; - multimesh->data_cache_used_dirty_regions = 0; + multimesh->data_cache_dirty_region_count = 0; + } + + if (multimesh->previous_data_cache_dirty_regions) { + memdelete_arr(multimesh->previous_data_cache_dirty_regions); + multimesh->previous_data_cache_dirty_regions = nullptr; + multimesh->previous_data_cache_dirty_region_count = 0; } multimesh->instances = p_instances; @@ -1228,14 +1235,67 @@ void MeshStorage::multimesh_allocate_data(RID p_multimesh, int p_instances, RS:: multimesh->aabb = AABB(); multimesh->aabb_dirty = false; multimesh->visible_instances = MIN(multimesh->visible_instances, multimesh->instances); + multimesh->motion_vectors_current_offset = 0; + multimesh->motion_vectors_previous_offset = 0; + multimesh->motion_vectors_last_change = -1; if (multimesh->instances) { - multimesh->buffer = RD::get_singleton()->storage_buffer_create(multimesh->instances * multimesh->stride_cache * 4); + uint32_t buffer_size = multimesh->instances * multimesh->stride_cache * sizeof(float); + if (multimesh->motion_vectors_enabled) { + buffer_size *= 2; + } + multimesh->buffer = RD::get_singleton()->storage_buffer_create(buffer_size); } multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_MULTIMESH); } +bool MeshStorage::_multimesh_enable_motion_vectors(RID p_multimesh) { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND_V(!multimesh, false); + + if (multimesh->motion_vectors_enabled) { + return false; + } + + multimesh->motion_vectors_enabled = true; + + multimesh->motion_vectors_current_offset = 0; + multimesh->motion_vectors_previous_offset = 0; + multimesh->motion_vectors_last_change = -1; + + if (!multimesh->data_cache.is_empty()) { + multimesh->data_cache.append_array(multimesh->data_cache); + } + + if (multimesh->buffer_set) { + RD::get_singleton()->barrier(); + Vector<uint8_t> buffer_data = RD::get_singleton()->buffer_get_data(multimesh->buffer); + if (!multimesh->data_cache.is_empty()) { + memcpy(buffer_data.ptrw(), multimesh->data_cache.ptr(), buffer_data.size()); + } + + RD::get_singleton()->free(multimesh->buffer); + uint32_t buffer_size = multimesh->instances * multimesh->stride_cache * sizeof(float) * 2; + multimesh->buffer = RD::get_singleton()->storage_buffer_create(buffer_size); + RD::get_singleton()->buffer_update(multimesh->buffer, 0, buffer_data.size(), buffer_data.ptr(), RD::BARRIER_MASK_NO_BARRIER); + RD::get_singleton()->buffer_update(multimesh->buffer, buffer_data.size(), buffer_data.size(), buffer_data.ptr()); + multimesh->uniform_set_3d = RID(); // Cleared by dependency + return true; + } + return false; // Update the transforms uniform set cache +} + +void MeshStorage::_multimesh_get_motion_vectors_offsets(RID p_multimesh, uint32_t &r_current_offset, uint32_t &r_prev_offset) { + MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); + ERR_FAIL_COND(!multimesh); + r_current_offset = multimesh->motion_vectors_current_offset; + if (RSG::rasterizer->get_frame_number() - multimesh->motion_vectors_last_change >= 2) { + multimesh->motion_vectors_previous_offset = multimesh->motion_vectors_current_offset; + } + r_prev_offset = multimesh->motion_vectors_previous_offset; +} + int MeshStorage::multimesh_get_instance_count(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); ERR_FAIL_COND_V(!multimesh, 0); @@ -1261,7 +1321,7 @@ void MeshStorage::multimesh_set_mesh(RID p_multimesh, RID p_mesh) { //need to re-create AABB unfortunately, calling this has a penalty if (multimesh->buffer_set) { Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); - const uint8_t *r = buffer.ptr(); + const uint8_t *r = buffer.ptr() + multimesh->motion_vectors_current_offset * multimesh->stride_cache * sizeof(float); const float *data = reinterpret_cast<const float *>(r); _multimesh_re_create_aabb(multimesh, data, multimesh->instances); } @@ -1276,10 +1336,14 @@ void MeshStorage::_multimesh_make_local(MultiMesh *multimesh) const { if (multimesh->data_cache.size() > 0) { return; //already local } - ERR_FAIL_COND(multimesh->data_cache.size() > 0); + // this means that the user wants to load/save individual elements, // for this, the data must reside on CPU, so just copy it there. - multimesh->data_cache.resize(multimesh->instances * multimesh->stride_cache); + uint32_t buffer_size = multimesh->instances * multimesh->stride_cache; + if (multimesh->motion_vectors_enabled) { + buffer_size *= 2; + } + multimesh->data_cache.resize(buffer_size); { float *w = multimesh->data_cache.ptrw(); @@ -1290,15 +1354,48 @@ void MeshStorage::_multimesh_make_local(MultiMesh *multimesh) const { memcpy(w, r, buffer.size()); } } else { - memset(w, 0, (size_t)multimesh->instances * multimesh->stride_cache * sizeof(float)); + memset(w, 0, buffer_size * sizeof(float)); } } uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; multimesh->data_cache_dirty_regions = memnew_arr(bool, data_cache_dirty_region_count); - for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { - multimesh->data_cache_dirty_regions[i] = false; + memset(multimesh->data_cache_dirty_regions, 0, data_cache_dirty_region_count * sizeof(bool)); + multimesh->data_cache_dirty_region_count = 0; + + multimesh->previous_data_cache_dirty_regions = memnew_arr(bool, data_cache_dirty_region_count); + memset(multimesh->previous_data_cache_dirty_regions, 0, data_cache_dirty_region_count * sizeof(bool)); + multimesh->previous_data_cache_dirty_region_count = 0; +} + +void MeshStorage::_multimesh_update_motion_vectors_data_cache(MultiMesh *multimesh) { + ERR_FAIL_COND(multimesh->data_cache.is_empty()); + + if (!multimesh->motion_vectors_enabled) { + return; + } + + uint32_t frame = RSG::rasterizer->get_frame_number(); + if (multimesh->motion_vectors_last_change != frame) { + multimesh->motion_vectors_previous_offset = multimesh->motion_vectors_current_offset; + multimesh->motion_vectors_current_offset = multimesh->instances - multimesh->motion_vectors_current_offset; + multimesh->motion_vectors_last_change = frame; + + if (multimesh->previous_data_cache_dirty_region_count > 0) { + uint8_t *data = (uint8_t *)multimesh->data_cache.ptrw(); + uint32_t current_ofs = multimesh->motion_vectors_current_offset * multimesh->stride_cache * sizeof(float); + uint32_t previous_ofs = multimesh->motion_vectors_previous_offset * multimesh->stride_cache * sizeof(float); + uint32_t visible_instances = multimesh->visible_instances >= 0 ? multimesh->visible_instances : multimesh->instances; + uint32_t visible_region_count = visible_instances == 0 ? 0 : (visible_instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; + uint32_t region_size = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float); + uint32_t size = multimesh->stride_cache * (uint32_t)multimesh->instances * (uint32_t)sizeof(float); + for (uint32_t i = 0; i < visible_region_count; i++) { + if (multimesh->previous_data_cache_dirty_regions[i]) { + uint32_t offset = i * region_size; + memcpy(data + current_ofs + offset, data + previous_ofs + offset, MIN(region_size, size - offset)); + } + } + } } - multimesh->data_cache_used_dirty_regions = 0; } void MeshStorage::_multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb) { @@ -1309,7 +1406,7 @@ void MeshStorage::_multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool #endif if (!multimesh->data_cache_dirty_regions[region_index]) { multimesh->data_cache_dirty_regions[region_index] = true; - multimesh->data_cache_used_dirty_regions++; + multimesh->data_cache_dirty_region_count++; } if (p_aabb) { @@ -1330,7 +1427,7 @@ void MeshStorage::_multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, b for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { if (!multimesh->data_cache_dirty_regions[i]) { multimesh->data_cache_dirty_regions[i] = true; - multimesh->data_cache_used_dirty_regions++; + multimesh->data_cache_dirty_region_count++; } } } @@ -1395,11 +1492,12 @@ void MeshStorage::multimesh_instance_set_transform(RID p_multimesh, int p_index, ERR_FAIL_COND(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_3D); _multimesh_make_local(multimesh); + _multimesh_update_motion_vectors_data_cache(multimesh); { float *w = multimesh->data_cache.ptrw(); - float *dataptr = w + p_index * multimesh->stride_cache; + float *dataptr = w + (multimesh->motion_vectors_current_offset + p_index) * multimesh->stride_cache; dataptr[0] = p_transform.basis.rows[0][0]; dataptr[1] = p_transform.basis.rows[0][1]; @@ -1425,11 +1523,12 @@ void MeshStorage::multimesh_instance_set_transform_2d(RID p_multimesh, int p_ind ERR_FAIL_COND(multimesh->xform_format != RS::MULTIMESH_TRANSFORM_2D); _multimesh_make_local(multimesh); + _multimesh_update_motion_vectors_data_cache(multimesh); { float *w = multimesh->data_cache.ptrw(); - float *dataptr = w + p_index * multimesh->stride_cache; + float *dataptr = w + (multimesh->motion_vectors_current_offset + p_index) * multimesh->stride_cache; dataptr[0] = p_transform.columns[0][0]; dataptr[1] = p_transform.columns[1][0]; @@ -1451,11 +1550,12 @@ void MeshStorage::multimesh_instance_set_color(RID p_multimesh, int p_index, con ERR_FAIL_COND(!multimesh->uses_colors); _multimesh_make_local(multimesh); + _multimesh_update_motion_vectors_data_cache(multimesh); { float *w = multimesh->data_cache.ptrw(); - float *dataptr = w + p_index * multimesh->stride_cache + multimesh->color_offset_cache; + float *dataptr = w + (multimesh->motion_vectors_current_offset + p_index) * multimesh->stride_cache + multimesh->color_offset_cache; dataptr[0] = p_color.r; dataptr[1] = p_color.g; @@ -1473,11 +1573,12 @@ void MeshStorage::multimesh_instance_set_custom_data(RID p_multimesh, int p_inde ERR_FAIL_COND(!multimesh->uses_custom_data); _multimesh_make_local(multimesh); + _multimesh_update_motion_vectors_data_cache(multimesh); { float *w = multimesh->data_cache.ptrw(); - float *dataptr = w + p_index * multimesh->stride_cache + multimesh->custom_data_offset_cache; + float *dataptr = w + (multimesh->motion_vectors_current_offset + p_index) * multimesh->stride_cache + multimesh->custom_data_offset_cache; dataptr[0] = p_color.r; dataptr[1] = p_color.g; @@ -1514,7 +1615,7 @@ Transform3D MeshStorage::multimesh_instance_get_transform(RID p_multimesh, int p { const float *r = multimesh->data_cache.ptr(); - const float *dataptr = r + p_index * multimesh->stride_cache; + const float *dataptr = r + (multimesh->motion_vectors_current_offset + p_index) * multimesh->stride_cache; t.basis.rows[0][0] = dataptr[0]; t.basis.rows[0][1] = dataptr[1]; @@ -1545,7 +1646,7 @@ Transform2D MeshStorage::multimesh_instance_get_transform_2d(RID p_multimesh, in { const float *r = multimesh->data_cache.ptr(); - const float *dataptr = r + p_index * multimesh->stride_cache; + const float *dataptr = r + (multimesh->motion_vectors_current_offset + p_index) * multimesh->stride_cache; t.columns[0][0] = dataptr[0]; t.columns[1][0] = dataptr[1]; @@ -1570,7 +1671,7 @@ Color MeshStorage::multimesh_instance_get_color(RID p_multimesh, int p_index) co { const float *r = multimesh->data_cache.ptr(); - const float *dataptr = r + p_index * multimesh->stride_cache + multimesh->color_offset_cache; + const float *dataptr = r + (multimesh->motion_vectors_current_offset + p_index) * multimesh->stride_cache + multimesh->color_offset_cache; c.r = dataptr[0]; c.g = dataptr[1]; @@ -1593,7 +1694,7 @@ Color MeshStorage::multimesh_instance_get_custom_data(RID p_multimesh, int p_ind { const float *r = multimesh->data_cache.ptr(); - const float *dataptr = r + p_index * multimesh->stride_cache + multimesh->custom_data_offset_cache; + const float *dataptr = r + (multimesh->motion_vectors_current_offset + p_index) * multimesh->stride_cache + multimesh->custom_data_offset_cache; c.r = dataptr[0]; c.g = dataptr[1]; @@ -1609,25 +1710,26 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b ERR_FAIL_COND(!multimesh); ERR_FAIL_COND(p_buffer.size() != (multimesh->instances * (int)multimesh->stride_cache)); + if (multimesh->motion_vectors_enabled) { + uint32_t frame = RSG::rasterizer->get_frame_number(); + + if (multimesh->motion_vectors_last_change != frame) { + multimesh->motion_vectors_previous_offset = multimesh->motion_vectors_current_offset; + multimesh->motion_vectors_current_offset = multimesh->instances - multimesh->motion_vectors_current_offset; + multimesh->motion_vectors_last_change = frame; + } + } + { const float *r = p_buffer.ptr(); - RD::get_singleton()->buffer_update(multimesh->buffer, 0, p_buffer.size() * sizeof(float), r); + RD::get_singleton()->buffer_update(multimesh->buffer, multimesh->motion_vectors_current_offset * multimesh->stride_cache * sizeof(float), p_buffer.size() * sizeof(float), r); multimesh->buffer_set = true; } if (multimesh->data_cache.size()) { - //if we have a data cache, just update it - multimesh->data_cache = p_buffer; - { - //clear dirty since nothing will be dirty anymore - uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; - for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { - multimesh->data_cache_dirty_regions[i] = false; - } - multimesh->data_cache_used_dirty_regions = 0; - } - - _multimesh_mark_all_dirty(multimesh, false, true); //update AABB + float *cache_data = multimesh->data_cache.ptrw(); + memcpy(cache_data + (multimesh->motion_vectors_current_offset * multimesh->stride_cache), p_buffer.ptr(), p_buffer.size() * sizeof(float)); + _multimesh_mark_all_dirty(multimesh, true, true); //update AABB } else if (multimesh->mesh.is_valid()) { //if we have a mesh set, we need to re-generate the AABB from the new data const float *data = p_buffer.ptr(); @@ -1642,20 +1744,19 @@ Vector<float> MeshStorage::multimesh_get_buffer(RID p_multimesh) const { ERR_FAIL_COND_V(!multimesh, Vector<float>()); if (multimesh->buffer.is_null()) { return Vector<float>(); - } else if (multimesh->data_cache.size()) { - return multimesh->data_cache; } else { - //get from memory - - Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); Vector<float> ret; ret.resize(multimesh->instances * multimesh->stride_cache); - { - float *w = ret.ptrw(); - const uint8_t *r = buffer.ptr(); - memcpy(w, r, buffer.size()); - } + float *w = ret.ptrw(); + if (multimesh->data_cache.size()) { + const uint8_t *r = (uint8_t *)multimesh->data_cache.ptr() + multimesh->motion_vectors_current_offset * multimesh->stride_cache * sizeof(float); + memcpy(w, r, ret.size() * sizeof(float)); + } else { + Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(multimesh->buffer); + const uint8_t *r = buffer.ptr() + multimesh->motion_vectors_current_offset * multimesh->stride_cache * sizeof(float); + memcpy(w, r, ret.size() * sizeof(float)); + } return ret; } } @@ -1698,36 +1799,38 @@ void MeshStorage::_update_dirty_multimeshes() { MultiMesh *multimesh = multimesh_dirty_list; if (multimesh->data_cache.size()) { //may have been cleared, so only process if it exists - const float *data = multimesh->data_cache.ptr(); uint32_t visible_instances = multimesh->visible_instances >= 0 ? multimesh->visible_instances : multimesh->instances; + uint32_t buffer_offset = multimesh->motion_vectors_current_offset * multimesh->stride_cache; + const float *data = multimesh->data_cache.ptr() + buffer_offset; - if (multimesh->data_cache_used_dirty_regions) { + uint32_t total_dirty_regions = multimesh->data_cache_dirty_region_count + multimesh->previous_data_cache_dirty_region_count; + if (total_dirty_regions != 0) { uint32_t data_cache_dirty_region_count = (multimesh->instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; uint32_t visible_region_count = visible_instances == 0 ? 0 : (visible_instances - 1) / MULTIMESH_DIRTY_REGION_SIZE + 1; uint32_t region_size = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * sizeof(float); - - if (multimesh->data_cache_used_dirty_regions > 32 || multimesh->data_cache_used_dirty_regions > visible_region_count / 2) { + if (total_dirty_regions > 32 || total_dirty_regions > visible_region_count / 2) { //if there too many dirty regions, or represent the majority of regions, just copy all, else transfer cost piles up too much - RD::get_singleton()->buffer_update(multimesh->buffer, 0, MIN(visible_region_count * region_size, multimesh->instances * (uint32_t)multimesh->stride_cache * (uint32_t)sizeof(float)), data); + RD::get_singleton()->buffer_update(multimesh->buffer, buffer_offset * sizeof(float), MIN(visible_region_count * region_size, multimesh->instances * (uint32_t)multimesh->stride_cache * (uint32_t)sizeof(float)), data); } else { //not that many regions? update them all for (uint32_t i = 0; i < visible_region_count; i++) { - if (multimesh->data_cache_dirty_regions[i]) { + if (multimesh->data_cache_dirty_regions[i] || multimesh->previous_data_cache_dirty_regions[i]) { uint32_t offset = i * region_size; uint32_t size = multimesh->stride_cache * (uint32_t)multimesh->instances * (uint32_t)sizeof(float); uint32_t region_start_index = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * i; - RD::get_singleton()->buffer_update(multimesh->buffer, offset, MIN(region_size, size - offset), &data[region_start_index]); + RD::get_singleton()->buffer_update(multimesh->buffer, buffer_offset * sizeof(float) + offset, MIN(region_size, size - offset), &data[region_start_index], RD::BARRIER_MASK_NO_BARRIER); } } + RD::get_singleton()->barrier(RD::BARRIER_MASK_NO_BARRIER, RD::BARRIER_MASK_ALL); } - for (uint32_t i = 0; i < data_cache_dirty_region_count; i++) { - multimesh->data_cache_dirty_regions[i] = false; - } + memcpy(multimesh->previous_data_cache_dirty_regions, multimesh->data_cache_dirty_regions, data_cache_dirty_region_count * sizeof(bool)); + memset(multimesh->data_cache_dirty_regions, 0, data_cache_dirty_region_count * sizeof(bool)); - multimesh->data_cache_used_dirty_regions = 0; + multimesh->previous_data_cache_dirty_region_count = multimesh->data_cache_dirty_region_count; + multimesh->data_cache_dirty_region_count = 0; } if (multimesh->aabb_dirty) { diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index 5c0d019c15..622f3911c7 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -205,13 +205,19 @@ private: AABB aabb; bool aabb_dirty = false; bool buffer_set = false; + bool motion_vectors_enabled = false; + uint32_t motion_vectors_current_offset = 0; + uint32_t motion_vectors_previous_offset = 0; + uint64_t motion_vectors_last_change = -1; uint32_t stride_cache = 0; uint32_t color_offset_cache = 0; uint32_t custom_data_offset_cache = 0; Vector<float> data_cache; //used if individual setting is used bool *data_cache_dirty_regions = nullptr; - uint32_t data_cache_used_dirty_regions = 0; + uint32_t data_cache_dirty_region_count = 0; + bool *previous_data_cache_dirty_regions = nullptr; + uint32_t previous_data_cache_dirty_region_count = 0; RID buffer; //storage buffer RID uniform_set_3d; @@ -228,6 +234,7 @@ private: MultiMesh *multimesh_dirty_list = nullptr; _FORCE_INLINE_ void _multimesh_make_local(MultiMesh *multimesh) const; + _FORCE_INLINE_ void _multimesh_update_motion_vectors_data_cache(MultiMesh *multimesh); _FORCE_INLINE_ void _multimesh_mark_dirty(MultiMesh *multimesh, int p_index, bool p_aabb); _FORCE_INLINE_ void _multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, bool p_aabb); _FORCE_INLINE_ void _multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances); @@ -579,6 +586,8 @@ public: virtual AABB multimesh_get_aabb(RID p_multimesh) const override; void _update_dirty_multimeshes(); + bool _multimesh_enable_motion_vectors(RID p_multimesh); + void _multimesh_get_motion_vectors_offsets(RID p_multimesh, uint32_t &r_current_offset, uint32_t &r_prev_offset); _FORCE_INLINE_ RS::MultimeshTransformFormat multimesh_get_transform_format(RID p_multimesh) const { MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh); diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index 47cb38f268..cd063f91ac 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -340,6 +340,11 @@ ShaderTypes::ShaderTypes() { shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_UINT); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["EMISSION_TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["FLAG_EMIT_POSITION"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["FLAG_EMIT_ROT_SCALE"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["FLAG_EMIT_VELOCITY"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["FLAG_EMIT_COLOR"] = constt(ShaderLanguage::TYPE_UINT); + shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["FLAG_EMIT_CUSTOM"] = constt(ShaderLanguage::TYPE_UINT); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_POSITION"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_ROT_SCALE"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["RESTART_VELOCITY"] = constt(ShaderLanguage::TYPE_BOOL); @@ -385,6 +390,7 @@ ShaderTypes::ShaderTypes() { emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("custom", ShaderLanguage::TYPE_VEC4)); emit_vertex_func.arguments.push_back(ShaderLanguage::StageFunctionInfo::Argument("flags", ShaderLanguage::TYPE_UINT)); emit_vertex_func.return_type = ShaderLanguage::TYPE_BOOL; //whether it could emit + shader_modes[RS::SHADER_PARTICLES].functions["start"].stage_functions["emit_subparticle"] = emit_vertex_func; shader_modes[RS::SHADER_PARTICLES].functions["process"].stage_functions["emit_subparticle"] = emit_vertex_func; } diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index aa3351c815..cbcfc8fe49 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1114,7 +1114,8 @@ Array RenderingServer::_get_array_from_surface(uint32_t p_format, Vector<uint8_t for (int j = 0; j < p_vertex_len; j++) { const uint32_t v = *(const uint32_t *)&r[j * vertex_elem_size + offsets[i]]; - w[j] = Vector3((v & 0x3FF) / 1023.0, ((v >> 10) & 0x3FF) / 1023.0, ((v >> 20) & 0x3FF) / 1023.0) * Vector3(2, 2, 2) - Vector3(1, 1, 1); + + w[j] = Vector3::octahedron_decode(Vector2((v & 0xFFFF) / 65535.0, ((v >> 16) & 0xFFFF) / 65535.0)); } ret[i] = arr; @@ -1129,11 +1130,12 @@ Array RenderingServer::_get_array_from_surface(uint32_t p_format, Vector<uint8_t for (int j = 0; j < p_vertex_len; j++) { const uint32_t v = *(const uint32_t *)&r[j * vertex_elem_size + offsets[i]]; - - w[j * 4 + 0] = ((v & 0x3FF) / 1023.0) * 2.0 - 1.0; - w[j * 4 + 1] = (((v >> 10) & 0x3FF) / 1023.0) * 2.0 - 1.0; - w[j * 4 + 2] = (((v >> 20) & 0x3FF) / 1023.0) * 2.0 - 1.0; - w[j * 4 + 3] = ((v >> 30) / 3.0) * 2.0 - 1.0; + float tangent_sign; + Vector3 res = Vector3::octahedron_tangent_decode(Vector2((v & 0xFFFF) / 65535.0, ((v >> 16) & 0xFFFF) / 65535.0), &tangent_sign); + w[j * 4 + 0] = res.x; + w[j * 4 + 1] = res.y; + w[j * 4 + 2] = res.z; + w[j * 4 + 3] = tangent_sign; } ret[i] = arr; diff --git a/version.py b/version.py index f9aa1dd0f4..cfa1773b85 100644 --- a/version.py +++ b/version.py @@ -3,7 +3,7 @@ name = "Godot Engine" major = 4 minor = 0 patch = 0 -status = "alpha" +status = "beta" module_config = "" year = 2022 website = "https://godotengine.org" |