diff options
214 files changed, 5831 insertions, 2700 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 697e09955c..f59c0e645d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -337,7 +337,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). #### Rendering -- Some Environment settings such as depth of field have been moved to a CameraEffects resource which is assigned to individual Camera nodes. +- Some Environment settings such as depth of field have been moved to a CameraAttributes resource which is assigned to individual Camera nodes. - [The ACES Fitted tonemapping algorithm is now used in place of the old ACES algorithm.](https://github.com/godotengine/godot/pull/52476) - The old non-fitted ACES tonemapping algorithm was removed. - Quality settings have been moved from individual nodes and resources to the Project Settings for better centralization. diff --git a/core/input/input.cpp b/core/input/input.cpp index 4e538a85ae..1390a0a7fa 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -169,11 +169,10 @@ void Input::_bind_methods() { void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { String pf = p_function; - if (p_idx == 0 && - (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || - pf == "is_action_just_pressed" || pf == "is_action_just_released" || - pf == "get_action_strength" || pf == "get_action_raw_strength" || - pf == "get_axis" || pf == "get_vector")) { + + if ((p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || pf == "get_action_strength" || pf == "get_action_raw_strength")) || + (p_idx < 2 && pf == "get_axis") || + (p_idx < 4 && pf == "get_vector")) { List<PropertyInfo> pinfo; ProjectSettings::get_singleton()->get_property_list(&pinfo); diff --git a/core/io/dir_access.h b/core/io/dir_access.h index d5318dfb45..2469c2a080 100644 --- a/core/io/dir_access.h +++ b/core/io/dir_access.h @@ -55,7 +55,7 @@ private: protected: String _get_root_path() const; - String _get_root_string() const; + virtual String _get_root_string() const; AccessType get_access_type() const; String fix_path(String p_path) const; diff --git a/core/math/transform_3d.cpp b/core/math/transform_3d.cpp index a634faca9a..2de9e81b38 100644 --- a/core/math/transform_3d.cpp +++ b/core/math/transform_3d.cpp @@ -94,9 +94,7 @@ void Transform3D::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, con origin = p_eye; } -Transform3D Transform3D::spherical_interpolate_with(const Transform3D &p_transform, real_t p_c) const { - /* not sure if very "efficient" but good enough? */ - +Transform3D Transform3D::interpolate_with(const Transform3D &p_transform, real_t p_c) const { Transform3D interp; Vector3 src_scale = basis.get_scale(); @@ -113,15 +111,6 @@ Transform3D Transform3D::spherical_interpolate_with(const Transform3D &p_transfo return interp; } -Transform3D Transform3D::interpolate_with(const Transform3D &p_transform, real_t p_c) const { - Transform3D interp; - - interp.basis = basis.lerp(p_transform.basis, p_c); - interp.origin = origin.lerp(p_transform.origin, p_c); - - return interp; -} - void Transform3D::scale(const Vector3 &p_scale) { basis.scale(p_scale); origin *= p_scale; diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h index b572e90859..c62e4a7b0e 100644 --- a/core/math/transform_3d.h +++ b/core/math/transform_3d.h @@ -103,7 +103,6 @@ struct _NO_DISCARD_ Transform3D { void operator*=(const real_t p_val); Transform3D operator*(const real_t p_val) const; - Transform3D spherical_interpolate_with(const Transform3D &p_transform, real_t p_c) const; Transform3D interpolate_with(const Transform3D &p_transform, real_t p_c) const; _FORCE_INLINE_ Transform3D inverse_xform(const Transform3D &t) const { diff --git a/core/object/object.cpp b/core/object/object.cpp index 4f7f55c8b6..33208be539 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -811,7 +811,7 @@ String Object::to_string() { _extension->to_string(_extension_instance, &ret); return ret; } - return "[" + get_class() + ":" + itos(get_instance_id()) + "]"; + return "<" + get_class() + "#" + itos(get_instance_id()) + ">"; } void Object::set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance) { diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index a5bc6c229d..b280fc9fe3 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -1787,7 +1787,7 @@ String stringify_vector(const T &vec, int recursion_count) { String Variant::stringify(int recursion_count) const { switch (type) { case NIL: - return "null"; + return "<null>"; case BOOL: return _data._bool ? "true" : "false"; case INT: @@ -1904,12 +1904,12 @@ String Variant::stringify(int recursion_count) const { case OBJECT: { if (_get_obj().obj) { if (!_get_obj().id.is_ref_counted() && ObjectDB::get_instance(_get_obj().id) == nullptr) { - return "[Freed Object]"; + return "<Freed Object>"; } return _get_obj().obj->to_string(); } else { - return "[Object:null]"; + return "<Object#null>"; } } break; @@ -1926,7 +1926,7 @@ String Variant::stringify(int recursion_count) const { return "RID(" + itos(s.get_id()) + ")"; } break; default: { - return "[" + get_type_name(type) + "]"; + return "<" + get_type_name(type) + ">"; } } diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 8af2a09111..a4bce979fe 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1969,7 +1969,6 @@ static void _register_variant_builtin_methods() { bind_method(Transform3D, translated, sarray("offset"), varray()); bind_method(Transform3D, translated_local, sarray("offset"), varray()); bind_method(Transform3D, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0))); - bind_method(Transform3D, spherical_interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform3D, interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform3D, is_equal_approx, sarray("xform"), varray()); diff --git a/doc/classes/AnimationNodeTransition.xml b/doc/classes/AnimationNodeTransition.xml index d574bc044d..f6e2fc5eb2 100644 --- a/doc/classes/AnimationNodeTransition.xml +++ b/doc/classes/AnimationNodeTransition.xml @@ -40,12 +40,12 @@ </method> </methods> <members> + <member name="enabled_inputs" type="int" setter="set_enabled_inputs" getter="get_enabled_inputs" default="0"> + The number of enabled input ports for this node. + </member> <member name="from_start" type="bool" setter="set_from_start" getter="is_from_start" default="true"> If [code]true[/code], the destination animation is played back from the beginning when switched. </member> - <member name="input_count" type="int" setter="set_enabled_inputs" getter="get_enabled_inputs" default="0"> - The number of available input ports for this node. - </member> <member name="xfade_curve" type="Curve" setter="set_xfade_curve" getter="get_xfade_curve"> </member> <member name="xfade_time" type="float" setter="set_xfade_time" getter="get_xfade_time" default="0.0"> diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index 2262c4fe47..ee28675d89 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -194,10 +194,13 @@ The emitted light's color. See [member emission_enabled]. </member> <member name="emission_enabled" type="bool" setter="set_feature" getter="get_feature" default="false"> - If [code]true[/code], the body emits light. Emitting light makes the object appear brighter. The object can also cast light on other objects if a [VoxelGI] is used and this object is used in baked lighting. + If [code]true[/code], the body emits light. Emitting light makes the object appear brighter. The object can also cast light on other objects if a [VoxelGI], SDFGI, or [LightmapGI] is used and this object is used in baked lighting. </member> - <member name="emission_energy" type="float" setter="set_emission_energy" getter="get_emission_energy" default="1.0"> - The emitted light's strength. See [member emission_enabled]. + <member name="emission_energy_multiplier" type="float" setter="set_emission_energy_multiplier" getter="get_emission_energy_multiplier" default="1.0"> + Multiplier for emitted light. See [member emission_enabled]. + </member> + <member name="emission_intensity" type="float" setter="set_emission_intensity" getter="get_emission_intensity"> + Luminance of emitted light, measured in nits (candela per square meter). Only available when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled. The default is roughly equivalent to an indoor lightbulb. </member> <member name="emission_on_uv2" type="bool" setter="set_flag" getter="get_flag" default="false"> Use [code]UV2[/code] to read from the [member emission_texture]. diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml index 65fdecc3c6..322c4f0bf4 100644 --- a/doc/classes/Camera3D.xml +++ b/doc/classes/Camera3D.xml @@ -150,6 +150,9 @@ </method> </methods> <members> + <member name="attributes" type="CameraAttributes" setter="set_attributes" getter="get_attributes"> + The [CameraAttributes] to use for this camera. + </member> <member name="cull_mask" type="int" setter="set_cull_mask" getter="get_cull_mask" default="1048575"> The culling mask that describes which 3D render layers are rendered by this camera. </member> @@ -160,9 +163,6 @@ <member name="doppler_tracking" type="int" setter="set_doppler_tracking" getter="get_doppler_tracking" enum="Camera3D.DopplerTracking" default="0"> If not [constant DOPPLER_TRACKING_DISABLED], this camera will simulate the [url=https://en.wikipedia.org/wiki/Doppler_effect]Doppler effect[/url] for objects changed in particular [code]_process[/code] methods. See [enum DopplerTracking] for possible values. </member> - <member name="effects" type="CameraEffects" setter="set_effects" getter="get_effects"> - The [CameraEffects] to use for this camera. - </member> <member name="environment" type="Environment" setter="set_environment" getter="get_environment"> The [Environment] to use for this camera. </member> diff --git a/doc/classes/CameraAttributes.xml b/doc/classes/CameraAttributes.xml new file mode 100644 index 0000000000..a741728c14 --- /dev/null +++ b/doc/classes/CameraAttributes.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="CameraAttributes" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Parent class for camera settings. + </brief_description> + <description> + Controls camera-specific attributes such as depth of field and exposure override. + When used in a [WorldEnvironment] it provides default settings for exposure, auto-exposure, and depth of field that will be used by all cameras without their own [CameraAttributes], including the editor camera. When used in a [Camera3D] it will override any [CameraAttributes] set in the [WorldEnvironment]. When used in [VoxelGI] or [LightmapGI], only the exposure settings will be used. + See also [Environment] for general 3D environment settings. + This is a pure virtual class that is inherited by [CameraAttributesPhysical] and [CameraAttributesPractical]. + </description> + <tutorials> + </tutorials> + <members> + <member name="auto_exposure_enabled" type="bool" setter="set_auto_exposure_enabled" getter="is_auto_exposure_enabled"> + If [code]true[/code], enables the tonemapping auto exposure mode of the scene renderer. If [code]true[/code], the renderer will automatically determine the exposure setting to adapt to the scene's illumination and the observed light. + </member> + <member name="auto_exposure_scale" type="float" setter="set_auto_exposure_scale" getter="get_auto_exposure_scale"> + The scale of the auto exposure effect. Affects the intensity of auto exposure. + </member> + <member name="auto_exposure_speed" type="float" setter="set_auto_exposure_speed" getter="get_auto_exposure_speed"> + The speed of the auto exposure effect. Affects the time needed for the camera to perform auto exposure. + </member> + <member name="exposure_multiplier" type="float" setter="set_exposure_multiplier" getter="get_exposure_multiplier"> + Multiplier for the exposure amount. A higher value results in a brighter image. + </member> + <member name="exposure_sensitivity" type="float" setter="set_exposure_sensitivity" getter="get_exposure_sensitivity"> + Sensitivity of camera sensors, measured in ISO. A higher sensitivity results in a brighter image. Only available when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled. When [member auto_exposure_enabled] this can be used as a method of exposure compensation, doubling the value will increase the exposure value (measured in EV100) by 1 stop. + </member> + </members> +</class> diff --git a/doc/classes/CameraAttributesPhysical.xml b/doc/classes/CameraAttributesPhysical.xml new file mode 100644 index 0000000000..a61e735932 --- /dev/null +++ b/doc/classes/CameraAttributesPhysical.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="CameraAttributesPhysical" inherits="CameraAttributes" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Physically-based camera settings. + </brief_description> + <description> + [CameraAttributesPhysical] is used to set rendering settings based on a physically-based camera's settings. It is responsible for exposure, auto-exposure, and depth of field. + When used in a [WorldEnvironment] it provides default settings for exposure, auto-exposure, and depth of field that will be used by all cameras without their own [CameraAttributes], including the editor camera. When used in a [Camera3D] it will override any [CameraAttributes] set in the [WorldEnvironment] and will override the [Camera3D]s [member Camera3D.far], [member Camera3D.near], [member Camera3D.fov], and [member Camera3D.keep_aspect] properties. When used in [VoxelGI] or [LightmapGI], only the exposure settings will be used. + The default settings are intended for use in an outdoor environment, tips for settings for use in an indoor environment can be found in each setting's documentation. + </description> + <tutorials> + </tutorials> + <methods> + <method name="get_fov" qualifiers="const"> + <return type="float" /> + <description> + Returns the vertical field of view that corresponds to the [member frustum_focal_length]. This value is calculated internally whenever [member frustum_focal_length] is changed. + </description> + </method> + </methods> + <members> + <member name="auto_exposure_max_exposure_value" type="float" setter="set_auto_exposure_max_exposure_value" getter="get_auto_exposure_max_exposure_value" default="10.0"> + The maximum luminance (in EV100) used when calculating auto exposure. When calculating scene average luminance, color values will be clamped to at least this value. This limits the auto-exposure from exposing below a certain brightness, resulting in a cut off point where the scene will remain bright. + </member> + <member name="auto_exposure_min_exposure_value" type="float" setter="set_auto_exposure_min_exposure_value" getter="get_auto_exposure_min_exposure_value" default="-8.0"> + The minimum luminance luminance (in EV100) used when calculating auto exposure. When calculating scene average luminance, color values will be clamped to at least this value. This limits the auto-exposure from exposing above a certain brightness, resulting in a cut off point where the scene will remain dark. + </member> + <member name="exposure_aperture" type="float" setter="set_aperture" getter="get_aperture" default="16.0"> + Size of the aperture of the camera, measured in f-stops. An f-stop is a unitless ratio between the focal length of the camera and the diameter of the aperture. A high aperture setting will result in a smaller aperture which leads to a dimmer image and sharper focus. A low aperture results in a wide aperture which lets in more light resulting in a brighter, less-focused image. Default is appropriate for outdoors at daytime (i.e. for use with a default [DirectionalLight3D]), for indoor lighting, a value between 2 and 4 is more appropriate. + Only available when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled. + </member> + <member name="exposure_shutter_speed" type="float" setter="set_shutter_speed" getter="get_shutter_speed" default="100.0"> + Time for shutter to open and close, measured in seconds. A higher value will let in more light leading to a brighter image, while a lower amount will let in less light leading to a darker image. + Only available when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled. + </member> + <member name="frustum_far" type="float" setter="set_far" getter="get_far" default="4000.0"> + Override value for [member Camera3D.far]. Used internally when calculating depth of field. When attached to a [Camera3D] as its [member Camera3D.attributes], it will override the [member Camera3D.far] property. + </member> + <member name="frustum_focal_length" type="float" setter="set_focal_length" getter="get_focal_length" default="35.0"> + Distance between camera lens and camera aperture, measured in millimeters. Controls field of view and depth of field. A larger focal length will result in a smaller field of view and a narrower depth of field meaning fewer objects will be in focus. A smaller focal length will result in a wider field of view and a larger depth of field meaning more objects will be in focus. When attached to a [Camera3D] as its [member Camera3D.attributes], it will override the [member Camera3D.fov] property and the [member Camera3D.keep_aspect] property. + </member> + <member name="frustum_focus_distance" type="float" setter="set_focus_distance" getter="get_focus_distance" default="10.0"> + Distance from camera of object that will be in focus, measured in meters. Internally this will be clamped to be at least 1 millimeter larger than [member frustum_focal_length]. + </member> + <member name="frustum_near" type="float" setter="set_near" getter="get_near" default="0.05"> + Override value for [member Camera3D.near]. Used internally when calculating depth of field. When attached to a [Camera3D] as its [member Camera3D.attributes], it will override the [member Camera3D.near] property. + </member> + </members> +</class> diff --git a/doc/classes/CameraAttributesPractical.xml b/doc/classes/CameraAttributesPractical.xml new file mode 100644 index 0000000000..924b02fc79 --- /dev/null +++ b/doc/classes/CameraAttributesPractical.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="CameraAttributesPractical" inherits="CameraAttributes" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Camera settings in an easy to use format. + </brief_description> + <description> + Controls camera-specific attributes such as auto-exposure, depth of field, and exposure override. + When used in a [WorldEnvironment] it provides default settings for exposure, auto-exposure, and depth of field that will be used by all cameras without their own [CameraAttributes], including the editor camera. When used in a [Camera3D] it will override any [CameraAttributes] set in the [WorldEnvironment]. When used in [VoxelGI] or [LightmapGI], only the exposure settings will be used. + </description> + <tutorials> + </tutorials> + <members> + <member name="auto_exposure_max_sensitivity" type="float" setter="set_auto_exposure_max_sensitivity" getter="get_auto_exposure_max_sensitivity" default="800.0"> + The maximum sensitivity (in ISO) used when calculating auto exposure. When calculating scene average luminance, color values will be clamped to at least this value. This limits the auto-exposure from exposing below a certain brightness, resulting in a cut off point where the scene will remain bright. + </member> + <member name="auto_exposure_min_sensitivity" type="float" setter="set_auto_exposure_min_sensitivity" getter="get_auto_exposure_min_sensitivity" default="0.0"> + The minimum sensitivity (in ISO) used when calculating auto exposure. When calculating scene average luminance, color values will be clamped to at least this value. This limits the auto-exposure from exposing above a certain brightness, resulting in a cut off point where the scene will remain dark. + </member> + <member name="dof_blur_amount" type="float" setter="set_dof_blur_amount" getter="get_dof_blur_amount" default="0.1"> + Sets the maximum amount of blur. When using physically-based blur amounts, will instead act as a multiplier. High values lead to an increased amount of bluriness, but can be much more expensive to calculate. It is best to keep this as low as possible for a given art style. + </member> + <member name="dof_blur_far_distance" type="float" setter="set_dof_blur_far_distance" getter="get_dof_blur_far_distance" default="10.0"> + Objects further from the [Camera3D] by this amount will be blurred by the depth of field effect. Measured in meters. + </member> + <member name="dof_blur_far_enabled" type="bool" setter="set_dof_blur_far_enabled" getter="is_dof_blur_far_enabled" default="false"> + Enables depth of field blur for objects further than [member dof_blur_far_distance]. Strength of blur is controlled by [member dof_blur_amount] and modulated by [member dof_blur_far_transition]. + </member> + <member name="dof_blur_far_transition" type="float" setter="set_dof_blur_far_transition" getter="get_dof_blur_far_transition" default="5.0"> + When positive, distance over which (starting from [member dof_blur_far_distance]) blur effect will scale from 0 to [member dof_blur_amount]. When negative, uses physically-based scaling so depth of field effect will scale from 0 at [member dof_blur_far_distance] and will increase in a physically accurate way as objects get further from the [Camera3D]. + </member> + <member name="dof_blur_near_distance" type="float" setter="set_dof_blur_near_distance" getter="get_dof_blur_near_distance" default="2.0"> + Objects closer from the [Camera3D] by this amount will be blurred by the depth of field effect. Measured in meters. + </member> + <member name="dof_blur_near_enabled" type="bool" setter="set_dof_blur_near_enabled" getter="is_dof_blur_near_enabled" default="false"> + Enables depth of field blur for objects closer than [member dof_blur_near_distance]. Strength of blur is controlled by [member dof_blur_amount] and modulated by [member dof_blur_near_transition]. + </member> + <member name="dof_blur_near_transition" type="float" setter="set_dof_blur_near_transition" getter="get_dof_blur_near_transition" default="1.0"> + When positive, distance over which blur effect will scale from 0 to [member dof_blur_amount], ending at [member dof_blur_near_distance]. When negative, uses physically-based scaling so depth of field effect will scale from 0 at [member dof_blur_near_distance] and will increase in a physically accurate way as objects get closer to the [Camera3D]. + </member> + </members> +</class> diff --git a/doc/classes/CameraEffects.xml b/doc/classes/CameraEffects.xml deleted file mode 100644 index c108b30f23..0000000000 --- a/doc/classes/CameraEffects.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="CameraEffects" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> - <brief_description> - Contains camera-specific effects such as depth of field and exposure override. - </brief_description> - <description> - Contains camera-specific effects such as depth of field and exposure override. - See also [Environment] for general 3D environment settings. - </description> - <tutorials> - </tutorials> - <members> - <member name="dof_blur_amount" type="float" setter="set_dof_blur_amount" getter="get_dof_blur_amount" default="0.1"> - The amount of blur for both near and far depth-of-field effects. The amount of blur increases the radius of the blur effect, making the affected area blurrier. However, If the amount is too high, you might start to see lines appearing, especially when using a low quality blur. - </member> - <member name="dof_blur_far_distance" type="float" setter="set_dof_blur_far_distance" getter="get_dof_blur_far_distance" default="10.0"> - The distance from the camera where the far blur effect affects the rendering. - </member> - <member name="dof_blur_far_enabled" type="bool" setter="set_dof_blur_far_enabled" getter="is_dof_blur_far_enabled" default="false"> - If [code]true[/code], enables the depth-of-field far blur effect. This has a significant performance cost. Consider disabling it in scenes where there are no far away objects. - </member> - <member name="dof_blur_far_transition" type="float" setter="set_dof_blur_far_transition" getter="get_dof_blur_far_transition" default="5.0"> - The length of the transition between the no-blur area and far blur. - </member> - <member name="dof_blur_near_distance" type="float" setter="set_dof_blur_near_distance" getter="get_dof_blur_near_distance" default="2.0"> - Distance from the camera where the near blur effect affects the rendering. - </member> - <member name="dof_blur_near_enabled" type="bool" setter="set_dof_blur_near_enabled" getter="is_dof_blur_near_enabled" default="false"> - If [code]true[/code], enables the depth-of-field near blur effect. This has a significant performance cost. Consider disabling it in scenes where there are no nearby objects. - </member> - <member name="dof_blur_near_transition" type="float" setter="set_dof_blur_near_transition" getter="get_dof_blur_near_transition" default="1.0"> - The length of the transition between the near blur and no-blur area. - </member> - <member name="override_exposure" type="float" setter="set_override_exposure" getter="get_override_exposure" default="1.0"> - The exposure override value to use. Higher values will result in a brighter scene. Only effective if [member override_exposure_enabled] is [code]true[/code]. - </member> - <member name="override_exposure_enabled" type="bool" setter="set_override_exposure_enabled" getter="is_override_exposure_enabled" default="false"> - If [code]true[/code], overrides the manual or automatic exposure defined in the [Environment] with the value in [member override_exposure]. - </member> - </members> -</class> diff --git a/doc/classes/Curve.xml b/doc/classes/Curve.xml index ae9add995b..20b9dafd0d 100644 --- a/doc/classes/Curve.xml +++ b/doc/classes/Curve.xml @@ -74,27 +74,27 @@ Returns the right tangent angle (in degrees) for the point at [param index]. </description> </method> - <method name="interpolate" qualifiers="const"> + <method name="remove_point"> + <return type="void" /> + <param index="0" name="index" type="int" /> + <description> + Removes the point at [code]index[/code] from the curve. + </description> + </method> + <method name="sample" qualifiers="const"> <return type="float" /> <param index="0" name="offset" type="float" /> <description> Returns the Y value for the point that would exist at the X position [param offset] along the curve. </description> </method> - <method name="interpolate_baked" qualifiers="const"> + <method name="sample_baked" qualifiers="const"> <return type="float" /> <param index="0" name="offset" type="float" /> <description> Returns the Y value for the point that would exist at the X position [param offset] along the curve using the baked cache. Bakes the curve's points if not already baked. </description> </method> - <method name="remove_point"> - <return type="void" /> - <param index="0" name="index" type="int" /> - <description> - Removes the point at [param index] from the curve. - </description> - </method> <method name="set_point_left_mode"> <return type="void" /> <param index="0" name="index" type="int" /> diff --git a/doc/classes/Curve2D.xml b/doc/classes/Curve2D.xml index f15c0d74ca..cc4124d084 100644 --- a/doc/classes/Curve2D.xml +++ b/doc/classes/Curve2D.xml @@ -43,7 +43,7 @@ <return type="float" /> <param index="0" name="to_point" type="Vector2" /> <description> - Returns the closest offset to [param to_point]. This offset is meant to be used in [method interpolate_baked]. + Returns the closest offset to [param to_point]. This offset is meant to be used in [method sample_baked]. [param to_point] must be in this curve's local space. </description> </method> @@ -76,7 +76,14 @@ Returns the position of the vertex [param idx]. If the index is out of bounds, the function sends an error to the console, and returns [code](0, 0)[/code]. </description> </method> - <method name="interpolate" qualifiers="const"> + <method name="remove_point"> + <return type="void" /> + <param index="0" name="idx" type="int" /> + <description> + Deletes the point [code]idx[/code] from the curve. Sends an error to the console if [code]idx[/code] is out of bounds. + </description> + </method> + <method name="sample" qualifiers="const"> <return type="Vector2" /> <param index="0" name="idx" type="int" /> <param index="1" name="t" type="float" /> @@ -85,7 +92,7 @@ If [param idx] is out of bounds it is truncated to the first or last vertex, and [param t] is ignored. If the curve has no points, the function sends an error to the console, and returns [code](0, 0)[/code]. </description> </method> - <method name="interpolate_baked" qualifiers="const"> + <method name="sample_baked" qualifiers="const"> <return type="Vector2" /> <param index="0" name="offset" type="float" /> <param index="1" name="cubic" type="bool" default="false" /> @@ -95,18 +102,11 @@ Cubic interpolation tends to follow the curves better, but linear is faster (and often, precise enough). </description> </method> - <method name="interpolatef" qualifiers="const"> + <method name="samplef" qualifiers="const"> <return type="Vector2" /> <param index="0" name="fofs" type="float" /> <description> - Returns the position at the vertex [param fofs]. It calls [method interpolate] using the integer part of [param fofs] as [code]idx[/code], and its fractional part as [code]t[/code]. - </description> - </method> - <method name="remove_point"> - <return type="void" /> - <param index="0" name="idx" type="int" /> - <description> - Deletes the point [param idx] from the curve. Sends an error to the console if [param idx] is out of bounds. + Returns the position at the vertex [param fofs]. It calls [method sample] using the integer part of [param fofs] as [code]idx[/code], and its fractional part as [code]t[/code]. </description> </method> <method name="set_point_in"> diff --git a/doc/classes/Curve3D.xml b/doc/classes/Curve3D.xml index 0843453820..3e4e05f51a 100644 --- a/doc/classes/Curve3D.xml +++ b/doc/classes/Curve3D.xml @@ -56,7 +56,7 @@ <return type="float" /> <param index="0" name="to_point" type="Vector3" /> <description> - Returns the closest offset to [param to_point]. This offset is meant to be used in [method interpolate_baked] or [method interpolate_baked_up_vector]. + Returns the closest offset to [param to_point]. This offset is meant to be used in [method sample_baked] or [method sample_baked_up_vector]. [param to_point] must be in this curve's local space. </description> </method> @@ -96,7 +96,14 @@ Returns the tilt angle in radians for the point [param idx]. If the index is out of bounds, the function sends an error to the console, and returns [code]0[/code]. </description> </method> - <method name="interpolate" qualifiers="const"> + <method name="remove_point"> + <return type="void" /> + <param index="0" name="idx" type="int" /> + <description> + Deletes the point [code]idx[/code] from the curve. Sends an error to the console if [code]idx[/code] is out of bounds. + </description> + </method> + <method name="sample" qualifiers="const"> <return type="Vector3" /> <param index="0" name="idx" type="int" /> <param index="1" name="t" type="float" /> @@ -105,7 +112,7 @@ If [param idx] is out of bounds it is truncated to the first or last vertex, and [param t] is ignored. If the curve has no points, the function sends an error to the console, and returns [code](0, 0, 0)[/code]. </description> </method> - <method name="interpolate_baked" qualifiers="const"> + <method name="sample_baked" qualifiers="const"> <return type="Vector3" /> <param index="0" name="offset" type="float" /> <param index="1" name="cubic" type="bool" default="false" /> @@ -115,7 +122,7 @@ Cubic interpolation tends to follow the curves better, but linear is faster (and often, precise enough). </description> </method> - <method name="interpolate_baked_up_vector" qualifiers="const"> + <method name="sample_baked_up_vector" qualifiers="const"> <return type="Vector3" /> <param index="0" name="offset" type="float" /> <param index="1" name="apply_tilt" type="bool" default="false" /> @@ -125,18 +132,11 @@ If the curve has no up vectors, the function sends an error to the console, and returns [code](0, 1, 0)[/code]. </description> </method> - <method name="interpolatef" qualifiers="const"> + <method name="samplef" qualifiers="const"> <return type="Vector3" /> <param index="0" name="fofs" type="float" /> <description> - Returns the position at the vertex [param fofs]. It calls [method interpolate] using the integer part of [param fofs] as [code]idx[/code], and its fractional part as [code]t[/code]. - </description> - </method> - <method name="remove_point"> - <return type="void" /> - <param index="0" name="idx" type="int" /> - <description> - Deletes the point [param idx] from the curve. Sends an error to the console if [param idx] is out of bounds. + Returns the position at the vertex [param fofs]. It calls [method sample] using the integer part of [param fofs] as [code]idx[/code], and its fractional part as [code]t[/code]. </description> </method> <method name="set_point_in"> diff --git a/doc/classes/EditorVCSInterface.xml b/doc/classes/EditorVCSInterface.xml index 89ba79f7d9..cca6a0591f 100644 --- a/doc/classes/EditorVCSInterface.xml +++ b/doc/classes/EditorVCSInterface.xml @@ -1,97 +1,276 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="EditorVCSInterface" inherits="Object" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Version Control System (VCS) interface which reads and writes to the local VCS in use. + Version Control System (VCS) interface, which reads and writes to the local VCS in use. </brief_description> <description> - Used by the editor to display VCS extracted information in the editor. The implementation of this API is included in VCS addons, which are essentially GDExtension plugins that need to be put into the project folder. These VCS addons are scripts which are attached (on demand) to the object instance of [code]EditorVCSInterface[/code]. All the functions listed below, instead of performing the task themselves, they call the internally defined functions in the VCS addons to provide a plug-n-play experience. + Defines the API that the editor uses to extract information from the underlying VCS. The implementation of this API is included in VCS plugins, which are GDExtension plugins that inherit [EditorVCSInterface] and are attached (on demand) to the singleton instance of [EditorVCSInterface]. Instead of performing the task themselves, all the virtual functions listed below are calling the internally overridden functions in the VCS plugins to provide a plug-n-play experience. A custom VCS plugin is supposed to inherit from [EditorVCSInterface] and override each of these virtual functions. </description> <tutorials> </tutorials> <methods> - <method name="commit"> + <method name="_checkout_branch" qualifiers="virtual"> + <return type="bool" /> + <param index="0" name="branch_name" type="String" /> + <description> + Checks out a [code]branch_name[/code] in the VCS. + </description> + </method> + <method name="_commit" qualifiers="virtual"> <return type="void" /> <param index="0" name="msg" type="String" /> <description> - Creates a version commit if the addon is initialized, else returns without doing anything. Uses the files which have been staged previously, with the commit message set to a value as provided as in the argument. + Commits the currently staged changes and applies the commit [code]msg[/code] to the resulting commit. </description> </method> - <method name="get_file_diff"> - <return type="Dictionary[]" /> + <method name="_create_branch" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="branch_name" type="String" /> + <description> + Creates a new branch named [code]branch_name[/code] in the VCS. + </description> + </method> + <method name="_create_remote" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="remote_name" type="String" /> + <param index="1" name="remote_url" type="String" /> + <description> + Creates a new remote destination with name [code]remote_name[/code] and points it to [code]remote_url[/code]. This can be an HTTPS remote or an SSH remote. + </description> + </method> + <method name="_discard_file" qualifiers="virtual"> + <return type="void" /> <param index="0" name="file_path" type="String" /> <description> - Returns an [Array] of [Dictionary] objects containing the diff output from the VCS in use, if a VCS addon is initialized, else returns an empty [Array] object. The diff contents also consist of some contextual lines which provide context to the observed line change in the file. - Each [Dictionary] object has the line diff contents under the keys: - - [code]"content"[/code] to store a [String] containing the line contents - - [code]"status"[/code] to store a [String] which contains [code]"+"[/code] in case the content is a line addition but it stores a [code]"-"[/code] in case of deletion and an empty string in the case the line content is neither an addition nor a deletion. - - [code]"new_line_number"[/code] to store an integer containing the new line number of the line content. - - [code]"line_count"[/code] to store an integer containing the number of lines in the line content. - - [code]"old_line_number"[/code] to store an integer containing the old line number of the line content. - - [code]"offset"[/code] to store the offset of the line change since the first contextual line content. + Discards the changes made in a file present at [code]file_path[/code]. </description> </method> - <method name="get_modified_files_data"> - <return type="Dictionary" /> + <method name="_fetch" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="remote" type="String" /> <description> - Returns a [Dictionary] containing the path of the detected file change mapped to an integer signifying what kind of change the corresponding file has experienced. - The following integer values are being used to signify that the detected file is: - - [code]0[/code]: New to the VCS working directory - - [code]1[/code]: Modified - - [code]2[/code]: Renamed - - [code]3[/code]: Deleted - - [code]4[/code]: Typechanged + Fetches new changes from the remote, but doesn't write changes to the current working directory. Equivalent to [code]git fetch[/code]. </description> </method> - <method name="get_project_name"> + <method name="_get_branch_list" qualifiers="virtual"> + <return type="Array" /> + <description> + Gets an instance of an [Array] of [String]s containing available branch names in the VCS. + </description> + </method> + <method name="_get_current_branch_name" qualifiers="virtual"> <return type="String" /> <description> - Returns the project name of the VCS working directory. + Gets the current branch name defined in the VCS. + </description> + </method> + <method name="_get_diff" qualifiers="virtual"> + <return type="Dictionary[]" /> + <param index="0" name="identifier" type="String" /> + <param index="1" name="area" type="int" /> + <description> + Returns an array of [Dictionary] items (see [method create_diff_file], [method create_diff_hunk], [method create_diff_line], [method add_line_diffs_into_diff_hunk] and [method add_diff_hunks_into_diff_file]), each containing information about a diff. If [code]identifier[/code] is a file path, returns a file diff, and if it is a commit identifier, then returns a commit diff. + </description> + </method> + <method name="_get_line_diff" qualifiers="virtual"> + <return type="Array" /> + <param index="0" name="file_path" type="String" /> + <param index="1" name="text" type="String" /> + <description> + Returns an [Array] of [Dictionary] items (see [method create_diff_hunk]), each containing a line diff between a file at [code]file_path[/code] and the [code]text[/code] which is passed in. + </description> + </method> + <method name="_get_modified_files_data" qualifiers="virtual"> + <return type="Array" /> + <description> + Returns an [Array] of [Dictionary] items (see [method create_status_file]), each containing the status data of every modified file in the project folder. + </description> + </method> + <method name="_get_previous_commits" qualifiers="virtual"> + <return type="Array" /> + <param index="0" name="max_commits" type="int" /> + <description> + Returns an [Array] of [Dictionary] items (see [method create_commit]), each containing the data for a past commit. </description> </method> - <method name="get_vcs_name"> + <method name="_get_remotes" qualifiers="virtual"> + <return type="Array" /> + <description> + Returns an [Array] of [String]s, each containing the name of a remote configured in the VCS. + </description> + </method> + <method name="_get_vcs_name" qualifiers="virtual"> <return type="String" /> <description> - Returns the name of the VCS if the VCS has been initialized, else return an empty string. + Returns the name of the underlying VCS provider. </description> </method> - <method name="initialize"> + <method name="_initialize" qualifiers="virtual"> <return type="bool" /> - <param index="0" name="project_root_path" type="String" /> + <param index="0" name="project_path" type="String" /> <description> - Initializes the VCS addon if not already. Uses the argument value as the path to the working directory of the project. Creates the initial commit if required. Returns [code]true[/code] if no failure occurs, else returns [code]false[/code]. + Initializes the VCS plugin when called from the editor. Returns whether or not the plugin was successfully initialized. A VCS project is initialized at [code]project_path[/code]. </description> </method> - <method name="is_addon_ready"> - <return type="bool" /> + <method name="_pull" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="remote" type="String" /> <description> - Returns [code]true[/code] if the addon is ready to respond to function calls, else returns [code]false[/code]. + Pulls changes from the remote. This can give rise to merge conflicts. </description> </method> - <method name="is_vcs_initialized"> - <return type="bool" /> + <method name="_push" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="remote" type="String" /> + <param index="1" name="force" type="bool" /> + <description> + Pushes changes to the [code]remote[/code]. Optionally, if [code]force[/code] is set to true, a force push will override the change history already present on the remote. + </description> + </method> + <method name="_remove_branch" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="branch_name" type="String" /> <description> - Returns [code]true[/code] if the VCS addon has been initialized, else returns [code]false[/code]. + Remove a branch from the local VCS. </description> </method> - <method name="shut_down"> + <method name="_remove_remote" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="remote_name" type="String" /> + <description> + Remove a remote from the local VCS. + </description> + </method> + <method name="_set_credentials" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="username" type="String" /> + <param index="1" name="password" type="String" /> + <param index="2" name="ssh_public_key_path" type="String" /> + <param index="3" name="ssh_private_key_path" type="String" /> + <param index="4" name="ssh_passphrase" type="String" /> + <description> + Set user credentials in the underlying VCS. [code]username[/code] and [code]password[/code] are used only during HTTPS authentication unless not already mentioned in the remote URL. [code]ssh_public_key_path[/code], [code]ssh_private_key_path[/code], and [code]ssh_passphrase[/code] are only used during SSH authentication. + </description> + </method> + <method name="_shut_down" qualifiers="virtual"> <return type="bool" /> <description> - Shuts down the VCS addon to allow cleanup code to run on call. Returns [code]true[/code] is no failure occurs, else returns [code]false[/code]. + Shuts down VCS plugin instance. Called when the user either closes the editor or shuts down the VCS plugin through the editor UI. </description> </method> - <method name="stage_file"> + <method name="_stage_file" qualifiers="virtual"> <return type="void" /> <param index="0" name="file_path" type="String" /> <description> - Stages the file which should be committed when [method EditorVCSInterface.commit] is called. Argument should contain the absolute path. + Stages the file present at [code]file_path[/code] to the staged area. </description> </method> - <method name="unstage_file"> + <method name="_unstage_file" qualifiers="virtual"> <return type="void" /> <param index="0" name="file_path" type="String" /> <description> - Unstages the file which was staged previously to be committed, so that it is no longer committed when [method EditorVCSInterface.commit] is called. Argument should contain the absolute path. + Unstages the file present at [code]file_path[/code] from the staged area to the unstaged area. + </description> + </method> + <method name="add_diff_hunks_into_diff_file"> + <return type="Dictionary" /> + <param index="0" name="diff_file" type="Dictionary" /> + <param index="1" name="diff_hunks" type="Array" /> + <description> + Helper function to add an array of [code]diff_hunks[/code] into a [code]diff_file[/code]. + </description> + </method> + <method name="add_line_diffs_into_diff_hunk"> + <return type="Dictionary" /> + <param index="0" name="diff_hunk" type="Dictionary" /> + <param index="1" name="line_diffs" type="Array" /> + <description> + Helper function to add an array of [code]line_diffs[/code] into a [code]diff_hunk[/code]. + </description> + </method> + <method name="create_commit"> + <return type="Dictionary" /> + <param index="0" name="msg" type="String" /> + <param index="1" name="author" type="String" /> + <param index="2" name="id" type="String" /> + <param index="3" name="unix_timestamp" type="int" /> + <param index="4" name="offset_minutes" type="int" /> + <description> + Helper function to create a commit [Dictionary] item. [code]msg[/code] is the commit message of the commit. [code]author[/code] is a single human-readable string containing all the author's details, e.g. the email and name configured in the VCS. [code]id[/code] is the identifier of the commit, in whichever format your VCS may provide an identifier to commits. [code]unix_timestamp[/code] is the UTC Unix timestamp of when the commit was created. [code]offset_minutes[/code] is the timezone offset in minutes, recorded from the system timezone where the commit was created. + </description> + </method> + <method name="create_diff_file"> + <return type="Dictionary" /> + <param index="0" name="new_file" type="String" /> + <param index="1" name="old_file" type="String" /> + <description> + Helper function to create a [code]Dictionary[/code] for storing old and new diff file paths. + </description> + </method> + <method name="create_diff_hunk"> + <return type="Dictionary" /> + <param index="0" name="old_start" type="int" /> + <param index="1" name="new_start" type="int" /> + <param index="2" name="old_lines" type="int" /> + <param index="3" name="new_lines" type="int" /> + <description> + Helper function to create a [code]Dictionary[/code] for storing diff hunk data. [code]old_start[/code] is the starting line number in old file. [code]new_start[/code] is the starting line number in new file. [code]old_lines[/code] is the number of lines in the old file. [code]new_lines[/code] is the number of lines in the new file. + </description> + </method> + <method name="create_diff_line"> + <return type="Dictionary" /> + <param index="0" name="new_line_no" type="int" /> + <param index="1" name="old_line_no" type="int" /> + <param index="2" name="content" type="String" /> + <param index="3" name="status" type="String" /> + <description> + Helper function to create a [code]Dictionary[/code] for storing a line diff. [code]new_line_no[/code] is the line number in the new file (can be [code]-1[/code] if the line is deleted). [code]old_line_no[/code] is the line number in the old file (can be [code]-1[/code] if the line is added). [code]content[/code] is the diff text. [code]status[/code] is a single character string which stores the line origin. + </description> + </method> + <method name="create_status_file"> + <return type="Dictionary" /> + <param index="0" name="file_path" type="String" /> + <param index="1" name="change_type" type="int" enum="EditorVCSInterface.ChangeType" /> + <param index="2" name="area" type="int" enum="EditorVCSInterface.TreeArea" /> + <description> + Helper function to create a [code]Dictionary[/code] used by editor to read the status of a file. + </description> + </method> + <method name="popup_error"> + <return type="void" /> + <param index="0" name="msg" type="String" /> + <description> + Pops up an error message in the edior which is shown as coming from the underlying VCS. Use this to show VCS specific error messages. </description> </method> </methods> + <constants> + <constant name="CHANGE_TYPE_NEW" value="0" enum="ChangeType"> + A new file has been added. + </constant> + <constant name="CHANGE_TYPE_MODIFIED" value="1" enum="ChangeType"> + An earlier added file has been modified. + </constant> + <constant name="CHANGE_TYPE_RENAMED" value="2" enum="ChangeType"> + An earlier added file has been renamed. + </constant> + <constant name="CHANGE_TYPE_DELETED" value="3" enum="ChangeType"> + An earlier added file has been deleted. + </constant> + <constant name="CHANGE_TYPE_TYPECHANGE" value="4" enum="ChangeType"> + An earlier added file has been typechanged. + </constant> + <constant name="CHANGE_TYPE_UNMERGED" value="5" enum="ChangeType"> + A file is left unmerged. + </constant> + <constant name="TREE_AREA_COMMIT" value="0" enum="TreeArea"> + A commit is encountered from the commit area. + </constant> + <constant name="TREE_AREA_STAGED" value="1" enum="TreeArea"> + A file is encountered from the staged area. + </constant> + <constant name="TREE_AREA_UNSTAGED" value="2" enum="TreeArea"> + A file is encountered from the unstaged area. + </constant> + </constants> </class> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index 864dbd423a..0182d543e1 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -63,21 +63,6 @@ <member name="ambient_light_source" type="int" setter="set_ambient_source" getter="get_ambient_source" enum="Environment.AmbientSource" default="0"> The ambient light source to use for rendering materials and global illumination. </member> - <member name="auto_exposure_enabled" type="bool" setter="set_tonemap_auto_exposure_enabled" getter="is_tonemap_auto_exposure_enabled" default="false"> - If [code]true[/code], enables the tonemapping auto exposure mode of the scene renderer. If [code]true[/code], the renderer will automatically determine the exposure setting to adapt to the scene's illumination and the observed light. - </member> - <member name="auto_exposure_max_luma" type="float" setter="set_tonemap_auto_exposure_max" getter="get_tonemap_auto_exposure_max" default="8.0"> - The maximum luminance value for the auto exposure. - </member> - <member name="auto_exposure_min_luma" type="float" setter="set_tonemap_auto_exposure_min" getter="get_tonemap_auto_exposure_min" default="0.05"> - The minimum luminance value for the auto exposure. - </member> - <member name="auto_exposure_scale" type="float" setter="set_tonemap_auto_exposure_grey" getter="get_tonemap_auto_exposure_grey" default="0.4"> - The scale of the auto exposure effect. Affects the intensity of auto exposure. - </member> - <member name="auto_exposure_speed" type="float" setter="set_tonemap_auto_exposure_speed" getter="get_tonemap_auto_exposure_speed" default="0.5"> - The speed of the auto exposure effect. Affects the time needed for the camera to perform auto exposure. - </member> <member name="background_camera_feed_id" type="int" setter="set_camera_feed_id" getter="get_camera_feed_id" default="1"> The ID of the camera feed to show in the background. </member> @@ -87,8 +72,11 @@ <member name="background_color" type="Color" setter="set_bg_color" getter="get_bg_color" default="Color(0, 0, 0, 1)"> The [Color] displayed for clear areas of the scene. Only effective when using the [constant BG_COLOR] background mode. </member> - <member name="background_energy" type="float" setter="set_bg_energy" getter="get_bg_energy" default="1.0"> - The power of the light emitted by the background. + <member name="background_energy_multiplier" type="float" setter="set_bg_energy_multiplier" getter="get_bg_energy_multiplier" default="1.0"> + Multiplier for background energy. Increase to make background brighter, decrease to make background dimmer. + </member> + <member name="background_intensity" type="float" setter="set_bg_intensity" getter="get_bg_intensity" default="30000.0"> + Luminance of background measured in nits (candela per square meter). Only used when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is enabled. The default value is roughly equivalent to the sky at midday. </member> <member name="background_mode" type="int" setter="set_background" getter="get_background" enum="Environment.BGMode" default="0"> The background mode. See [enum BGMode] for possible values. diff --git a/doc/classes/Gradient.xml b/doc/classes/Gradient.xml index f081174b67..33f0a7979a 100644 --- a/doc/classes/Gradient.xml +++ b/doc/classes/Gradient.xml @@ -38,13 +38,6 @@ Returns the number of colors in the gradient. </description> </method> - <method name="interpolate"> - <return type="Color" /> - <param index="0" name="offset" type="float" /> - <description> - Returns the interpolated color specified by [param offset]. - </description> - </method> <method name="remove_point"> <return type="void" /> <param index="0" name="point" type="int" /> @@ -58,6 +51,13 @@ Reverses/mirrors the gradient. </description> </method> + <method name="sample"> + <return type="Color" /> + <param index="0" name="offset" type="float" /> + <description> + Returns the interpolated color specified by [code]offset[/code]. + </description> + </method> <method name="set_color"> <return type="void" /> <param index="0" name="point" type="int" /> diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml index 80ff83ec46..e9ebbc0a41 100644 --- a/doc/classes/Light3D.xml +++ b/doc/classes/Light3D.xml @@ -11,6 +11,12 @@ <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <methods> + <method name="get_correlated_color" qualifiers="const"> + <return type="Color" /> + <description> + Returns the [Color] of an idealized blackbody at the given [member light_temperature]. This value is calculated internally based on the [member light_temperature]. This [Color] is multiplied by [member light_color] before being sent to the [RenderingServer]. + </description> + </method> <method name="get_param" qualifiers="const"> <return type="float" /> <param index="0" name="param" type="int" enum="Light3D.Param" /> @@ -67,6 +73,15 @@ Secondary multiplier used with indirect light (light bounces). Used with [VoxelGI] and SDFGI (see [member Environment.sdfgi_enabled]). [b]Note:[/b] This property is ignored if [member light_energy] is equal to [code]0.0[/code], as the light won't be present at all in the GI shader. </member> + <member name="light_intensity_lumens" type="float" setter="set_param" getter="get_param"> + Used by positional lights ([OmniLight3D] and [SpotLight3D]) when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is [code]true[/code]. Sets the intensity of the light source measured in Lumens. Lumens are a measure of luminous flux, which is the total amount of visible light emitted by a light source per unit of time. + For [SpotLight3D]s, we assume that the area outside the visible cone is surrounded by a perfect light absorbing material. Accordingly, the apparent brightness of the cone area does not change as the cone increases and decreases in size. + A typical household lightbulb can range from around 600 lumens to 1,200 lumens, a candle is about 13 lumens, while a streetlight can be approximately 60,000 lumens. + </member> + <member name="light_intensity_lux" type="float" setter="set_param" getter="get_param"> + Used by [DirectionalLight3D]s when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is [code]true[/code]. Sets the intensity of the light source measured in Lux. Lux is a measure pf luminous flux per unit area, it is equal to one lumen per square metre. Lux is the measure of how much light hits a surface at a given time. + On a clear sunny day a surface in direct sunlight may be approximately 100,000 lux, a typical room in a home may be approximately 50 lux, while the moonlit ground may be approximately 0.1 lux. + </member> <member name="light_negative" type="bool" setter="set_negative" getter="is_negative" default="false"> If [code]true[/code], the light's effect is reversed, darkening areas and casting bright shadows. </member> @@ -80,6 +95,14 @@ <member name="light_specular" type="float" setter="set_param" getter="get_param" default="0.5"> The intensity of the specular blob in objects affected by the light. At [code]0[/code], the light becomes a pure diffuse light. When not baking emission, this can be used to avoid unrealistic reflections when placing lights above an emissive surface. </member> + <member name="light_temperature" type="float" setter="set_temperature" getter="get_temperature"> + Sets the color temperature of the light source, measured in Kelvin. This is used to calculate a correlated color temperature which tints the [member light_color]. + The sun on a cloudy day is approximately 6500 Kelvin, on a clear day it is between 5500 to 6000 Kelvin, and on a clear day at sunrise or sunset it ranges to around 1850 Kelvin. + </member> + <member name="light_volumetric_fog_energy" type="float" setter="set_param" getter="get_param" default="1.0"> + Secondary multiplier multiplied with [member light_energy] then used with the [Environment]'s volumetric fog (if enabled). If set to [code]0.0[/code], computing volumetric fog will be skipped for this light, which can improve performance for large amounts of lights when volumetric fog is enabled. + [b]Note:[/b] To prevent short-lived dynamic light effects from poorly interacting with volumetric fog, lights used in those effects should have [member light_volumetric_fog_energy] set to [code]0.0[/code] unless [member Environment.volumetric_fog_temporal_reprojection_enabled] is disabled (or unless the reprojection amount is significantly lowered). + </member> <member name="shadow_bias" type="float" setter="set_param" getter="get_param" default="0.1"> Used to adjust shadow appearance. Too small a value results in self-shadowing ("shadow acne"), while too large a value causes shadows to separate from casters ("peter-panning"). Adjust as needed. </member> @@ -89,8 +112,6 @@ <member name="shadow_enabled" type="bool" setter="set_shadow" getter="has_shadow" default="false"> If [code]true[/code], the light will cast real-time shadows. This has a significant performance cost. Only enable shadow rendering when it makes a noticeable difference in the scene's appearance, and consider using [member distance_fade_enabled] to hide the light when far away from the [Camera3D]. </member> - <member name="shadow_fog_fade" type="float" setter="set_param" getter="get_param" default="0.1"> - </member> <member name="shadow_normal_bias" type="float" setter="set_param" getter="get_param" default="1.0"> Offsets the lookup into the shadow map by the object's normal. This can be used to reduce self-shadowing artifacts without using [member shadow_bias]. In practice, this value should be tweaked along with [member shadow_bias] to reduce artifacts as much as possible. </member> @@ -110,61 +131,64 @@ <constant name="PARAM_INDIRECT_ENERGY" value="1" enum="Param"> Constant for accessing [member light_indirect_energy]. </constant> - <constant name="PARAM_SPECULAR" value="2" enum="Param"> + <constant name="PARAM_VOLUMETRIC_FOG_ENERGY" value="2" enum="Param"> + Constant for accessing [member light_volumetric_fog_energy]. + </constant> + <constant name="PARAM_SPECULAR" value="3" enum="Param"> Constant for accessing [member light_specular]. </constant> - <constant name="PARAM_RANGE" value="3" enum="Param"> + <constant name="PARAM_RANGE" value="4" enum="Param"> Constant for accessing [member OmniLight3D.omni_range] or [member SpotLight3D.spot_range]. </constant> - <constant name="PARAM_SIZE" value="4" enum="Param"> + <constant name="PARAM_SIZE" value="5" enum="Param"> Constant for accessing [member light_size]. </constant> - <constant name="PARAM_ATTENUATION" value="5" enum="Param"> + <constant name="PARAM_ATTENUATION" value="6" enum="Param"> Constant for accessing [member OmniLight3D.omni_attenuation] or [member SpotLight3D.spot_attenuation]. </constant> - <constant name="PARAM_SPOT_ANGLE" value="6" enum="Param"> + <constant name="PARAM_SPOT_ANGLE" value="7" enum="Param"> Constant for accessing [member SpotLight3D.spot_angle]. </constant> - <constant name="PARAM_SPOT_ATTENUATION" value="7" enum="Param"> + <constant name="PARAM_SPOT_ATTENUATION" value="8" enum="Param"> Constant for accessing [member SpotLight3D.spot_angle_attenuation]. </constant> - <constant name="PARAM_SHADOW_MAX_DISTANCE" value="8" enum="Param"> + <constant name="PARAM_SHADOW_MAX_DISTANCE" value="9" enum="Param"> Constant for accessing [member DirectionalLight3D.directional_shadow_max_distance]. </constant> - <constant name="PARAM_SHADOW_SPLIT_1_OFFSET" value="9" enum="Param"> + <constant name="PARAM_SHADOW_SPLIT_1_OFFSET" value="10" enum="Param"> Constant for accessing [member DirectionalLight3D.directional_shadow_split_1]. </constant> - <constant name="PARAM_SHADOW_SPLIT_2_OFFSET" value="10" enum="Param"> + <constant name="PARAM_SHADOW_SPLIT_2_OFFSET" value="11" enum="Param"> Constant for accessing [member DirectionalLight3D.directional_shadow_split_2]. </constant> - <constant name="PARAM_SHADOW_SPLIT_3_OFFSET" value="11" enum="Param"> + <constant name="PARAM_SHADOW_SPLIT_3_OFFSET" value="12" enum="Param"> Constant for accessing [member DirectionalLight3D.directional_shadow_split_3]. </constant> - <constant name="PARAM_SHADOW_FADE_START" value="12" enum="Param"> + <constant name="PARAM_SHADOW_FADE_START" value="13" enum="Param"> Constant for accessing [member DirectionalLight3D.directional_shadow_fade_start]. </constant> - <constant name="PARAM_SHADOW_NORMAL_BIAS" value="13" enum="Param"> + <constant name="PARAM_SHADOW_NORMAL_BIAS" value="14" enum="Param"> Constant for accessing [member shadow_normal_bias]. </constant> - <constant name="PARAM_SHADOW_BIAS" value="14" enum="Param"> + <constant name="PARAM_SHADOW_BIAS" value="15" enum="Param"> Constant for accessing [member shadow_bias]. </constant> - <constant name="PARAM_SHADOW_PANCAKE_SIZE" value="15" enum="Param"> + <constant name="PARAM_SHADOW_PANCAKE_SIZE" value="16" enum="Param"> Constant for accessing [member DirectionalLight3D.directional_shadow_pancake_size]. </constant> - <constant name="PARAM_SHADOW_OPACITY" value="16" enum="Param"> + <constant name="PARAM_SHADOW_OPACITY" value="17" enum="Param"> Constant for accessing [member shadow_opacity]. </constant> - <constant name="PARAM_SHADOW_BLUR" value="17" enum="Param"> + <constant name="PARAM_SHADOW_BLUR" value="18" enum="Param"> Constant for accessing [member shadow_blur]. </constant> - <constant name="PARAM_SHADOW_VOLUMETRIC_FOG_FADE" value="18" enum="Param"> - Constant for accessing [member shadow_fog_fade]. - </constant> <constant name="PARAM_TRANSMITTANCE_BIAS" value="19" enum="Param"> Constant for accessing [member shadow_transmittance_bias]. </constant> - <constant name="PARAM_MAX" value="20" enum="Param"> + <constant name="PARAM_INTENSITY" value="20" enum="Param"> + Constant for accessing [member light_intensity_lumens] and [member light_intensity_lux]. Only used when [member ProjectSettings.rendering/lights_and_shadows/use_physical_light_units] is [code]true[/code]. + </constant> + <constant name="PARAM_MAX" value="21" enum="Param"> Represents the size of the [enum Param] enum. </constant> <constant name="BAKE_DISABLED" value="0" enum="BakeMode"> diff --git a/doc/classes/LightmapGI.xml b/doc/classes/LightmapGI.xml index c0766cd1ec..dd8c7be489 100644 --- a/doc/classes/LightmapGI.xml +++ b/doc/classes/LightmapGI.xml @@ -20,6 +20,9 @@ <member name="bounces" type="int" setter="set_bounces" getter="get_bounces" default="1"> Number of light bounces that are taken into account during baking. Higher values result in brighter, more realistic lighting, at the cost of longer bake times. If set to [code]0[/code], only environment lighting, direct light and emissive lighting is baked. </member> + <member name="camera_attributes" type="CameraAttributes" setter="set_camera_attributes" getter="get_camera_attributes"> + The [CameraAttributes] resource that specifies exposure levels to bake at. Auto-exposure and non exposure properties will be ignored. Exposure settings should be used to reduce the dynamic range present when baking. If exposure is too high, the [LightmapGI] will have banding artifacts or may have over-exposure artifacts. + </member> <member name="directional" type="bool" setter="set_directional" getter="is_directional" default="false"> If [code]true[/code], bakes lightmaps to contain directional information as spherical harmonics. This results in more realistic lighting appearance, especially with normal mapped materials and for lights that have their direct light baked ([member Light3D.light_bake_mode] set to [constant Light3D.BAKE_STATIC]). The directional information is also used to provide rough reflections for static and dynamic objects. This has a small run-time performance cost as the shader has to perform more work to interpret the direction information from the lightmap. Directional lightmaps also take longer to bake and result in larger file sizes. [b]Note:[/b] The property's name has no relationship with [DirectionalLight3D]. [member directional] works with all light types. diff --git a/doc/classes/PhysicalSkyMaterial.xml b/doc/classes/PhysicalSkyMaterial.xml index 7c2ea088c8..9d303d80e5 100644 --- a/doc/classes/PhysicalSkyMaterial.xml +++ b/doc/classes/PhysicalSkyMaterial.xml @@ -11,8 +11,7 @@ <tutorials> </tutorials> <members> - <member name="exposure" type="float" setter="set_exposure" getter="get_exposure" default="0.1"> - Sets the exposure of the sky. Higher exposure values make the entire sky brighter. + <member name="energy_multiplier" type="float" setter="set_energy_multiplier" getter="get_energy_multiplier" default="1.0"> </member> <member name="ground_color" type="Color" setter="set_ground_color" getter="get_ground_color" default="Color(0.1, 0.07, 0.034, 1)"> Modulates the [Color] on the bottom half of the sky to represent the ground. diff --git a/doc/classes/ProceduralSkyMaterial.xml b/doc/classes/ProceduralSkyMaterial.xml index 3cc4bd71f7..6ba8e57380 100644 --- a/doc/classes/ProceduralSkyMaterial.xml +++ b/doc/classes/ProceduralSkyMaterial.xml @@ -17,14 +17,14 @@ <member name="ground_curve" type="float" setter="set_ground_curve" getter="get_ground_curve" default="0.02"> How quickly the [member ground_horizon_color] fades into the [member ground_bottom_color]. </member> - <member name="ground_energy" type="float" setter="set_ground_energy" getter="get_ground_energy" default="1.0"> - Amount of energy contribution from the ground. + <member name="ground_energy_multiplier" type="float" setter="set_ground_energy_multiplier" getter="get_ground_energy_multiplier" default="1.0"> + Multiplier for ground color. A higher value will make the ground brighter. </member> <member name="ground_horizon_color" type="Color" setter="set_ground_horizon_color" getter="get_ground_horizon_color" default="Color(0.6463, 0.6558, 0.6708, 1)"> Color of the ground at the horizon. Blends with [member ground_bottom_color]. </member> <member name="sky_cover" type="Texture2D" setter="set_sky_cover" getter="get_sky_cover"> - The sky cover texture to use. This texture must use an equirectangular projection (similar to [PanoramaSkyMaterial]). The texture's colors will be [i]added[/i] to the existing sky color, and will be multiplied by [member sky_energy] and [member sky_cover_modulate]. This is mainly suited to displaying stars at night, but it can also be used to display clouds at day or night (with a non-physically-accurate look). + The sky cover texture to use. This texture must use an equirectangular projection (similar to [PanoramaSkyMaterial]). The texture's colors will be [i]added[/i] to the existing sky color, and will be multiplied by [member sky_energy_multiplier] and [member sky_cover_modulate]. This is mainly suited to displaying stars at night, but it can also be used to display clouds at day or night (with a non-physically-accurate look). </member> <member name="sky_cover_modulate" type="Color" setter="set_sky_cover_modulate" getter="get_sky_cover_modulate" default="Color(1, 1, 1, 1)"> The tint to apply to the [member sky_cover] texture. This can be used to change the sky cover's colors or opacity independently of the sky energy, which is useful for day/night or weather transitions. Only effective if a texture is defined in [member sky_cover]. @@ -32,8 +32,8 @@ <member name="sky_curve" type="float" setter="set_sky_curve" getter="get_sky_curve" default="0.15"> How quickly the [member sky_horizon_color] fades into the [member sky_top_color]. </member> - <member name="sky_energy" type="float" setter="set_sky_energy" getter="get_sky_energy" default="1.0"> - Amount of energy contribution from the sky. + <member name="sky_energy_multiplier" type="float" setter="set_sky_energy_multiplier" getter="get_sky_energy_multiplier" default="1.0"> + Multiplier for sky color. A higher value will make the sky brighter. </member> <member name="sky_horizon_color" type="Color" setter="set_sky_horizon_color" getter="get_sky_horizon_color" default="Color(0.6463, 0.6558, 0.6708, 1)"> Color of the sky at the horizon. Blends with [member sky_top_color]. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index e8e8ca3725..ffafff0ff4 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1807,6 +1807,55 @@ <member name="rendering/lightmapping/probe_capture/update_speed" type="float" setter="" getter="" default="15"> The framerate-independent update speed when representing dynamic object lighting from [LightmapProbe]s. Higher values make dynamic object lighting update faster. Higher values can prevent fast-moving objects from having "outdated" indirect lighting displayed on them, at the cost of possible flickering when an object moves from a bright area to a shaded area. </member> + <member name="rendering/lights_and_shadows/directional_shadow/16_bits" type="bool" setter="" getter="" default="true"> + Use 16 bits for shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices. + </member> + <member name="rendering/lights_and_shadows/directional_shadow/size" type="int" setter="" getter="" default="4096"> + The directional shadow's size in pixels. Higher values will result in sharper shadows, at the cost of performance. The value will be rounded up to the nearest power of 2. + </member> + <member name="rendering/lights_and_shadows/directional_shadow/size.mobile" type="int" setter="" getter="" default="2048"> + Lower-end override for [member rendering/lights_and_shadows/directional_shadow/size] on mobile devices, due to performance concerns or driver support. + </member> + <member name="rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality" type="int" setter="" getter="" default="2"> + Quality setting for shadows cast by [DirectionalLight3D]s. Higher quality settings use more samples when reading from shadow maps and are thus slower. Low quality settings may result in shadows looking grainy. + [b]Note:[/b] The Soft Very Low setting will automatically multiply [i]constant[/i] shadow blur by 0.75x to reduce the amount of noise visible. This automatic blur change only affects the constant blur factor defined in [member Light3D.shadow_blur], not the variable blur performed by [DirectionalLight3D]s' [member Light3D.light_angular_distance]. + [b]Note:[/b] The Soft High and Soft Ultra settings will automatically multiply [i]constant[/i] shadow blur by 1.5× and 2× respectively to make better use of the increased sample count. This increased blur also improves stability of dynamic object shadows. + </member> + <member name="rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality.mobile" type="int" setter="" getter="" default="0"> + Lower-end override for [member rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality] on mobile devices, due to performance concerns or driver support. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_16_bits" type="bool" setter="" getter="" default="true"> + Use 16 bits for shadow depth map. Enabling this results in shadows having less precision and may result in shadow acne, but can lead to performance improvements on some devices. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv" type="int" setter="" getter="" default="2"> + Subdivision quadrant size for shadow mapping. See shadow mapping documentation. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv" type="int" setter="" getter="" default="2"> + Subdivision quadrant size for shadow mapping. See shadow mapping documentation. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv" type="int" setter="" getter="" default="3"> + Subdivision quadrant size for shadow mapping. See shadow mapping documentation. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv" type="int" setter="" getter="" default="4"> + Subdivision quadrant size for shadow mapping. See shadow mapping documentation. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_size" type="int" setter="" getter="" default="4096"> + Size for shadow atlas (used for OmniLights and SpotLights). See documentation. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/atlas_size.mobile" type="int" setter="" getter="" default="2048"> + Lower-end override for [member rendering/lights_and_shadows/positional_shadow/atlas_size] on mobile devices, due to performance concerns or driver support. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality" type="int" setter="" getter="" default="2"> + Quality setting for shadows cast by [OmniLight3D]s and [SpotLight3D]s. Higher quality settings use more samples when reading from shadow maps and are thus slower. Low quality settings may result in shadows looking grainy. + [b]Note:[/b] The Soft Very Low setting will automatically multiply [i]constant[/i] shadow blur by 0.75x to reduce the amount of noise visible. This automatic blur change only affects the constant blur factor defined in [member Light3D.shadow_blur], not the variable blur performed by [DirectionalLight3D]s' [member Light3D.light_angular_distance]. + [b]Note:[/b] The Soft High and Soft Ultra settings will automatically multiply shadow blur by 1.5× and 2× respectively to make better use of the increased sample count. This increased blur also improves stability of dynamic object shadows. + </member> + <member name="rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality.mobile" type="int" setter="" getter="" default="0"> + Lower-end override for [member rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality] on mobile devices, due to performance concerns or driver support. + </member> + <member name="rendering/lights_and_shadows/use_physical_light_units" type="bool" setter="" getter="" default="false"> + Enables the use of physically based units for light sources. Physically based units tend to be much larger than the arbitrary units used by Godot, but they can be used to match lighting within Godot to real-world lighting. Due to the large dynamic range of lighting conditions present in nature, Godot bakes exposure into the various lighting quantities before rendering. Most light sources bake exposure automatically at run time based on the active [CameraAttributes] resource, but [LightmapGI] and [VoxelGI] require a [CameraAttributes] resource to be set at bake time to reduce the dynamic range. At run time, Godot will automatically reconcile the baked exposure with the active exposure to ensure lighting remains consistent. + </member> <member name="rendering/limits/cluster_builder/max_clustered_elements" type="float" setter="" getter="" default="512"> </member> <member name="rendering/limits/forward_renderer/threaded_render_minimum_instances" type="int" setter="" getter="" default="500"> @@ -1901,50 +1950,6 @@ <member name="rendering/shading/overrides/force_vertex_shading.mobile" type="bool" setter="" getter="" default="true"> Lower-end override for [member rendering/shading/overrides/force_vertex_shading] on mobile devices, due to performance concerns or driver support. </member> - <member name="rendering/shadows/directional_shadow/16_bits" type="bool" setter="" getter="" default="true"> - </member> - <member name="rendering/shadows/directional_shadow/size" type="int" setter="" getter="" default="4096"> - The directional shadow's size in pixels. Higher values will result in sharper shadows, at the cost of performance. The value will be rounded up to the nearest power of 2. - </member> - <member name="rendering/shadows/directional_shadow/size.mobile" type="int" setter="" getter="" default="2048"> - Lower-end override for [member rendering/shadows/directional_shadow/size] on mobile devices, due to performance concerns or driver support. - </member> - <member name="rendering/shadows/directional_shadow/soft_shadow_filter_quality" type="int" setter="" getter="" default="2"> - Quality setting for shadows cast by [DirectionalLight3D]s. Higher quality settings use more samples when reading from shadow maps and are thus slower. Low quality settings may result in shadows looking grainy. - [b]Note:[/b] The Soft Very Low setting will automatically multiply [i]constant[/i] shadow blur by 0.75x to reduce the amount of noise visible. This automatic blur change only affects the constant blur factor defined in [member Light3D.shadow_blur], not the variable blur performed by [DirectionalLight3D]s' [member Light3D.light_angular_distance]. - [b]Note:[/b] The Soft High and Soft Ultra settings will automatically multiply [i]constant[/i] shadow blur by 1.5× and 2× respectively to make better use of the increased sample count. This increased blur also improves stability of dynamic object shadows. - </member> - <member name="rendering/shadows/directional_shadow/soft_shadow_filter_quality.mobile" type="int" setter="" getter="" default="0"> - Lower-end override for [member rendering/shadows/directional_shadow/soft_shadow_filter_quality] on mobile devices, due to performance concerns or driver support. - </member> - <member name="rendering/shadows/positional_shadow/atlas_16_bits" type="bool" setter="" getter="" default="true"> - </member> - <member name="rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv" type="int" setter="" getter="" default="2"> - Subdivision quadrant size for shadow mapping. See shadow mapping documentation. - </member> - <member name="rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv" type="int" setter="" getter="" default="2"> - Subdivision quadrant size for shadow mapping. See shadow mapping documentation. - </member> - <member name="rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv" type="int" setter="" getter="" default="3"> - Subdivision quadrant size for shadow mapping. See shadow mapping documentation. - </member> - <member name="rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv" type="int" setter="" getter="" default="4"> - Subdivision quadrant size for shadow mapping. See shadow mapping documentation. - </member> - <member name="rendering/shadows/positional_shadow/atlas_size" type="int" setter="" getter="" default="4096"> - Size for shadow atlas (used for OmniLights and SpotLights). See documentation. - </member> - <member name="rendering/shadows/positional_shadow/atlas_size.mobile" type="int" setter="" getter="" default="2048"> - Lower-end override for [member rendering/shadows/positional_shadow/atlas_size] on mobile devices, due to performance concerns or driver support. - </member> - <member name="rendering/shadows/positional_shadow/soft_shadow_filter_quality" type="int" setter="" getter="" default="2"> - Quality setting for shadows cast by [OmniLight3D]s and [SpotLight3D]s. Higher quality settings use more samples when reading from shadow maps and are thus slower. Low quality settings may result in shadows looking grainy. - [b]Note:[/b] The Soft Very Low setting will automatically multiply [i]constant[/i] shadow blur by 0.75x to reduce the amount of noise visible. This automatic blur change only affects the constant blur factor defined in [member Light3D.shadow_blur], not the variable blur performed by [DirectionalLight3D]s' [member Light3D.light_angular_distance]. - [b]Note:[/b] The Soft High and Soft Ultra settings will automatically multiply shadow blur by 1.5× and 2× respectively to make better use of the increased sample count. This increased blur also improves stability of dynamic object shadows. - </member> - <member name="rendering/shadows/positional_shadow/soft_shadow_filter_quality.mobile" type="int" setter="" getter="" default="0"> - Lower-end override for [member rendering/shadows/positional_shadow/soft_shadow_filter_quality] on mobile devices, due to performance concerns or driver support. - </member> <member name="rendering/textures/decals/filter" type="int" setter="" getter="" default="3"> The filtering quality to use for [Decal] nodes. When using one of the anisotropic filtering modes, the anisotropic filtering level is controlled by [member rendering/textures/default_filters/anisotropic_filtering_level]. </member> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 755e8a5839..1366099e75 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -26,29 +26,27 @@ <description> </description> </method> - <method name="camera_create"> + <method name="camera_attributes_create"> <return type="RID" /> <description> - Creates a camera and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]camera_*[/code] RenderingServer functions. + Creates a camera attributes object and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]camera_attributes_[/code] RenderingServer functions. Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method. </description> </method> - <method name="camera_effects_create"> - <return type="RID" /> - <description> - </description> - </method> - <method name="camera_effects_set_custom_exposure"> + <method name="camera_attributes_set_auto_exposure"> <return type="void" /> - <param index="0" name="camera_effects" type="RID" /> + <param index="0" name="camera_attributes" type="RID" /> <param index="1" name="enable" type="bool" /> - <param index="2" name="exposure" type="float" /> + <param index="2" name="min_sensitivity" type="float" /> + <param index="3" name="max_sensitivity" type="float" /> + <param index="4" name="speed" type="float" /> + <param index="5" name="scale" type="float" /> <description> </description> </method> - <method name="camera_effects_set_dof_blur"> + <method name="camera_attributes_set_dof_blur"> <return type="void" /> - <param index="0" name="camera_effects" type="RID" /> + <param index="0" name="camera_attributes" type="RID" /> <param index="1" name="far_enable" type="bool" /> <param index="2" name="far_distance" type="float" /> <param index="3" name="far_transition" type="float" /> @@ -59,20 +57,46 @@ <description> </description> </method> - <method name="camera_effects_set_dof_blur_bokeh_shape"> + <method name="camera_attributes_set_dof_blur_bokeh_shape"> <return type="void" /> <param index="0" name="shape" type="int" enum="RenderingServer.DOFBokehShape" /> <description> </description> </method> - <method name="camera_effects_set_dof_blur_quality"> + <method name="camera_attributes_set_dof_blur_quality"> <return type="void" /> <param index="0" name="quality" type="int" enum="RenderingServer.DOFBlurQuality" /> <param index="1" name="use_jitter" type="bool" /> <description> </description> </method> - <method name="camera_set_camera_effects"> + <method name="camera_attributes_set_exposure"> + <return type="void" /> + <param index="0" name="camera_attributes" type="RID" /> + <param index="1" name="multiplier" type="float" /> + <param index="2" name="normalization" type="float" /> + <description> + Sets the exposure values that will be used by the renderers. The normalization amount is used to bake a given Exposure Value (EV) into rendering calculations to reduce the dynamic range of the scene. + The normalization factor can be calculated from exposure value (EV100) as follows: + [codeblock] + func get_exposure_normalization(float ev100): + return 1.0 / (pow(2.0, ev100) * 1.2) + [/codeblock] + The exposure value can be calculated from aperture (in f-stops), shutter speed (in seconds), and sensitivity (in ISO) as follows: + [codeblock] + func get_exposure(float aperture, float shutter_speed, float sensitivity): + return log2((aperture * aperture) / shutterSpeed * (100.0 / sensitivity)) + [/codeblock] + </description> + </method> + <method name="camera_create"> + <return type="RID" /> + <description> + Creates a camera and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]camera_*[/code] RenderingServer functions. + Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method. + </description> + </method> + <method name="camera_set_camera_attributes"> <return type="void" /> <param index="0" name="camera" type="RID" /> <param index="1" name="effects" type="RID" /> @@ -976,7 +1000,8 @@ <method name="environment_set_bg_energy"> <return type="void" /> <param index="0" name="env" type="RID" /> - <param index="1" name="energy" type="float" /> + <param index="1" name="multiplier" type="float" /> + <param index="2" name="exposure_value" type="float" /> <description> Sets the intensity of the background color. </description> @@ -1143,11 +1168,6 @@ <param index="1" name="tone_mapper" type="int" enum="RenderingServer.EnvironmentToneMapper" /> <param index="2" name="exposure" type="float" /> <param index="3" name="white" type="float" /> - <param index="4" name="auto_exposure" type="bool" /> - <param index="5" name="min_luminance" type="float" /> - <param index="6" name="max_luminance" type="float" /> - <param index="7" name="auto_exp_speed" type="float" /> - <param index="8" name="auto_exp_grey" type="float" /> <description> Sets the variables to be used with the "tonemap" post-process effect. See [Environment] for more details. </description> @@ -1767,6 +1787,14 @@ <description> </description> </method> + <method name="lightmap_set_baked_exposure_normalization"> + <return type="void" /> + <param index="0" name="lightmap" type="RID" /> + <param index="1" name="baked_exposure" type="float" /> + <description> + Used to inform the renderer what exposure normalization value was used while baking the lightmap. This value will be used and modulated at run time to ensure that the lightmap maintains a consistent level of exposure even if the scene-wide exposure normalization is changed at run time. For more information see [method camera_attributes_set_exposure]. + </description> + </method> <method name="lightmap_set_probe_bounds"> <return type="void" /> <param index="0" name="lightmap" type="RID" /> @@ -2665,7 +2693,7 @@ The scenario is the 3D world that all the visual instances exist in. </description> </method> - <method name="scenario_set_camera_effects"> + <method name="scenario_set_camera_attributes"> <return type="void" /> <param index="0" name="scenario" type="RID" /> <param index="1" name="effects" type="RID" /> @@ -3480,6 +3508,14 @@ <description> </description> </method> + <method name="voxel_gi_set_baked_exposure_normalization"> + <return type="void" /> + <param index="0" name="voxel_gi" type="RID" /> + <param index="1" name="baked_exposure" type="float" /> + <description> + Used to inform the renderer what exposure normalization value was used while baking the voxel gi. This value will be used and modulated at run time to ensure that the voxel gi maintains a consistent level of exposure even if the scene-wide exposure normalization is changed at run time. For more information see [method camera_attributes_set_exposure]. + </description> + </method> <method name="voxel_gi_set_bias"> <return type="void" /> <param index="0" name="voxel_gi" type="RID" /> @@ -3792,64 +3828,65 @@ Is a spot light. </constant> <constant name="LIGHT_PARAM_ENERGY" value="0" enum="LightParam"> - The light's energy. + The light's energy multiplier. </constant> <constant name="LIGHT_PARAM_INDIRECT_ENERGY" value="1" enum="LightParam"> The light's indirect energy multiplier (final indirect energy is [constant LIGHT_PARAM_ENERGY] * [constant LIGHT_PARAM_INDIRECT_ENERGY]). </constant> - <constant name="LIGHT_PARAM_SPECULAR" value="2" enum="LightParam"> + <constant name="LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY" value="2" enum="LightParam"> + The light's volumetric fog energy multiplier (final volumetric fog energy is [constant LIGHT_PARAM_ENERGY] * [constant LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY]). + </constant> + <constant name="LIGHT_PARAM_SPECULAR" value="3" enum="LightParam"> The light's influence on specularity. </constant> - <constant name="LIGHT_PARAM_RANGE" value="3" enum="LightParam"> + <constant name="LIGHT_PARAM_RANGE" value="4" enum="LightParam"> The light's range. </constant> - <constant name="LIGHT_PARAM_SIZE" value="4" enum="LightParam"> + <constant name="LIGHT_PARAM_SIZE" value="5" enum="LightParam"> The size of the light when using spot light or omni light. The angular size of the light when using directional light. </constant> - <constant name="LIGHT_PARAM_ATTENUATION" value="5" enum="LightParam"> + <constant name="LIGHT_PARAM_ATTENUATION" value="6" enum="LightParam"> The light's attenuation. </constant> - <constant name="LIGHT_PARAM_SPOT_ANGLE" value="6" enum="LightParam"> + <constant name="LIGHT_PARAM_SPOT_ANGLE" value="7" enum="LightParam"> The spotlight's angle. </constant> - <constant name="LIGHT_PARAM_SPOT_ATTENUATION" value="7" enum="LightParam"> + <constant name="LIGHT_PARAM_SPOT_ATTENUATION" value="8" enum="LightParam"> The spotlight's attenuation. </constant> - <constant name="LIGHT_PARAM_SHADOW_MAX_DISTANCE" value="8" enum="LightParam"> + <constant name="LIGHT_PARAM_SHADOW_MAX_DISTANCE" value="9" enum="LightParam"> Max distance that shadows will be rendered. </constant> - <constant name="LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET" value="9" enum="LightParam"> + <constant name="LIGHT_PARAM_SHADOW_SPLIT_1_OFFSET" value="10" enum="LightParam"> Proportion of shadow atlas occupied by the first split. </constant> - <constant name="LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET" value="10" enum="LightParam"> + <constant name="LIGHT_PARAM_SHADOW_SPLIT_2_OFFSET" value="11" enum="LightParam"> Proportion of shadow atlas occupied by the second split. </constant> - <constant name="LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET" value="11" enum="LightParam"> + <constant name="LIGHT_PARAM_SHADOW_SPLIT_3_OFFSET" value="12" enum="LightParam"> Proportion of shadow atlas occupied by the third split. The fourth split occupies the rest. </constant> - <constant name="LIGHT_PARAM_SHADOW_FADE_START" value="12" enum="LightParam"> + <constant name="LIGHT_PARAM_SHADOW_FADE_START" value="13" enum="LightParam"> Proportion of shadow max distance where the shadow will start to fade out. </constant> - <constant name="LIGHT_PARAM_SHADOW_NORMAL_BIAS" value="13" enum="LightParam"> + <constant name="LIGHT_PARAM_SHADOW_NORMAL_BIAS" value="14" enum="LightParam"> Normal bias used to offset shadow lookup by object normal. Can be used to fix self-shadowing artifacts. </constant> - <constant name="LIGHT_PARAM_SHADOW_BIAS" value="14" enum="LightParam"> + <constant name="LIGHT_PARAM_SHADOW_BIAS" value="15" enum="LightParam"> Bias the shadow lookup to fix self-shadowing artifacts. </constant> - <constant name="LIGHT_PARAM_SHADOW_PANCAKE_SIZE" value="15" enum="LightParam"> + <constant name="LIGHT_PARAM_SHADOW_PANCAKE_SIZE" value="16" enum="LightParam"> Sets the size of the directional shadow pancake. The pancake offsets the start of the shadow's camera frustum to provide a higher effective depth resolution for the shadow. However, a high pancake size can cause artifacts in the shadows of large objects that are close to the edge of the frustum. Reducing the pancake size can help. Setting the size to [code]0[/code] turns off the pancaking effect. </constant> - <constant name="LIGHT_PARAM_SHADOW_OPACITY" value="16" enum="LightParam"> + <constant name="LIGHT_PARAM_SHADOW_OPACITY" value="17" enum="LightParam"> The light's shadow opacity. Values lower than [code]1.0[/code] make the light appear through shadows. This can be used to fake global illumination at a low performance cost. </constant> - <constant name="LIGHT_PARAM_SHADOW_BLUR" value="17" enum="LightParam"> + <constant name="LIGHT_PARAM_SHADOW_BLUR" value="18" enum="LightParam"> Blurs the edges of the shadow. Can be used to hide pixel artifacts in low resolution shadow maps. A high value can make shadows appear grainy and can cause other unwanted artifacts. Try to keep as near default as possible. </constant> - <constant name="LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE" value="18" enum="LightParam"> - </constant> <constant name="LIGHT_PARAM_TRANSMITTANCE_BIAS" value="19" enum="LightParam"> </constant> - <constant name="LIGHT_PARAM_MAX" value="20" enum="LightParam"> + <constant name="LIGHT_PARAM_MAX" value="21" enum="LightParam"> Represents the size of the [enum LightParam] enum. </constant> <constant name="LIGHT_BAKE_DISABLED" value="0" enum="LightBakeMode"> diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index aad83211f5..1aec6d2c0e 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -1624,6 +1624,10 @@ Break the line between any unconnected graphemes. </constant> <constant name="BREAK_ADAPTIVE" value="8" enum="LineBreakFlag" is_bitfield="true"> + Should be used only in conjunction with [constant BREAK_WORD_BOUND], break the line between any unconnected graphemes, if it's impossible to break it between the words. + </constant> + <constant name="BREAK_TRIM_EDGE_SPACES" value="16" enum="LineBreakFlag" is_bitfield="true"> + Remove edge spaces from the broken line segments. </constant> <constant name="VC_CHARS_BEFORE_SHAPING" value="0" enum="VisibleCharactersBehavior"> Trims text before the shaping. e.g, increasing [member Label.visible_characters] or [member RichTextLabel.visible_characters] value is visually identical to typing the text. diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml index 14aa72b80c..18b4f9e6f9 100644 --- a/doc/classes/Transform3D.xml +++ b/doc/classes/Transform3D.xml @@ -141,14 +141,6 @@ This can be seen as transforming with respect to the local frame. </description> </method> - <method name="spherical_interpolate_with" qualifiers="const"> - <return type="Transform3D" /> - <param index="0" name="xform" type="Transform3D" /> - <param index="1" name="weight" type="float" /> - <description> - Returns a transform spherically interpolated between this transform and another by a given [param weight] (on the range of 0.0 to 1.0). - </description> - </method> <method name="translated" qualifiers="const"> <return type="Transform3D" /> <param index="0" name="offset" type="Vector3" /> diff --git a/doc/classes/VoxelGI.xml b/doc/classes/VoxelGI.xml index ba4995a5fb..394611b78f 100644 --- a/doc/classes/VoxelGI.xml +++ b/doc/classes/VoxelGI.xml @@ -32,6 +32,9 @@ </method> </methods> <members> + <member name="camera_attributes" type="CameraAttributes" setter="set_camera_attributes" getter="get_camera_attributes"> + The [CameraAttributes] resource that specifies exposure levels to bake at. Auto-exposure and non exposure properties will be ignored. Exposure settings should be used to reduce the dynamic range present when baking. If exposure is too high, the [VoxelGI] will have banding artifacts or may have over-exposure artifacts. + </member> <member name="data" type="VoxelGIData" setter="set_probe_data" getter="get_probe_data"> The [VoxelGIData] resource that holds the data for this [VoxelGI]. </member> diff --git a/doc/classes/World3D.xml b/doc/classes/World3D.xml index 56a662d062..f3c7136075 100644 --- a/doc/classes/World3D.xml +++ b/doc/classes/World3D.xml @@ -10,7 +10,8 @@ <link title="Ray-casting">$DOCS_URL/tutorials/physics/ray-casting.html</link> </tutorials> <members> - <member name="camera_effects" type="CameraEffects" setter="set_camera_effects" getter="get_camera_effects"> + <member name="camera_attributes" type="CameraAttributes" setter="set_camera_attributes" getter="get_camera_attributes"> + The default [CameraAttributes] resource to use if none set on the [Camera3D]. </member> <member name="direct_space_state" type="PhysicsDirectSpaceState3D" setter="" getter="get_direct_space_state"> Direct access to the world's physics 3D space state. Used for querying current and potential collisions. diff --git a/doc/classes/WorldEnvironment.xml b/doc/classes/WorldEnvironment.xml index ed8f0b9a04..5255179bb8 100644 --- a/doc/classes/WorldEnvironment.xml +++ b/doc/classes/WorldEnvironment.xml @@ -15,8 +15,8 @@ <link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link> </tutorials> <members> - <member name="camera_effects" type="CameraEffects" setter="set_camera_effects" getter="get_camera_effects"> - The [CameraEffects] resource used by this [WorldEnvironment], defining the default properties. This [CameraEffects] resource will be used by all [Camera3D]s that do not define their own [CameraEffects]. + <member name="camera_attributes" type="CameraAttributes" setter="set_camera_attributes" getter="get_camera_attributes"> + The default [CameraAttributes] resource to use if none set on the [Camera3D]. </member> <member name="environment" type="Environment" setter="set_environment" getter="get_environment"> The [Environment] resource used by this [WorldEnvironment], defining the default properties. diff --git a/drivers/gles3/environment/gi.cpp b/drivers/gles3/environment/gi.cpp index 84cdb81d35..5b16d3539f 100644 --- a/drivers/gles3/environment/gi.cpp +++ b/drivers/gles3/environment/gi.cpp @@ -98,6 +98,13 @@ float GI::voxel_gi_get_energy(RID p_voxel_gi) const { return 0.0; } +void GI::voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) { +} + +float GI::voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const { + return 1.0; +} + void GI::voxel_gi_set_bias(RID p_voxel_gi, float p_range) { } diff --git a/drivers/gles3/environment/gi.h b/drivers/gles3/environment/gi.h index 7a0634f22b..5b0aad380e 100644 --- a/drivers/gles3/environment/gi.h +++ b/drivers/gles3/environment/gi.h @@ -74,6 +74,9 @@ public: virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override; virtual float voxel_gi_get_energy(RID p_voxel_gi) const override; + virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) override; + virtual float voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const override; + virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override; virtual float voxel_gi_get_bias(RID p_voxel_gi) const override; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index cb479dda39..504a7e218d 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -483,6 +483,13 @@ void RasterizerSceneGLES3::sky_set_material(RID p_sky, RID p_material) { _invalidate_sky(sky); } +float RasterizerSceneGLES3::sky_get_baked_exposure(RID p_sky) const { + Sky *sky = sky_owner.get_or_null(p_sky); + ERR_FAIL_COND_V(!sky, 1.0); + + return sky->baked_exposure; +} + void RasterizerSceneGLES3::_invalidate_sky(Sky *p_sky) { if (!p_sky->dirty) { p_sky->dirty = true; @@ -561,13 +568,13 @@ void RasterizerSceneGLES3::_update_dirty_skys() { dirty_sky_list = nullptr; } -void RasterizerSceneGLES3::_setup_sky(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size) { +void RasterizerSceneGLES3::_setup_sky(const RenderDataGLES3 *p_render_data, RID p_render_buffers, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size) { GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); - ERR_FAIL_COND(p_env.is_null()); + ERR_FAIL_COND(p_render_data->environment.is_null()); GLES3::SkyMaterialData *material = nullptr; - Sky *sky = sky_owner.get_or_null(environment_get_sky(p_env)); + Sky *sky = sky_owner.get_or_null(environment_get_sky(p_render_data->environment)); RID sky_material; @@ -639,6 +646,14 @@ void RasterizerSceneGLES3::_setup_sky(RID p_env, RID p_render_buffers, const Pag float sign = light_storage->light_is_negative(base) ? -1 : 1; sky_light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); + if (is_using_physical_light_units()) { + sky_light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + } + + if (p_render_data->camera_attributes.is_valid()) { + sky_light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + Color linear_col = light_storage->light_get_color(base); sky_light_data.color[0] = linear_col.r; sky_light_data.color[1] = linear_col.g; @@ -708,7 +723,7 @@ void RasterizerSceneGLES3::_setup_sky(RID p_env, RID p_render_buffers, const Pag } } -void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform) { +void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); ERR_FAIL_COND(p_env.is_null()); @@ -768,12 +783,13 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, camera.matrix[2][0], camera.matrix[0][0], camera.matrix[2][1], camera.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); glBindVertexArray(sky_globals.screen_triangle_array); glDrawArrays(GL_TRIANGLES, 0, 3); } -void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform) { +void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); ERR_FAIL_COND(p_env.is_null()); @@ -866,6 +882,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, cm.matrix[2][0], cm.matrix[0][0], cm.matrix[2][1], cm.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); glBindVertexArray(sky_globals.screen_triangle_array); @@ -887,7 +904,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p _filter_sky_radiance(sky, 0); //Just copy over the first mipmap } sky->processing_layer = 1; - + sky->baked_exposure = p_luminance_multiplier; sky->reflection_dirty = false; } else { if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) { @@ -1061,25 +1078,6 @@ Ref<Image> RasterizerSceneGLES3::environment_bake_panorama(RID p_env, bool p_bak return Ref<Image>(); } -RID RasterizerSceneGLES3::camera_effects_allocate() { - return RID(); -} - -void RasterizerSceneGLES3::camera_effects_initialize(RID p_rid) { -} - -void RasterizerSceneGLES3::camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) { -} - -void RasterizerSceneGLES3::camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) { -} - -void RasterizerSceneGLES3::camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) { -} - -void RasterizerSceneGLES3::camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) { -} - void RasterizerSceneGLES3::positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) { } @@ -1403,8 +1401,9 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da RS::EnvironmentBG env_bg = environment_get_background(p_render_data->environment); RS::EnvironmentAmbientSource ambient_src = environment_get_ambient_source(p_render_data->environment); - float bg_energy = environment_get_bg_energy(p_render_data->environment); - scene_state.ubo.ambient_light_color_energy[3] = bg_energy; + float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); + + scene_state.ubo.ambient_light_color_energy[3] = bg_energy_multiplier; scene_state.ubo.ambient_color_sky_mix = environment_get_ambient_sky_contribution(p_render_data->environment); @@ -1413,9 +1412,9 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da Color color = env_bg == RS::ENV_BG_CLEAR_COLOR ? p_default_bg_color : environment_get_bg_color(p_render_data->environment); color = color.srgb_to_linear(); - scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy; - scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy; - scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy; + scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy_multiplier; scene_state.ubo.use_ambient_light = true; scene_state.ubo.use_ambient_cubemap = false; } else { @@ -1459,6 +1458,25 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da } else { } + if (p_render_data->camera_attributes.is_valid()) { + scene_state.ubo.emissive_exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + scene_state.ubo.IBL_exposure_normalization = 1.0; + if (is_environment(p_render_data->environment)) { + RID sky_rid = environment_get_sky(p_render_data->environment); + if (sky_rid.is_valid()) { + float current_exposure = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes) * environment_get_bg_intensity(p_render_data->environment); + scene_state.ubo.IBL_exposure_normalization = current_exposure / MAX(0.001, sky_get_baked_exposure(sky_rid)); + } + } + } else if (scene_state.ubo.emissive_exposure_normalization > 0.0) { + // This branch is triggered when using render_material(). + // Emissive is set outside the function, so don't set it. + // IBL isn't used don't set it. + } else { + scene_state.ubo.emissive_exposure_normalization = 1.0; + scene_state.ubo.IBL_exposure_normalization = 1.0; + } + if (scene_state.ubo_buffer == 0) { glGenBuffers(1, &scene_state.ubo_buffer); } @@ -1510,7 +1528,17 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b float sign = light_storage->light_is_negative(base) ? -1 : 1; - light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI; + light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); + + if (is_using_physical_light_units()) { + light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + } else { + light_data.energy *= Math_PI; + } + + if (p_render_data->camera_attributes.is_valid()) { + light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } Color linear_col = light_storage->light_get_color(base).srgb_to_linear(); light_data.color[0] = linear_col.r; @@ -1590,7 +1618,7 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b for (uint32_t i = 0; i < (r_omni_light_count + r_spot_light_count); i++) { uint32_t index = (i < r_omni_light_count) ? i : i - (r_omni_light_count); LightData &light_data = (i < r_omni_light_count) ? scene_state.omni_lights[index] : scene_state.spot_lights[index]; - //RS::LightType type = (i < omni_light_count) ? RS::LIGHT_OMNI : RS::LIGHT_SPOT; + RS::LightType type = (i < r_omni_light_count) ? RS::LIGHT_OMNI : RS::LIGHT_SPOT; LightInstance *li = (i < r_omni_light_count) ? scene_state.omni_light_sort[index].instance : scene_state.spot_light_sort[index].instance; RID base = li->light; @@ -1634,7 +1662,26 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b } } - float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI * fade; + float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * fade; + + if (is_using_physical_light_units()) { + energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + + // Convert from Luminous Power to Luminous Intensity + if (type == RS::LIGHT_OMNI) { + energy *= 1.0 / (Math_PI * 4.0); + } else { + // Spot Lights are not physically accurate, Luminous Intensity should change in relation to the cone angle. + // We make this assumption to keep them easy to control. + energy *= 1.0 / Math_PI; + } + } else { + energy *= Math_PI; + } + + if (p_render_data->camera_attributes.is_valid()) { + energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } light_data.color[0] = linear_col.r * energy; light_data.color[1] = linear_col.g * energy; @@ -1671,7 +1718,7 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b glBindBuffer(GL_UNIFORM_BUFFER, 0); } -void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) { +void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); RENDER_TIMESTAMP("Setup 3D Scene"); @@ -1707,7 +1754,7 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * render_data.lights = &p_lights; render_data.reflection_probes = &p_reflection_probes; render_data.environment = p_environment; - render_data.camera_effects = p_camera_effects; + render_data.camera_attributes = p_camera_attributes; render_data.reflection_probe = p_reflection_probe; render_data.reflection_probe_pass = p_reflection_probe_pass; @@ -1768,6 +1815,8 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_TONEMAP_UNIFORM_LOCATION, scene_state.tonemap_buffer); glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::TonemapUBO), &tonemap_ubo, GL_STREAM_DRAW); + scene_state.ubo.emissive_exposure_normalization = -1.0; // Use default exposure normalization. + _setup_lights(&render_data, false, render_data.directional_light_count, render_data.omni_light_count, render_data.spot_light_count); _setup_environment(&render_data, render_data.reflection_probe.is_valid(), screen_size, !render_data.reflection_probe.is_valid(), clear_color, false); @@ -1778,17 +1827,24 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * bool draw_sky = false; bool draw_sky_fog_only = false; bool keep_color = false; + float sky_energy_multiplier = 1.0; if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) { clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black } else if (render_data.environment.is_valid()) { RS::EnvironmentBG bg_mode = environment_get_background(render_data.environment); - float bg_energy = environment_get_bg_energy(render_data.environment); + float bg_energy_multiplier = environment_get_bg_energy_multiplier(render_data.environment); + bg_energy_multiplier *= environment_get_bg_intensity(render_data.environment); + + if (render_data.camera_attributes.is_valid()) { + bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(render_data.camera_attributes); + } + switch (bg_mode) { case RS::ENV_BG_CLEAR_COLOR: { - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; if (environment_get_fog_enabled(render_data.environment)) { draw_sky_fog_only = true; GLES3::MaterialStorage::get_singleton()->material_set_param(sky_globals.fog_material, "clear_color", Variant(clear_color)); @@ -1796,9 +1852,9 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * } break; case RS::ENV_BG_COLOR: { clear_color = environment_get_bg_color(render_data.environment); - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; if (environment_get_fog_enabled(render_data.environment)) { draw_sky_fog_only = true; GLES3::MaterialStorage::get_singleton()->material_set_param(sky_globals.fog_material, "clear_color", Variant(clear_color)); @@ -1828,11 +1884,13 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * projection = correction * render_data.cam_projection; } - _setup_sky(render_data.environment, p_render_buffers, *render_data.lights, projection, render_data.cam_transform, screen_size); + sky_energy_multiplier *= bg_energy_multiplier; + + _setup_sky(&render_data, p_render_buffers, *render_data.lights, projection, render_data.cam_transform, screen_size); if (environment_get_sky(render_data.environment).is_valid()) { if (environment_get_reflection_source(render_data.environment) == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(render_data.environment) == RS::ENV_AMBIENT_SOURCE_SKY || (environment_get_reflection_source(render_data.environment) == RS::ENV_REFLECTION_SOURCE_BG && environment_get_background(render_data.environment) == RS::ENV_BG_SKY)) { - _update_sky_radiance(render_data.environment, projection, render_data.cam_transform); + _update_sky_radiance(render_data.environment, projection, render_data.cam_transform, sky_energy_multiplier); } } else { // do not try to draw sky if invalid @@ -1936,7 +1994,7 @@ void RasterizerSceneGLES3::render_scene(RID p_render_buffers, const CameraData * scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_DISABLED; scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK; - _draw_sky(render_data.environment, render_data.cam_projection, render_data.cam_transform); + _draw_sky(render_data.environment, render_data.cam_projection, render_data.cam_transform, sky_energy_multiplier); } RENDER_TIMESTAMP("Render 3D Transparent Pass"); @@ -2406,6 +2464,9 @@ bool RasterizerSceneGLES3::free(RID p_rid) { LightInstance *light_instance = light_instance_owner.get_or_null(p_rid); ERR_FAIL_COND_V(!light_instance, false); light_instance_owner.free(p_rid); + } else if (RSG::camera_attributes->owns_camera_attributes(p_rid)) { + //not much to delete, just free it + RSG::camera_attributes->camera_attributes_free(p_rid); } else { return false; } @@ -2431,6 +2492,9 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); + // Quality settings. + use_physical_light_units = GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units"); + { // Setup Lights diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index a54d87a3a3..526da88ef6 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -111,7 +111,7 @@ struct RenderDataGLES3 { const PagedArray<RID> *lights = nullptr; const PagedArray<RID> *reflection_probes = nullptr; RID environment = RID(); - RID camera_effects = RID(); + RID camera_attributes = RID(); RID reflection_probe = RID(); int reflection_probe_pass = 0; @@ -344,7 +344,7 @@ private: float ambient_color_sky_mix; uint32_t material_uv2_mode; - float pad2; + float emissive_exposure_normalization; uint32_t use_ambient_light = 0; uint32_t use_ambient_cubemap = 0; @@ -357,7 +357,7 @@ private: uint32_t directional_light_count; float z_far; float z_near; - float pad1; + float IBL_exposure_normalization; uint32_t fog_enabled; float fog_density; @@ -537,6 +537,16 @@ protected: void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas, RID p_occlusion_buffer); + /* Camera Attributes */ + + struct CameraAttributes { + float exposure_multiplier = 1.0; + float exposure_normalization = 1.0; + }; + + bool use_physical_light_units = false; + mutable RID_Owner<CameraAttributes, true> camera_attributes_owner; + /* Environment */ RS::EnvironmentSSAOQuality ssao_quality = RS::ENV_SSAO_QUALITY_MEDIUM; @@ -605,6 +615,7 @@ protected: bool dirty = false; int processing_layer = 0; Sky *dirty_list = nullptr; + float baked_exposure = 1.0; //State to track when radiance cubemap needs updating GLES3::SkyMaterialData *prev_material; @@ -615,12 +626,12 @@ protected: Sky *dirty_sky_list = nullptr; mutable RID_Owner<Sky, true> sky_owner; - void _setup_sky(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size); + void _setup_sky(const RenderDataGLES3 *p_render_data, RID p_render_buffers, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size); void _invalidate_sky(Sky *p_sky); void _update_dirty_skys(); - void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform); + void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier); void _filter_sky_radiance(Sky *p_sky, int p_base_layer); - void _draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform); + void _draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier); void _free_sky_data(Sky *p_sky); public: @@ -665,6 +676,7 @@ public: void sky_set_mode(RID p_sky, RS::SkyMode p_mode) override; void sky_set_material(RID p_sky, RID p_material) override; Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) override; + float sky_get_baked_exposure(RID p_sky) const; /* ENVIRONMENT API */ @@ -686,13 +698,9 @@ public: Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override; - RID camera_effects_allocate() override; - void camera_effects_initialize(RID p_rid) override; - void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) override; - void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) override; - - void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) override; - void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override; + _FORCE_INLINE_ bool is_using_physical_light_units() { + return use_physical_light_units; + } void positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override; void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override; @@ -743,7 +751,7 @@ public: void voxel_gi_set_quality(RS::VoxelGIQuality) override; - void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) override; + void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) override; void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) override; diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index eb1befe38e..21f01d2a8f 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -92,6 +92,7 @@ uniform mat4 orientation; uniform vec4 projection; uniform vec3 position; uniform float time; +uniform float luminance_multiplier; uniform float fog_aerial_perspective; uniform vec3 fog_light_color; @@ -149,6 +150,8 @@ void main() { } + color *= luminance_multiplier; + // Convert to Linear for tonemapping so color matches scene shader better color = srgb_to_linear(color); color *= exposure; diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index e2781bfbc0..6411590aee 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -58,6 +58,7 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) { light.param[RS::LIGHT_PARAM_ENERGY] = 1.0; light.param[RS::LIGHT_PARAM_INDIRECT_ENERGY] = 1.0; + light.param[RS::LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY] = 1.0; light.param[RS::LIGHT_PARAM_SPECULAR] = 0.5; light.param[RS::LIGHT_PARAM_RANGE] = 1.0; light.param[RS::LIGHT_PARAM_SIZE] = 0.0; @@ -74,7 +75,6 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) { light.param[RS::LIGHT_PARAM_SHADOW_BIAS] = 0.02; light.param[RS::LIGHT_PARAM_SHADOW_BLUR] = 0; light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0; - light.param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE] = 0.1; light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05; light_owner.initialize_rid(p_light, light); @@ -422,13 +422,17 @@ float LightStorage::reflection_probe_get_mesh_lod_threshold(RID p_probe) const { /* LIGHTMAP CAPTURE */ RID LightStorage::lightmap_allocate() { - return RID(); + return lightmap_owner.allocate_rid(); } void LightStorage::lightmap_initialize(RID p_rid) { + lightmap_owner.initialize_rid(p_rid, Lightmap()); } void LightStorage::lightmap_free(RID p_rid) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_rid); + lightmap->dependency.deleted_notify(p_rid); + lightmap_owner.free(p_rid); } void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) { @@ -443,6 +447,9 @@ void LightStorage::lightmap_set_probe_interior(RID p_lightmap, bool p_interior) void LightStorage::lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { } +void LightStorage::lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) { +} + PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const { return PackedVector3Array(); } diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index 857a0261fa..f054f0fdc6 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -92,6 +92,7 @@ struct ReflectionProbe { bool enable_shadows = false; uint32_t cull_mask = (1 << 20) - 1; float mesh_lod_threshold = 0.01; + float baked_exposure = 1.0; Dependency dependency; }; @@ -103,6 +104,7 @@ struct Lightmap { bool uses_spherical_harmonics = false; bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); + float baked_exposure = 1.0; int32_t array_index = -1; //unassigned PackedVector3Array points; PackedColorArray point_sh; @@ -261,13 +263,6 @@ public: return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS]; } - _FORCE_INLINE_ float light_get_shadow_volumetric_fog_fade(RID p_light) const { - const Light *light = light_owner.get_or_null(p_light); - ERR_FAIL_COND_V(!light, 0.0); - - return light->param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE]; - } - virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override; virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; } virtual uint64_t light_get_version(RID p_light) const override; @@ -304,6 +299,9 @@ public: /* LIGHTMAP CAPTURE */ + Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); }; + bool owns_lightmap(RID p_rid) { return lightmap_owner.owns(p_rid); }; + virtual RID lightmap_allocate() override; virtual void lightmap_initialize(RID p_rid) override; virtual void lightmap_free(RID p_rid) override; @@ -312,6 +310,7 @@ public: virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override; virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override; + virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) override; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override; diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index 654104722b..16bacf1829 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -82,6 +82,8 @@ RS::InstanceType Utilities::get_base_type(RID p_rid) const { return RS::INSTANCE_MULTIMESH; } else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) { return RS::INSTANCE_LIGHT; + } else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) { + return RS::INSTANCE_LIGHTMAP; } return RS::INSTANCE_NONE; } @@ -114,6 +116,9 @@ bool Utilities::free(RID p_rid) { } else if (GLES3::LightStorage::get_singleton()->owns_light(p_rid)) { GLES3::LightStorage::get_singleton()->light_free(p_rid); return true; + } else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) { + GLES3::LightStorage::get_singleton()->lightmap_free(p_rid); + return true; } else { return false; } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index faa2eeb230..877581a93b 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -253,11 +253,8 @@ void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vecto } // Normalize trailing slashes when normalizing directory names. - if (scene_name.rfind("/") == scene_name.length() - 1 && full_path.rfind("/") != full_path.length() - 1) { - full_path = full_path + "/"; - } else if (scene_name.rfind("/") != scene_name.length() - 1 && full_path.rfind("/") == full_path.length() - 1) { - scene_name = scene_name + "/"; - } + scene_name = scene_name.trim_suffix("/"); + full_path = full_path.trim_suffix("/"); int scene_name_size = scene_name.size(); int full_path_size = full_path.size(); @@ -301,17 +298,23 @@ void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vecto // and the scene name first to remove extensions so that this // comparison actually works. String path = p_full_paths[E->get()]; + + // Get rid of file extensions and res:// prefixes. + if (scene_name.rfind(".") >= 0) { + scene_name = scene_name.substr(0, scene_name.rfind(".")); + } if (path.begins_with("res://")) { path = path.substr(6); } if (path.rfind(".") >= 0) { path = path.substr(0, path.rfind(".")); } - if (scene_name.rfind(".") >= 0) { - scene_name = scene_name.substr(0, scene_name.rfind(".")); - } - // We can proceed iff the full path is longer than the scene name, + // Normalize trailing slashes when normalizing directory names. + scene_name = scene_name.trim_suffix("/"); + path = path.trim_suffix("/"); + + // We can proceed if the full path is longer than the scene name, // meaning that there is at least one more parent folder we can // tack onto the name. can_proceed = can_proceed || (path.size() - scene_name.size()) >= 1; @@ -420,9 +423,6 @@ void EditorNode::_version_control_menu_option(int p_idx) { case RUN_VCS_SETTINGS: { VersionControlEditorPlugin::get_singleton()->popup_vcs_set_up_dialog(gui_base); } break; - case RUN_VCS_SHUT_DOWN: { - VersionControlEditorPlugin::get_singleton()->shut_down(); - } break; } } @@ -501,10 +501,10 @@ void EditorNode::_update_from_settings() { } RS::DOFBokehShape dof_shape = RS::DOFBokehShape(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_shape"))); - RS::get_singleton()->camera_effects_set_dof_blur_bokeh_shape(dof_shape); + RS::get_singleton()->camera_attributes_set_dof_blur_bokeh_shape(dof_shape); RS::DOFBlurQuality dof_quality = RS::DOFBlurQuality(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_quality"))); bool dof_jitter = GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_use_jitter"); - RS::get_singleton()->camera_effects_set_dof_blur_quality(dof_quality, dof_jitter); + RS::get_singleton()->camera_attributes_set_dof_blur_quality(dof_quality, dof_jitter); RS::get_singleton()->environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/environment/ssao/quality"))), GLOBAL_GET("rendering/environment/ssao/half_size"), GLOBAL_GET("rendering/environment/ssao/adaptive_target"), GLOBAL_GET("rendering/environment/ssao/blur_passes"), GLOBAL_GET("rendering/environment/ssao/fadeout_from"), GLOBAL_GET("rendering/environment/ssao/fadeout_to")); RS::get_singleton()->screen_space_roughness_limiter_set_active(GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/enabled"), GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/amount"), GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/limit")); bool glow_bicubic = int(GLOBAL_GET("rendering/environment/glow/upscale_mode")) > 0; @@ -520,13 +520,13 @@ void EditorNode::_update_from_settings() { float sss_depth_scale = GLOBAL_GET("rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale"); RS::get_singleton()->sub_surface_scattering_set_scale(sss_scale, sss_depth_scale); - uint32_t directional_shadow_size = GLOBAL_GET("rendering/shadows/directional_shadow/size"); - uint32_t directional_shadow_16_bits = GLOBAL_GET("rendering/shadows/directional_shadow/16_bits"); + uint32_t directional_shadow_size = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/size"); + uint32_t directional_shadow_16_bits = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/16_bits"); RS::get_singleton()->directional_shadow_atlas_set_size(directional_shadow_size, directional_shadow_16_bits); - RS::ShadowQuality shadows_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/shadows/positional_shadow/soft_shadow_filter_quality"))); + RS::ShadowQuality shadows_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality"))); RS::get_singleton()->positional_soft_shadow_filter_set_quality(shadows_quality); - RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/shadows/directional_shadow/soft_shadow_filter_quality"))); + RS::ShadowQuality directional_shadow_quality = RS::ShadowQuality(int(GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality"))); RS::get_singleton()->directional_soft_shadow_filter_set_quality(directional_shadow_quality); float probe_update_speed = GLOBAL_GET("rendering/lightmapping/probe_capture/update_speed"); RS::get_singleton()->lightmap_set_probe_capture_update_speed(probe_update_speed); @@ -6741,8 +6741,7 @@ EditorNode::EditorNode() { project_menu->add_child(vcs_actions_menu); project_menu->add_submenu_item(TTR("Version Control"), "Version Control"); vcs_actions_menu->add_item(TTR("Create Version Control Metadata"), RUN_VCS_METADATA); - vcs_actions_menu->add_item(TTR("Set Up Version Control"), RUN_VCS_SETTINGS); - vcs_actions_menu->add_item(TTR("Shut Down Version Control"), RUN_VCS_SHUT_DOWN); + vcs_actions_menu->add_item(TTR("Version Control Settings"), RUN_VCS_SETTINGS); project_menu->add_separator(); project_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/export", TTR("Export..."), Key::NONE, TTR("Export")), FILE_EXPORT_PROJECT); diff --git a/editor/editor_node.h b/editor/editor_node.h index 55d448ec2a..c3b4c985cc 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -185,7 +185,6 @@ private: RUN_PROJECT_MANAGER, RUN_VCS_METADATA, RUN_VCS_SETTINGS, - RUN_VCS_SHUT_DOWN, SETTINGS_UPDATE_CONTINUOUSLY, SETTINGS_UPDATE_WHEN_CHANGED, SETTINGS_UPDATE_ALWAYS, diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index b7910a152e..d78fee6ad6 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -52,7 +52,7 @@ void EditorPropertyNil::update_property() { EditorPropertyNil::EditorPropertyNil() { Label *label = memnew(Label); - label->set_text("[null]"); + label->set_text("<null>"); add_child(label); } @@ -832,26 +832,35 @@ void EditorPropertyFlags::setup(const Vector<String> &p_options) { bool first = true; uint32_t current_val; for (int i = 0; i < p_options.size(); i++) { + // An empty option is not considered a "flag". String option = p_options[i].strip_edges(); - if (!option.is_empty()) { - CheckBox *cb = memnew(CheckBox); - cb->set_text(option); - cb->set_clip_text(true); - cb->connect("pressed", callable_mp(this, &EditorPropertyFlags::_flag_toggled).bind(i)); - add_focusable(cb); - vbox->add_child(cb); - flags.push_back(cb); - Vector<String> text_split = p_options[i].split(":"); - if (text_split.size() != 1) { - current_val = text_split[1].to_int(); - } else { - current_val = 1 << i; - } - flag_values.push_back(current_val); - if (first) { - set_label_reference(cb); - first = false; - } + if (option.is_empty()) { + continue; + } + const int flag_index = flags.size(); // Index of the next element (added by the code below). + + // Value for a flag can be explicitly overridden. + Vector<String> text_split = p_options[i].split(":"); + if (text_split.size() != 1) { + current_val = text_split[1].to_int(); + } else { + current_val = 1 << i; + } + flag_values.push_back(current_val); + + // Create a CheckBox for the current flag. + CheckBox *cb = memnew(CheckBox); + cb->set_text(option); + cb->set_clip_text(true); + cb->connect("pressed", callable_mp(this, &EditorPropertyFlags::_flag_toggled).bind(flag_index)); + add_focusable(cb); + vbox->add_child(cb); + flags.push_back(cb); + + // Can't use `i == 0` because we want to find the first none-empty option. + if (first) { + set_label_reference(cb); + first = false; } } } @@ -1382,7 +1391,7 @@ void EditorPropertyObjectID::update_property() { edit->set_disabled(false); edit->set_icon(EditorNode::get_singleton()->get_class_icon(type)); } else { - edit->set_text(TTR("[Empty]")); + edit->set_text(TTR("<empty>")); edit->set_disabled(true); edit->set_icon(Ref<Texture2D>()); } diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 22e7b01418..f717188b3b 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -61,7 +61,7 @@ void EditorResourcePicker::_update_resource() { if (edited_resource == Ref<Resource>()) { assign_button->set_icon(Ref<Texture2D>()); - assign_button->set_text(TTR("[empty]")); + assign_button->set_text(TTR("<empty>")); assign_button->set_tooltip_text(""); } else { assign_button->set_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->(), "Object")); @@ -1113,7 +1113,7 @@ void EditorAudioStreamPicker::_update_resource() { void EditorAudioStreamPicker::_preview_draw() { Ref<AudioStream> audio_stream = get_edited_resource(); if (!audio_stream.is_valid()) { - get_assign_button()->set_text(TTR("[empty]")); + get_assign_button()->set_text(TTR("<empty>")); return; } diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 73c365ce4a..33632649c8 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -155,6 +155,10 @@ void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; + if (is_read_only()) { + return; + } + if (grabbing_grabber) { if (mb.is_valid()) { if (mb->get_button_index() == MouseButton::WHEEL_UP) { @@ -196,7 +200,7 @@ void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) { void EditorSpinSlider::_value_input_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; - if (k.is_valid() && k->is_pressed()) { + if (k.is_valid() && k->is_pressed() && !is_read_only()) { double step = get_step(); double real_step = step; if (step < 1) { diff --git a/editor/editor_vcs_interface.cpp b/editor/editor_vcs_interface.cpp index 53cc8d5b22..0c6c876b2f 100644 --- a/editor/editor_vcs_interface.cpp +++ b/editor/editor_vcs_interface.cpp @@ -30,132 +30,371 @@ #include "editor_vcs_interface.h" +#include "editor_node.h" + +#define UNIMPLEMENTED() ERR_PRINT(vformat("Unimplemented virtual function in EditorVCSInterface based plugin: %s", __func__)) + EditorVCSInterface *EditorVCSInterface::singleton = nullptr; -void EditorVCSInterface::_bind_methods() { - // Proxy end points that act as fallbacks to unavailability of a function in the VCS addon - ClassDB::bind_method(D_METHOD("_initialize", "project_root_path"), &EditorVCSInterface::_initialize); - ClassDB::bind_method(D_METHOD("_is_vcs_initialized"), &EditorVCSInterface::_is_vcs_initialized); - ClassDB::bind_method(D_METHOD("_get_vcs_name"), &EditorVCSInterface::_get_vcs_name); - ClassDB::bind_method(D_METHOD("_shut_down"), &EditorVCSInterface::_shut_down); - ClassDB::bind_method(D_METHOD("_get_project_name"), &EditorVCSInterface::_get_project_name); - ClassDB::bind_method(D_METHOD("_get_modified_files_data"), &EditorVCSInterface::_get_modified_files_data); - ClassDB::bind_method(D_METHOD("_commit", "msg"), &EditorVCSInterface::_commit); - ClassDB::bind_method(D_METHOD("_get_file_diff", "file_path"), &EditorVCSInterface::_get_file_diff); - ClassDB::bind_method(D_METHOD("_stage_file", "file_path"), &EditorVCSInterface::_stage_file); - ClassDB::bind_method(D_METHOD("_unstage_file", "file_path"), &EditorVCSInterface::_unstage_file); +void EditorVCSInterface::popup_error(String p_msg) { + // TRANSLATORS: %s refers to the name of a version control system (e.g. "Git"). + EditorNode::get_singleton()->show_warning(p_msg.strip_edges(), vformat(TTR("%s Error"), get_vcs_name())); +} - ClassDB::bind_method(D_METHOD("is_addon_ready"), &EditorVCSInterface::is_addon_ready); +bool EditorVCSInterface::initialize(String p_project_path) { + bool result = false; + if (!GDVIRTUAL_CALL(_initialize, p_project_path, result)) { + UNIMPLEMENTED(); + return false; + } + return result; +} - // API methods that redirect calls to the proxy end points - ClassDB::bind_method(D_METHOD("initialize", "project_root_path"), &EditorVCSInterface::initialize); - ClassDB::bind_method(D_METHOD("is_vcs_initialized"), &EditorVCSInterface::is_vcs_initialized); - ClassDB::bind_method(D_METHOD("get_modified_files_data"), &EditorVCSInterface::get_modified_files_data); - ClassDB::bind_method(D_METHOD("stage_file", "file_path"), &EditorVCSInterface::stage_file); - ClassDB::bind_method(D_METHOD("unstage_file", "file_path"), &EditorVCSInterface::unstage_file); - ClassDB::bind_method(D_METHOD("commit", "msg"), &EditorVCSInterface::commit); - ClassDB::bind_method(D_METHOD("get_file_diff", "file_path"), &EditorVCSInterface::get_file_diff); - ClassDB::bind_method(D_METHOD("shut_down"), &EditorVCSInterface::shut_down); - ClassDB::bind_method(D_METHOD("get_project_name"), &EditorVCSInterface::get_project_name); - ClassDB::bind_method(D_METHOD("get_vcs_name"), &EditorVCSInterface::get_vcs_name); +void EditorVCSInterface::set_credentials(String p_username, String p_password, String p_ssh_public_key, String p_ssh_private_key, String p_ssh_passphrase) { + if (!GDVIRTUAL_CALL(_set_credentials, p_username, p_password, p_ssh_public_key, p_ssh_private_key, p_ssh_passphrase)) { + UNIMPLEMENTED(); + } } -bool EditorVCSInterface::_initialize(String p_project_root_path) { - WARN_PRINT("Selected VCS addon does not implement an initialization function. This warning will be suppressed."); - return true; +List<String> EditorVCSInterface::get_remotes() { + Array result; + if (!GDVIRTUAL_CALL(_get_remotes, result)) { + UNIMPLEMENTED(); + return {}; + } + + List<String> remotes; + for (int i = 0; i < result.size(); i++) { + remotes.push_back(result[i]); + } + return remotes; } -bool EditorVCSInterface::_is_vcs_initialized() { - return false; +List<EditorVCSInterface::StatusFile> EditorVCSInterface::get_modified_files_data() { + Array result; + if (!GDVIRTUAL_CALL(_get_modified_files_data, result)) { + UNIMPLEMENTED(); + return {}; + } + + List<EditorVCSInterface::StatusFile> status_files; + for (int i = 0; i < result.size(); i++) { + status_files.push_back(_convert_status_file(result[i])); + } + return status_files; } -Dictionary EditorVCSInterface::_get_modified_files_data() { - return Dictionary(); +void EditorVCSInterface::stage_file(String p_file_path) { + if (!GDVIRTUAL_CALL(_stage_file, p_file_path)) { + UNIMPLEMENTED(); + } } -void EditorVCSInterface::_stage_file(String p_file_path) { +void EditorVCSInterface::unstage_file(String p_file_path) { + if (!GDVIRTUAL_CALL(_unstage_file, p_file_path)) { + UNIMPLEMENTED(); + } } -void EditorVCSInterface::_unstage_file(String p_file_path) { +void EditorVCSInterface::discard_file(String p_file_path) { + if (!GDVIRTUAL_CALL(_discard_file, p_file_path)) { + UNIMPLEMENTED(); + } } -void EditorVCSInterface::_commit(String p_msg) { +void EditorVCSInterface::commit(String p_msg) { + if (!GDVIRTUAL_CALL(_commit, p_msg)) { + UNIMPLEMENTED(); + } } -TypedArray<Dictionary> EditorVCSInterface::_get_file_diff(String p_file_path) { - return TypedArray<Dictionary>(); +List<EditorVCSInterface::DiffFile> EditorVCSInterface::get_diff(String p_identifier, TreeArea p_area) { + TypedArray<Dictionary> result; + if (!GDVIRTUAL_CALL(_get_diff, p_identifier, int(p_area), result)) { + UNIMPLEMENTED(); + return {}; + } + + List<DiffFile> diff_files; + for (int i = 0; i < result.size(); i++) { + diff_files.push_back(_convert_diff_file(result[i])); + } + return diff_files; } -bool EditorVCSInterface::_shut_down() { - return false; +List<EditorVCSInterface::Commit> EditorVCSInterface::get_previous_commits(int p_max_commits) { + Array result; + if (!GDVIRTUAL_CALL(_get_previous_commits, p_max_commits, result)) { + UNIMPLEMENTED(); + return {}; + } + + List<EditorVCSInterface::Commit> commits; + for (int i = 0; i < result.size(); i++) { + commits.push_back(_convert_commit(result[i])); + } + return commits; } -String EditorVCSInterface::_get_project_name() { - return String(); +List<String> EditorVCSInterface::get_branch_list() { + Array result; + if (!GDVIRTUAL_CALL(_get_branch_list, result)) { + UNIMPLEMENTED(); + return {}; + } + + List<String> branch_list; + for (int i = 0; i < result.size(); i++) { + branch_list.push_back(result[i]); + } + return branch_list; } -String EditorVCSInterface::_get_vcs_name() { - return ""; +void EditorVCSInterface::create_branch(String p_branch_name) { + if (!GDVIRTUAL_CALL(_create_branch, p_branch_name)) { + UNIMPLEMENTED(); + } } -bool EditorVCSInterface::initialize(String p_project_root_path) { - is_initialized = call("_initialize", p_project_root_path); - return is_initialized; +void EditorVCSInterface::create_remote(String p_remote_name, String p_remote_url) { + if (!GDVIRTUAL_CALL(_create_remote, p_remote_name, p_remote_url)) { + UNIMPLEMENTED(); + } } -bool EditorVCSInterface::is_vcs_initialized() { - return call("_is_vcs_initialized"); +void EditorVCSInterface::remove_branch(String p_branch_name) { + if (!GDVIRTUAL_CALL(_remove_branch, p_branch_name)) { + UNIMPLEMENTED(); + } } -Dictionary EditorVCSInterface::get_modified_files_data() { - return call("_get_modified_files_data"); +void EditorVCSInterface::remove_remote(String p_remote_name) { + if (!GDVIRTUAL_CALL(_remove_remote, p_remote_name)) { + UNIMPLEMENTED(); + } } -void EditorVCSInterface::stage_file(String p_file_path) { - if (is_addon_ready()) { - call("_stage_file", p_file_path); +String EditorVCSInterface::get_current_branch_name() { + String result; + if (!GDVIRTUAL_CALL(_get_current_branch_name, result)) { + UNIMPLEMENTED(); + return ""; } + return result; } -void EditorVCSInterface::unstage_file(String p_file_path) { - if (is_addon_ready()) { - call("_unstage_file", p_file_path); +bool EditorVCSInterface::checkout_branch(String p_branch_name) { + bool result = false; + if (!GDVIRTUAL_CALL(_checkout_branch, p_branch_name, result)) { + UNIMPLEMENTED(); } + return result; } -bool EditorVCSInterface::is_addon_ready() { - return is_initialized; +void EditorVCSInterface::pull(String p_remote) { + if (!GDVIRTUAL_CALL(_pull, p_remote)) { + UNIMPLEMENTED(); + } } -void EditorVCSInterface::commit(String p_msg) { - if (is_addon_ready()) { - call("_commit", p_msg); +void EditorVCSInterface::push(String p_remote, bool p_force) { + if (!GDVIRTUAL_CALL(_push, p_remote, p_force)) { + UNIMPLEMENTED(); } } -TypedArray<Dictionary> EditorVCSInterface::get_file_diff(String p_file_path) { - if (is_addon_ready()) { - return call("_get_file_diff", p_file_path); +void EditorVCSInterface::fetch(String p_remote) { + if (!GDVIRTUAL_CALL(_fetch, p_remote)) { + UNIMPLEMENTED(); } - return TypedArray<Dictionary>(); } -bool EditorVCSInterface::shut_down() { - return call("_shut_down"); +List<EditorVCSInterface::DiffHunk> EditorVCSInterface::get_line_diff(String p_file_path, String p_text) { + Array result; + if (!GDVIRTUAL_CALL(_get_line_diff, p_file_path, p_text, result)) { + UNIMPLEMENTED(); + return {}; + } + + List<DiffHunk> diff_hunks; + for (int i = 0; i < result.size(); i++) { + diff_hunks.push_back(_convert_diff_hunk(result[i])); + } + return diff_hunks; } -String EditorVCSInterface::get_project_name() { - return call("_get_project_name"); +bool EditorVCSInterface::shut_down() { + bool result = false; + if (!GDVIRTUAL_CALL(_shut_down, result)) { + UNIMPLEMENTED(); + return false; + } + return result; } String EditorVCSInterface::get_vcs_name() { - return call("_get_vcs_name"); + String result; + if (!GDVIRTUAL_CALL(_get_vcs_name, result)) { + UNIMPLEMENTED(); + return {}; + } + return result; +} + +Dictionary EditorVCSInterface::create_diff_line(int p_new_line_no, int p_old_line_no, String p_content, String p_status) { + Dictionary diff_line; + diff_line["new_line_no"] = p_new_line_no; + diff_line["old_line_no"] = p_old_line_no; + diff_line["content"] = p_content; + diff_line["status"] = p_status; + + return diff_line; +} + +Dictionary EditorVCSInterface::create_diff_hunk(int p_old_start, int p_new_start, int p_old_lines, int p_new_lines) { + Dictionary diff_hunk; + diff_hunk["new_lines"] = p_new_lines; + diff_hunk["old_lines"] = p_old_lines; + diff_hunk["new_start"] = p_new_start; + diff_hunk["old_start"] = p_old_start; + diff_hunk["diff_lines"] = Array(); + return diff_hunk; +} + +Dictionary EditorVCSInterface::add_line_diffs_into_diff_hunk(Dictionary p_diff_hunk, Array p_line_diffs) { + p_diff_hunk["diff_lines"] = p_line_diffs; + return p_diff_hunk; +} + +Dictionary EditorVCSInterface::create_diff_file(String p_new_file, String p_old_file) { + Dictionary file_diff; + file_diff["new_file"] = p_new_file; + file_diff["old_file"] = p_old_file; + file_diff["diff_hunks"] = Array(); + return file_diff; +} + +Dictionary EditorVCSInterface::create_commit(String p_msg, String p_author, String p_id, int64_t p_unix_timestamp, int64_t p_offset_minutes) { + Dictionary commit_info; + commit_info["message"] = p_msg; + commit_info["author"] = p_author; + commit_info["unix_timestamp"] = p_unix_timestamp; + commit_info["offset_minutes"] = p_offset_minutes; + commit_info["id"] = p_id; + return commit_info; } -EditorVCSInterface::EditorVCSInterface() { +Dictionary EditorVCSInterface::add_diff_hunks_into_diff_file(Dictionary p_diff_file, Array p_diff_hunks) { + p_diff_file["diff_hunks"] = p_diff_hunks; + return p_diff_file; } -EditorVCSInterface::~EditorVCSInterface() { +Dictionary EditorVCSInterface::create_status_file(String p_file_path, ChangeType p_change, TreeArea p_area) { + Dictionary sf; + sf["file_path"] = p_file_path; + sf["change_type"] = p_change; + sf["area"] = p_area; + return sf; +} + +EditorVCSInterface::DiffLine EditorVCSInterface::_convert_diff_line(Dictionary p_diff_line) { + DiffLine d; + d.new_line_no = p_diff_line["new_line_no"]; + d.old_line_no = p_diff_line["old_line_no"]; + d.content = p_diff_line["content"]; + d.status = p_diff_line["status"]; + return d; +} + +EditorVCSInterface::DiffHunk EditorVCSInterface::_convert_diff_hunk(Dictionary p_diff_hunk) { + DiffHunk dh; + dh.new_lines = p_diff_hunk["new_lines"]; + dh.old_lines = p_diff_hunk["old_lines"]; + dh.new_start = p_diff_hunk["new_start"]; + dh.old_start = p_diff_hunk["old_start"]; + Array diff_lines = p_diff_hunk["diff_lines"]; + for (int i = 0; i < diff_lines.size(); i++) { + DiffLine dl = _convert_diff_line(diff_lines[i]); + dh.diff_lines.push_back(dl); + } + return dh; +} + +EditorVCSInterface::DiffFile EditorVCSInterface::_convert_diff_file(Dictionary p_diff_file) { + DiffFile df; + df.new_file = p_diff_file["new_file"]; + df.old_file = p_diff_file["old_file"]; + Array diff_hunks = p_diff_file["diff_hunks"]; + for (int i = 0; i < diff_hunks.size(); i++) { + DiffHunk dh = _convert_diff_hunk(diff_hunks[i]); + df.diff_hunks.push_back(dh); + } + return df; +} + +EditorVCSInterface::Commit EditorVCSInterface::_convert_commit(Dictionary p_commit) { + EditorVCSInterface::Commit c; + c.msg = p_commit["message"]; + c.author = p_commit["author"]; + c.unix_timestamp = p_commit["unix_timestamp"]; + c.offset_minutes = p_commit["offset_minutes"]; + c.id = p_commit["id"]; + return c; +} + +EditorVCSInterface::StatusFile EditorVCSInterface::_convert_status_file(Dictionary p_status_file) { + StatusFile sf; + sf.file_path = p_status_file["file_path"]; + sf.change_type = (ChangeType)(int)p_status_file["change_type"]; + sf.area = (TreeArea)(int)p_status_file["area"]; + return sf; +} + +void EditorVCSInterface::_bind_methods() { + // Proxy end points that implement the VCS specific operations that the editor demands. + GDVIRTUAL_BIND(_initialize, "project_path"); + GDVIRTUAL_BIND(_set_credentials, "username", "password", "ssh_public_key_path", "ssh_private_key_path", "ssh_passphrase"); + GDVIRTUAL_BIND(_get_modified_files_data); + GDVIRTUAL_BIND(_stage_file, "file_path"); + GDVIRTUAL_BIND(_unstage_file, "file_path"); + GDVIRTUAL_BIND(_discard_file, "file_path"); + GDVIRTUAL_BIND(_commit, "msg"); + GDVIRTUAL_BIND(_get_diff, "identifier", "area"); + GDVIRTUAL_BIND(_shut_down); + GDVIRTUAL_BIND(_get_vcs_name); + GDVIRTUAL_BIND(_get_previous_commits, "max_commits"); + GDVIRTUAL_BIND(_get_branch_list); + GDVIRTUAL_BIND(_get_remotes); + GDVIRTUAL_BIND(_create_branch, "branch_name"); + GDVIRTUAL_BIND(_remove_branch, "branch_name"); + GDVIRTUAL_BIND(_create_remote, "remote_name", "remote_url"); + GDVIRTUAL_BIND(_remove_remote, "remote_name"); + GDVIRTUAL_BIND(_get_current_branch_name); + GDVIRTUAL_BIND(_checkout_branch, "branch_name"); + GDVIRTUAL_BIND(_pull, "remote"); + GDVIRTUAL_BIND(_push, "remote", "force"); + GDVIRTUAL_BIND(_fetch, "remote"); + GDVIRTUAL_BIND(_get_line_diff, "file_path", "text"); + + ClassDB::bind_method(D_METHOD("create_diff_line", "new_line_no", "old_line_no", "content", "status"), &EditorVCSInterface::create_diff_line); + ClassDB::bind_method(D_METHOD("create_diff_hunk", "old_start", "new_start", "old_lines", "new_lines"), &EditorVCSInterface::create_diff_hunk); + ClassDB::bind_method(D_METHOD("create_diff_file", "new_file", "old_file"), &EditorVCSInterface::create_diff_file); + ClassDB::bind_method(D_METHOD("create_commit", "msg", "author", "id", "unix_timestamp", "offset_minutes"), &EditorVCSInterface::create_commit); + ClassDB::bind_method(D_METHOD("create_status_file", "file_path", "change_type", "area"), &EditorVCSInterface::create_status_file); + ClassDB::bind_method(D_METHOD("add_diff_hunks_into_diff_file", "diff_file", "diff_hunks"), &EditorVCSInterface::add_diff_hunks_into_diff_file); + ClassDB::bind_method(D_METHOD("add_line_diffs_into_diff_hunk", "diff_hunk", "line_diffs"), &EditorVCSInterface::add_line_diffs_into_diff_hunk); + ClassDB::bind_method(D_METHOD("popup_error", "msg"), &EditorVCSInterface::popup_error); + + BIND_ENUM_CONSTANT(CHANGE_TYPE_NEW); + BIND_ENUM_CONSTANT(CHANGE_TYPE_MODIFIED); + BIND_ENUM_CONSTANT(CHANGE_TYPE_RENAMED); + BIND_ENUM_CONSTANT(CHANGE_TYPE_DELETED); + BIND_ENUM_CONSTANT(CHANGE_TYPE_TYPECHANGE); + BIND_ENUM_CONSTANT(CHANGE_TYPE_UNMERGED); + + BIND_ENUM_CONSTANT(TREE_AREA_COMMIT); + BIND_ENUM_CONSTANT(TREE_AREA_STAGED); + BIND_ENUM_CONSTANT(TREE_AREA_UNSTAGED); } EditorVCSInterface *EditorVCSInterface::get_singleton() { @@ -170,14 +409,14 @@ void EditorVCSInterface::create_vcs_metadata_files(VCSMetadata p_vcs_metadata_ty if (p_vcs_metadata_type == VCSMetadata::GIT) { Ref<FileAccess> f = FileAccess::open(p_dir.path_join(".gitignore"), FileAccess::WRITE); if (f.is_null()) { - ERR_FAIL_MSG(TTR("Couldn't create .gitignore in project path.")); + ERR_FAIL_MSG("Couldn't create .gitignore in project path."); } else { f->store_line("# Godot 4+ specific ignores"); f->store_line(".godot/"); } f = FileAccess::open(p_dir.path_join(".gitattributes"), FileAccess::WRITE); if (f.is_null()) { - ERR_FAIL_MSG(TTR("Couldn't create .gitattributes in project path.")); + ERR_FAIL_MSG("Couldn't create .gitattributes in project path."); } else { f->store_line("# Normalize EOL for all files that Git considers text files."); f->store_line("* text=auto eol=lf"); diff --git a/editor/editor_vcs_interface.h b/editor/editor_vcs_interface.h index d6d7ffa0e9..e23e032ab9 100644 --- a/editor/editor_vcs_interface.h +++ b/editor/editor_vcs_interface.h @@ -32,30 +32,103 @@ #define EDITOR_VCS_INTERFACE_H #include "core/object/class_db.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language_extension.h" #include "core/string/ustring.h" -#include "scene/gui/panel_container.h" +#include "core/variant/type_info.h" class EditorVCSInterface : public Object { GDCLASS(EditorVCSInterface, Object) - bool is_initialized = false; +public: + enum ChangeType { + CHANGE_TYPE_NEW = 0, + CHANGE_TYPE_MODIFIED = 1, + CHANGE_TYPE_RENAMED = 2, + CHANGE_TYPE_DELETED = 3, + CHANGE_TYPE_TYPECHANGE = 4, + CHANGE_TYPE_UNMERGED = 5 + }; + + enum TreeArea { + TREE_AREA_COMMIT = 0, + TREE_AREA_STAGED = 1, + TREE_AREA_UNSTAGED = 2 + }; + + struct DiffLine { + int new_line_no; + int old_line_no; + String content; + String status; + + String old_text; + String new_text; + }; + + struct DiffHunk { + int new_start; + int old_start; + int new_lines; + int old_lines; + List<DiffLine> diff_lines; + }; + + struct DiffFile { + String new_file; + String old_file; + List<DiffHunk> diff_hunks; + }; + + struct Commit { + String author; + String msg; + String id; + int64_t unix_timestamp; + int64_t offset_minutes; + }; + + struct StatusFile { + TreeArea area; + ChangeType change_type; + String file_path; + }; protected: static EditorVCSInterface *singleton; static void _bind_methods(); - // Implemented by addons as end points for the proxy functions - virtual bool _initialize(String p_project_root_path); - virtual bool _is_vcs_initialized(); - virtual Dictionary _get_modified_files_data(); - virtual void _stage_file(String p_file_path); - virtual void _unstage_file(String p_file_path); - virtual void _commit(String p_msg); - virtual TypedArray<Dictionary> _get_file_diff(String p_file_path); - virtual bool _shut_down(); - virtual String _get_project_name(); - virtual String _get_vcs_name(); + DiffLine _convert_diff_line(Dictionary p_diff_line); + DiffHunk _convert_diff_hunk(Dictionary p_diff_hunk); + DiffFile _convert_diff_file(Dictionary p_diff_file); + Commit _convert_commit(Dictionary p_commit); + StatusFile _convert_status_file(Dictionary p_status_file); + + // Proxy endpoints for extensions to implement + GDVIRTUAL1R(bool, _initialize, String); + GDVIRTUAL5(_set_credentials, String, String, String, String, String); + GDVIRTUAL0R(Array, _get_modified_files_data); + GDVIRTUAL1(_stage_file, String); + GDVIRTUAL1(_unstage_file, String); + GDVIRTUAL1(_discard_file, String); + GDVIRTUAL1(_commit, String); + GDVIRTUAL2R(TypedArray<Dictionary>, _get_diff, String, int); + GDVIRTUAL0R(bool, _shut_down); + GDVIRTUAL0R(String, _get_vcs_name); + GDVIRTUAL1R(Array, _get_previous_commits, int); + GDVIRTUAL0R(Array, _get_branch_list); + GDVIRTUAL0R(Array, _get_remotes); + GDVIRTUAL1(_create_branch, String); + GDVIRTUAL1(_remove_branch, String); + GDVIRTUAL2(_create_remote, String, String); + GDVIRTUAL1(_remove_remote, String); + GDVIRTUAL0R(String, _get_current_branch_name); + GDVIRTUAL1R(bool, _checkout_branch, String); + GDVIRTUAL1(_pull, String); + GDVIRTUAL2(_push, String, bool); + GDVIRTUAL1(_fetch, String); + GDVIRTUAL2R(Array, _get_line_diff, String, String); public: static EditorVCSInterface *get_singleton(); @@ -67,22 +140,44 @@ public: }; static void create_vcs_metadata_files(VCSMetadata p_vcs_metadata_type, String &p_dir); - bool is_addon_ready(); - - // Proxy functions to the editor for use - bool initialize(String p_project_root_path); - bool is_vcs_initialized(); - Dictionary get_modified_files_data(); + // Proxies to the editor for use + bool initialize(String p_project_path); + void set_credentials(String p_username, String p_password, String p_ssh_public_key_path, String p_ssh_private_key_path, String p_ssh_passphrase); + List<StatusFile> get_modified_files_data(); void stage_file(String p_file_path); void unstage_file(String p_file_path); + void discard_file(String p_file_path); void commit(String p_msg); - TypedArray<Dictionary> get_file_diff(String p_file_path); + List<DiffFile> get_diff(String p_identifier, TreeArea p_area); bool shut_down(); - String get_project_name(); String get_vcs_name(); + List<Commit> get_previous_commits(int p_max_commits); + List<String> get_branch_list(); + List<String> get_remotes(); + void create_branch(String p_branch_name); + void remove_branch(String p_branch_name); + void create_remote(String p_remote_name, String p_remote_url); + void remove_remote(String p_remote_name); + String get_current_branch_name(); + bool checkout_branch(String p_branch_name); + void pull(String p_remote); + void push(String p_remote, bool p_force); + void fetch(String p_remote); + List<DiffHunk> get_line_diff(String p_file_path, String p_text); - EditorVCSInterface(); - virtual ~EditorVCSInterface(); + // Helper functions to create and convert Dictionary into data structures + Dictionary create_diff_line(int p_new_line_no, int p_old_line_no, String p_content, String p_status); + Dictionary create_diff_hunk(int p_old_start, int p_new_start, int p_old_lines, int p_new_lines); + Dictionary create_diff_file(String p_new_file, String p_old_file); + Dictionary create_commit(String p_msg, String p_author, String p_id, int64_t p_unix_timestamp, int64_t p_offset_minutes); + Dictionary create_status_file(String p_file_path, ChangeType p_change, TreeArea p_area); + Dictionary add_line_diffs_into_diff_hunk(Dictionary p_diff_hunk, Array p_line_diffs); + Dictionary add_diff_hunks_into_diff_file(Dictionary p_diff_file, Array p_diff_hunks); + + void popup_error(String p_msg); }; +VARIANT_ENUM_CAST(EditorVCSInterface::ChangeType); +VARIANT_ENUM_CAST(EditorVCSInterface::TreeArea); + #endif // EDITOR_VCS_INTERFACE_H diff --git a/editor/icons/CameraEffects.svg b/editor/icons/CameraAttributes.svg index 1ee7e15c87..1ee7e15c87 100644 --- a/editor/icons/CameraEffects.svg +++ b/editor/icons/CameraAttributes.svg diff --git a/editor/icons/VcsBranches.svg b/editor/icons/VcsBranches.svg new file mode 100644 index 0000000000..e79019590f --- /dev/null +++ b/editor/icons/VcsBranches.svg @@ -0,0 +1 @@ +<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="1.5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-rule="nonzero"><path d="m3.755 1.396c-1.599 0-2.914 1.315-2.914 2.913 0 1.599 1.315 2.914 2.914 2.914 1.598 0 2.913-1.315 2.913-2.914 0-1.598-1.315-2.913-2.913-2.913zm0 1.462c.796 0 1.451.655 1.451 1.451 0 .797-.655 1.452-1.451 1.452-.797 0-1.452-.655-1.452-1.452 0-.796.655-1.451 1.452-1.451z"/><path d="m12.073 8.956c-1.599 0-2.914 1.316-2.914 2.914s1.315 2.914 2.914 2.914c1.598 0 2.914-1.316 2.914-2.914s-1.316-2.914-2.914-2.914zm0 1.463c.796 0 1.451.655 1.451 1.451s-.655 1.451-1.451 1.451-1.451-.655-1.451-1.451.655-1.451 1.451-1.451z"/><path d="m12.073 1.396c-1.599 0-2.914 1.315-2.914 2.913 0 1.599 1.315 2.914 2.914 2.914 1.598 0 2.914-1.315 2.914-2.914 0-1.598-1.316-2.913-2.914-2.913zm0 1.462c.796 0 1.451.655 1.451 1.451 0 .797-.655 1.452-1.451 1.452s-1.451-.655-1.451-1.452c0-.796.655-1.451 1.451-1.451z"/></g><path d="m9.159 11.87h-2.491l-2.913-2.914v-1.733" fill="none" stroke="#e0e0e0" stroke-width="1.5"/><path d="m9.159 4.309h-2.491" fill="none" stroke="#e0e0e0" stroke-width="1.5"/></svg> diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 1ff771bcce..730aa3bd61 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -30,6 +30,7 @@ #include "scene_import_settings.h" +#include "core/config/project_settings.h" #include "editor/editor_file_dialog.h" #include "editor/editor_file_system.h" #include "editor/editor_inspector.h" @@ -1288,6 +1289,11 @@ SceneImportSettings::SceneImportSettings() { base_viewport->add_child(camera); camera->make_current(); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + camera_attributes.instantiate(); + camera->set_attributes(camera_attributes); + } + light = memnew(DirectionalLight3D); light->set_transform(Transform3D().looking_at(Vector3(-1, -2, -0.6), Vector3(0, 1, 0))); base_viewport->add_child(light); diff --git a/editor/import/scene_import_settings.h b/editor/import/scene_import_settings.h index b5cf82f64b..104a7a9f7e 100644 --- a/editor/import/scene_import_settings.h +++ b/editor/import/scene_import_settings.h @@ -74,6 +74,7 @@ class SceneImportSettings : public ConfirmationDialog { SubViewport *base_viewport = nullptr; Camera3D *camera = nullptr; + Ref<CameraAttributesPractical> camera_attributes; bool first_aabb = false; AABB contents_aabb; diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index cded53e054..2809eb01cd 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -802,8 +802,7 @@ void AnimationNodeStateMachineEditor::_open_connect_menu(const Vector2 &p_positi if (anodesm.is_valid()) { _create_submenu(connect_menu, anodesm, connecting_to_node, connecting_to_node); } else { - Ref<AnimationNodeStateMachine> prev = state_machine; - _create_submenu(connect_menu, prev, connecting_to_node, connecting_to_node, true); + _create_submenu(connect_menu, state_machine, connecting_to_node, connecting_to_node, true); } connect_menu->add_submenu_item(TTR("To") + " Animation", connecting_to_node); @@ -835,6 +834,10 @@ bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<Ani String prev_path; Vector<Ref<AnimationNodeStateMachine>> parents = p_parents; + if (from_root && p_nodesm->get_prev_state_machine() == nullptr) { + return false; + } + if (from_root) { AnimationNodeStateMachine *prev = p_nodesm->get_prev_state_machine(); @@ -844,6 +847,8 @@ bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<Ani prev_path += "../"; prev = prev->get_prev_state_machine(); } + end_menu->add_item("Root", nodes_to_connect.size()); + nodes_to_connect.push_back(prev_path + state_machine->end_node); prev_path.remove_at(prev_path.size() - 1); } @@ -874,22 +879,22 @@ bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<Ani } if (ansm.is_valid()) { - bool found = false; + bool parent_found = false; for (int i = 0; i < parents.size(); i++) { if (parents[i] == ansm) { path = path.replace_first("/../" + E, ""); - found = true; + parent_found = true; break; } } - if (!found) { - state_machine_menu->add_item(E, nodes_to_connect.size()); - nodes_to_connect.push_back(path); - } else { + if (parent_found) { end_menu->add_item(E, nodes_to_connect.size()); nodes_to_connect.push_back(path + "/" + state_machine->end_node); + } else { + state_machine_menu->add_item(E, nodes_to_connect.size()); + nodes_to_connect.push_back(path); } if (_create_submenu(nodes_menu, ansm, E, path, false, parents)) { diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 9a31263f9a..0e84381279 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -579,7 +579,7 @@ template <typename T> static void plot_curve_accurate(const Curve &curve, float step, T plot_func) { if (curve.get_point_count() <= 1) { // Not enough points to make a curve, so it's just a straight line - float y = curve.interpolate(0); + float y = curve.sample(0); plot_func(Vector2(0, y), Vector2(1.f, y), true); } else { @@ -603,7 +603,7 @@ static void plot_curve_accurate(const Curve &curve, float step, T plot_func) { for (float x = step; x < len; x += step) { pos.x = a.x + x; - pos.y = curve.interpolate_local_nocheck(i - 1, x); + pos.y = curve.sample_local_nocheck(i - 1, x); plot_func(prev_pos, pos, true); prev_pos = pos; } @@ -817,7 +817,7 @@ Ref<Texture2D> CurvePreviewGenerator::generate(const Ref<Resource> &p_from, cons int prev_y = 0; for (int x = 0; x < im.get_width(); ++x) { float t = static_cast<float>(x) / im.get_width(); - float v = (curve.interpolate_baked(t) - curve.get_min_value()) / range_y; + float v = (curve.sample_baked(t) - curve.get_min_value()) / range_y; int y = CLAMP(im.get_height() - v * im.get_height(), 0, im.get_height()); // Plot point diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index f4a718119e..369ab0745e 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -342,6 +342,12 @@ EditorMaterialPreviewPlugin::EditorMaterialPreviewPlugin() { RS::get_singleton()->camera_set_transform(camera, Transform3D(Basis(), Vector3(0, 0, 3))); RS::get_singleton()->camera_set_perspective(camera, 45, 0.1, 10); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + camera_attributes = RS::get_singleton()->camera_attributes_create(); + RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, 1.0, 0.000032552); // Matches default CameraAttributesPhysical to work well with default DirectionalLight3Ds. + RS::get_singleton()->camera_set_camera_attributes(camera, camera_attributes); + } + light = RS::get_singleton()->directional_light_create(); light_instance = RS::get_singleton()->instance_create2(light, scenario); RS::get_singleton()->instance_set_transform(light_instance, Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0))); @@ -440,6 +446,7 @@ EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() { RS::get_singleton()->free(light2); RS::get_singleton()->free(light_instance2); RS::get_singleton()->free(camera); + RS::get_singleton()->free(camera_attributes); RS::get_singleton()->free(scenario); } @@ -743,6 +750,12 @@ EditorMeshPreviewPlugin::EditorMeshPreviewPlugin() { //RS::get_singleton()->camera_set_perspective(camera,45,0.1,10); RS::get_singleton()->camera_set_orthogonal(camera, 1.0, 0.01, 1000.0); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + camera_attributes = RS::get_singleton()->camera_attributes_create(); + RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, 1.0, 0.000032552); // Matches default CameraAttributesPhysical to work well with default DirectionalLight3Ds. + RS::get_singleton()->camera_set_camera_attributes(camera, camera_attributes); + } + light = RS::get_singleton()->directional_light_create(); light_instance = RS::get_singleton()->instance_create2(light, scenario); RS::get_singleton()->instance_set_transform(light_instance, Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0))); @@ -768,6 +781,7 @@ EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() { RS::get_singleton()->free(light2); RS::get_singleton()->free(light_instance2); RS::get_singleton()->free(camera); + RS::get_singleton()->free(camera_attributes); RS::get_singleton()->free(scenario); } diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index 163cfe79f9..efb2c80cfd 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -91,6 +91,7 @@ class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator { RID light2; RID light_instance2; RID camera; + RID camera_attributes; Semaphore preview_done; void _generate_frame_started(); @@ -133,6 +134,7 @@ class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator { RID light2; RID light_instance2; RID camera; + RID camera_attributes; Semaphore preview_done; void _generate_frame_started(); diff --git a/scene/gui/gradient_edit.cpp b/editor/plugins/gradient_editor.cpp index 5e56be7c29..c13b162db6 100644 --- a/scene/gui/gradient_edit.cpp +++ b/editor/plugins/gradient_editor.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* gradient_edit.cpp */ +/* gradient_editor.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,25 +28,29 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "gradient_edit.h" +#include "gradient_editor.h" #include "core/os/keyboard.h" +#include "editor/editor_node.h" +#include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" + +void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) { + gradient = p_gradient; + connect("ramp_changed", callable_mp(this, &GradientEditor::_ramp_changed)); + gradient->connect("changed", callable_mp(this, &GradientEditor::_gradient_changed)); + set_points(gradient->get_points()); + set_interpolation_mode(gradient->get_interpolation_mode()); +} -GradientEdit::GradientEdit() { - set_focus_mode(FOCUS_ALL); - - popup = memnew(PopupPanel); - picker = memnew(ColorPicker); - popup->add_child(picker); - - gradient_cache.instantiate(); - preview_texture.instantiate(); - - preview_texture->set_width(1024); - add_child(popup, false, INTERNAL_MODE_FRONT); +void GradientEditor::reverse_gradient() { + gradient->reverse(); + set_points(gradient->get_points()); + emit_signal(SNAME("ramp_changed")); + queue_redraw(); } -int GradientEdit::_get_point_from_pos(int x) { +int GradientEditor::_get_point_from_pos(int x) { int result = -1; int total_w = get_size().width - get_size().height - draw_spacing; float min_distance = 1e20; @@ -62,7 +66,7 @@ int GradientEdit::_get_point_from_pos(int x) { return result; } -void GradientEdit::_show_color_picker() { +void GradientEditor::_show_color_picker() { if (grabbed == -1) { return; } @@ -80,10 +84,106 @@ void GradientEdit::_show_color_picker() { popup->popup(); } -GradientEdit::~GradientEdit() { +void GradientEditor::_gradient_changed() { + if (editing) { + return; + } + + editing = true; + Vector<Gradient::Point> points = gradient->get_points(); + set_points(points); + set_interpolation_mode(gradient->get_interpolation_mode()); + queue_redraw(); + editing = false; +} + +void GradientEditor::_ramp_changed() { + editing = true; + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); + undo_redo->create_action(TTR("Gradient Edited"), UndoRedo::MERGE_ENDS); + undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets()); + undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors()); + undo_redo->add_do_method(gradient.ptr(), "set_interpolation_mode", get_interpolation_mode()); + undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets()); + undo_redo->add_undo_method(gradient.ptr(), "set_colors", gradient->get_colors()); + undo_redo->add_undo_method(gradient.ptr(), "set_interpolation_mode", gradient->get_interpolation_mode()); + undo_redo->commit_action(); + editing = false; +} + +void GradientEditor::_color_changed(const Color &p_color) { + if (grabbed == -1) { + return; + } + points.write[grabbed].color = p_color; + queue_redraw(); + emit_signal(SNAME("ramp_changed")); +} + +void GradientEditor::set_ramp(const Vector<float> &p_offsets, const Vector<Color> &p_colors) { + ERR_FAIL_COND(p_offsets.size() != p_colors.size()); + points.clear(); + for (int i = 0; i < p_offsets.size(); i++) { + Gradient::Point p; + p.offset = p_offsets[i]; + p.color = p_colors[i]; + points.push_back(p); + } + + points.sort(); + queue_redraw(); +} + +Vector<float> GradientEditor::get_offsets() const { + Vector<float> ret; + for (int i = 0; i < points.size(); i++) { + ret.push_back(points[i].offset); + } + return ret; +} + +Vector<Color> GradientEditor::get_colors() const { + Vector<Color> ret; + for (int i = 0; i < points.size(); i++) { + ret.push_back(points[i].color); + } + return ret; +} + +void GradientEditor::set_points(Vector<Gradient::Point> &p_points) { + if (points.size() != p_points.size()) { + grabbed = -1; + } + points.clear(); + points = p_points; + points.sort(); +} + +Vector<Gradient::Point> &GradientEditor::get_points() { + return points; +} + +void GradientEditor::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) { + interpolation_mode = p_interp_mode; +} + +Gradient::InterpolationMode GradientEditor::get_interpolation_mode() { + return interpolation_mode; } -void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { +ColorPicker *GradientEditor::get_picker() { + return picker; +} + +PopupPanel *GradientEditor::get_popup() { + return popup; +} + +Size2 GradientEditor::get_minimum_size() const { + return Size2(0, 60) * EDSCALE; +} + +void GradientEditor::gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); Ref<InputEventKey> k = p_event; @@ -286,11 +386,11 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { } } -void GradientEdit::_notification(int p_what) { +void GradientEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - if (!picker->is_connected("color_changed", callable_mp(this, &GradientEdit::_color_changed))) { - picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed)); + if (!picker->is_connected("color_changed", callable_mp(this, &GradientEditor::_color_changed))) { + picker->connect("color_changed", callable_mp(this, &GradientEditor::_color_changed)); } [[fallthrough]]; } @@ -369,78 +469,24 @@ void GradientEdit::_notification(int p_what) { } } -Size2 GradientEdit::get_minimum_size() const { - return Vector2(0, 16); -} - -void GradientEdit::_color_changed(const Color &p_color) { - if (grabbed == -1) { - return; - } - points.write[grabbed].color = p_color; - queue_redraw(); - emit_signal(SNAME("ramp_changed")); -} - -void GradientEdit::set_ramp(const Vector<float> &p_offsets, const Vector<Color> &p_colors) { - ERR_FAIL_COND(p_offsets.size() != p_colors.size()); - points.clear(); - for (int i = 0; i < p_offsets.size(); i++) { - Gradient::Point p; - p.offset = p_offsets[i]; - p.color = p_colors[i]; - points.push_back(p); - } - - points.sort(); - queue_redraw(); -} - -Vector<float> GradientEdit::get_offsets() const { - Vector<float> ret; - for (int i = 0; i < points.size(); i++) { - ret.push_back(points[i].offset); - } - return ret; -} - -Vector<Color> GradientEdit::get_colors() const { - Vector<Color> ret; - for (int i = 0; i < points.size(); i++) { - ret.push_back(points[i].color); - } - return ret; -} - -void GradientEdit::set_points(Vector<Gradient::Point> &p_points) { - if (points.size() != p_points.size()) { - grabbed = -1; - } - points.clear(); - points = p_points; - points.sort(); -} - -Vector<Gradient::Point> &GradientEdit::get_points() { - return points; +void GradientEditor::_bind_methods() { + ADD_SIGNAL(MethodInfo("ramp_changed")); } -void GradientEdit::set_interpolation_mode(Gradient::InterpolationMode p_interp_mode) { - interpolation_mode = p_interp_mode; -} +GradientEditor::GradientEditor() { + set_focus_mode(FOCUS_ALL); -Gradient::InterpolationMode GradientEdit::get_interpolation_mode() { - return interpolation_mode; -} + popup = memnew(PopupPanel); + picker = memnew(ColorPicker); + popup->add_child(picker); + popup->connect("about_to_popup", callable_mp(EditorNode::get_singleton(), &EditorNode::setup_color_picker).bind(GradientEditor::get_picker())); -ColorPicker *GradientEdit::get_picker() { - return picker; -} + gradient_cache.instantiate(); + preview_texture.instantiate(); -PopupPanel *GradientEdit::get_popup() { - return popup; + preview_texture->set_width(1024); + add_child(popup, false, INTERNAL_MODE_FRONT); } -void GradientEdit::_bind_methods() { - ADD_SIGNAL(MethodInfo("ramp_changed")); +GradientEditor::~GradientEditor() { } diff --git a/scene/gui/gradient_edit.h b/editor/plugins/gradient_editor.h index b7c99f1f1c..816b539ba2 100644 --- a/scene/gui/gradient_edit.h +++ b/editor/plugins/gradient_editor.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* gradient_edit.h */ +/* gradient_editor.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,15 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GRADIENT_EDIT_H -#define GRADIENT_EDIT_H +#ifndef GRADIENT_EDITOR_H +#define GRADIENT_EDITOR_H #include "scene/gui/color_picker.h" #include "scene/gui/popup.h" #include "scene/resources/gradient.h" -class GradientEdit : public Control { - GDCLASS(GradientEdit, Control); +class GradientEditor : public Control { + GDCLASS(GradientEditor, Control); PopupPanel *popup = nullptr; ColorPicker *picker = nullptr; @@ -46,6 +46,8 @@ class GradientEdit : public Control { Vector<Gradient::Point> points; Gradient::InterpolationMode interpolation_mode = Gradient::GRADIENT_INTERPOLATE_LINEAR; + bool editing = false; + Ref<Gradient> gradient; Ref<Gradient> gradient_cache; Ref<GradientTexture1D> preview_texture; @@ -56,8 +58,10 @@ class GradientEdit : public Control { int draw_spacing = BASE_SPACING; int draw_point_width = BASE_POINT_WIDTH; - void _draw_checker(int x, int y, int w, int h); + void _gradient_changed(); + void _ramp_changed(); void _color_changed(const Color &p_color); + int _get_point_from_pos(int x); void _show_color_picker(); @@ -67,20 +71,26 @@ protected: static void _bind_methods(); public: + void set_gradient(const Ref<Gradient> &p_gradient); + void reverse_gradient(); + void set_ramp(const Vector<float> &p_offsets, const Vector<Color> &p_colors); + Vector<float> get_offsets() const; Vector<Color> get_colors() const; void set_points(Vector<Gradient::Point> &p_points); Vector<Gradient::Point> &get_points(); + void set_interpolation_mode(Gradient::InterpolationMode p_interp_mode); Gradient::InterpolationMode get_interpolation_mode(); + ColorPicker *get_picker(); PopupPanel *get_popup(); virtual Size2 get_minimum_size() const override; - GradientEdit(); - virtual ~GradientEdit(); + GradientEditor(); + virtual ~GradientEditor(); }; -#endif // GRADIENT_EDIT_H +#endif // GRADIENT_EDITOR_H diff --git a/editor/plugins/gradient_editor_plugin.cpp b/editor/plugins/gradient_editor_plugin.cpp index 890090c899..0f412aaefd 100644 --- a/editor/plugins/gradient_editor_plugin.cpp +++ b/editor/plugins/gradient_editor_plugin.cpp @@ -37,62 +37,6 @@ #include "editor/editor_undo_redo_manager.h" #include "node_3d_editor_plugin.h" -Size2 GradientEditor::get_minimum_size() const { - return Size2(0, 60) * EDSCALE; -} - -void GradientEditor::_gradient_changed() { - if (editing) { - return; - } - - editing = true; - Vector<Gradient::Point> points = gradient->get_points(); - set_points(points); - set_interpolation_mode(gradient->get_interpolation_mode()); - queue_redraw(); - editing = false; -} - -void GradientEditor::_ramp_changed() { - editing = true; - Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); - undo_redo->create_action(TTR("Gradient Edited"), UndoRedo::MERGE_ENDS); - undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets()); - undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors()); - undo_redo->add_do_method(gradient.ptr(), "set_interpolation_mode", get_interpolation_mode()); - undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets()); - undo_redo->add_undo_method(gradient.ptr(), "set_colors", gradient->get_colors()); - undo_redo->add_undo_method(gradient.ptr(), "set_interpolation_mode", gradient->get_interpolation_mode()); - undo_redo->commit_action(); - editing = false; -} - -void GradientEditor::_bind_methods() { -} - -void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) { - gradient = p_gradient; - connect("ramp_changed", callable_mp(this, &GradientEditor::_ramp_changed)); - gradient->connect("changed", callable_mp(this, &GradientEditor::_gradient_changed)); - set_points(gradient->get_points()); - set_interpolation_mode(gradient->get_interpolation_mode()); -} - -void GradientEditor::reverse_gradient() { - gradient->reverse(); - set_points(gradient->get_points()); - emit_signal(SNAME("ramp_changed")); - queue_redraw(); -} - -GradientEditor::GradientEditor() { - GradientEdit::get_popup()->connect("about_to_popup", callable_mp(EditorNode::get_singleton(), &EditorNode::setup_color_picker).bind(GradientEdit::get_picker())); - editing = false; -} - -/////////////////////// - void GradientReverseButton::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { diff --git a/editor/plugins/gradient_editor_plugin.h b/editor/plugins/gradient_editor_plugin.h index 26bf76fecd..ab191d83e2 100644 --- a/editor/plugins/gradient_editor_plugin.h +++ b/editor/plugins/gradient_editor_plugin.h @@ -32,26 +32,7 @@ #define GRADIENT_EDITOR_PLUGIN_H #include "editor/editor_plugin.h" -#include "scene/gui/gradient_edit.h" - -class GradientEditor : public GradientEdit { - GDCLASS(GradientEditor, GradientEdit); - - bool editing; - Ref<Gradient> gradient; - - void _gradient_changed(); - void _ramp_changed(); - -protected: - static void _bind_methods(); - -public: - virtual Size2 get_minimum_size() const override; - void set_gradient(const Ref<Gradient> &p_gradient); - void reverse_gradient(); - GradientEditor(); -}; +#include "gradient_editor.h" class GradientReverseButton : public BaseButton { GDCLASS(GradientReverseButton, BaseButton); diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index 709f19016c..d204873f92 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -30,6 +30,7 @@ #include "material_editor_plugin.h" +#include "core/config/project_settings.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" @@ -161,6 +162,10 @@ MaterialEditor::MaterialEditor() { // without much distortion. camera->set_perspective(20, 0.1, 10); camera->make_current(); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + camera_attributes.instantiate(); + camera->set_attributes(camera_attributes); + } viewport->add_child(camera); light1 = memnew(DirectionalLight3D); diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h index 06ae43e6d7..828dd9f972 100644 --- a/editor/plugins/material_editor_plugin.h +++ b/editor/plugins/material_editor_plugin.h @@ -55,6 +55,7 @@ class MaterialEditor : public Control { DirectionalLight3D *light1 = nullptr; DirectionalLight3D *light2 = nullptr; Camera3D *camera = nullptr; + Ref<CameraAttributesPractical> camera_attributes; Ref<SphereMesh> sphere_mesh; Ref<BoxMesh> box_mesh; diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp index 90d50d30d9..d8977ea6fc 100644 --- a/editor/plugins/mesh_editor_plugin.cpp +++ b/editor/plugins/mesh_editor_plugin.cpp @@ -30,6 +30,7 @@ #include "mesh_editor_plugin.h" +#include "core/config/project_settings.h" #include "editor/editor_scale.h" void MeshEditor::gui_input(const Ref<InputEvent> &p_event) { @@ -119,6 +120,11 @@ MeshEditor::MeshEditor() { camera->set_perspective(45, 0.1, 10); viewport->add_child(camera); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + camera_attributes.instantiate(); + camera->set_attributes(camera_attributes); + } + light1 = memnew(DirectionalLight3D); light1->set_transform(Transform3D().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0))); viewport->add_child(light1); diff --git a/editor/plugins/mesh_editor_plugin.h b/editor/plugins/mesh_editor_plugin.h index fb61f03485..ab7b5db7c4 100644 --- a/editor/plugins/mesh_editor_plugin.h +++ b/editor/plugins/mesh_editor_plugin.h @@ -36,6 +36,7 @@ #include "scene/3d/light_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/gui/subviewport_container.h" +#include "scene/resources/camera_attributes.h" #include "scene/resources/material.h" class MeshEditor : public SubViewportContainer { @@ -50,6 +51,7 @@ class MeshEditor : public SubViewportContainer { DirectionalLight3D *light1 = nullptr; DirectionalLight3D *light2 = nullptr; Camera3D *camera = nullptr; + Ref<CameraAttributesPractical> camera_attributes; Ref<Mesh> mesh; diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 878f8c9a95..0c27ed46c5 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -1365,7 +1365,8 @@ void Light3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_i void Light3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Light3D *light = Object::cast_to<Light3D>(p_gizmo->get_spatial_node()); - Color color = light->get_color(); + Color color = light->get_color().srgb_to_linear() * light->get_correlated_color().srgb_to_linear(); + color = color.linear_to_srgb(); // Make the gizmo color as bright as possible for better visibility color.set_hsv(color.get_h(), color.get_s(), 1); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index b771faeb33..1a704a5777 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -2375,12 +2375,12 @@ void Node3DEditorPlugin::edited_scene_changed() { void Node3DEditorViewport::_project_settings_changed() { //update shadow atlas if changed - int shadowmap_size = ProjectSettings::get_singleton()->get("rendering/shadows/positional_shadow/atlas_size"); - bool shadowmap_16_bits = ProjectSettings::get_singleton()->get("rendering/shadows/positional_shadow/atlas_16_bits"); - int atlas_q0 = ProjectSettings::get_singleton()->get("rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv"); - int atlas_q1 = ProjectSettings::get_singleton()->get("rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv"); - int atlas_q2 = ProjectSettings::get_singleton()->get("rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv"); - int atlas_q3 = ProjectSettings::get_singleton()->get("rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv"); + int shadowmap_size = ProjectSettings::get_singleton()->get("rendering/lights_and_shadows/positional_shadow/atlas_size"); + bool shadowmap_16_bits = ProjectSettings::get_singleton()->get("rendering/lights_and_shadows/positional_shadow/atlas_16_bits"); + int atlas_q0 = ProjectSettings::get_singleton()->get("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv"); + int atlas_q1 = ProjectSettings::get_singleton()->get("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv"); + int atlas_q2 = ProjectSettings::get_singleton()->get("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv"); + int atlas_q3 = ProjectSettings::get_singleton()->get("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv"); viewport->set_positional_shadow_atlas_size(shadowmap_size); viewport->set_positional_shadow_atlas_16_bits(shadowmap_16_bits); @@ -7122,6 +7122,9 @@ void Node3DEditor::_add_environment_to_scene(bool p_already_added_sun) { WorldEnvironment *new_env = memnew(WorldEnvironment); new_env->set_environment(preview_environment->get_environment()->duplicate(true)); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + new_env->set_camera_attributes(preview_environment->get_camera_attributes()->duplicate(true)); + } undo_redo->create_action(TTR("Add Preview Environment to Scene")); undo_redo->add_do_method(base, "add_child", new_env, true); @@ -7588,7 +7591,7 @@ void Node3DEditor::_preview_settings_changed() { } { //preview env - sky_material->set_sky_energy(environ_energy->get_value()); + sky_material->set_sky_energy_multiplier(environ_energy->get_value()); Color hz_color = environ_sky_color->get_pick_color().lerp(environ_ground_color->get_pick_color(), 0.5).lerp(Color(1, 1, 1), 0.5); sky_material->set_sky_top_color(environ_sky_color->get_pick_color()); sky_material->set_sky_horizon_color(hz_color); @@ -8308,6 +8311,10 @@ void fragment() { preview_environment = memnew(WorldEnvironment); environment.instantiate(); preview_environment->set_environment(environment); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + camera_attributes.instantiate(); + preview_environment->set_camera_attributes(camera_attributes); + } Ref<Sky> sky; sky.instantiate(); sky_material.instantiate(); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index d38c5948cc..580cb878ce 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -765,6 +765,7 @@ private: DirectionalLight3D *preview_sun = nullptr; WorldEnvironment *preview_environment = nullptr; Ref<Environment> environment; + Ref<CameraAttributesPhysical> camera_attributes; Ref<ProceduralSkyMaterial> sky_material; bool sun_environ_updating = false; diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index aa1d630372..ad114e022f 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -940,50 +940,6 @@ void ScriptEditor::_resave_scripts(const String &p_str) { disk_changed->hide(); } -void ScriptEditor::_reload_scripts() { - for (int i = 0; i < tab_container->get_tab_count(); i++) { - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i)); - if (!se) { - continue; - } - - Ref<Resource> edited_res = se->get_edited_resource(); - - if (edited_res->is_built_in()) { - continue; //internal script, who cares - } - - uint64_t last_date = edited_res->get_last_modified_time(); - uint64_t date = FileAccess::get_modified_time(edited_res->get_path()); - - if (last_date == date) { - continue; - } - - Ref<Script> script = edited_res; - if (script != nullptr) { - Ref<Script> rel_script = ResourceLoader::load(script->get_path(), script->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE); - ERR_CONTINUE(!rel_script.is_valid()); - script->set_source_code(rel_script->get_source_code()); - script->set_last_modified_time(rel_script->get_last_modified_time()); - script->reload(true); - } - - Ref<TextFile> text_file = edited_res; - if (text_file != nullptr) { - Error err; - Ref<TextFile> rel_text_file = _load_text_file(text_file->get_path(), &err); - ERR_CONTINUE(!rel_text_file.is_valid()); - text_file->set_text(rel_text_file->get_text()); - text_file->set_last_modified_time(rel_text_file->get_last_modified_time()); - } - se->reload_text(); - } - - disk_changed->hide(); - _update_script_names(); -} - void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) { for (int i = 0; i < tab_container->get_tab_count(); i++) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i)); @@ -1077,7 +1033,7 @@ bool ScriptEditor::_test_script_times_on_disk(Ref<Resource> p_for_script) { if (need_reload) { if (!need_ask) { - script_editor->_reload_scripts(); + script_editor->reload_scripts(); need_reload = false; } else { disk_changed->call_deferred(SNAME("popup_centered_ratio"), 0.5); @@ -2588,6 +2544,50 @@ void ScriptEditor::apply_scripts() const { } } +void ScriptEditor::reload_scripts() { + for (int i = 0; i < tab_container->get_tab_count(); i++) { + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i)); + if (!se) { + continue; + } + + Ref<Resource> edited_res = se->get_edited_resource(); + + if (edited_res->is_built_in()) { + continue; //internal script, who cares + } + + uint64_t last_date = edited_res->get_last_modified_time(); + uint64_t date = FileAccess::get_modified_time(edited_res->get_path()); + + if (last_date == date) { + continue; + } + + Ref<Script> script = edited_res; + if (script != nullptr) { + Ref<Script> rel_script = ResourceLoader::load(script->get_path(), script->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE); + ERR_CONTINUE(!rel_script.is_valid()); + script->set_source_code(rel_script->get_source_code()); + script->set_last_modified_time(rel_script->get_last_modified_time()); + script->reload(true); + } + + Ref<TextFile> text_file = edited_res; + if (text_file != nullptr) { + Error err; + Ref<TextFile> rel_text_file = _load_text_file(text_file->get_path(), &err); + ERR_CONTINUE(!rel_text_file.is_valid()); + text_file->set_text(rel_text_file->get_text()); + text_file->set_last_modified_time(rel_text_file->get_last_modified_time()); + } + se->reload_text(); + } + + disk_changed->hide(); + _update_script_names(); +} + void ScriptEditor::open_script_create_dialog(const String &p_base_name, const String &p_base_path) { _menu_option(FILE_NEW); script_create_dialog->config(p_base_name, p_base_path); @@ -3918,7 +3918,7 @@ ScriptEditor::ScriptEditor() { vbc->add_child(disk_changed_list); disk_changed_list->set_v_size_flags(SIZE_EXPAND_FILL); - disk_changed->connect("confirmed", callable_mp(this, &ScriptEditor::_reload_scripts)); + disk_changed->connect("confirmed", callable_mp(this, &ScriptEditor::reload_scripts)); disk_changed->set_ok_button_text(TTR("Reload")); disk_changed->add_button(TTR("Resave"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index d1898efb69..a8e6cc6868 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -327,7 +327,6 @@ class ScriptEditor : public PanelContainer { String _get_debug_tooltip(const String &p_text, Node *_se); void _resave_scripts(const String &p_str); - void _reload_scripts(); bool _test_script_times_on_disk(Ref<Resource> p_for_script = Ref<Resource>()); @@ -478,6 +477,7 @@ public: bool toggle_scripts_panel(); bool is_scripts_panel_toggled(); void apply_scripts() const; + void reload_scripts(); void open_script_create_dialog(const String &p_base_name, const String &p_base_path); void open_text_file_create_dialog(const String &p_base_path, const String &p_base_name = ""); Ref<Resource> open_file(const String &p_file); diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index 2b55ba64c3..fba760d57f 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -30,25 +30,59 @@ #include "version_control_editor_plugin.h" -#include "core/object/script_language.h" +#include "core/config/project_settings.h" #include "core/os/keyboard.h" +#include "core/os/time.h" #include "editor/editor_file_system.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/filesystem_dock.h" #include "scene/gui/separator.h" +#define CHECK_PLUGIN_INITIALIZED() \ + ERR_FAIL_COND_MSG(!EditorVCSInterface::get_singleton(), "No VCS plugin is initialized. Select a Version Control Plugin from Project menu."); + VersionControlEditorPlugin *VersionControlEditorPlugin::singleton = nullptr; void VersionControlEditorPlugin::_bind_methods() { - ClassDB::bind_method(D_METHOD("popup_vcs_set_up_dialog"), &VersionControlEditorPlugin::popup_vcs_set_up_dialog); + ClassDB::bind_method(D_METHOD("_initialize_vcs"), &VersionControlEditorPlugin::_initialize_vcs); + ClassDB::bind_method(D_METHOD("_set_credentials"), &VersionControlEditorPlugin::_set_credentials); + ClassDB::bind_method(D_METHOD("_update_set_up_warning"), &VersionControlEditorPlugin::_update_set_up_warning); + ClassDB::bind_method(D_METHOD("_commit"), &VersionControlEditorPlugin::_commit); + ClassDB::bind_method(D_METHOD("_refresh_stage_area"), &VersionControlEditorPlugin::_refresh_stage_area); + ClassDB::bind_method(D_METHOD("_move_all"), &VersionControlEditorPlugin::_move_all); + ClassDB::bind_method(D_METHOD("_load_diff"), &VersionControlEditorPlugin::_load_diff); + ClassDB::bind_method(D_METHOD("_display_diff"), &VersionControlEditorPlugin::_display_diff); + ClassDB::bind_method(D_METHOD("_item_activated"), &VersionControlEditorPlugin::_item_activated); + ClassDB::bind_method(D_METHOD("_update_branch_create_button"), &VersionControlEditorPlugin::_update_branch_create_button); + ClassDB::bind_method(D_METHOD("_update_remote_create_button"), &VersionControlEditorPlugin::_update_remote_create_button); + ClassDB::bind_method(D_METHOD("_update_commit_button"), &VersionControlEditorPlugin::_update_commit_button); + ClassDB::bind_method(D_METHOD("_refresh_branch_list"), &VersionControlEditorPlugin::_refresh_branch_list); + ClassDB::bind_method(D_METHOD("_set_commit_list_size"), &VersionControlEditorPlugin::_set_commit_list_size); + ClassDB::bind_method(D_METHOD("_refresh_commit_list"), &VersionControlEditorPlugin::_refresh_commit_list); + ClassDB::bind_method(D_METHOD("_refresh_remote_list"), &VersionControlEditorPlugin::_refresh_remote_list); + ClassDB::bind_method(D_METHOD("_ssh_public_key_selected"), &VersionControlEditorPlugin::_ssh_public_key_selected); + ClassDB::bind_method(D_METHOD("_ssh_private_key_selected"), &VersionControlEditorPlugin::_ssh_private_key_selected); + ClassDB::bind_method(D_METHOD("_commit_message_gui_input"), &VersionControlEditorPlugin::_commit_message_gui_input); + ClassDB::bind_method(D_METHOD("_cell_button_pressed"), &VersionControlEditorPlugin::_cell_button_pressed); + ClassDB::bind_method(D_METHOD("_discard_all"), &VersionControlEditorPlugin::_discard_all); + ClassDB::bind_method(D_METHOD("_create_branch"), &VersionControlEditorPlugin::_create_branch); + ClassDB::bind_method(D_METHOD("_create_remote"), &VersionControlEditorPlugin::_create_remote); + ClassDB::bind_method(D_METHOD("_remove_branch"), &VersionControlEditorPlugin::_remove_branch); + ClassDB::bind_method(D_METHOD("_remove_remote"), &VersionControlEditorPlugin::_remove_remote); + ClassDB::bind_method(D_METHOD("_branch_item_selected"), &VersionControlEditorPlugin::_branch_item_selected); + ClassDB::bind_method(D_METHOD("_remote_selected"), &VersionControlEditorPlugin::_remote_selected); + ClassDB::bind_method(D_METHOD("_fetch"), &VersionControlEditorPlugin::_fetch); + ClassDB::bind_method(D_METHOD("_pull"), &VersionControlEditorPlugin::_pull); + ClassDB::bind_method(D_METHOD("_push"), &VersionControlEditorPlugin::_push); + ClassDB::bind_method(D_METHOD("_extra_option_selected"), &VersionControlEditorPlugin::_extra_option_selected); + ClassDB::bind_method(D_METHOD("_update_extra_options"), &VersionControlEditorPlugin::_update_extra_options); + ClassDB::bind_method(D_METHOD("_popup_branch_remove_confirm"), &VersionControlEditorPlugin::_popup_branch_remove_confirm); + ClassDB::bind_method(D_METHOD("_popup_remote_remove_confirm"), &VersionControlEditorPlugin::_popup_remote_remove_confirm); + ClassDB::bind_method(D_METHOD("_popup_file_dialog"), &VersionControlEditorPlugin::_popup_file_dialog); - // Used to track the status of files in the staging area - BIND_ENUM_CONSTANT(CHANGE_TYPE_NEW); - BIND_ENUM_CONSTANT(CHANGE_TYPE_MODIFIED); - BIND_ENUM_CONSTANT(CHANGE_TYPE_RENAMED); - BIND_ENUM_CONSTANT(CHANGE_TYPE_DELETED); - BIND_ENUM_CONSTANT(CHANGE_TYPE_TYPECHANGE); + ClassDB::bind_method(D_METHOD("popup_vcs_set_up_dialog"), &VersionControlEditorPlugin::popup_vcs_set_up_dialog); } void VersionControlEditorPlugin::_create_vcs_metadata_files() { @@ -56,21 +90,25 @@ void VersionControlEditorPlugin::_create_vcs_metadata_files() { EditorVCSInterface::create_vcs_metadata_files(EditorVCSInterface::VCSMetadata(metadata_selection->get_selected()), dir); } -void VersionControlEditorPlugin::_selected_a_vcs(int p_id) { - List<StringName> available_addons = get_available_vcs_names(); - const StringName selected_vcs = set_up_choice->get_item_text(p_id); -} +void VersionControlEditorPlugin::_notification(int p_what) { + if (p_what == NOTIFICATION_READY) { + String installed_plugin = GLOBAL_DEF("editor/version_control/plugin_name", ""); + String project_path = GLOBAL_DEF("editor/version_control/project_path", OS::get_singleton()->get_resource_dir()); + project_path_input->set_text(project_path); + bool has_autoload_enable = GLOBAL_DEF("editor/version_control/autoload_on_startup", false); -void VersionControlEditorPlugin::_populate_available_vcs_names() { - static bool called = false; - - if (!called) { - List<StringName> available_addons = get_available_vcs_names(); - for (int i = 0; i < available_addons.size(); i++) { - set_up_choice->add_item(available_addons[i]); + if (installed_plugin != "" && has_autoload_enable) { + if (_load_plugin(installed_plugin, project_path)) { + _set_credentials(); + } } + } +} - called = true; +void VersionControlEditorPlugin::_populate_available_vcs_names() { + set_up_choice->clear(); + for (int i = 0; i < available_plugins.size(); i++) { + set_up_choice->add_item(available_plugins[i]); } } @@ -83,9 +121,8 @@ void VersionControlEditorPlugin::popup_vcs_metadata_dialog() { } void VersionControlEditorPlugin::popup_vcs_set_up_dialog(const Control *p_gui_base) { - fetch_available_vcs_addon_names(); - List<StringName> available_addons = get_available_vcs_names(); - if (available_addons.size() >= 1) { + fetch_available_vcs_plugin_names(); + if (!available_plugins.is_empty()) { Size2 popup_size = Size2(400, 100); Size2 window_size = p_gui_base->get_viewport_rect().size; popup_size.x = MIN(window_size.x * 0.5, popup_size.x); @@ -95,213 +132,782 @@ void VersionControlEditorPlugin::popup_vcs_set_up_dialog(const Control *p_gui_ba set_up_dialog->popup_centered_clamped(popup_size * EDSCALE); } else { - EditorNode::get_singleton()->show_warning(TTR("No VCS addons are available."), TTR("Error")); + // TODO: Give info to user on how to fix this error. + EditorNode::get_singleton()->show_warning(TTR("No VCS plugins are available in the project. Install a VCS plugin to use VCS integration features."), TTR("Error")); } } void VersionControlEditorPlugin::_initialize_vcs() { - register_editor(); - - ERR_FAIL_COND_MSG(EditorVCSInterface::get_singleton(), EditorVCSInterface::get_singleton()->get_vcs_name() + " is already active"); + ERR_FAIL_COND_MSG(EditorVCSInterface::get_singleton(), EditorVCSInterface::get_singleton()->get_vcs_name() + " is already active."); const int id = set_up_choice->get_selected_id(); - String selected_addon = set_up_choice->get_item_text(id); + String selected_plugin = set_up_choice->get_item_text(id); - String path = ScriptServer::get_global_class_path(selected_addon); - Ref<Script> script = ResourceLoader::load(path); + if (_load_plugin(selected_plugin, project_path_input->get_text())) { + ProjectSettings::get_singleton()->set("editor/version_control/autoload_on_startup", true); + ProjectSettings::get_singleton()->set("editor/version_control/plugin_name", selected_plugin); + ProjectSettings::get_singleton()->set("editor/version_control/project_path", project_path_input->get_text()); + ProjectSettings::get_singleton()->save(); + } +} - ERR_FAIL_COND_MSG(!script.is_valid(), "VCS Addon path is invalid"); +void VersionControlEditorPlugin::_set_vcs_ui_state(bool p_enabled) { + select_project_path_button->set_disabled(p_enabled); + set_up_dialog->get_ok_button()->set_disabled(!p_enabled); + project_path_input->set_editable(!p_enabled); + set_up_choice->set_disabled(p_enabled); + toggle_vcs_choice->set_pressed_no_signal(p_enabled); +} - EditorVCSInterface *vcs_interface = memnew(EditorVCSInterface); - ScriptInstance *addon_script_instance = script->instance_create(vcs_interface); +void VersionControlEditorPlugin::_set_credentials() { + CHECK_PLUGIN_INITIALIZED(); + + String username = set_up_username->get_text(); + String password = set_up_password->get_text(); + String ssh_public_key = set_up_ssh_public_key_path->get_text(); + String ssh_private_key = set_up_ssh_private_key_path->get_text(); + String ssh_passphrase = set_up_ssh_passphrase->get_text(); + + EditorVCSInterface::get_singleton()->set_credentials( + username, + password, + ssh_public_key, + ssh_private_key, + ssh_passphrase); + + EditorSettings::get_singleton()->set_setting("version_control/username", username); + EditorSettings::get_singleton()->set_setting("version_control/ssh_public_key_path", ssh_public_key); + EditorSettings::get_singleton()->set_setting("version_control/ssh_private_key_path", ssh_private_key); +} - ERR_FAIL_COND_MSG(!addon_script_instance, "Failed to create addon script instance."); +bool VersionControlEditorPlugin::_load_plugin(String p_name, String p_project_path) { + Object *extension_instance = ClassDB::instantiate(p_name); + ERR_FAIL_NULL_V_MSG(extension_instance, false, "Received a nullptr VCS extension instance during construction."); - // The addon is attached as a script to the VCS interface as a proxy end-point - vcs_interface->set_script_and_instance(script, addon_script_instance); + EditorVCSInterface *vcs_plugin = Object::cast_to<EditorVCSInterface>(extension_instance); + ERR_FAIL_NULL_V_MSG(vcs_plugin, false, vformat("Could not cast VCS extension instance to %s.", EditorVCSInterface::get_class_static())); - EditorVCSInterface::set_singleton(vcs_interface); - EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &VersionControlEditorPlugin::_refresh_stage_area)); + String res_dir = project_path_input->get_text(); - String res_dir = OS::get_singleton()->get_resource_dir(); + ERR_FAIL_COND_V_MSG(!vcs_plugin->initialize(res_dir), false, "Could not initialize " + p_name); - ERR_FAIL_COND_MSG(!EditorVCSInterface::get_singleton()->initialize(res_dir), "VCS was not initialized"); + EditorVCSInterface::set_singleton(vcs_plugin); + + register_editor(); + EditorFileSystem::get_singleton()->connect(SNAME("filesystem_changed"), callable_mp(this, &VersionControlEditorPlugin::_refresh_stage_area)); _refresh_stage_area(); + _refresh_commit_list(); + _refresh_branch_list(); + _refresh_remote_list(); + + return true; } -void VersionControlEditorPlugin::_send_commit_msg() { - if (EditorVCSInterface::get_singleton()) { - if (staged_files_count == 0) { - commit_status->set_text(TTR("No files added to stage")); - return; +void VersionControlEditorPlugin::_update_set_up_warning(String p_new_text) { + bool empty_settings = set_up_username->get_text().strip_edges().is_empty() && + set_up_password->get_text().is_empty() && + set_up_ssh_public_key_path->get_text().strip_edges().is_empty() && + set_up_ssh_private_key_path->get_text().strip_edges().is_empty() && + set_up_ssh_passphrase->get_text().is_empty(); + + if (empty_settings) { + set_up_warning_text->add_theme_color_override(SNAME("font_color"), EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + set_up_warning_text->set_text(TTR("Remote settings are empty. VCS features that use the network may not work.")); + } else { + set_up_warning_text->set_text(""); + } +} + +void VersionControlEditorPlugin::_refresh_branch_list() { + CHECK_PLUGIN_INITIALIZED(); + + List<String> branch_list = EditorVCSInterface::get_singleton()->get_branch_list(); + branch_select->clear(); + + branch_select->set_disabled(branch_list.is_empty()); + + String current_branch = EditorVCSInterface::get_singleton()->get_current_branch_name(); + + for (int i = 0; i < branch_list.size(); i++) { + branch_select->add_icon_item(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("VcsBranches"), SNAME("EditorIcons")), branch_list[i], i); + + if (branch_list[i] == current_branch) { + branch_select->select(i); } + } +} + +String VersionControlEditorPlugin::_get_date_string_from(int64_t p_unix_timestamp, int64_t p_offset_minutes) const { + return vformat( + "%s %s", + Time::get_singleton()->get_datetime_string_from_unix_time(p_unix_timestamp + p_offset_minutes * 60, true), + Time::get_singleton()->get_offset_string_from_offset_minutes(p_offset_minutes)); +} - EditorVCSInterface::get_singleton()->commit(commit_message->get_text()); +void VersionControlEditorPlugin::_set_commit_list_size(int p_index) { + _refresh_commit_list(); +} - commit_message->set_text(""); - version_control_dock_button->set_pressed(false); - } else { - WARN_PRINT("No VCS addon is initialized. Select a Version Control Addon from Project menu"); +void VersionControlEditorPlugin::_refresh_commit_list() { + CHECK_PLUGIN_INITIALIZED(); + + commit_list->get_root()->clear_children(); + + List<EditorVCSInterface::Commit> commit_info_list = EditorVCSInterface::get_singleton()->get_previous_commits(commit_list_size_button->get_selected_metadata()); + + for (List<EditorVCSInterface::Commit>::Element *e = commit_info_list.front(); e; e = e->next()) { + EditorVCSInterface::Commit commit = e->get(); + TreeItem *item = commit_list->create_item(); + + // Only display the first line of a commit message + int line_ending = commit.msg.find_char('\n'); + String commit_display_msg = commit.msg.substr(0, line_ending); + String commit_date_string = _get_date_string_from(commit.unix_timestamp, commit.offset_minutes); + + Dictionary meta_data; + meta_data[SNAME("commit_id")] = commit.id; + meta_data[SNAME("commit_title")] = commit_display_msg; + meta_data[SNAME("commit_subtitle")] = commit.msg.substr(line_ending).strip_edges(); + meta_data[SNAME("commit_unix_timestamp")] = commit.unix_timestamp; + meta_data[SNAME("commit_author")] = commit.author; + meta_data[SNAME("commit_date_string")] = commit_date_string; + + item->set_text(0, commit_display_msg); + item->set_text(1, commit.author.strip_edges()); + item->set_metadata(0, meta_data); } +} + +void VersionControlEditorPlugin::_refresh_remote_list() { + CHECK_PLUGIN_INITIALIZED(); + + List<String> remotes = EditorVCSInterface::get_singleton()->get_remotes(); + + String current_remote = remote_select->get_selected_metadata(); + remote_select->clear(); - _update_commit_status(); + remote_select->set_disabled(remotes.is_empty()); + + for (int i = 0; i < remotes.size(); i++) { + remote_select->add_icon_item(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("ArrowUp"), SNAME("EditorIcons")), remotes[i], i); + remote_select->set_item_metadata(i, remotes[i]); + + if (remotes[i] == current_remote) { + remote_select->select(i); + } + } +} + +void VersionControlEditorPlugin::_commit() { + CHECK_PLUGIN_INITIALIZED(); + + String msg = commit_message->get_text().strip_edges(); + + ERR_FAIL_COND_MSG(msg.is_empty(), "No commit message was provided."); + + EditorVCSInterface::get_singleton()->commit(msg); + + version_control_dock_button->set_pressed(false); + + commit_message->release_focus(); + commit_button->release_focus(); + commit_message->set_text(""); + + _refresh_stage_area(); + _refresh_commit_list(); + _refresh_branch_list(); + _clear_diff(); +} + +void VersionControlEditorPlugin::_branch_item_selected(int p_index) { + CHECK_PLUGIN_INITIALIZED(); + + String branch_name = branch_select->get_item_text(p_index); + EditorVCSInterface::get_singleton()->checkout_branch(branch_name); + + EditorFileSystem::get_singleton()->scan_changes(); + ScriptEditor::get_singleton()->reload_scripts(); + + _refresh_branch_list(); + _refresh_commit_list(); _refresh_stage_area(); - _clear_file_diff(); + _clear_diff(); + + _update_opened_tabs(); +} + +void VersionControlEditorPlugin::_remote_selected(int p_index) { + _refresh_remote_list(); +} + +void VersionControlEditorPlugin::_ssh_public_key_selected(String p_path) { + set_up_ssh_public_key_path->set_text(p_path); +} + +void VersionControlEditorPlugin::_ssh_private_key_selected(String p_path) { + set_up_ssh_private_key_path->set_text(p_path); +} + +void VersionControlEditorPlugin::_popup_file_dialog(Variant p_file_dialog_variant) { + FileDialog *file_dialog = Object::cast_to<FileDialog>(p_file_dialog_variant); + ERR_FAIL_NULL(file_dialog); + + file_dialog->popup_centered_ratio(); +} + +void VersionControlEditorPlugin::_create_branch() { + CHECK_PLUGIN_INITIALIZED(); + + String new_branch_name = branch_create_name_input->get_text().strip_edges(); + + EditorVCSInterface::get_singleton()->create_branch(new_branch_name); + EditorVCSInterface::get_singleton()->checkout_branch(new_branch_name); + + branch_create_name_input->clear(); + _refresh_branch_list(); +} + +void VersionControlEditorPlugin::_create_remote() { + CHECK_PLUGIN_INITIALIZED(); + + String new_remote_name = remote_create_name_input->get_text().strip_edges(); + String new_remote_url = remote_create_url_input->get_text().strip_edges(); + + EditorVCSInterface::get_singleton()->create_remote(new_remote_name, new_remote_url); + + remote_create_name_input->clear(); + remote_create_url_input->clear(); + _refresh_remote_list(); +} + +void VersionControlEditorPlugin::_update_branch_create_button(String p_new_text) { + branch_create_ok->set_disabled(p_new_text.strip_edges().is_empty()); +} + +void VersionControlEditorPlugin::_update_remote_create_button(String p_new_text) { + remote_create_ok->set_disabled(p_new_text.strip_edges().is_empty()); +} + +int VersionControlEditorPlugin::_get_item_count(Tree *p_tree) { + if (!p_tree->get_root()) { + return 0; + } + return p_tree->get_root()->get_children().size(); } void VersionControlEditorPlugin::_refresh_stage_area() { - if (EditorVCSInterface::get_singleton()) { - staged_files_count = 0; - clear_stage_area(); - - Dictionary modified_file_paths = EditorVCSInterface::get_singleton()->get_modified_files_data(); - String file_path; - for (int i = 0; i < modified_file_paths.size(); i++) { - file_path = modified_file_paths.get_key_at_index(i); - TreeItem *found = stage_files->search_item_text(file_path, nullptr, true); - if (!found) { - ChangeType change_index = (ChangeType)(int)modified_file_paths.get_value_at_index(i); - String change_text = file_path + " (" + change_type_to_strings[change_index] + ")"; - Color &change_color = change_type_to_color[change_index]; - TreeItem *new_item = stage_files->create_item(stage_files->get_root()); - new_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - new_item->set_text(0, change_text); - new_item->set_metadata(0, file_path); - new_item->set_custom_color(0, change_color); - new_item->set_checked(0, true); - new_item->set_editable(0, true); - } else { - if (found->get_metadata(0) == diff_file_name->get_text()) { - _refresh_file_diff(); - } - } - commit_status->set_text(TTR("New changes detected")); + CHECK_PLUGIN_INITIALIZED(); + + staged_files->get_root()->clear_children(); + unstaged_files->get_root()->clear_children(); + + List<EditorVCSInterface::StatusFile> status_files = EditorVCSInterface::get_singleton()->get_modified_files_data(); + for (List<EditorVCSInterface::StatusFile>::Element *E = status_files.front(); E; E = E->next()) { + EditorVCSInterface::StatusFile sf = E->get(); + if (sf.area == EditorVCSInterface::TREE_AREA_STAGED) { + _add_new_item(staged_files, sf.file_path, sf.change_type); + } else if (sf.area == EditorVCSInterface::TREE_AREA_UNSTAGED) { + _add_new_item(unstaged_files, sf.file_path, sf.change_type); } + } + + staged_files->queue_redraw(); + unstaged_files->queue_redraw(); + + int total_changes = status_files.size(); + String commit_tab_title = TTR("Commit") + (total_changes > 0 ? " (" + itos(total_changes) + ")" : ""); + version_commit_dock->set_name(commit_tab_title); +} + +void VersionControlEditorPlugin::_discard_file(String p_file_path, EditorVCSInterface::ChangeType p_change) { + CHECK_PLUGIN_INITIALIZED(); + + if (p_change == EditorVCSInterface::CHANGE_TYPE_NEW) { + Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_RESOURCES); + dir->remove(p_file_path); } else { - WARN_PRINT("No VCS addon is initialized. Select a Version Control Addon from Project menu."); + CHECK_PLUGIN_INITIALIZED(); + EditorVCSInterface::get_singleton()->discard_file(p_file_path); } + // FIXIT: The project.godot file shows weird behaviour + EditorFileSystem::get_singleton()->update_file(p_file_path); } -void VersionControlEditorPlugin::_stage_selected() { - if (!EditorVCSInterface::get_singleton()) { - WARN_PRINT("No VCS addon is initialized. Select a Version Control Addon from Project menu"); - return; +void VersionControlEditorPlugin::_discard_all() { + TreeItem *file_entry = unstaged_files->get_root()->get_first_child(); + while (file_entry) { + String file_path = file_entry->get_meta(SNAME("file_path")); + EditorVCSInterface::ChangeType change = (EditorVCSInterface::ChangeType)(int)file_entry->get_meta(SNAME("change_type")); + _discard_file(file_path, change); + + file_entry = file_entry->get_next(); } + _refresh_stage_area(); +} - staged_files_count = 0; - TreeItem *root = stage_files->get_root(); - if (root) { - TreeItem *file_entry = root->get_first_child(); - while (file_entry) { - if (file_entry->is_checked(0)) { - EditorVCSInterface::get_singleton()->stage_file(file_entry->get_metadata(0)); - file_entry->set_icon_modulate(0, EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("success_color"), SNAME("Editor"))); - staged_files_count++; - } else { - EditorVCSInterface::get_singleton()->unstage_file(file_entry->get_metadata(0)); - file_entry->set_icon_modulate(0, EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor"))); - } +void VersionControlEditorPlugin::_add_new_item(Tree *p_tree, String p_file_path, EditorVCSInterface::ChangeType p_change) { + String change_text = p_file_path + " (" + change_type_to_strings[p_change] + ")"; - file_entry = file_entry->get_next(); - } + TreeItem *new_item = p_tree->create_item(); + new_item->set_text(0, change_text); + new_item->set_icon(0, change_type_to_icon[p_change]); + new_item->set_meta(SNAME("file_path"), p_file_path); + new_item->set_meta(SNAME("change_type"), p_change); + new_item->set_custom_color(0, change_type_to_color[p_change]); + + new_item->add_button(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("File"), SNAME("EditorIcons")), BUTTON_TYPE_OPEN, false, TTR("Open in editor")); + if (p_tree == unstaged_files) { + new_item->add_button(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Close"), SNAME("EditorIcons")), BUTTON_TYPE_DISCARD, false, TTR("Discard changes")); } +} + +void VersionControlEditorPlugin::_fetch() { + CHECK_PLUGIN_INITIALIZED(); - _update_stage_status(); + EditorVCSInterface::get_singleton()->fetch(remote_select->get_selected_metadata()); + _refresh_branch_list(); } -void VersionControlEditorPlugin::_stage_all() { - if (!EditorVCSInterface::get_singleton()) { - WARN_PRINT("No VCS addon is initialized. Select a Version Control Addon from Project menu"); - return; - } +void VersionControlEditorPlugin::_pull() { + CHECK_PLUGIN_INITIALIZED(); + + EditorVCSInterface::get_singleton()->pull(remote_select->get_selected_metadata()); + _refresh_stage_area(); + _refresh_branch_list(); + _refresh_commit_list(); + _clear_diff(); + _update_opened_tabs(); +} + +void VersionControlEditorPlugin::_push() { + CHECK_PLUGIN_INITIALIZED(); + + EditorVCSInterface::get_singleton()->push(remote_select->get_selected_metadata(), false); +} + +void VersionControlEditorPlugin::_force_push() { + CHECK_PLUGIN_INITIALIZED(); - staged_files_count = 0; - TreeItem *root = stage_files->get_root(); - if (root) { - TreeItem *file_entry = root->get_first_child(); - while (file_entry) { - EditorVCSInterface::get_singleton()->stage_file(file_entry->get_metadata(0)); - file_entry->set_icon_modulate(0, EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("success_color"), SNAME("Editor"))); - file_entry->set_checked(0, true); - staged_files_count++; + EditorVCSInterface::get_singleton()->push(remote_select->get_selected_metadata(), true); +} - file_entry = file_entry->get_next(); +void VersionControlEditorPlugin::_update_opened_tabs() { + Vector<EditorData::EditedScene> open_scenes = EditorNode::get_singleton()->get_editor_data().get_edited_scenes(); + for (int i = 0; i < open_scenes.size(); i++) { + if (open_scenes[i].root == NULL) { + continue; } + EditorNode::get_singleton()->reload_scene(open_scenes[i].path); } +} - _update_stage_status(); +void VersionControlEditorPlugin::_move_all(Object *p_tree) { + Tree *tree = Object::cast_to<Tree>(p_tree); + + TreeItem *file_entry = tree->get_root()->get_first_child(); + while (file_entry) { + _move_item(tree, file_entry); + + file_entry = file_entry->get_next(); + } + _refresh_stage_area(); } -void VersionControlEditorPlugin::_view_file_diff() { +void VersionControlEditorPlugin::_load_diff(Object *p_tree) { + CHECK_PLUGIN_INITIALIZED(); + version_control_dock_button->set_pressed(true); - String file_path = stage_files->get_selected()->get_metadata(0); + Tree *tree = Object::cast_to<Tree>(p_tree); + if (tree == staged_files) { + show_commit_diff_header = false; + String file_path = tree->get_selected()->get_meta(SNAME("file_path")); + diff_title->set_text(TTR("Staged Changes")); + diff_content = EditorVCSInterface::get_singleton()->get_diff(file_path, EditorVCSInterface::TREE_AREA_STAGED); + } else if (tree == unstaged_files) { + show_commit_diff_header = false; + String file_path = tree->get_selected()->get_meta(SNAME("file_path")); + diff_title->set_text(TTR("Unstaged Changes")); + diff_content = EditorVCSInterface::get_singleton()->get_diff(file_path, EditorVCSInterface::TREE_AREA_UNSTAGED); + } else if (tree == commit_list) { + show_commit_diff_header = true; + Dictionary meta_data = tree->get_selected()->get_metadata(0); + String commit_id = meta_data[SNAME("commit_id")]; + String commit_title = meta_data[SNAME("commit_title")]; + diff_title->set_text(commit_title); + diff_content = EditorVCSInterface::get_singleton()->get_diff(commit_id, EditorVCSInterface::TREE_AREA_COMMIT); + } + _display_diff(0); +} + +void VersionControlEditorPlugin::_clear_diff() { + diff->clear(); + diff_content.clear(); + diff_title->set_text(""); +} + +void VersionControlEditorPlugin::_item_activated(Object *p_tree) { + Tree *tree = Object::cast_to<Tree>(p_tree); - _display_file_diff(file_path); + _move_item(tree, tree->get_selected()); + _refresh_stage_area(); +} + +void VersionControlEditorPlugin::_move_item(Tree *p_tree, TreeItem *p_item) { + CHECK_PLUGIN_INITIALIZED(); + + if (p_tree == staged_files) { + EditorVCSInterface::get_singleton()->unstage_file(p_item->get_meta(SNAME("file_path"))); + } else { + EditorVCSInterface::get_singleton()->stage_file(p_item->get_meta(SNAME("file_path"))); + } } -void VersionControlEditorPlugin::_display_file_diff(String p_file_path) { - TypedArray<Dictionary> diff_content = EditorVCSInterface::get_singleton()->get_file_diff(p_file_path); +void VersionControlEditorPlugin::_cell_button_pressed(Object *p_item, int p_column, int p_id, int p_mouse_button_index) { + TreeItem *item = Object::cast_to<TreeItem>(p_item); + String file_path = item->get_meta(SNAME("file_path")); + EditorVCSInterface::ChangeType change = (EditorVCSInterface::ChangeType)(int)item->get_meta(SNAME("change_type")); - diff_file_name->set_text(p_file_path); + if (p_id == BUTTON_TYPE_OPEN && change != EditorVCSInterface::CHANGE_TYPE_DELETED) { + Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (!dir->file_exists(file_path)) { + return; + } + + file_path = "res://" + file_path; + if (ResourceLoader::get_resource_type(file_path) == "PackedScene") { + EditorNode::get_singleton()->open_request(file_path); + } else if (file_path.ends_with(".gd")) { + EditorNode::get_singleton()->load_resource(file_path); + ScriptEditor::get_singleton()->reload_scripts(); + } else { + FileSystemDock::get_singleton()->navigate_to_path(file_path); + } + + } else if (p_id == BUTTON_TYPE_DISCARD) { + _discard_file(file_path, change); + _refresh_stage_area(); + } +} + +void VersionControlEditorPlugin::_display_diff(int p_idx) { + DiffViewType diff_view = (DiffViewType)diff_view_type_select->get_selected(); diff->clear(); - diff->push_font(EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("source"), SNAME("EditorFonts"))); + + if (show_commit_diff_header) { + Dictionary meta_data = commit_list->get_selected()->get_metadata(0); + String commit_id = meta_data[SNAME("commit_id")]; + String commit_subtitle = meta_data[SNAME("commit_subtitle")]; + String commit_date = meta_data[SNAME("commit_date")]; + String commit_author = meta_data[SNAME("commit_author")]; + String commit_date_string = meta_data[SNAME("commit_date_string")]; + + diff->push_font(EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts"))); + diff->push_color(EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("accent_color"), SNAME("Editor"))); + diff->add_text(TTR("Commit:") + " " + commit_id); + diff->add_newline(); + diff->add_text(TTR("Author:") + " " + commit_author); + diff->add_newline(); + diff->add_text(TTR("Date:") + " " + commit_date_string); + diff->add_newline(); + if (!commit_subtitle.is_empty()) { + diff->add_text(TTR("Subtitle:") + " " + commit_subtitle); + diff->add_newline(); + } + diff->add_newline(); + diff->pop(); + diff->pop(); + } + for (int i = 0; i < diff_content.size(); i++) { - Dictionary line_result = diff_content[i]; + EditorVCSInterface::DiffFile diff_file = diff_content[i]; + + diff->push_font(EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts"))); + diff->push_color(EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("accent_color"), SNAME("Editor"))); + diff->add_text(TTR("File:") + " " + diff_file.new_file); + diff->pop(); + diff->pop(); + + diff->add_newline(); + diff->push_font(EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("status_source"), SNAME("EditorFonts"))); + for (int j = 0; j < diff_file.diff_hunks.size(); j++) { + EditorVCSInterface::DiffHunk hunk = diff_file.diff_hunks[j]; + + String old_start = String::num_int64(hunk.old_start); + String new_start = String::num_int64(hunk.new_start); + String old_lines = String::num_int64(hunk.old_lines); + String new_lines = String::num_int64(hunk.new_lines); + + diff->append_text("[center]@@ " + old_start + "," + old_lines + " " + new_start + "," + new_lines + " @@[/center]"); + diff->add_newline(); + + switch (diff_view) { + case DIFF_VIEW_TYPE_SPLIT: + _display_diff_split_view(hunk.diff_lines); + break; + case DIFF_VIEW_TYPE_UNIFIED: + _display_diff_unified_view(hunk.diff_lines); + break; + } + diff->add_newline(); + diff->add_newline(); + } + diff->pop(); + + diff->add_newline(); + } +} + +void VersionControlEditorPlugin::_display_diff_split_view(List<EditorVCSInterface::DiffLine> &p_diff_content) { + List<EditorVCSInterface::DiffLine> parsed_diff; + + for (int i = 0; i < p_diff_content.size(); i++) { + EditorVCSInterface::DiffLine diff_line = p_diff_content[i]; + String line = diff_line.content.strip_edges(false, true); + + if (diff_line.new_line_no >= 0 && diff_line.old_line_no >= 0) { + diff_line.new_text = line; + diff_line.old_text = line; + parsed_diff.push_back(diff_line); + } else if (diff_line.new_line_no == -1) { + diff_line.new_text = ""; + diff_line.old_text = line; + parsed_diff.push_back(diff_line); + } else if (diff_line.old_line_no == -1) { + int j = parsed_diff.size() - 1; + while (j >= 0 && parsed_diff[j].new_line_no == -1) { + j--; + } + + if (j == parsed_diff.size() - 1) { + // no lines are modified + diff_line.new_text = line; + diff_line.old_text = ""; + parsed_diff.push_back(diff_line); + } else { + // lines are modified + EditorVCSInterface::DiffLine modified_line = parsed_diff[j + 1]; + modified_line.new_text = line; + modified_line.new_line_no = diff_line.new_line_no; + parsed_diff[j + 1] = modified_line; + } + } + } + + diff->push_table(6); + /* + [cell]Old Line No[/cell] + [cell]prefix[/cell] + [cell]Old Code[/cell] + + [cell]New Line No[/cell] + [cell]prefix[/cell] + [cell]New Line[/cell] + */ + + diff->set_table_column_expand(2, true); + diff->set_table_column_expand(5, true); + + for (int i = 0; i < parsed_diff.size(); i++) { + EditorVCSInterface::DiffLine diff_line = parsed_diff[i]; + + bool has_change = diff_line.status != " "; + static const Color red = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor")); + static const Color green = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("success_color"), SNAME("Editor")); + static const Color white = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("font_color"), SNAME("Label")) * Color(1, 1, 1, 0.6); + + if (diff_line.old_line_no >= 0) { + diff->push_cell(); + diff->push_indent(1); + diff->push_color(has_change ? red : white); + diff->add_text(String::num_int64(diff_line.old_line_no)); + diff->pop(); + diff->pop(); + diff->pop(); + + diff->push_cell(); + diff->push_color(has_change ? red : white); + diff->add_text(has_change ? "-|" : " |"); + diff->pop(); + diff->pop(); + + diff->push_cell(); + diff->push_color(has_change ? red : white); + diff->add_text(diff_line.old_text); + diff->pop(); + diff->pop(); - if (line_result["status"] == "+") { - diff->push_color(EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("success_color"), SNAME("Editor"))); - } else if (line_result["status"] == "-") { - diff->push_color(EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor"))); } else { - diff->push_color(EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("font_color"), SNAME("Label"))); + diff->push_cell(); + diff->pop(); + + diff->push_cell(); + diff->pop(); + + diff->push_cell(); + diff->pop(); } - diff->add_text((String)line_result["content"]); + if (diff_line.new_line_no >= 0) { + diff->push_cell(); + diff->push_indent(1); + diff->push_color(has_change ? green : white); + diff->add_text(String::num_int64(diff_line.new_line_no)); + diff->pop(); + diff->pop(); + diff->pop(); + + diff->push_cell(); + diff->push_color(has_change ? green : white); + diff->add_text(has_change ? "+|" : " |"); + diff->pop(); + diff->pop(); + + diff->push_cell(); + diff->push_color(has_change ? green : white); + diff->add_text(diff_line.new_text); + diff->pop(); + diff->pop(); + } else { + diff->push_cell(); + diff->pop(); - diff->pop(); + diff->push_cell(); + diff->pop(); + + diff->push_cell(); + diff->pop(); + } } diff->pop(); } -void VersionControlEditorPlugin::_refresh_file_diff() { - String open_file = diff_file_name->get_text(); - if (!open_file.is_empty()) { - _display_file_diff(diff_file_name->get_text()); +void VersionControlEditorPlugin::_display_diff_unified_view(List<EditorVCSInterface::DiffLine> &p_diff_content) { + diff->push_table(4); + diff->set_table_column_expand(3, true); + + /* + [cell]Old Line No[/cell] + [cell]New Line No[/cell] + [cell]status[/cell] + [cell]code[/cell] + */ + for (int i = 0; i < p_diff_content.size(); i++) { + EditorVCSInterface::DiffLine diff_line = p_diff_content[i]; + String line = diff_line.content.strip_edges(false, true); + + Color color; + if (diff_line.status == "+") { + color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("success_color"), SNAME("Editor")); + } else if (diff_line.status == "-") { + color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor")); + } else { + color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("font_color"), SNAME("Label")); + color *= Color(1, 1, 1, 0.6); + } + + diff->push_cell(); + diff->push_color(color); + diff->push_indent(1); + diff->add_text(diff_line.old_line_no >= 0 ? String::num_int64(diff_line.old_line_no) : ""); + diff->pop(); + diff->pop(); + diff->pop(); + + diff->push_cell(); + diff->push_color(color); + diff->push_indent(1); + diff->add_text(diff_line.new_line_no >= 0 ? String::num_int64(diff_line.new_line_no) : ""); + diff->pop(); + diff->pop(); + diff->pop(); + + diff->push_cell(); + diff->push_color(color); + diff->add_text(diff_line.status != "" ? diff_line.status + "|" : " |"); + diff->pop(); + diff->pop(); + + diff->push_cell(); + diff->push_color(color); + diff->add_text(line); + diff->pop(); + diff->pop(); } + + diff->pop(); } -void VersionControlEditorPlugin::_clear_file_diff() { - diff->clear(); - diff_file_name->set_text(""); - version_control_dock_button->set_pressed(false); +void VersionControlEditorPlugin::_update_commit_button() { + commit_button->set_disabled(commit_message->get_text().strip_edges().is_empty()); } -void VersionControlEditorPlugin::_update_stage_status() { - String status; - if (staged_files_count == 1) { - status = TTR("Stage contains 1 file"); - } else { - status = vformat(TTR("Stage contains %d files"), staged_files_count); +void VersionControlEditorPlugin::_remove_branch() { + CHECK_PLUGIN_INITIALIZED(); + + EditorVCSInterface::get_singleton()->remove_branch(branch_to_remove); + branch_to_remove.clear(); + + _refresh_branch_list(); +} + +void VersionControlEditorPlugin::_remove_remote() { + CHECK_PLUGIN_INITIALIZED(); + + EditorVCSInterface::get_singleton()->remove_remote(remote_to_remove); + remote_to_remove.clear(); + + _refresh_remote_list(); +} + +void VersionControlEditorPlugin::_extra_option_selected(int p_index) { + CHECK_PLUGIN_INITIALIZED(); + + switch ((ExtraOption)p_index) { + case EXTRA_OPTION_FORCE_PUSH: + _force_push(); + break; + case EXTRA_OPTION_CREATE_BRANCH: + branch_create_confirm->popup_centered(); + break; + case EXTRA_OPTION_CREATE_REMOTE: + remote_create_confirm->popup_centered(); + break; } - commit_status->set_text(status); } -void VersionControlEditorPlugin::_update_commit_status() { - String status; - if (staged_files_count == 1) { - status = TTR("Committed 1 file"); - } else { - status = vformat(TTR("Committed %d files"), staged_files_count); +void VersionControlEditorPlugin::_popup_branch_remove_confirm(int p_index) { + branch_to_remove = extra_options_remove_branch_list->get_item_text(p_index); + + branch_remove_confirm->set_text(vformat(TTR("Do you want to remove the %s branch?"), branch_to_remove)); + branch_remove_confirm->popup_centered(); +} + +void VersionControlEditorPlugin::_popup_remote_remove_confirm(int p_index) { + remote_to_remove = extra_options_remove_remote_list->get_item_text(p_index); + + remote_remove_confirm->set_text(vformat(TTR("Do you want to remove the %s remote?"), branch_to_remove)); + remote_remove_confirm->popup_centered(); +} + +void VersionControlEditorPlugin::_update_extra_options() { + extra_options_remove_branch_list->clear(); + for (int i = 0; i < branch_select->get_item_count(); i++) { + extra_options_remove_branch_list->add_icon_item(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("VcsBranches"), SNAME("EditorIcons")), branch_select->get_item_text(branch_select->get_item_id(i))); + } + extra_options_remove_branch_list->update_canvas_items(); + + extra_options_remove_remote_list->clear(); + for (int i = 0; i < remote_select->get_item_count(); i++) { + extra_options_remove_remote_list->add_icon_item(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("ArrowUp"), SNAME("EditorIcons")), remote_select->get_item_text(remote_select->get_item_id(i))); } - commit_status->set_text(status); - staged_files_count = 0; + extra_options_remove_remote_list->update_canvas_items(); } -void VersionControlEditorPlugin::_update_commit_button() { - commit_button->set_disabled(commit_message->get_text().strip_edges().is_empty()); +bool VersionControlEditorPlugin::_is_staging_area_empty() { + return staged_files->get_root()->get_child_count() == 0; } void VersionControlEditorPlugin::_commit_message_gui_input(const Ref<InputEvent> &p_event) { @@ -316,266 +922,660 @@ void VersionControlEditorPlugin::_commit_message_gui_input(const Ref<InputEvent> if (k.is_valid() && k->is_pressed()) { if (ED_IS_SHORTCUT("version_control/commit", p_event)) { - if (staged_files_count == 0) { + if (_is_staging_area_empty()) { // Stage all files only when no files were previously staged. - _stage_all(); + _move_all(unstaged_files); } - _send_commit_msg(); + + _commit(); + commit_message->accept_event(); - return; } } } -void VersionControlEditorPlugin::register_editor() { - if (!EditorVCSInterface::get_singleton()) { - EditorNode::get_singleton()->add_control_to_dock(EditorNode::DOCK_SLOT_RIGHT_UL, version_commit_dock); - TabContainer *dock_vbc = (TabContainer *)version_commit_dock->get_parent_control(); - dock_vbc->set_tab_title(dock_vbc->get_tab_idx_from_control(version_commit_dock), TTR("Commit")); - - Button *vc = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Version Control"), version_control_dock); - set_version_control_tool_button(vc); +void VersionControlEditorPlugin::_toggle_vcs_integration(bool p_toggled) { + if (p_toggled) { + _initialize_vcs(); + } else { + shut_down(); } } -void VersionControlEditorPlugin::fetch_available_vcs_addon_names() { - List<StringName> global_classes; - ScriptServer::get_global_class_list(&global_classes); - - for (int i = 0; i != global_classes.size(); i++) { - String path = ScriptServer::get_global_class_path(global_classes[i]); - Ref<Script> script = ResourceLoader::load(path); - ERR_FAIL_COND(script.is_null()); +void VersionControlEditorPlugin::_project_path_selected(String p_project_path) { + project_path_input->set_text(p_project_path); +} - if (script->get_instance_base_type() == "EditorVCSInterface") { - available_addons.push_back(global_classes[i]); - } - } +void VersionControlEditorPlugin::fetch_available_vcs_plugin_names() { + available_plugins.clear(); + ClassDB::get_direct_inheriters_from_class(EditorVCSInterface::get_class_static(), &available_plugins); } -void VersionControlEditorPlugin::clear_stage_area() { - stage_files->get_root()->clear_children(); +void VersionControlEditorPlugin::register_editor() { + EditorNode::get_singleton()->add_control_to_dock(EditorNode::DOCK_SLOT_RIGHT_UL, version_commit_dock); + + version_control_dock_button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Version Control"), version_control_dock); + + _set_vcs_ui_state(true); } void VersionControlEditorPlugin::shut_down() { - if (EditorVCSInterface::get_singleton()) { - if (EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &VersionControlEditorPlugin::_refresh_stage_area))) { - EditorFileSystem::get_singleton()->disconnect("filesystem_changed", callable_mp(this, &VersionControlEditorPlugin::_refresh_stage_area)); - } - EditorVCSInterface::get_singleton()->shut_down(); - memdelete(EditorVCSInterface::get_singleton()); - EditorVCSInterface::set_singleton(nullptr); + if (!EditorVCSInterface::get_singleton()) { + return; + } - EditorNode::get_singleton()->remove_control_from_dock(version_commit_dock); - EditorNode::get_singleton()->remove_bottom_panel_item(version_control_dock); + if (EditorFileSystem::get_singleton()->is_connected(SNAME("filesystem_changed"), callable_mp(this, &VersionControlEditorPlugin::_refresh_stage_area))) { + EditorFileSystem::get_singleton()->disconnect(SNAME("filesystem_changed"), callable_mp(this, &VersionControlEditorPlugin::_refresh_stage_area)); } -} -bool VersionControlEditorPlugin::is_vcs_initialized() const { - return EditorVCSInterface::get_singleton() ? EditorVCSInterface::get_singleton()->is_vcs_initialized() : false; -} + EditorVCSInterface::get_singleton()->shut_down(); + memdelete(EditorVCSInterface::get_singleton()); + EditorVCSInterface::set_singleton(nullptr); + + EditorNode::get_singleton()->remove_control_from_dock(version_commit_dock); + EditorNode::get_singleton()->remove_bottom_panel_item(version_control_dock); -const String VersionControlEditorPlugin::get_vcs_name() const { - return EditorVCSInterface::get_singleton() ? EditorVCSInterface::get_singleton()->get_vcs_name() : ""; + _set_vcs_ui_state(false); } VersionControlEditorPlugin::VersionControlEditorPlugin() { singleton = this; - staged_files_count = 0; version_control_actions = memnew(PopupMenu); metadata_dialog = memnew(ConfirmationDialog); metadata_dialog->set_title(TTR("Create Version Control Metadata")); metadata_dialog->set_min_size(Size2(200, 40)); + metadata_dialog->get_ok_button()->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_create_vcs_metadata_files)); version_control_actions->add_child(metadata_dialog); VBoxContainer *metadata_vb = memnew(VBoxContainer); + metadata_dialog->add_child(metadata_vb); + HBoxContainer *metadata_hb = memnew(HBoxContainer); metadata_hb->set_custom_minimum_size(Size2(200, 20)); + metadata_vb->add_child(metadata_hb); + Label *l = memnew(Label); l->set_text(TTR("Create VCS metadata files for:")); metadata_hb->add_child(l); + metadata_selection = memnew(OptionButton); metadata_selection->set_custom_minimum_size(Size2(100, 20)); metadata_selection->add_item("None", (int)EditorVCSInterface::VCSMetadata::NONE); metadata_selection->add_item("Git", (int)EditorVCSInterface::VCSMetadata::GIT); metadata_selection->select((int)EditorVCSInterface::VCSMetadata::GIT); - metadata_dialog->get_ok_button()->connect("pressed", callable_mp(this, &VersionControlEditorPlugin::_create_vcs_metadata_files)); metadata_hb->add_child(metadata_selection); - metadata_vb->add_child(metadata_hb); + l = memnew(Label); l->set_text(TTR("Existing VCS metadata files will be overwritten.")); metadata_vb->add_child(l); - metadata_dialog->add_child(metadata_vb); set_up_dialog = memnew(AcceptDialog); - set_up_dialog->set_title(TTR("Set Up Version Control")); - set_up_dialog->set_min_size(Size2(400, 100)); + set_up_dialog->set_title(TTR("Local Settings")); + set_up_dialog->set_min_size(Size2(600, 100)); + set_up_dialog->add_cancel_button("Cancel"); + set_up_dialog->set_hide_on_ok(true); version_control_actions->add_child(set_up_dialog); - set_up_ok_button = set_up_dialog->get_ok_button(); - set_up_ok_button->set_text(TTR("Close")); + Button *set_up_apply_button = set_up_dialog->get_ok_button(); + set_up_apply_button->set_text(TTR("Apply")); + set_up_apply_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_set_credentials)); set_up_vbc = memnew(VBoxContainer); set_up_vbc->set_alignment(BoxContainer::ALIGNMENT_CENTER); set_up_dialog->add_child(set_up_vbc); - set_up_hbc = memnew(HBoxContainer); - set_up_hbc->set_h_size_flags(BoxContainer::SIZE_EXPAND_FILL); + HBoxContainer *set_up_hbc = memnew(HBoxContainer); + set_up_hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); set_up_vbc->add_child(set_up_hbc); - set_up_vcs_status = memnew(RichTextLabel); - set_up_vcs_status->set_text(TTR("VCS Addon is not initialized")); - set_up_vbc->add_child(set_up_vcs_status); - - set_up_vcs_label = memnew(Label); - set_up_vcs_label->set_text(TTR("Version Control System")); + Label *set_up_vcs_label = memnew(Label); + set_up_vcs_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_vcs_label->set_text(TTR("VCS Provider")); set_up_hbc->add_child(set_up_vcs_label); set_up_choice = memnew(OptionButton); - set_up_choice->set_h_size_flags(HBoxContainer::SIZE_EXPAND_FILL); - set_up_choice->connect("item_selected", callable_mp(this, &VersionControlEditorPlugin::_selected_a_vcs)); + set_up_choice->set_h_size_flags(Control::SIZE_EXPAND_FILL); set_up_hbc->add_child(set_up_choice); - set_up_init_settings = nullptr; - - set_up_init_button = memnew(Button); - set_up_init_button->set_text(TTR("Initialize")); - set_up_init_button->connect("pressed", callable_mp(this, &VersionControlEditorPlugin::_initialize_vcs)); - set_up_vbc->add_child(set_up_init_button); + HBoxContainer *project_path_hbc = memnew(HBoxContainer); + project_path_hbc->set_h_size_flags(Control::SIZE_FILL); + set_up_vbc->add_child(project_path_hbc); + + Label *project_path_label = memnew(Label); + project_path_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + project_path_label->set_text(TTR("VCS Project Path")); + project_path_hbc->add_child(project_path_label); + + project_path_input = memnew(LineEdit); + project_path_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); + project_path_input->set_text(OS::get_singleton()->get_resource_dir()); + project_path_hbc->add_child(project_path_input); + + FileDialog *select_project_path_file_dialog = memnew(FileDialog); + select_project_path_file_dialog->set_access(FileDialog::ACCESS_FILESYSTEM); + select_project_path_file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_DIR); + select_project_path_file_dialog->set_show_hidden_files(true); + select_project_path_file_dialog->set_current_dir(OS::get_singleton()->get_resource_dir()); + select_project_path_file_dialog->connect(SNAME("dir_selected"), callable_mp(this, &VersionControlEditorPlugin::_project_path_selected)); + project_path_hbc->add_child(select_project_path_file_dialog); + + select_project_path_button = memnew(Button); + select_project_path_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Folder", "EditorIcons")); + select_project_path_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_popup_file_dialog).bind(select_project_path_file_dialog)); + select_project_path_button->set_tooltip_text(TTR("Select VCS project path")); + project_path_hbc->add_child(select_project_path_button); + + HBoxContainer *toggle_vcs_hbc = memnew(HBoxContainer); + toggle_vcs_hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_vbc->add_child(toggle_vcs_hbc); + + Label *toggle_vcs_label = memnew(Label); + toggle_vcs_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + toggle_vcs_label->set_text(TTR("Connect to VCS")); + toggle_vcs_hbc->add_child(toggle_vcs_label); + + toggle_vcs_choice = memnew(CheckButton); + toggle_vcs_choice->set_h_size_flags(Control::SIZE_EXPAND_FILL); + toggle_vcs_choice->set_pressed_no_signal(false); + toggle_vcs_choice->connect(SNAME("toggled"), callable_mp(this, &VersionControlEditorPlugin::_toggle_vcs_integration)); + toggle_vcs_hbc->add_child(toggle_vcs_choice); + + set_up_vbc->add_child(memnew(HSeparator)); + + set_up_settings_vbc = memnew(VBoxContainer); + set_up_settings_vbc->set_alignment(BoxContainer::ALIGNMENT_CENTER); + set_up_vbc->add_child(set_up_settings_vbc); + + Label *remote_login = memnew(Label); + remote_login->set_h_size_flags(Control::SIZE_EXPAND_FILL); + remote_login->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + remote_login->set_text(TTR("Remote Login")); + set_up_settings_vbc->add_child(remote_login); + + HBoxContainer *set_up_username_input = memnew(HBoxContainer); + set_up_username_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_settings_vbc->add_child(set_up_username_input); + + Label *set_up_username_label = memnew(Label); + set_up_username_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_username_label->set_text(TTR("Username")); + set_up_username_input->add_child(set_up_username_label); + + set_up_username = memnew(LineEdit); + set_up_username->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_username->set_text(EDITOR_DEF("version_control/username", "")); + set_up_username->connect(SNAME("text_changed"), callable_mp(this, &VersionControlEditorPlugin::_update_set_up_warning)); + set_up_username_input->add_child(set_up_username); + + HBoxContainer *set_up_password_input = memnew(HBoxContainer); + set_up_password_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_settings_vbc->add_child(set_up_password_input); + + Label *set_up_password_label = memnew(Label); + set_up_password_label->set_text(TTR("Password")); + set_up_password_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_password_input->add_child(set_up_password_label); + + set_up_password = memnew(LineEdit); + set_up_password->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_password->set_secret(true); + set_up_password->connect(SNAME("text_changed"), callable_mp(this, &VersionControlEditorPlugin::_update_set_up_warning)); + set_up_password_input->add_child(set_up_password); + + HBoxContainer *set_up_ssh_public_key_input = memnew(HBoxContainer); + set_up_ssh_public_key_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_settings_vbc->add_child(set_up_ssh_public_key_input); + + Label *set_up_ssh_public_key_label = memnew(Label); + set_up_ssh_public_key_label->set_text(TTR("SSH Public Key Path")); + set_up_ssh_public_key_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_ssh_public_key_input->add_child(set_up_ssh_public_key_label); + + HBoxContainer *set_up_ssh_public_key_input_hbc = memnew(HBoxContainer); + set_up_ssh_public_key_input_hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_ssh_public_key_input->add_child(set_up_ssh_public_key_input_hbc); + + set_up_ssh_public_key_path = memnew(LineEdit); + set_up_ssh_public_key_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_ssh_public_key_path->set_text(EDITOR_DEF("version_control/ssh_public_key_path", "")); + set_up_ssh_public_key_path->connect(SNAME("text_changed"), callable_mp(this, &VersionControlEditorPlugin::_update_set_up_warning)); + set_up_ssh_public_key_input_hbc->add_child(set_up_ssh_public_key_path); + + set_up_ssh_public_key_file_dialog = memnew(FileDialog); + set_up_ssh_public_key_file_dialog->set_access(FileDialog::ACCESS_FILESYSTEM); + set_up_ssh_public_key_file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE); + set_up_ssh_public_key_file_dialog->set_show_hidden_files(true); + // TODO: Make this start at the user's home folder + Ref<DirAccess> d = DirAccess::open(OS::get_singleton()->get_system_dir(OS::SYSTEM_DIR_DOCUMENTS)); + d->change_dir("../"); + set_up_ssh_public_key_file_dialog->set_current_dir(d->get_current_dir()); + set_up_ssh_public_key_file_dialog->connect(SNAME("file_selected"), callable_mp(this, &VersionControlEditorPlugin::_ssh_public_key_selected)); + set_up_ssh_public_key_input_hbc->add_child(set_up_ssh_public_key_file_dialog); + + Button *select_public_path_button = memnew(Button); + select_public_path_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Folder", "EditorIcons")); + select_public_path_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_popup_file_dialog).bind(set_up_ssh_public_key_file_dialog)); + select_public_path_button->set_tooltip_text(TTR("Select SSH public key path")); + set_up_ssh_public_key_input_hbc->add_child(select_public_path_button); + + HBoxContainer *set_up_ssh_private_key_input = memnew(HBoxContainer); + set_up_ssh_private_key_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_settings_vbc->add_child(set_up_ssh_private_key_input); + + Label *set_up_ssh_private_key_label = memnew(Label); + set_up_ssh_private_key_label->set_text(TTR("SSH Private Key Path")); + set_up_ssh_private_key_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_ssh_private_key_input->add_child(set_up_ssh_private_key_label); + + HBoxContainer *set_up_ssh_private_key_input_hbc = memnew(HBoxContainer); + set_up_ssh_private_key_input_hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_ssh_private_key_input->add_child(set_up_ssh_private_key_input_hbc); + + set_up_ssh_private_key_path = memnew(LineEdit); + set_up_ssh_private_key_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_ssh_private_key_path->set_text(EDITOR_DEF("version_control/ssh_private_key_path", "")); + set_up_ssh_private_key_path->connect(SNAME("text_changed"), callable_mp(this, &VersionControlEditorPlugin::_update_set_up_warning)); + set_up_ssh_private_key_input_hbc->add_child(set_up_ssh_private_key_path); + + set_up_ssh_private_key_file_dialog = memnew(FileDialog); + set_up_ssh_private_key_file_dialog->set_access(FileDialog::ACCESS_FILESYSTEM); + set_up_ssh_private_key_file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE); + set_up_ssh_private_key_file_dialog->set_show_hidden_files(true); + // TODO: Make this start at the user's home folder + set_up_ssh_private_key_file_dialog->set_current_dir(d->get_current_dir()); + set_up_ssh_private_key_file_dialog->connect("file_selected", callable_mp(this, &VersionControlEditorPlugin::_ssh_private_key_selected)); + set_up_ssh_private_key_input_hbc->add_child(set_up_ssh_private_key_file_dialog); + + Button *select_private_path_button = memnew(Button); + select_private_path_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Folder", "EditorIcons")); + select_private_path_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_popup_file_dialog).bind(set_up_ssh_private_key_file_dialog)); + select_private_path_button->set_tooltip_text(TTR("Select SSH private key path")); + set_up_ssh_private_key_input_hbc->add_child(select_private_path_button); + + HBoxContainer *set_up_ssh_passphrase_input = memnew(HBoxContainer); + set_up_ssh_passphrase_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_settings_vbc->add_child(set_up_ssh_passphrase_input); + + Label *set_up_ssh_passphrase_label = memnew(Label); + set_up_ssh_passphrase_label->set_text(TTR("SSH Passphrase")); + set_up_ssh_passphrase_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_ssh_passphrase_input->add_child(set_up_ssh_passphrase_label); + + set_up_ssh_passphrase = memnew(LineEdit); + set_up_ssh_passphrase->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_ssh_passphrase->set_secret(true); + set_up_ssh_passphrase->connect(SNAME("text_changed"), callable_mp(this, &VersionControlEditorPlugin::_update_set_up_warning)); + set_up_ssh_passphrase_input->add_child(set_up_ssh_passphrase); + + set_up_warning_text = memnew(Label); + set_up_warning_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + set_up_warning_text->set_h_size_flags(Control::SIZE_EXPAND_FILL); + set_up_settings_vbc->add_child(set_up_warning_text); version_commit_dock = memnew(VBoxContainer); version_commit_dock->set_visible(false); + version_commit_dock->set_name(TTR("Commit")); - commit_box_vbc = memnew(VBoxContainer); - commit_box_vbc->set_alignment(VBoxContainer::ALIGNMENT_BEGIN); - commit_box_vbc->set_h_size_flags(VBoxContainer::SIZE_EXPAND_FILL); - commit_box_vbc->set_v_size_flags(VBoxContainer::SIZE_EXPAND_FILL); - version_commit_dock->add_child(commit_box_vbc); + VBoxContainer *unstage_area = memnew(VBoxContainer); + unstage_area->set_v_size_flags(Control::SIZE_EXPAND_FILL); + unstage_area->set_h_size_flags(Control::SIZE_EXPAND_FILL); + version_commit_dock->add_child(unstage_area); - stage_tools = memnew(HSplitContainer); - stage_tools->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN_COLLAPSED); - commit_box_vbc->add_child(stage_tools); + HBoxContainer *unstage_title = memnew(HBoxContainer); + unstage_area->add_child(unstage_title); - staging_area_label = memnew(Label); - staging_area_label->set_h_size_flags(Label::SIZE_EXPAND_FILL); - staging_area_label->set_text(TTR("Staging area")); - stage_tools->add_child(staging_area_label); + Label *unstage_label = memnew(Label); + unstage_label->set_text(TTR("Unstaged Changes")); + unstage_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + unstage_title->add_child(unstage_label); refresh_button = memnew(Button); refresh_button->set_tooltip_text(TTR("Detect new changes")); - refresh_button->set_text(TTR("Refresh")); + refresh_button->set_flat(true); refresh_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); - refresh_button->connect("pressed", callable_mp(this, &VersionControlEditorPlugin::_refresh_stage_area)); - stage_tools->add_child(refresh_button); - - stage_files = memnew(Tree); - stage_files->set_h_size_flags(Tree::SIZE_EXPAND_FILL); - stage_files->set_v_size_flags(Tree::SIZE_EXPAND_FILL); - stage_files->set_columns(1); - stage_files->set_column_title(0, TTR("Changes")); - stage_files->set_column_titles_visible(true); - stage_files->set_allow_reselect(true); - stage_files->set_allow_rmb_select(true); - stage_files->set_select_mode(Tree::SelectMode::SELECT_MULTI); - stage_files->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true); - stage_files->connect("cell_selected", callable_mp(this, &VersionControlEditorPlugin::_view_file_diff)); - stage_files->create_item(); - stage_files->set_hide_root(true); - commit_box_vbc->add_child(stage_files); - - change_type_to_strings[CHANGE_TYPE_NEW] = TTR("New"); - change_type_to_strings[CHANGE_TYPE_MODIFIED] = TTR("Modified"); - change_type_to_strings[CHANGE_TYPE_RENAMED] = TTR("Renamed"); - change_type_to_strings[CHANGE_TYPE_DELETED] = TTR("Deleted"); - change_type_to_strings[CHANGE_TYPE_TYPECHANGE] = TTR("Typechange"); - - change_type_to_color[CHANGE_TYPE_NEW] = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("success_color"), SNAME("Editor")); - change_type_to_color[CHANGE_TYPE_MODIFIED] = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("warning_color"), SNAME("Editor")); - change_type_to_color[CHANGE_TYPE_RENAMED] = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("disabled_font_color"), SNAME("Editor")); - change_type_to_color[CHANGE_TYPE_DELETED] = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor")); - change_type_to_color[CHANGE_TYPE_TYPECHANGE] = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("font_color"), SNAME("Editor")); - - stage_buttons = memnew(HSplitContainer); - stage_buttons->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN_COLLAPSED); - commit_box_vbc->add_child(stage_buttons); - - stage_selected_button = memnew(Button); - stage_selected_button->set_h_size_flags(Button::SIZE_EXPAND_FILL); - stage_selected_button->set_text(TTR("Stage Selected")); - stage_selected_button->connect("pressed", callable_mp(this, &VersionControlEditorPlugin::_stage_selected)); - stage_buttons->add_child(stage_selected_button); + refresh_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_refresh_stage_area)); + refresh_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_refresh_commit_list)); + refresh_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_refresh_branch_list)); + refresh_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_refresh_remote_list)); + unstage_title->add_child(refresh_button); + + discard_all_button = memnew(Button); + discard_all_button->set_tooltip_text(TTR("Discard all changes")); + discard_all_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Close"), SNAME("EditorIcons"))); + discard_all_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_discard_all)); + discard_all_button->set_flat(true); + unstage_title->add_child(discard_all_button); stage_all_button = memnew(Button); - stage_all_button->set_text(TTR("Stage All")); - stage_all_button->connect("pressed", callable_mp(this, &VersionControlEditorPlugin::_stage_all)); - stage_buttons->add_child(stage_all_button); - - commit_box_vbc->add_child(memnew(HSeparator)); + stage_all_button->set_flat(true); + stage_all_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("MoveDown"), SNAME("EditorIcons"))); + stage_all_button->set_tooltip_text(TTR("Stage all changes")); + unstage_title->add_child(stage_all_button); + + unstaged_files = memnew(Tree); + unstaged_files->set_h_size_flags(Tree::SIZE_EXPAND_FILL); + unstaged_files->set_v_size_flags(Tree::SIZE_EXPAND_FILL); + unstaged_files->set_select_mode(Tree::SELECT_ROW); + unstaged_files->connect(SNAME("item_selected"), callable_mp(this, &VersionControlEditorPlugin::_load_diff).bind(unstaged_files)); + unstaged_files->connect(SNAME("item_activated"), callable_mp(this, &VersionControlEditorPlugin::_item_activated).bind(unstaged_files)); + unstaged_files->connect(SNAME("button_clicked"), callable_mp(this, &VersionControlEditorPlugin::_cell_button_pressed)); + unstaged_files->create_item(); + unstaged_files->set_hide_root(true); + unstage_area->add_child(unstaged_files); + + VBoxContainer *stage_area = memnew(VBoxContainer); + stage_area->set_v_size_flags(Control::SIZE_EXPAND_FILL); + stage_area->set_h_size_flags(Control::SIZE_EXPAND_FILL); + version_commit_dock->add_child(stage_area); + + HBoxContainer *stage_title = memnew(HBoxContainer); + stage_area->add_child(stage_title); + + Label *stage_label = memnew(Label); + stage_label->set_text(TTR("Staged Changes")); + stage_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + stage_title->add_child(stage_label); + + unstage_all_button = memnew(Button); + unstage_all_button->set_flat(true); + unstage_all_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons"))); + unstage_all_button->set_tooltip_text(TTR("Unstage all changes")); + stage_title->add_child(unstage_all_button); + + staged_files = memnew(Tree); + staged_files->set_h_size_flags(Tree::SIZE_EXPAND_FILL); + staged_files->set_v_size_flags(Tree::SIZE_EXPAND_FILL); + staged_files->set_select_mode(Tree::SELECT_ROW); + staged_files->connect(SNAME("item_selected"), callable_mp(this, &VersionControlEditorPlugin::_load_diff).bind(staged_files)); + staged_files->connect(SNAME("button_clicked"), callable_mp(this, &VersionControlEditorPlugin::_cell_button_pressed)); + staged_files->connect(SNAME("item_activated"), callable_mp(this, &VersionControlEditorPlugin::_item_activated).bind(staged_files)); + staged_files->create_item(); + staged_files->set_hide_root(true); + stage_area->add_child(staged_files); + + // Editor crashes if bind is null + unstage_all_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_move_all).bind(staged_files)); + stage_all_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_move_all).bind(unstaged_files)); + + version_commit_dock->add_child(memnew(HSeparator)); + + VBoxContainer *commit_area = memnew(VBoxContainer); + version_commit_dock->add_child(commit_area); + + Label *commit_label = memnew(Label); + commit_label->set_text(TTR("Commit Message")); + commit_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + commit_area->add_child(commit_label); commit_message = memnew(TextEdit); commit_message->set_h_size_flags(Control::SIZE_EXPAND_FILL); commit_message->set_h_grow_direction(Control::GrowDirection::GROW_DIRECTION_BEGIN); commit_message->set_v_grow_direction(Control::GrowDirection::GROW_DIRECTION_END); commit_message->set_custom_minimum_size(Size2(200, 100)); - commit_message->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY); - commit_message->connect("text_changed", callable_mp(this, &VersionControlEditorPlugin::_update_commit_button)); - commit_message->connect("gui_input", callable_mp(this, &VersionControlEditorPlugin::_commit_message_gui_input)); - commit_box_vbc->add_child(commit_message); + commit_message->set_line_wrapping_mode(TextEdit::LINE_WRAPPING_BOUNDARY); + commit_message->connect(SNAME("text_changed"), callable_mp(this, &VersionControlEditorPlugin::_update_commit_button)); + commit_message->connect(SNAME("gui_input"), callable_mp(this, &VersionControlEditorPlugin::_commit_message_gui_input)); + commit_area->add_child(commit_message); + ED_SHORTCUT("version_control/commit", TTR("Commit"), KeyModifierMask::CMD | Key::ENTER); commit_button = memnew(Button); commit_button->set_text(TTR("Commit Changes")); commit_button->set_disabled(true); - commit_button->connect("pressed", callable_mp(this, &VersionControlEditorPlugin::_send_commit_msg)); - commit_box_vbc->add_child(commit_button); - - commit_status = memnew(Label); - commit_status->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); - commit_box_vbc->add_child(commit_status); - - version_control_dock = memnew(PanelContainer); + commit_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_commit)); + commit_area->add_child(commit_button); + + version_commit_dock->add_child(memnew(HSeparator)); + + HBoxContainer *commit_list_hbc = memnew(HBoxContainer); + version_commit_dock->add_child(commit_list_hbc); + + Label *commit_list_label = memnew(Label); + commit_list_label->set_text(TTR("Commit List")); + commit_list_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + commit_list_hbc->add_child(commit_list_label); + + commit_list_size_button = memnew(OptionButton); + commit_list_size_button->set_tooltip_text(TTR("Commit list size")); + commit_list_size_button->add_item("10"); + commit_list_size_button->set_item_metadata(0, 10); + commit_list_size_button->add_item("20"); + commit_list_size_button->set_item_metadata(1, 20); + commit_list_size_button->add_item("30"); + commit_list_size_button->set_item_metadata(2, 30); + commit_list_size_button->connect(SNAME("item_selected"), callable_mp(this, &VersionControlEditorPlugin::_set_commit_list_size)); + commit_list_hbc->add_child(commit_list_size_button); + + commit_list = memnew(Tree); + commit_list->set_h_size_flags(Control::SIZE_EXPAND_FILL); + commit_list->set_v_grow_direction(Control::GrowDirection::GROW_DIRECTION_END); + commit_list->set_custom_minimum_size(Size2(200, 160)); + commit_list->create_item(); + commit_list->set_hide_root(true); + commit_list->set_select_mode(Tree::SELECT_ROW); + commit_list->set_columns(2); // Commit msg, author + commit_list->set_column_custom_minimum_width(0, 40); + commit_list->set_column_custom_minimum_width(1, 20); + commit_list->connect(SNAME("item_selected"), callable_mp(this, &VersionControlEditorPlugin::_load_diff).bind(commit_list)); + version_commit_dock->add_child(commit_list); + + version_commit_dock->add_child(memnew(HSeparator)); + + HBoxContainer *menu_bar = memnew(HBoxContainer); + menu_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL); + menu_bar->set_v_size_flags(Control::SIZE_FILL); + version_commit_dock->add_child(menu_bar); + + branch_select = memnew(OptionButton); + branch_select->set_tooltip_text(TTR("Branches")); + branch_select->set_h_size_flags(Control::SIZE_EXPAND_FILL); + branch_select->connect(SNAME("item_selected"), callable_mp(this, &VersionControlEditorPlugin::_branch_item_selected)); + branch_select->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_refresh_branch_list)); + menu_bar->add_child(branch_select); + + branch_create_confirm = memnew(AcceptDialog); + branch_create_confirm->set_title(TTR("Create New Branch")); + branch_create_confirm->set_min_size(Size2(400, 100)); + branch_create_confirm->set_hide_on_ok(true); + version_commit_dock->add_child(branch_create_confirm); + + branch_create_ok = branch_create_confirm->get_ok_button(); + branch_create_ok->set_text(TTR("Create")); + branch_create_ok->set_disabled(true); + branch_create_ok->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_create_branch)); + + branch_remove_confirm = memnew(AcceptDialog); + branch_remove_confirm->set_title(TTR("Remove Branch")); + branch_remove_confirm->add_cancel_button(); + version_commit_dock->add_child(branch_remove_confirm); + + Button *branch_remove_ok = branch_remove_confirm->get_ok_button(); + branch_remove_ok->set_text(TTR("Remove")); + branch_remove_ok->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_remove_branch)); + + VBoxContainer *branch_create_vbc = memnew(VBoxContainer); + branch_create_vbc->set_alignment(BoxContainer::ALIGNMENT_CENTER); + branch_create_confirm->add_child(branch_create_vbc); + + HBoxContainer *branch_create_hbc = memnew(HBoxContainer); + branch_create_hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + branch_create_vbc->add_child(branch_create_hbc); + + Label *branch_create_name_label = memnew(Label); + branch_create_name_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + branch_create_name_label->set_text(TTR("Branch Name")); + branch_create_hbc->add_child(branch_create_name_label); + + branch_create_name_input = memnew(LineEdit); + branch_create_name_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); + branch_create_name_input->connect(SNAME("text_changed"), callable_mp(this, &VersionControlEditorPlugin::_update_branch_create_button)); + branch_create_hbc->add_child(branch_create_name_input); + + remote_select = memnew(OptionButton); + remote_select->set_tooltip_text(TTR("Remotes")); + remote_select->set_h_size_flags(Control::SIZE_EXPAND_FILL); + remote_select->connect(SNAME("item_selected"), callable_mp(this, &VersionControlEditorPlugin::_remote_selected)); + remote_select->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_refresh_remote_list)); + menu_bar->add_child(remote_select); + + remote_create_confirm = memnew(AcceptDialog); + remote_create_confirm->set_title(TTR("Create New Remote")); + remote_create_confirm->set_min_size(Size2(400, 100)); + remote_create_confirm->set_hide_on_ok(true); + version_commit_dock->add_child(remote_create_confirm); + + remote_create_ok = remote_create_confirm->get_ok_button(); + remote_create_ok->set_text(TTR("Create")); + remote_create_ok->set_disabled(true); + remote_create_ok->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_create_remote)); + + remote_remove_confirm = memnew(AcceptDialog); + remote_remove_confirm->set_title(TTR("Remove Remote")); + remote_remove_confirm->add_cancel_button(); + version_commit_dock->add_child(remote_remove_confirm); + + Button *remote_remove_ok = remote_remove_confirm->get_ok_button(); + remote_remove_ok->set_text(TTR("Remove")); + remote_remove_ok->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_remove_remote)); + + VBoxContainer *remote_create_vbc = memnew(VBoxContainer); + remote_create_vbc->set_alignment(BoxContainer::ALIGNMENT_CENTER); + remote_create_confirm->add_child(remote_create_vbc); + + HBoxContainer *remote_create_name_hbc = memnew(HBoxContainer); + remote_create_name_hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + remote_create_vbc->add_child(remote_create_name_hbc); + + Label *remote_create_name_label = memnew(Label); + remote_create_name_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + remote_create_name_label->set_text(TTR("Remote Name")); + remote_create_name_hbc->add_child(remote_create_name_label); + + remote_create_name_input = memnew(LineEdit); + remote_create_name_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); + remote_create_name_input->connect(SNAME("text_changed"), callable_mp(this, &VersionControlEditorPlugin::_update_remote_create_button)); + remote_create_name_hbc->add_child(remote_create_name_input); + + HBoxContainer *remote_create_hbc = memnew(HBoxContainer); + remote_create_hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); + remote_create_vbc->add_child(remote_create_hbc); + + Label *remote_create_url_label = memnew(Label); + remote_create_url_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + remote_create_url_label->set_text(TTR("Remote URL")); + remote_create_hbc->add_child(remote_create_url_label); + + remote_create_url_input = memnew(LineEdit); + remote_create_url_input->set_h_size_flags(Control::SIZE_EXPAND_FILL); + remote_create_url_input->connect(SNAME("text_changed"), callable_mp(this, &VersionControlEditorPlugin::_update_remote_create_button)); + remote_create_hbc->add_child(remote_create_url_input); + + fetch_button = memnew(Button); + fetch_button->set_flat(true); + fetch_button->set_tooltip_text(TTR("Fetch")); + fetch_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); + fetch_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_fetch)); + menu_bar->add_child(fetch_button); + + pull_button = memnew(Button); + pull_button->set_flat(true); + pull_button->set_tooltip_text(TTR("Pull")); + pull_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("MoveDown"), SNAME("EditorIcons"))); + pull_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_pull)); + menu_bar->add_child(pull_button); + + push_button = memnew(Button); + push_button->set_flat(true); + push_button->set_tooltip_text(TTR("Push")); + push_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons"))); + push_button->connect(SNAME("pressed"), callable_mp(this, &VersionControlEditorPlugin::_push)); + menu_bar->add_child(push_button); + + extra_options = memnew(MenuButton); + extra_options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); + extra_options->get_popup()->connect(SNAME("about_to_popup"), callable_mp(this, &VersionControlEditorPlugin::_update_extra_options)); + extra_options->get_popup()->connect(SNAME("id_pressed"), callable_mp(this, &VersionControlEditorPlugin::_extra_option_selected)); + menu_bar->add_child(extra_options); + + extra_options->get_popup()->add_item(TTR("Force Push"), EXTRA_OPTION_FORCE_PUSH); + extra_options->get_popup()->add_separator(); + extra_options->get_popup()->add_item(TTR("Create New Branch"), EXTRA_OPTION_CREATE_BRANCH); + + extra_options_remove_branch_list = memnew(PopupMenu); + extra_options_remove_branch_list->connect(SNAME("id_pressed"), callable_mp(this, &VersionControlEditorPlugin::_popup_branch_remove_confirm)); + extra_options_remove_branch_list->set_name("Remove Branch"); + extra_options->get_popup()->add_child(extra_options_remove_branch_list); + extra_options->get_popup()->add_submenu_item(TTR("Remove Branch"), "Remove Branch"); + + extra_options->get_popup()->add_separator(); + extra_options->get_popup()->add_item(TTR("Create New Remote"), EXTRA_OPTION_CREATE_REMOTE); + + extra_options_remove_remote_list = memnew(PopupMenu); + extra_options_remove_remote_list->connect(SNAME("id_pressed"), callable_mp(this, &VersionControlEditorPlugin::_popup_remote_remove_confirm)); + extra_options_remove_remote_list->set_name("Remove Remote"); + extra_options->get_popup()->add_child(extra_options_remove_remote_list); + extra_options->get_popup()->add_submenu_item(TTR("Remove Remote"), "Remove Remote"); + + change_type_to_strings[EditorVCSInterface::CHANGE_TYPE_NEW] = TTR("New"); + change_type_to_strings[EditorVCSInterface::CHANGE_TYPE_MODIFIED] = TTR("Modified"); + change_type_to_strings[EditorVCSInterface::CHANGE_TYPE_RENAMED] = TTR("Renamed"); + change_type_to_strings[EditorVCSInterface::CHANGE_TYPE_DELETED] = TTR("Deleted"); + change_type_to_strings[EditorVCSInterface::CHANGE_TYPE_TYPECHANGE] = TTR("Typechange"); + change_type_to_strings[EditorVCSInterface::CHANGE_TYPE_UNMERGED] = TTR("Unmerged"); + + change_type_to_color[EditorVCSInterface::CHANGE_TYPE_NEW] = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("success_color"), SNAME("Editor")); + change_type_to_color[EditorVCSInterface::CHANGE_TYPE_MODIFIED] = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("warning_color"), SNAME("Editor")); + change_type_to_color[EditorVCSInterface::CHANGE_TYPE_RENAMED] = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("warning_color"), SNAME("Editor")); + change_type_to_color[EditorVCSInterface::CHANGE_TYPE_DELETED] = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor")); + change_type_to_color[EditorVCSInterface::CHANGE_TYPE_TYPECHANGE] = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("font_color"), SNAME("Editor")); + change_type_to_color[EditorVCSInterface::CHANGE_TYPE_UNMERGED] = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("warning_color"), SNAME("Editor")); + + change_type_to_icon[EditorVCSInterface::CHANGE_TYPE_NEW] = EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")); + change_type_to_icon[EditorVCSInterface::CHANGE_TYPE_MODIFIED] = EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")); + change_type_to_icon[EditorVCSInterface::CHANGE_TYPE_RENAMED] = EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")); + change_type_to_icon[EditorVCSInterface::CHANGE_TYPE_TYPECHANGE] = EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")); + change_type_to_icon[EditorVCSInterface::CHANGE_TYPE_DELETED] = EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons")); + change_type_to_icon[EditorVCSInterface::CHANGE_TYPE_UNMERGED] = EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")); + + version_control_dock = memnew(VBoxContainer); version_control_dock->set_v_size_flags(Control::SIZE_EXPAND_FILL); version_control_dock->set_custom_minimum_size(Size2(0, 300) * EDSCALE); version_control_dock->hide(); - diff_vbc = memnew(VBoxContainer); - diff_vbc->set_h_size_flags(HBoxContainer::SIZE_FILL); - diff_vbc->set_v_size_flags(HBoxContainer::SIZE_FILL); - version_control_dock->add_child(diff_vbc); - - diff_hbc = memnew(HBoxContainer); - diff_hbc->set_h_size_flags(HBoxContainer::SIZE_FILL); - diff_vbc->add_child(diff_hbc); - - diff_heading = memnew(Label); - diff_heading->set_text(TTR("Status")); + HBoxContainer *diff_heading = memnew(HBoxContainer); + diff_heading->set_h_size_flags(Control::SIZE_EXPAND_FILL); diff_heading->set_tooltip_text(TTR("View file diffs before committing them to the latest version")); - diff_hbc->add_child(diff_heading); + version_control_dock->add_child(diff_heading); - diff_file_name = memnew(Label); - diff_file_name->set_text(TTR("No file diff is active")); - diff_file_name->set_h_size_flags(Label::SIZE_EXPAND_FILL); - diff_file_name->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); - diff_hbc->add_child(diff_file_name); + diff_title = memnew(Label); + diff_title->set_h_size_flags(Control::SIZE_EXPAND_FILL); + diff_heading->add_child(diff_title); - diff_refresh_button = memnew(Button); - diff_refresh_button->set_tooltip_text(TTR("Detect changes in file diff")); - diff_refresh_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); - diff_refresh_button->connect("pressed", callable_mp(this, &VersionControlEditorPlugin::_refresh_file_diff)); - diff_hbc->add_child(diff_refresh_button); + Label *view = memnew(Label); + view->set_text(TTR("View:")); + diff_heading->add_child(view); + + diff_view_type_select = memnew(OptionButton); + diff_view_type_select->add_item(TTR("Split"), DIFF_VIEW_TYPE_SPLIT); + diff_view_type_select->add_item(TTR("Unified"), DIFF_VIEW_TYPE_UNIFIED); + diff_view_type_select->connect(SNAME("item_selected"), callable_mp(this, &VersionControlEditorPlugin::_display_diff)); + diff_heading->add_child(diff_view_type_select); diff = memnew(RichTextLabel); diff->set_h_size_flags(TextEdit::SIZE_EXPAND_FILL); diff->set_v_size_flags(TextEdit::SIZE_EXPAND_FILL); + diff->set_use_bbcode(true); diff->set_selection_enabled(true); - diff_vbc->add_child(diff); + version_control_dock->add_child(diff); + + _update_set_up_warning(""); } VersionControlEditorPlugin::~VersionControlEditorPlugin() { shut_down(); - memdelete(version_control_dock); memdelete(version_commit_dock); + memdelete(version_control_dock); memdelete(version_control_actions); } diff --git a/editor/plugins/version_control_editor_plugin.h b/editor/plugins/version_control_editor_plugin.h index fa721268ba..3340384a92 100644 --- a/editor/plugins/version_control_editor_plugin.h +++ b/editor/plugins/version_control_editor_plugin.h @@ -33,9 +33,12 @@ #include "editor/editor_plugin.h" #include "editor/editor_vcs_interface.h" -#include "scene/gui/box_container.h" +#include "scene/gui/check_button.h" +#include "scene/gui/container.h" +#include "scene/gui/file_dialog.h" +#include "scene/gui/menu_button.h" #include "scene/gui/rich_text_label.h" -#include "scene/gui/split_container.h" +#include "scene/gui/tab_container.h" #include "scene/gui/text_edit.h" #include "scene/gui/tree.h" @@ -43,79 +46,154 @@ class VersionControlEditorPlugin : public EditorPlugin { GDCLASS(VersionControlEditorPlugin, EditorPlugin) public: - enum ChangeType { - CHANGE_TYPE_NEW = 0, - CHANGE_TYPE_MODIFIED = 1, - CHANGE_TYPE_RENAMED = 2, - CHANGE_TYPE_DELETED = 3, - CHANGE_TYPE_TYPECHANGE = 4 + enum ButtonType { + BUTTON_TYPE_OPEN = 0, + BUTTON_TYPE_DISCARD = 1, + }; + + enum DiffViewType { + DIFF_VIEW_TYPE_SPLIT = 0, + DIFF_VIEW_TYPE_UNIFIED = 1, + }; + + enum ExtraOption { + EXTRA_OPTION_FORCE_PUSH, + EXTRA_OPTION_CREATE_BRANCH, + EXTRA_OPTION_CREATE_REMOTE, }; private: static VersionControlEditorPlugin *singleton; - int staged_files_count; - List<StringName> available_addons; + List<StringName> available_plugins; PopupMenu *version_control_actions = nullptr; ConfirmationDialog *metadata_dialog = nullptr; OptionButton *metadata_selection = nullptr; AcceptDialog *set_up_dialog = nullptr; - VBoxContainer *set_up_vbc = nullptr; - HBoxContainer *set_up_hbc = nullptr; - Label *set_up_vcs_label = nullptr; + CheckButton *toggle_vcs_choice = nullptr; OptionButton *set_up_choice = nullptr; - PanelContainer *set_up_init_settings = nullptr; - Button *set_up_init_button = nullptr; - RichTextLabel *set_up_vcs_status = nullptr; - Button *set_up_ok_button = nullptr; - - HashMap<ChangeType, String> change_type_to_strings; - HashMap<ChangeType, Color> change_type_to_color; + LineEdit *project_path_input = nullptr; + Button *select_project_path_button = nullptr; + VBoxContainer *set_up_vbc = nullptr; + VBoxContainer *set_up_settings_vbc = nullptr; + LineEdit *set_up_username = nullptr; + LineEdit *set_up_password = nullptr; + LineEdit *set_up_ssh_public_key_path = nullptr; + LineEdit *set_up_ssh_private_key_path = nullptr; + LineEdit *set_up_ssh_passphrase = nullptr; + FileDialog *set_up_ssh_public_key_file_dialog = nullptr; + FileDialog *set_up_ssh_private_key_file_dialog = nullptr; + Label *set_up_warning_text = nullptr; + + OptionButton *commit_list_size_button = nullptr; + + AcceptDialog *branch_create_confirm = nullptr; + LineEdit *branch_create_name_input = nullptr; + Button *branch_create_ok = nullptr; + + AcceptDialog *remote_create_confirm = nullptr; + LineEdit *remote_create_name_input = nullptr; + LineEdit *remote_create_url_input = nullptr; + Button *remote_create_ok = nullptr; + + HashMap<EditorVCSInterface::ChangeType, String> change_type_to_strings; + HashMap<EditorVCSInterface::ChangeType, Color> change_type_to_color; + HashMap<EditorVCSInterface::ChangeType, Ref<Texture>> change_type_to_icon; VBoxContainer *version_commit_dock = nullptr; - VBoxContainer *commit_box_vbc = nullptr; - HSplitContainer *stage_tools = nullptr; - Tree *stage_files = nullptr; - TreeItem *new_files = nullptr; - TreeItem *modified_files = nullptr; - TreeItem *renamed_files = nullptr; - TreeItem *deleted_files = nullptr; - TreeItem *typechange_files = nullptr; - Label *staging_area_label = nullptr; - HSplitContainer *stage_buttons = nullptr; + Tree *staged_files = nullptr; + Tree *unstaged_files = nullptr; + Tree *commit_list = nullptr; + + OptionButton *branch_select = nullptr; + Button *branch_remove_button = nullptr; + AcceptDialog *branch_remove_confirm = nullptr; + + Button *fetch_button = nullptr; + Button *pull_button = nullptr; + Button *push_button = nullptr; + OptionButton *remote_select = nullptr; + Button *remote_remove_button = nullptr; + AcceptDialog *remote_remove_confirm = nullptr; + MenuButton *extra_options = nullptr; + PopupMenu *extra_options_remove_branch_list = nullptr; + PopupMenu *extra_options_remove_remote_list = nullptr; + + String branch_to_remove; + String remote_to_remove; + Button *stage_all_button = nullptr; - Button *stage_selected_button = nullptr; + Button *unstage_all_button = nullptr; + Button *discard_all_button = nullptr; Button *refresh_button = nullptr; TextEdit *commit_message = nullptr; Button *commit_button = nullptr; - Label *commit_status = nullptr; - PanelContainer *version_control_dock = nullptr; + VBoxContainer *version_control_dock = nullptr; Button *version_control_dock_button = nullptr; - VBoxContainer *diff_vbc = nullptr; - HBoxContainer *diff_hbc = nullptr; - Button *diff_refresh_button = nullptr; - Label *diff_file_name = nullptr; - Label *diff_heading = nullptr; + Label *diff_title = nullptr; RichTextLabel *diff = nullptr; + OptionButton *diff_view_type_select = nullptr; + bool show_commit_diff_header = false; + List<EditorVCSInterface::DiffFile> diff_content; - void _populate_available_vcs_names(); - void _create_vcs_metadata_files(); - void _selected_a_vcs(int p_id); + void _notification(int p_what); void _initialize_vcs(); - void _send_commit_msg(); + void _set_vcs_ui_state(bool p_enabled); + void _set_credentials(); + void _ssh_public_key_selected(String p_path); + void _ssh_private_key_selected(String p_path); + void _populate_available_vcs_names(); + void _update_remotes_list(); + void _update_set_up_warning(String p_new_text); + void _update_opened_tabs(); + void _update_extra_options(); + + bool _load_plugin(String p_name, String p_project_path); + + void _pull(); + void _push(); + void _force_push(); + void _fetch(); + void _commit(); + void _discard_all(); void _refresh_stage_area(); - void _stage_selected(); - void _stage_all(); - void _view_file_diff(); - void _display_file_diff(String p_file_path); - void _refresh_file_diff(); - void _clear_file_diff(); - void _update_stage_status(); - void _update_commit_status(); + void _refresh_branch_list(); + void _set_commit_list_size(int p_index); + void _refresh_commit_list(); + void _refresh_remote_list(); + void _display_diff(int p_idx); + void _move_all(Object *p_tree); + void _load_diff(Object *p_tree); + void _clear_diff(); + int _get_item_count(Tree *p_tree); + void _item_activated(Object *p_tree); + void _create_branch(); + void _create_remote(); + void _update_branch_create_button(String p_new_text); + void _update_remote_create_button(String p_new_text); + void _branch_item_selected(int p_index); + void _remote_selected(int p_index); + void _remove_branch(); + void _remove_remote(); + void _popup_branch_remove_confirm(int p_index); + void _popup_remote_remove_confirm(int p_index); + void _move_item(Tree *p_tree, TreeItem *p_itme); + void _display_diff_split_view(List<EditorVCSInterface::DiffLine> &p_diff_content); + void _display_diff_unified_view(List<EditorVCSInterface::DiffLine> &p_diff_content); + void _discard_file(String p_file_path, EditorVCSInterface::ChangeType p_change); + void _cell_button_pressed(Object *p_item, int p_column, int p_id, int p_mouse_button_index); + void _add_new_item(Tree *p_tree, String p_file_path, EditorVCSInterface::ChangeType p_change); void _update_commit_button(); void _commit_message_gui_input(const Ref<InputEvent> &p_event); + void _extra_option_selected(int p_index); + bool _is_staging_area_empty(); + String _get_date_string_from(int64_t p_unix_timestamp, int64_t p_offset_minutes) const; + void _create_vcs_metadata_files(); + void _popup_file_dialog(Variant p_file_dialog_variant); + void _toggle_vcs_integration(bool p_toggled); + void _project_path_selected(String p_project_path); friend class EditorVCSInterface; @@ -127,25 +205,15 @@ public: void popup_vcs_metadata_dialog(); void popup_vcs_set_up_dialog(const Control *p_gui_base); - void set_version_control_tool_button(Button *p_button) { version_control_dock_button = p_button; } PopupMenu *get_version_control_actions_panel() const { return version_control_actions; } - VBoxContainer *get_version_commit_dock() const { return version_commit_dock; } - PanelContainer *get_version_control_dock() const { return version_control_dock; } - - List<StringName> get_available_vcs_names() const { return available_addons; } - bool is_vcs_initialized() const; - const String get_vcs_name() const; void register_editor(); - void fetch_available_vcs_addon_names(); - void clear_stage_area(); + void fetch_available_vcs_plugin_names(); void shut_down(); VersionControlEditorPlugin(); ~VersionControlEditorPlugin(); }; -VARIANT_ENUM_CAST(VersionControlEditorPlugin::ChangeType); - #endif // VERSION_CONTROL_EDITOR_PLUGIN_H diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 83275a2a7b..dbd2a7555d 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -397,6 +397,7 @@ static const char *gdscript_function_renames[][2] = { { "http_unescape", "uri_decode" }, // String { "import_scene_from_other_importer", "_import_scene" }, //EditorSceneFormatImporter { "instance_set_surface_material", "instance_set_surface_override_material" }, // RenderingServer + { "interpolate", "sample" }, // Curve, Curve2D, Curve3D, Gradient { "intersect_polygons_2d", "intersect_polygons" }, // Geometry2D { "intersect_polyline_with_polygon_2d", "intersect_polyline_with_polygon" }, // Geometry2D { "is_a_parent_of", "is_ancestor_of" }, // Node @@ -526,7 +527,6 @@ static const char *gdscript_function_renames[][2] = { { "set_tangent", "surface_set_tangent" }, // ImmediateGeometry broke SurfaceTool { "set_text_align", "set_text_alignment" }, // Button { "set_timer_process_mode", "set_timer_process_callback" }, // Timer - { "set_tonemap_auto_exposure", "set_tonemap_auto_exposure_enabled" }, // Environment { "set_translation", "set_position" }, // Node3D - this broke GLTFNode which is used rarely { "set_unit_offset", "set_progress_ratio" }, // PathFollow2D, PathFollow3D { "set_uv2", "surface_set_uv2" }, // ImmediateMesh broke Surffacetool @@ -1246,12 +1246,12 @@ static const char *project_settings_renames[][2] = { { "rendering/quality/shading/force_lambert_over_burley.mobile", "rendering/shading/overrides/force_lambert_over_burley.mobile" }, { "rendering/quality/shading/force_vertex_shading", "rendering/shading/overrides/force_vertex_shading" }, { "rendering/quality/shading/force_vertex_shading.mobile", "rendering/shading/overrides/force_vertex_shading.mobile" }, - { "rendering/quality/shadow_atlas/quadrant_0_subdiv", "rendering/shadows/shadow_atlas/quadrant_0_subdiv" }, - { "rendering/quality/shadow_atlas/quadrant_1_subdiv", "rendering/shadows/shadow_atlas/quadrant_1_subdiv" }, - { "rendering/quality/shadow_atlas/quadrant_2_subdiv", "rendering/shadows/shadow_atlas/quadrant_2_subdiv" }, - { "rendering/quality/shadow_atlas/quadrant_3_subdiv", "rendering/shadows/shadow_atlas/quadrant_3_subdiv" }, - { "rendering/quality/shadow_atlas/size", "rendering/shadows/shadow_atlas/size" }, - { "rendering/quality/shadow_atlas/size.mobile", "rendering/shadows/shadow_atlas/size.mobile" }, + { "rendering/quality/shadow_atlas/quadrant_0_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_0_subdiv" }, + { "rendering/quality/shadow_atlas/quadrant_1_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_1_subdiv" }, + { "rendering/quality/shadow_atlas/quadrant_2_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_2_subdiv" }, + { "rendering/quality/shadow_atlas/quadrant_3_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_3_subdiv" }, + { "rendering/quality/shadow_atlas/size", "rendering/lights_and_shadows/shadow_atlas/size" }, + { "rendering/quality/shadow_atlas/size.mobile", "rendering/lights_and_shadows/shadow_atlas/size.mobile" }, { "rendering/vram_compression/import_bptc", "rendering/textures/vram_compression/import_bptc" }, { "rendering/vram_compression/import_etc", "rendering/textures/vram_compression/import_etc" }, { "rendering/vram_compression/import_etc2", "rendering/textures/vram_compression/import_etc2" }, diff --git a/methods.py b/methods.py index 3a00aa12ca..649072d098 100644 --- a/methods.py +++ b/methods.py @@ -412,16 +412,17 @@ def use_windows_spawn_fix(self, platform=None): startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - proc = subprocess.Popen( - cmdline, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - startupinfo=startupinfo, - shell=False, - env=env, - text=True, - ) + popen_args = { + "stdin": subprocess.PIPE, + "stdout": subprocess.PIPE, + "stderr": subprocess.PIPE, + "startupinfo": startupinfo, + "shell": False, + "env": env, + } + if sys.version_info >= (3, 7, 0): + popen_args["text"] = True + proc = subprocess.Popen(cmdline, **popen_args) _, err = proc.communicate() rv = proc.wait() if rv: diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 6294790132..3932c2377f 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -1852,13 +1852,13 @@ CSGBrush *CSGPolygon3D::_build_brush() { base_xform = path->get_global_transform(); } - Vector3 current_point = curve->interpolate_baked(0); - Vector3 next_point = curve->interpolate_baked(extrusion_step); + Vector3 current_point = curve->sample_baked(0); + Vector3 next_point = curve->sample_baked(extrusion_step); Vector3 current_up = Vector3(0, 1, 0); Vector3 direction = next_point - current_point; if (path_joined) { - Vector3 last_point = curve->interpolate_baked(curve->get_baked_length()); + Vector3 last_point = curve->sample_baked(curve->get_baked_length()); direction = next_point - last_point; } @@ -1869,7 +1869,7 @@ CSGBrush *CSGPolygon3D::_build_brush() { case PATH_ROTATION_PATH: break; case PATH_ROTATION_PATH_FOLLOW: - current_up = curve->interpolate_baked_up_vector(0); + current_up = curve->sample_baked_up_vector(0); break; } @@ -1931,9 +1931,9 @@ CSGBrush *CSGPolygon3D::_build_brush() { } } - Vector3 previous_point = curve->interpolate_baked(previous_offset); - Vector3 current_point = curve->interpolate_baked(current_offset); - Vector3 next_point = curve->interpolate_baked(next_offset); + Vector3 previous_point = curve->sample_baked(previous_offset); + Vector3 current_point = curve->sample_baked(current_offset); + Vector3 next_point = curve->sample_baked(next_offset); Vector3 current_up = Vector3(0, 1, 0); Vector3 direction = next_point - previous_point; Vector3 current_dir = (current_point - previous_point).normalized(); @@ -1956,7 +1956,7 @@ CSGBrush *CSGPolygon3D::_build_brush() { case PATH_ROTATION_PATH: break; case PATH_ROTATION_PATH_FOLLOW: - current_up = curve->interpolate_baked_up_vector(current_offset); + current_up = curve->sample_baked_up_vector(current_offset); break; } diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index 02922086f0..afb59b486c 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -318,8 +318,18 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l Color col = Color(); if (global_functions.has(word)) { // "assert" and "preload" are reserved, so highlight even if not followed by a bracket. - if (word == "assert" || word == "preload" || str[to] == '(') { + if (word == "assert" || word == "preload") { col = global_function_color; + } else { + // For other global functions, check if followed by bracket. + int k = to; + while (k < line_length && is_whitespace(str[k])) { + k++; + } + + if (str[k] == '(') { + col = global_function_color; + } } } else if (keywords.has(word)) { col = keywords[word]; @@ -668,14 +678,14 @@ void GDScriptSyntaxHighlighter::_update_cache() { if (godot_2_theme || EditorSettings::get_singleton()->is_dark_theme()) { function_definition_color = Color(0.4, 0.9, 1.0); - global_function_color = Color(0.6, 0.6, 0.9); + global_function_color = Color(0.64, 0.64, 0.96); node_path_color = Color(0.72, 0.77, 0.49); node_ref_color = Color(0.39, 0.76, 0.35); annotation_color = Color(1.0, 0.7, 0.45); string_name_color = Color(1.0, 0.76, 0.65); } else { function_definition_color = Color(0, 0.6, 0.6); - global_function_color = Color(0.4, 0.2, 0.8); + global_function_color = Color(0.36, 0.18, 0.72); node_path_color = Color(0.18, 0.55, 0); node_ref_color = Color(0.0, 0.5, 0); annotation_color = Color(0.8, 0.37, 0); diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index c8c876369f..e37ac1dc3b 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2726,6 +2726,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod result.builtin_type = Variant::INT; result.native_type = base.native_type; result.enum_type = base.enum_type; + result.enum_values = base.enum_values; p_identifier->set_datatype(result); return; } else { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index c18412bc63..4c908166df 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -2031,8 +2031,13 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, r_type.type.kind = GDScriptParser::DataType::NATIVE; r_type.type.native_type = p_identifier; r_type.type.is_constant = true; - r_type.type.is_meta_type = !Engine::get_singleton()->has_singleton(p_identifier); - r_type.value = Variant(); + if (Engine::get_singleton()->has_singleton(p_identifier)) { + r_type.type.is_meta_type = false; + r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier); + } else { + r_type.type.is_meta_type = true; + r_type.value = Variant(); + } } return false; diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index fd213e7b37..16461b0a6c 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -499,9 +499,9 @@ Error GDScriptWorkspace::parse_local_script(const String &p_path) { } String GDScriptWorkspace::get_file_path(const String &p_uri) const { - String path = p_uri; - path = path.uri_decode(); - path = path.replacen(root_uri + "/", "res://"); + String path = p_uri.uri_decode(); + String base_uri = root_uri.uri_decode(); + path = path.replacen(base_uri + "/", "res://"); return path; } diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_inline.out b/modules/gdscript/tests/scripts/analyzer/features/property_inline.out index 5482592e90..63e59398ae 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/property_inline.out +++ b/modules/gdscript/tests/scripts/analyzer/features/property_inline.out @@ -1,5 +1,5 @@ GDTEST_OK -null +<null> 0 1 2 diff --git a/modules/gdscript/tests/scripts/parser/features/function_many_parameters.out b/modules/gdscript/tests/scripts/parser/features/function_many_parameters.out index 3a979227d4..80df7a3d4c 100644 --- a/modules/gdscript/tests/scripts/parser/features/function_many_parameters.out +++ b/modules/gdscript/tests/scripts/parser/features/function_many_parameters.out @@ -1,2 +1,2 @@ GDTEST_OK -123456789101112131415161718192212223242526272829303132333435363738394041424344454647falsetruenull +123456789101112131415161718192212223242526272829303132333435363738394041424344454647falsetrue<null> diff --git a/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out index abba38e87c..867f45f0ac 100644 --- a/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out +++ b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out @@ -1,4 +1,4 @@ GDTEST_OK -null +<null> true false diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 2dcf644a06..5b039e65c0 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -670,7 +670,7 @@ LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShade return BAKE_OK; } -LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata) { +LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata, float p_exposure_normalization) { if (p_step_function) { p_step_function(0.0, RTR("Begin Bake"), p_bake_userdata, true); } @@ -1165,6 +1165,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0); rd->compute_list_bind_uniform_set(compute_list, light_uniform_set, 1); + push_constant.environment_xform[11] = p_exposure_normalization; + for (int i = 0; i < atlas_slices; i++) { push_constant.atlas_slice = i; rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant)); @@ -1172,6 +1174,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d //no barrier, let them run all together } rd->compute_list_end(); //done + + push_constant.environment_xform[11] = 0.0; } #ifdef DEBUG_TEXTURES diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h index bf6b4399ca..b33a475dbc 100644 --- a/modules/lightmapper_rd/lightmapper_rd.h +++ b/modules/lightmapper_rd/lightmapper_rd.h @@ -241,7 +241,7 @@ public: virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) override; virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) override; virtual void add_probe(const Vector3 &p_position) override; - virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr) override; + virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr, float p_exposure_normalization = 1.0) override; int get_bake_texture_count() const override; Ref<Image> get_bake_texture(int p_index) const override; diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl index efa6cd50b4..c2557dfed3 100644 --- a/modules/lightmapper_rd/lm_compute.glsl +++ b/modules/lightmapper_rd/lm_compute.glsl @@ -434,6 +434,7 @@ void main() { imageStore(primary_dynamic, ivec3(atlas_pos, params.atlas_slice), vec4(dynamic_light, 1.0)); dynamic_light += static_light * albedo; //send for bounces + dynamic_light *= params.env_transform[2][3]; // exposure_normalization imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), vec4(dynamic_light, 1.0)); #ifdef USE_SH_LIGHTMAPS @@ -444,6 +445,7 @@ void main() { imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice * 4 + 3), sh_accum[3]); #else + static_light *= params.env_transform[2][3]; // exposure_normalization imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), vec4(static_light, 1.0)); #endif diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index 44806e8ecf..fa79c2efbc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -130,14 +130,14 @@ namespace Godot.NativeInterop [FieldOffset(0)] public AABB* _aabb; [FieldOffset(0)] public Basis* _basis; [FieldOffset(0)] public Transform3D* _transform3D; - [FieldOffset(0)] public Vector4* _vector4; - [FieldOffset(0)] public Vector4i* _vector4i; [FieldOffset(0)] public Projection* _projection; [FieldOffset(0)] private godot_variant_data_mem _mem; // The following fields are not in the C++ union, but this is how they're stored in _mem. [FieldOffset(0)] public godot_string_name _m_string_name; [FieldOffset(0)] public godot_string _m_string; + [FieldOffset(0)] public Vector4 _m_vector4; + [FieldOffset(0)] public Vector4i _m_vector4i; [FieldOffset(0)] public Vector3 _m_vector3; [FieldOffset(0)] public Vector3i _m_vector3i; [FieldOffset(0)] public Vector2 _m_vector2; @@ -232,18 +232,6 @@ namespace Godot.NativeInterop get => _data._transform3D; } - public readonly unsafe Vector4* Vector4 - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _data._vector4; - } - - public readonly unsafe Vector4i* Vector4i - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _data._vector4i; - } - public readonly unsafe Projection* Projection { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -266,6 +254,22 @@ namespace Godot.NativeInterop set => _data._m_string = value; } + public Vector4 Vector4 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => _data._m_vector4; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => _data._m_vector4 = value; + } + + public Vector4i Vector4i + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => _data._m_vector4i; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => _data._m_vector4i = value; + } + public Vector3 Vector3 { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -406,6 +410,8 @@ namespace Godot.NativeInterop case Variant.Type.Rect2i: case Variant.Type.Vector3: case Variant.Type.Vector3i: + case Variant.Type.Vector4: + case Variant.Type.Vector4i: case Variant.Type.Plane: case Variant.Type.Quaternion: case Variant.Type.Color: diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index eee19aea46..140fc167ba 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -613,9 +613,9 @@ namespace Godot.NativeInterop case Variant.Type.Transform2d: return *p_var.Transform2D; case Variant.Type.Vector4: - return *p_var.Vector4; + return p_var.Vector4; case Variant.Type.Vector4i: - return *p_var.Vector4i; + return p_var.Vector4i; case Variant.Type.Plane: return p_var.Plane; case Variant.Type.Quaternion: diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index e84bba1179..bd00611383 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -176,10 +176,6 @@ namespace Godot.NativeInterop public static partial void godotsharp_variant_new_transform2d(out godot_variant r_dest, in Transform2D p_t2d); - public static partial void godotsharp_variant_new_vector4(out godot_variant r_dest, in Vector4 p_vec4); - - public static partial void godotsharp_variant_new_vector4i(out godot_variant r_dest, in Vector4i p_vec4i); - public static partial void godotsharp_variant_new_basis(out godot_variant r_dest, in Basis p_basis); public static partial void godotsharp_variant_new_transform3d(out godot_variant r_dest, in Transform3D p_trans); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs index 26fffc079c..9f0b55431b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.extended.cs @@ -28,6 +28,10 @@ namespace Godot.NativeInterop return new godot_variant() { Vector3 = src.Vector3, Type = Variant.Type.Vector3 }; case Variant.Type.Vector3i: return new godot_variant() { Vector3i = src.Vector3i, Type = Variant.Type.Vector3i }; + case Variant.Type.Vector4: + return new godot_variant() { Vector4 = src.Vector4, Type = Variant.Type.Vector4 }; + case Variant.Type.Vector4i: + return new godot_variant() { Vector4i = src.Vector4i, Type = Variant.Type.Vector4i }; case Variant.Type.Plane: return new godot_variant() { Plane = src.Plane, Type = Variant.Type.Plane }; case Variant.Type.Quaternion: diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs index a13fb936e8..9cde62c7c5 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantConversionCallbacks.cs @@ -74,6 +74,12 @@ internal static unsafe class VariantConversionCallbacks static godot_variant FromTransform3D(in Transform3D @transform3d) => VariantUtils.CreateFromTransform3D(@transform3d); + static godot_variant FromVector4(in Vector4 @vector4) => + VariantUtils.CreateFromVector4(@vector4); + + static godot_variant FromVector4I(in Vector4i vector4I) => + VariantUtils.CreateFromVector4i(vector4I); + static godot_variant FromAabb(in AABB @aabb) => VariantUtils.CreateFromAABB(@aabb); @@ -283,6 +289,18 @@ internal static unsafe class VariantConversionCallbacks &FromTransform3D; } + if (typeOfT == typeof(Vector4)) + { + return (delegate*<in T, godot_variant>)(delegate*<in Vector4, godot_variant>) + &FromVector4; + } + + if (typeOfT == typeof(Vector4i)) + { + return (delegate*<in T, godot_variant>)(delegate*<in Vector4i, godot_variant>) + &FromVector4I; + } + if (typeOfT == typeof(AABB)) { return (delegate*<in T, godot_variant>)(delegate*<in AABB, godot_variant>) @@ -556,6 +574,12 @@ internal static unsafe class VariantConversionCallbacks static Transform3D ToTransform3D(in godot_variant variant) => VariantUtils.ConvertToTransform3D(variant); + static Vector4 ToVector4(in godot_variant variant) => + VariantUtils.ConvertToVector4(variant); + + static Vector4i ToVector4I(in godot_variant variant) => + VariantUtils.ConvertToVector4i(variant); + static AABB ToAabb(in godot_variant variant) => VariantUtils.ConvertToAABB(variant); @@ -768,6 +792,18 @@ internal static unsafe class VariantConversionCallbacks &ToTransform3D; } + if (typeOfT == typeof(Vector4)) + { + return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector4>) + &ToVector4; + } + + if (typeOfT == typeof(Vector4i)) + { + return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, Vector4i>) + &ToVector4I; + } + if (typeOfT == typeof(AABB)) { return (delegate*<in godot_variant, T>)(delegate*<in godot_variant, AABB>) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs index 491ccf904e..57f9ec7d95 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs @@ -37,6 +37,12 @@ namespace Godot.NativeInterop public static godot_variant CreateFromVector3i(Vector3i from) => new() { Type = Variant.Type.Vector3i, Vector3i = from }; + public static godot_variant CreateFromVector4(Vector4 from) + => new() { Type = Variant.Type.Vector4, Vector4 = from }; + + public static godot_variant CreateFromVector4i(Vector4i from) + => new() { Type = Variant.Type.Vector4i, Vector4i = from }; + public static godot_variant CreateFromRect2(Rect2 from) => new() { Type = Variant.Type.Rect2, Rect2 = from }; @@ -58,18 +64,6 @@ namespace Godot.NativeInterop return ret; } - public static godot_variant CreateFromVector4(Vector4 from) - { - NativeFuncs.godotsharp_variant_new_vector4(out godot_variant ret, from); - return ret; - } - - public static godot_variant CreateFromVector4i(Vector4i from) - { - NativeFuncs.godotsharp_variant_new_vector4i(out godot_variant ret, from); - return ret; - } - public static godot_variant CreateFromBasis(Basis from) { NativeFuncs.godotsharp_variant_new_basis(out godot_variant ret, from); @@ -386,12 +380,12 @@ namespace Godot.NativeInterop public static unsafe Vector4 ConvertToVector4(in godot_variant p_var) => p_var.Type == Variant.Type.Vector4 ? - *p_var.Vector4 : + p_var.Vector4 : NativeFuncs.godotsharp_variant_as_vector4(p_var); public static unsafe Vector4i ConvertToVector4i(in godot_variant p_var) => p_var.Type == Variant.Type.Vector4i ? - *p_var.Vector4i : + p_var.Vector4i : NativeFuncs.godotsharp_variant_as_vector4i(p_var); public static unsafe Basis ConvertToBasis(in godot_variant p_var) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs index 85ef258922..1f37694995 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Variant.cs @@ -65,6 +65,8 @@ public partial struct Variant : IDisposable case Type.Rect2i: case Type.Vector3: case Type.Vector3i: + case Type.Vector4: + case Type.Vector4i: case Type.Plane: case Type.Quaternion: case Type.Color: diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index 529bda4c38..276701cdaa 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -548,14 +548,6 @@ void godotsharp_variant_new_transform2d(godot_variant *r_dest, const Transform2D memnew_placement(r_dest, Variant(*p_t2d)); } -void godotsharp_variant_new_vector4(godot_variant *r_dest, const Vector4 *p_vec4) { - memnew_placement(r_dest, Variant(*p_vec4)); -} - -void godotsharp_variant_new_vector4i(godot_variant *r_dest, const Vector4i *p_vec4i) { - memnew_placement(r_dest, Variant(*p_vec4i)); -} - void godotsharp_variant_new_basis(godot_variant *r_dest, const Basis *p_basis) { memnew_placement(r_dest, Variant(*p_basis)); } @@ -1319,7 +1311,7 @@ void godotsharp_object_to_string(Object *p_ptr, godot_string *r_str) { #endif // Can't call 'Object::to_string()' here, as that can end up calling 'ToString' again resulting in an endless circular loop. memnew_placement(r_str, - String("[" + p_ptr->get_class() + ":" + itos(p_ptr->get_instance_id()) + "]")); + String("<" + p_ptr->get_class() + "#" + itos(p_ptr->get_instance_id()) + ">")); } #ifdef __cplusplus @@ -1377,8 +1369,6 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_variant_new_node_path, (void *)godotsharp_variant_new_object, (void *)godotsharp_variant_new_transform2d, - (void *)godotsharp_variant_new_vector4, - (void *)godotsharp_variant_new_vector4i, (void *)godotsharp_variant_new_basis, (void *)godotsharp_variant_new_transform3d, (void *)godotsharp_variant_new_projection, diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 8f2abda00f..abef48e2a1 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -2622,11 +2622,11 @@ Vector2 TextServerAdvanced::font_get_glyph_advance(const RID &p_font_rid, int64_ } if (fd->msdf) { - return (gl[p_glyph].advance + ea) * (double)p_size / (double)fd->msdf_source_size; + return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size; } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - return (gl[p_glyph].advance + ea).round(); + return (gl[p_glyph | mod].advance + ea).round(); } else { - return gl[p_glyph].advance + ea; + return gl[p_glyph | mod].advance + ea; } } @@ -2669,9 +2669,9 @@ Vector2 TextServerAdvanced::font_get_glyph_offset(const RID &p_font_rid, const V const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; if (fd->msdf) { - return gl[p_glyph].rect.position * (double)p_size.x / (double)fd->msdf_source_size; + return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size; } else { - return gl[p_glyph].rect.position; + return gl[p_glyph | mod].rect.position; } } @@ -2714,9 +2714,9 @@ Vector2 TextServerAdvanced::font_get_glyph_size(const RID &p_font_rid, const Vec const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; if (fd->msdf) { - return gl[p_glyph].rect.size * (double)p_size.x / (double)fd->msdf_source_size; + return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size; } else { - return gl[p_glyph].rect.size; + return gl[p_glyph | mod].rect.size; } } @@ -2757,7 +2757,7 @@ Rect2 TextServerAdvanced::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph].uv_rect; + return gl[p_glyph | mod].uv_rect; } void TextServerAdvanced::font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) { @@ -2797,7 +2797,7 @@ int64_t TextServerAdvanced::font_get_glyph_texture_idx(const RID &p_font_rid, co } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph].texture_idx; + return gl[p_glyph | mod].texture_idx; } void TextServerAdvanced::font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) { @@ -2837,12 +2837,12 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), RID()); + ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), RID()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) { - FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx]; + if (gl[p_glyph | mod].texture_idx != -1) { + if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { + FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; Ref<Image> img; img.instantiate(); img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); @@ -2856,7 +2856,7 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_rid(); + return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_rid(); } } @@ -2885,12 +2885,12 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), Size2()); + ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), Size2()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) { - FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx]; + if (gl[p_glyph | mod].texture_idx != -1) { + if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { + FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; Ref<Image> img; img.instantiate(); img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); @@ -2904,7 +2904,7 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_size(); + return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_size(); } } diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 8e4a4ff424..359bb056a8 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -1697,11 +1697,11 @@ Vector2 TextServerFallback::font_get_glyph_advance(const RID &p_font_rid, int64_ } if (fd->msdf) { - return (gl[p_glyph].advance + ea) * (double)p_size / (double)fd->msdf_source_size; + return (gl[p_glyph | mod].advance + ea) * (double)p_size / (double)fd->msdf_source_size; } else if ((fd->subpixel_positioning == SUBPIXEL_POSITIONING_DISABLED) || (fd->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && size.x > SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE)) { - return (gl[p_glyph].advance + ea).round(); + return (gl[p_glyph | mod].advance + ea).round(); } else { - return gl[p_glyph].advance + ea; + return gl[p_glyph | mod].advance + ea; } } @@ -1744,9 +1744,9 @@ Vector2 TextServerFallback::font_get_glyph_offset(const RID &p_font_rid, const V const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; if (fd->msdf) { - return gl[p_glyph].rect.position * (double)p_size.x / (double)fd->msdf_source_size; + return gl[p_glyph | mod].rect.position * (double)p_size.x / (double)fd->msdf_source_size; } else { - return gl[p_glyph].rect.position; + return gl[p_glyph | mod].rect.position; } } @@ -1789,9 +1789,9 @@ Vector2 TextServerFallback::font_get_glyph_size(const RID &p_font_rid, const Vec const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; if (fd->msdf) { - return gl[p_glyph].rect.size * (double)p_size.x / (double)fd->msdf_source_size; + return gl[p_glyph | mod].rect.size * (double)p_size.x / (double)fd->msdf_source_size; } else { - return gl[p_glyph].rect.size; + return gl[p_glyph | mod].rect.size; } } @@ -1832,7 +1832,7 @@ Rect2 TextServerFallback::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph].uv_rect; + return gl[p_glyph | mod].uv_rect; } void TextServerFallback::font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) { @@ -1872,7 +1872,7 @@ int64_t TextServerFallback::font_get_glyph_texture_idx(const RID &p_font_rid, co } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - return gl[p_glyph].texture_idx; + return gl[p_glyph | mod].texture_idx; } void TextServerFallback::font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) { @@ -1912,12 +1912,12 @@ RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), RID()); + ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), RID()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) { - FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx]; + if (gl[p_glyph | mod].texture_idx != -1) { + if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { + FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; Ref<Image> img; img.instantiate(); img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); @@ -1931,7 +1931,7 @@ RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_rid(); + return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_rid(); } } @@ -1960,12 +1960,12 @@ Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, con } const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map; - ERR_FAIL_COND_V(gl[p_glyph].texture_idx < -1 || gl[p_glyph].texture_idx >= fd->cache[size]->textures.size(), Size2()); + ERR_FAIL_COND_V(gl[p_glyph | mod].texture_idx < -1 || gl[p_glyph | mod].texture_idx >= fd->cache[size]->textures.size(), Size2()); if (RenderingServer::get_singleton() != nullptr) { - if (gl[p_glyph].texture_idx != -1) { - if (fd->cache[size]->textures[gl[p_glyph].texture_idx].dirty) { - FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph].texture_idx]; + if (gl[p_glyph | mod].texture_idx != -1) { + if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { + FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; Ref<Image> img; img.instantiate(); img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); @@ -1979,7 +1979,7 @@ Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, con } tex.dirty = false; } - return fd->cache[size]->textures[gl[p_glyph].texture_idx].texture->get_size(); + return fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].texture->get_size(); } } diff --git a/platform/android/dir_access_jandroid.cpp b/platform/android/dir_access_jandroid.cpp index 373c6e37f1..4f1ac16975 100644 --- a/platform/android/dir_access_jandroid.cpp +++ b/platform/android/dir_access_jandroid.cpp @@ -135,6 +135,13 @@ String DirAccessJAndroid::get_drive(int p_drive) { } } +String DirAccessJAndroid::_get_root_string() const { + if (get_access_type() == ACCESS_FILESYSTEM) { + return "/"; + } + return DirAccessUnix::_get_root_string(); +} + String DirAccessJAndroid::get_current_dir(bool p_include_drive) const { String base = _get_root_path(); String bd = current_dir; @@ -142,10 +149,13 @@ String DirAccessJAndroid::get_current_dir(bool p_include_drive) const { bd = current_dir.replace_first(base, ""); } - if (bd.begins_with("/")) { - return _get_root_string() + bd.substr(1, bd.length()); + String root_string = _get_root_string(); + if (bd.begins_with(root_string)) { + return bd; + } else if (bd.begins_with("/")) { + return root_string + bd.substr(1, bd.length()); } else { - return _get_root_string() + bd; + return root_string + bd; } } diff --git a/platform/android/dir_access_jandroid.h b/platform/android/dir_access_jandroid.h index 5b7b4a9c4d..5c4f1852a9 100644 --- a/platform/android/dir_access_jandroid.h +++ b/platform/android/dir_access_jandroid.h @@ -91,6 +91,9 @@ public: DirAccessJAndroid(); ~DirAccessJAndroid(); +protected: + String _get_root_string() const override; + private: int id = 0; diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index 017fc3e3a2..0d619904bc 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -1823,6 +1823,47 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a return retval; } +bool DisplayServerX11::_window_minimize_check(WindowID p_window) const { + const WindowData &wd = windows[p_window]; + + // Using ICCCM -- Inter-Client Communication Conventions Manual + Atom property = XInternAtom(x11_display, "WM_STATE", True); + if (property == None) { + return false; + } + + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = nullptr; + + int result = XGetWindowProperty( + x11_display, + wd.x11_window, + property, + 0, + 32, + False, + AnyPropertyType, + &type, + &format, + &len, + &remaining, + &data); + + if (result == Success && data) { + long *state = (long *)data; + if (state[0] == WM_IconicState) { + XFree(data); + return true; + } + XFree(data); + } + + return false; +} + bool DisplayServerX11::_window_fullscreen_check(WindowID p_window) const { ERR_FAIL_COND_V(!windows.has(p_window), false); const WindowData &wd = windows[p_window]; @@ -1869,11 +1910,15 @@ bool DisplayServerX11::_window_fullscreen_check(WindowID p_window) const { return retval; } -void DisplayServerX11::_validate_fullscreen_on_map(WindowID p_window) { +void DisplayServerX11::_validate_mode_on_map(WindowID p_window) { + // Check if we applied any window modes that didn't take effect while unmapped const WindowData &wd = windows[p_window]; if (wd.fullscreen && !_window_fullscreen_check(p_window)) { - // Unmapped fullscreen attempt didn't take effect, redo... _set_wm_fullscreen(p_window, true); + } else if (wd.maximized && !_window_maximize_check(p_window, "_NET_WM_STATE")) { + _set_wm_maximized(p_window, true); + } else if (wd.minimized && !_window_minimize_check(p_window)) { + _set_wm_minimized(p_window, true); } } @@ -1911,6 +1956,37 @@ void DisplayServerX11::_set_wm_maximized(WindowID p_window, bool p_enabled) { usleep(10000); } } + wd.maximized = p_enabled; +} + +void DisplayServerX11::_set_wm_minimized(WindowID p_window, bool p_enabled) { + WindowData &wd = windows[p_window]; + // Using ICCCM -- Inter-Client Communication Conventions Manual + XEvent xev; + Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_change; + xev.xclient.format = 32; + xev.xclient.data.l[0] = p_enabled ? WM_IconicState : WM_NormalState; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + xev.xclient.data.l[1] = wm_hidden; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + wd.minimized = p_enabled; } void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) { @@ -1992,32 +2068,7 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) { //do nothing } break; case WINDOW_MODE_MINIMIZED: { - //Un-Minimize - // Using ICCCM -- Inter-Client Communication Conventions Manual - XEvent xev; - Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = wd.x11_window; - xev.xclient.message_type = wm_change; - xev.xclient.format = 32; - xev.xclient.data.l[0] = WM_NormalState; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); - - Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); - Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = wd.x11_window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = _NET_WM_STATE_ADD; - xev.xclient.data.l[1] = wm_hidden; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + _set_wm_minimized(p_window, false); } break; case WINDOW_MODE_EXCLUSIVE_FULLSCREEN: case WINDOW_MODE_FULLSCREEN: { @@ -2046,31 +2097,7 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) { //do nothing } break; case WINDOW_MODE_MINIMIZED: { - // Using ICCCM -- Inter-Client Communication Conventions Manual - XEvent xev; - Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = wd.x11_window; - xev.xclient.message_type = wm_change; - xev.xclient.format = 32; - xev.xclient.data.l[0] = WM_IconicState; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); - - Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); - Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = wd.x11_window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = _NET_WM_STATE_ADD; - xev.xclient.data.l[1] = wm_hidden; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + _set_wm_minimized(p_window, true); } break; case WINDOW_MODE_EXCLUSIVE_FULLSCREEN: case WINDOW_MODE_FULLSCREEN: { @@ -2105,40 +2132,9 @@ DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) c return WINDOW_MODE_MAXIMIZED; } - { // Test minimized. - // Using ICCCM -- Inter-Client Communication Conventions Manual - Atom property = XInternAtom(x11_display, "WM_STATE", True); - if (property == None) { - return WINDOW_MODE_WINDOWED; - } - - Atom type; - int format; - unsigned long len; - unsigned long remaining; - unsigned char *data = nullptr; - - int result = XGetWindowProperty( - x11_display, - wd.x11_window, - property, - 0, - 32, - False, - AnyPropertyType, - &type, - &format, - &len, - &remaining, - &data); - - if (result == Success && data) { - long *state = (long *)data; - if (state[0] == WM_IconicState) { - XFree(data); - return WINDOW_MODE_MINIMIZED; - } - XFree(data); + { + if (_window_minimize_check(p_window)) { + return WINDOW_MODE_MINIMIZED; } } @@ -3658,9 +3654,6 @@ void DisplayServerX11::process_events() { const WindowData &wd = windows[window_id]; - // Have we failed to set fullscreen while the window was unmapped? - _validate_fullscreen_on_map(window_id); - XWindowAttributes xwa; XSync(x11_display, False); XGetWindowAttributes(x11_display, wd.x11_window, &xwa); @@ -3671,6 +3664,9 @@ void DisplayServerX11::process_events() { if ((xwa.map_state == IsViewable) && !wd.no_focus && !wd.is_popup) { XSetInputFocus(x11_display, wd.x11_window, RevertToPointerRoot, CurrentTime); } + + // Have we failed to set fullscreen while the window was unmapped? + _validate_mode_on_map(window_id); } break; case Expose: { @@ -3690,8 +3686,7 @@ void DisplayServerX11::process_events() { case VisibilityNotify: { DEBUG_LOG_X11("[%u] VisibilityNotify window=%lu (%u), state=%u \n", frame, event.xvisibility.window, window_id, event.xvisibility.state); - XVisibilityEvent *visibility = (XVisibilityEvent *)&event; - windows[window_id].minimized = (visibility->state == VisibilityFullyObscured); + windows[window_id].minimized = _window_minimize_check(window_id); } break; case LeaveNotify: { @@ -5003,7 +4998,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode _window_changed(&xevent); } else if (xevent.type == MapNotify) { // Have we failed to set fullscreen while the window was unmapped? - _validate_fullscreen_on_map(main_window); + _validate_mode_on_map(main_window); } } diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h index 6b9f57d78f..650598d243 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/display_server_x11.h @@ -152,6 +152,7 @@ class DisplayServerX11 : public DisplayServer { Vector2i last_position_before_fs; bool focused = true; bool minimized = false; + bool maximized = false; bool is_popup = false; Rect2i parent_safe_rect; @@ -268,10 +269,12 @@ class DisplayServerX11 : public DisplayServer { void _update_real_mouse_position(const WindowData &wd); bool _window_maximize_check(WindowID p_window, const char *p_atom_name) const; bool _window_fullscreen_check(WindowID p_window) const; - void _validate_fullscreen_on_map(WindowID p_window); + bool _window_minimize_check(WindowID p_window) const; + void _validate_mode_on_map(WindowID p_window); void _update_size_hints(WindowID p_window); void _set_wm_fullscreen(WindowID p_window, bool p_enabled); void _set_wm_maximized(WindowID p_window, bool p_enabled); + void _set_wm_minimized(WindowID p_window, bool p_enabled); void _update_context(WindowData &wd); diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index dfe2ce0ec3..58e357ab5f 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -719,17 +719,17 @@ void CPUParticles2D::_particles_process(double p_delta) { /*real_t tex_linear_velocity = 0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(0); + tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->sample(0); }*/ real_t tex_angle = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); + tex_angle = curve_parameters[PARAM_ANGLE]->sample(tv); } real_t tex_anim_offset = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_anim_offset = curve_parameters[PARAM_ANGLE]->interpolate(tv); + tex_anim_offset = curve_parameters[PARAM_ANGLE]->sample(tv); } p.seed = Math::rand(); @@ -825,51 +825,51 @@ void CPUParticles2D::_particles_process(double p_delta) { real_t tex_linear_velocity = 1.0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(tv); + tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->sample(tv); } real_t tex_orbit_velocity = 1.0; if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { - tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(tv); + tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->sample(tv); } real_t tex_angular_velocity = 1.0; if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { - tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(tv); + tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->sample(tv); } real_t tex_linear_accel = 1.0; if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) { - tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(tv); + tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->sample(tv); } real_t tex_tangential_accel = 1.0; if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { - tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(tv); + tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->sample(tv); } real_t tex_radial_accel = 1.0; if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) { - tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(tv); + tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->sample(tv); } real_t tex_damping = 1.0; if (curve_parameters[PARAM_DAMPING].is_valid()) { - tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(tv); + tex_damping = curve_parameters[PARAM_DAMPING]->sample(tv); } real_t tex_angle = 1.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); + tex_angle = curve_parameters[PARAM_ANGLE]->sample(tv); } real_t tex_anim_speed = 1.0; if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) { - tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(tv); + tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->sample(tv); } real_t tex_anim_offset = 1.0; if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) { - tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(tv); + tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->sample(tv); } Vector2 force = gravity; @@ -921,18 +921,18 @@ void CPUParticles2D::_particles_process(double p_delta) { Vector2 tex_scale = Vector2(1.0, 1.0); if (split_scale) { if (scale_curve_x.is_valid()) { - tex_scale.x = scale_curve_x->interpolate(tv); + tex_scale.x = scale_curve_x->sample(tv); } else { tex_scale.x = 1.0; } if (scale_curve_y.is_valid()) { - tex_scale.y = scale_curve_y->interpolate(tv); + tex_scale.y = scale_curve_y->sample(tv); } else { tex_scale.y = 1.0; } } else { if (curve_parameters[PARAM_SCALE].is_valid()) { - real_t tmp_scale = curve_parameters[PARAM_SCALE]->interpolate(tv); + real_t tmp_scale = curve_parameters[PARAM_SCALE]->sample(tv); tex_scale.x = tmp_scale; tex_scale.y = tmp_scale; } @@ -940,7 +940,7 @@ void CPUParticles2D::_particles_process(double p_delta) { real_t tex_hue_variation = 0.0; if (curve_parameters[PARAM_HUE_VARIATION].is_valid()) { - tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(tv); + tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->sample(tv); } real_t hue_rot_angle = (tex_hue_variation)*Math_TAU * Math::lerp(parameters_min[PARAM_HUE_VARIATION], parameters_max[PARAM_HUE_VARIATION], p.hue_rot_rand); diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index 25eb9b9851..2bbe88b0e0 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -137,14 +137,14 @@ void LineBuilder::build() { // The line's outer length will be a little higher due to begin and end caps if (begin_cap_mode == Line2D::LINE_CAP_BOX || begin_cap_mode == Line2D::LINE_CAP_ROUND) { if (retrieve_curve) { - total_distance += width * curve->interpolate_baked(0.f) * 0.5f; + total_distance += width * curve->sample_baked(0.f) * 0.5f; } else { total_distance += width * 0.5f; } } if (end_cap_mode == Line2D::LINE_CAP_BOX || end_cap_mode == Line2D::LINE_CAP_ROUND) { if (retrieve_curve) { - total_distance += width * curve->interpolate_baked(1.f) * 0.5f; + total_distance += width * curve->sample_baked(1.f) * 0.5f; } else { total_distance += width * 0.5f; } @@ -160,7 +160,7 @@ void LineBuilder::build() { float uvx1 = 0.f; if (retrieve_curve) { - width_factor = curve->interpolate_baked(0.f); + width_factor = curve->sample_baked(0.f); } pos_up0 += u0 * hw * width_factor; @@ -219,7 +219,7 @@ void LineBuilder::build() { color1 = gradient->get_color_at_offset(current_distance1 / total_distance); } if (retrieve_curve) { - width_factor = curve->interpolate_baked(current_distance1 / total_distance); + width_factor = curve->sample_baked(current_distance1 / total_distance); } Vector2 inner_normal0, inner_normal1; @@ -383,7 +383,7 @@ void LineBuilder::build() { color1 = gradient->get_color(gradient->get_points_count() - 1); } if (retrieve_curve) { - width_factor = curve->interpolate_baked(1.f); + width_factor = curve->sample_baked(1.f); } Vector2 pos_up1 = pos1 + u0 * hw * width_factor; diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index b883d14a41..be17299f07 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -47,7 +47,7 @@ Rect2 Path2D::_edit_get_rect() const { for (int i = 0; i < curve->get_point_count(); i++) { for (int j = 0; j <= 8; j++) { real_t frac = j / 8.0; - Vector2 p = curve->interpolate(i, frac); + Vector2 p = curve->sample(i, frac); aabb.expand_to(p); } } @@ -70,7 +70,7 @@ bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toleranc for (int j = 1; j <= 8; j++) { real_t frac = j / 8.0; - s[1] = curve->interpolate(i, frac); + s[1] = curve->sample(i, frac); Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, s); if (p.distance_to(p_point) <= p_tolerance) { @@ -112,7 +112,7 @@ void Path2D::_notification(int p_what) { for (int i = 0; i < curve->get_point_count(); i++) { for (int j = 0; j < 8; j++) { real_t frac = j * (1.0 / 8.0); - Vector2 p = curve->interpolate(i, frac); + Vector2 p = curve->sample(i, frac); _cached_draw_pts.set(count++, p); } } @@ -175,7 +175,7 @@ void PathFollow2D::_update_transform() { if (path_length == 0) { return; } - Vector2 pos = c->interpolate_baked(progress, cubic); + Vector2 pos = c->sample_baked(progress, cubic); if (rotates) { real_t ahead = progress + lookahead; @@ -195,14 +195,14 @@ void PathFollow2D::_update_transform() { } } - Vector2 ahead_pos = c->interpolate_baked(ahead, cubic); + Vector2 ahead_pos = c->sample_baked(ahead, cubic); Vector2 tangent_to_curve; if (ahead_pos == pos) { // This will happen at the end of non-looping or non-closed paths. // We'll try a look behind instead, in order to get a meaningful angle. tangent_to_curve = - (pos - c->interpolate_baked(progress - lookahead, cubic)).normalized(); + (pos - c->sample_baked(progress - lookahead, cubic)).normalized(); } else { tangent_to_curve = (ahead_pos - pos).normalized(); } diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index b8b6296c45..304e56326d 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -31,6 +31,7 @@ #include "camera_3d.h" #include "collision_object_3d.h" +#include "core/core_string_names.h" #include "core/math/projection.h" #include "scene/main/viewport.h" @@ -71,6 +72,17 @@ void Camera3D::_validate_property(PropertyInfo &p_property) const { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } + + if (attributes.is_valid()) { + const CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); + if (physical_attributes) { + if (p_property.name == "near" || p_property.name == "far" || p_property.name == "fov" || p_property.name == "keep_aspect") { + p_property.usage = PROPERTY_USAGE_READ_ONLY | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR; + } + } + } + + Node3D::_validate_property(p_property); } void Camera3D::_update_camera() { @@ -400,18 +412,44 @@ Ref<Environment> Camera3D::get_environment() const { return environment; } -void Camera3D::set_effects(const Ref<CameraEffects> &p_effects) { - effects = p_effects; - if (effects.is_valid()) { - RS::get_singleton()->camera_set_camera_effects(camera, effects->get_rid()); +void Camera3D::set_attributes(const Ref<CameraAttributes> &p_attributes) { + if (attributes.is_valid()) { + CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); + if (physical_attributes) { + attributes->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Camera3D::_attributes_changed)); + } + } + + attributes = p_attributes; + + if (attributes.is_valid()) { + CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); + if (physical_attributes) { + attributes->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Camera3D::_attributes_changed)); + _attributes_changed(); + } + + RS::get_singleton()->camera_set_camera_attributes(camera, attributes->get_rid()); } else { - RS::get_singleton()->camera_set_camera_effects(camera, RID()); + RS::get_singleton()->camera_set_camera_attributes(camera, RID()); } - _update_camera_mode(); + + notify_property_list_changed(); } -Ref<CameraEffects> Camera3D::get_effects() const { - return effects; +Ref<CameraAttributes> Camera3D::get_attributes() const { + return attributes; +} + +void Camera3D::_attributes_changed() { + CameraAttributesPhysical *physical_attributes = Object::cast_to<CameraAttributesPhysical>(attributes.ptr()); + ERR_FAIL_COND(!physical_attributes); + + fov = physical_attributes->get_fov(); + near = physical_attributes->get_near(); + far = physical_attributes->get_far(); + keep_aspect = KEEP_HEIGHT; + _update_camera_mode(); } void Camera3D::set_keep_aspect_mode(KeepAspect p_aspect) { @@ -479,8 +517,8 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cull_mask"), &Camera3D::get_cull_mask); ClassDB::bind_method(D_METHOD("set_environment", "env"), &Camera3D::set_environment); ClassDB::bind_method(D_METHOD("get_environment"), &Camera3D::get_environment); - ClassDB::bind_method(D_METHOD("set_effects", "env"), &Camera3D::set_effects); - ClassDB::bind_method(D_METHOD("get_effects"), &Camera3D::get_effects); + ClassDB::bind_method(D_METHOD("set_attributes", "env"), &Camera3D::set_attributes); + ClassDB::bind_method(D_METHOD("get_attributes"), &Camera3D::get_attributes); ClassDB::bind_method(D_METHOD("set_keep_aspect_mode", "mode"), &Camera3D::set_keep_aspect_mode); ClassDB::bind_method(D_METHOD("get_keep_aspect_mode"), &Camera3D::get_keep_aspect_mode); ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera3D::set_doppler_tracking); @@ -498,7 +536,7 @@ void Camera3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "keep_aspect", PROPERTY_HINT_ENUM, "Keep Width,Keep Height"), "set_keep_aspect_mode", "get_keep_aspect_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_effects", "get_effects"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_attributes", "get_attributes"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "h_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_h_offset", "get_h_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_offset", PROPERTY_HINT_NONE, "suffix:m"), "set_v_offset", "get_v_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking"); diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index bba9b7d1e4..f150a23e27 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -33,7 +33,7 @@ #include "scene/3d/node_3d.h" #include "scene/3d/velocity_tracker_3d.h" -#include "scene/resources/camera_effects.h" +#include "scene/resources/camera_attributes.h" #include "scene/resources/environment.h" class Camera3D : public Node3D { @@ -64,11 +64,11 @@ private: ProjectionType mode = PROJECTION_PERSPECTIVE; - real_t fov = 0.0; + real_t fov = 75.0; real_t size = 1.0; Vector2 frustum_offset; - real_t near = 0.0; - real_t far = 0.0; + real_t near = 0.05; + real_t far = 4000.0; real_t v_offset = 0.0; real_t h_offset = 0.0; KeepAspect keep_aspect = KEEP_HEIGHT; @@ -81,7 +81,8 @@ private: uint32_t layers = 0xfffff; Ref<Environment> environment; - Ref<CameraEffects> effects; + Ref<CameraAttributes> attributes; + void _attributes_changed(); // void _camera_make_current(Node *p_camera); friend class Viewport; @@ -159,8 +160,8 @@ public: void set_environment(const Ref<Environment> &p_environment); Ref<Environment> get_environment() const; - void set_effects(const Ref<CameraEffects> &p_effects); - Ref<CameraEffects> get_effects() const; + void set_attributes(const Ref<CameraAttributes> &p_effects); + Ref<CameraAttributes> get_attributes() const; void set_keep_aspect_mode(KeepAspect p_aspect); KeepAspect get_keep_aspect_mode() const; diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 9dc61b35af..e14bb1aa94 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -739,17 +739,17 @@ void CPUParticles3D::_particles_process(double p_delta) { /*real_t tex_linear_velocity = 0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(0); + tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->sample(0); }*/ real_t tex_angle = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); + tex_angle = curve_parameters[PARAM_ANGLE]->sample(tv); } real_t tex_anim_offset = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_anim_offset = curve_parameters[PARAM_ANGLE]->interpolate(tv); + tex_anim_offset = curve_parameters[PARAM_ANGLE]->sample(tv); } p.seed = Math::rand(); @@ -907,53 +907,53 @@ void CPUParticles3D::_particles_process(double p_delta) { real_t tex_linear_velocity = 1.0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(tv); + tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->sample(tv); } real_t tex_orbit_velocity = 1.0; if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { - tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(tv); + tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->sample(tv); } } real_t tex_angular_velocity = 1.0; if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { - tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(tv); + tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->sample(tv); } real_t tex_linear_accel = 1.0; if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) { - tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(tv); + tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->sample(tv); } real_t tex_tangential_accel = 1.0; if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { - tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(tv); + tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->sample(tv); } real_t tex_radial_accel = 1.0; if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) { - tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(tv); + tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->sample(tv); } real_t tex_damping = 1.0; if (curve_parameters[PARAM_DAMPING].is_valid()) { - tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(tv); + tex_damping = curve_parameters[PARAM_DAMPING]->sample(tv); } real_t tex_angle = 1.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); + tex_angle = curve_parameters[PARAM_ANGLE]->sample(tv); } real_t tex_anim_speed = 1.0; if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) { - tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(tv); + tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->sample(tv); } real_t tex_anim_offset = 1.0; if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) { - tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(tv); + tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->sample(tv); } Vector3 force = gravity; @@ -1016,23 +1016,23 @@ void CPUParticles3D::_particles_process(double p_delta) { Vector3 tex_scale = Vector3(1.0, 1.0, 1.0); if (split_scale) { if (scale_curve_x.is_valid()) { - tex_scale.x = scale_curve_x->interpolate(tv); + tex_scale.x = scale_curve_x->sample(tv); } else { tex_scale.x = 1.0; } if (scale_curve_y.is_valid()) { - tex_scale.y = scale_curve_y->interpolate(tv); + tex_scale.y = scale_curve_y->sample(tv); } else { tex_scale.y = 1.0; } if (scale_curve_z.is_valid()) { - tex_scale.z = scale_curve_z->interpolate(tv); + tex_scale.z = scale_curve_z->sample(tv); } else { tex_scale.z = 1.0; } } else { if (curve_parameters[PARAM_SCALE].is_valid()) { - float tmp_scale = curve_parameters[PARAM_SCALE]->interpolate(tv); + float tmp_scale = curve_parameters[PARAM_SCALE]->sample(tv); tex_scale.x = tmp_scale; tex_scale.y = tmp_scale; tex_scale.z = tmp_scale; @@ -1041,7 +1041,7 @@ void CPUParticles3D::_particles_process(double p_delta) { real_t tex_hue_variation = 0.0; if (curve_parameters[PARAM_HUE_VARIATION].is_valid()) { - tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(tv); + tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->sample(tv); } real_t hue_rot_angle = (tex_hue_variation)*Math_TAU * Math::lerp(parameters_min[PARAM_HUE_VARIATION], parameters_max[PARAM_HUE_VARIATION], p.hue_rot_rand); diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp index 319129603e..cfee7028d4 100644 --- a/scene/3d/fog_volume.cpp +++ b/scene/3d/fog_volume.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "fog_volume.h" +#include "scene/resources/environment.h" /////////////////////////// diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index e4a7cf6ee5..d977874911 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -486,8 +486,9 @@ void Label3D::_shape() { case TextServer::AUTOWRAP_OFF: break; } - PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); + autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); float max_line_w = 0.0; for (int i = 0; i < line_breaks.size(); i = i + 2) { RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 8d96d13f0c..e51f06e083 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -28,6 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#include "core/config/project_settings.h" + #include "light_3d.h" void Light3D::set_param(Param p_param, real_t p_value) { @@ -122,7 +124,14 @@ uint32_t Light3D::get_cull_mask() const { void Light3D::set_color(const Color &p_color) { color = p_color; - RS::get_singleton()->light_set_color(light, p_color); + + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + Color combined = color.srgb_to_linear(); + combined *= correlated_color.srgb_to_linear(); + RS::get_singleton()->light_set_color(light, combined.linear_to_srgb()); + } else { + RS::get_singleton()->light_set_color(light, color); + } // The gizmo color depends on the light color, so update it. update_gizmos(); } @@ -181,6 +190,56 @@ void Light3D::owner_changed_notify() { _update_visibility(); } +// Temperature expressed in Kelvins. Valid range 1000 - 15000 +// First converts to CIE 1960 then to sRGB +// As explained in the Filament documentation: https://google.github.io/filament/Filament.md.html#lighting/directlighting/lightsparameterization +Color _color_from_temperature(float p_temperature) { + float T2 = p_temperature * p_temperature; + float u = (0.860117757f + 1.54118254e-4f * p_temperature + 1.28641212e-7f * T2) / + (1.0f + 8.42420235e-4f * p_temperature + 7.08145163e-7f * T2); + float v = (0.317398726f + 4.22806245e-5f * p_temperature + 4.20481691e-8f * T2) / + (1.0f - 2.89741816e-5f * p_temperature + 1.61456053e-7f * T2); + + // Convert to xyY space. + float d = 1.0f / (2.0f * u - 8.0f * v + 4.0f); + float x = 3.0f * u * d; + float y = 2.0f * v * d; + + // Convert to XYZ space + const float a = 1.0 / MAX(y, 1e-5f); + Vector3 xyz = Vector3(x * a, 1.0, (1.0f - x - y) * a); + + // Convert from XYZ to sRGB(linear) + Vector3 linear = Vector3(3.2404542f * xyz.x - 1.5371385f * xyz.y - 0.4985314f * xyz.z, + -0.9692660f * xyz.x + 1.8760108f * xyz.y + 0.0415560f * xyz.z, + 0.0556434f * xyz.x - 0.2040259f * xyz.y + 1.0572252f * xyz.z); + linear /= MAX(1e-5f, linear[linear.max_axis_index()]); + // Normalize, clamp, and convert to sRGB. + return Color(linear.x, linear.y, linear.z).clamp().linear_to_srgb(); +} + +void Light3D::set_temperature(const float p_temperature) { + temperature = p_temperature; + if (!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + return; + } + correlated_color = _color_from_temperature(temperature); + + Color combined = color.srgb_to_linear() * correlated_color.srgb_to_linear(); + + RS::get_singleton()->light_set_color(light, combined.linear_to_srgb()); + // The gizmo color depends on the light color, so update it. + update_gizmos(); +} + +Color Light3D::get_correlated_color() const { + return correlated_color; +} + +float Light3D::get_temperature() const { + return temperature; +} + void Light3D::_update_visibility() { if (!is_inside_tree()) { return; @@ -224,12 +283,18 @@ bool Light3D::is_editor_only() const { } void Light3D::_validate_property(PropertyInfo &p_property) const { - if (!shadow && (p_property.name == "shadow_bias" || p_property.name == "shadow_normal_bias" || p_property.name == "shadow_reverse_cull_face" || p_property.name == "shadow_transmittance_bias" || p_property.name == "shadow_fog_fade" || p_property.name == "shadow_opacity" || p_property.name == "shadow_blur" || p_property.name == "distance_fade_shadow")) { + if (!shadow && (p_property.name == "shadow_bias" || p_property.name == "shadow_normal_bias" || p_property.name == "shadow_reverse_cull_face" || p_property.name == "shadow_transmittance_bias" || p_property.name == "shadow_opacity" || p_property.name == "shadow_blur" || p_property.name == "distance_fade_shadow")) { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (get_light_type() != RS::LIGHT_DIRECTIONAL && p_property.name == "light_angular_distance") { - // Angular distance is only used in DirectionalLight3D. + if (get_light_type() != RS::LIGHT_DIRECTIONAL && (p_property.name == "light_angular_distance" || p_property.name == "light_intensity_lux")) { + // Angular distance and Light Intensity Lux are only used in DirectionalLight3D. + p_property.usage = PROPERTY_USAGE_NONE; + } else if (get_light_type() == RS::LIGHT_DIRECTIONAL && p_property.name == "light_intensity_lumens") { + p_property.usage = PROPERTY_USAGE_NONE; + } + + if (!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units") && (p_property.name == "light_intensity_lumens" || p_property.name == "light_intensity_lux" || p_property.name == "light_temperature")) { p_property.usage = PROPERTY_USAGE_NONE; } @@ -278,10 +343,18 @@ void Light3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_projector", "projector"), &Light3D::set_projector); ClassDB::bind_method(D_METHOD("get_projector"), &Light3D::get_projector); + ClassDB::bind_method(D_METHOD("set_temperature", "temperature"), &Light3D::set_temperature); + ClassDB::bind_method(D_METHOD("get_temperature"), &Light3D::get_temperature); + ClassDB::bind_method(D_METHOD("get_correlated_color"), &Light3D::get_correlated_color); + ADD_GROUP("Light", "light_"); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_intensity_lumens", PROPERTY_HINT_RANGE, "0,100000.0,0.01,or_greater,suffix:lm"), "set_param", "get_param", PARAM_INTENSITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_intensity_lux", PROPERTY_HINT_RANGE, "0,150000.0,0.01,or_greater,suffix:lx"), "set_param", "get_param", PARAM_INTENSITY); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "light_temperature", PROPERTY_HINT_RANGE, "1000,15000.0,1.0,suffix:k"), "set_temperature", "get_temperature"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_ENERGY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_volumetric_fog_energy", PROPERTY_HINT_RANGE, "0,16,0.001,or_greater"), "set_param", "get_param", PARAM_VOLUMETRIC_FOG_ENERGY); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_projector", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_projector", "get_projector"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_size", PROPERTY_HINT_RANGE, "0,1,0.001,or_greater,suffix:m"), "set_param", "get_param", PARAM_SIZE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01,degrees"), "set_param", "get_param", PARAM_SIZE); @@ -296,7 +369,6 @@ void Light3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_normal_bias", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_reverse_cull_face"), "set_shadow_reverse_cull_face", "get_shadow_reverse_cull_face"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.001"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_fog_fade", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_param", "get_param", PARAM_SHADOW_VOLUMETRIC_FOG_FADE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_opacity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_OPACITY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_BLUR); @@ -313,6 +385,7 @@ void Light3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_ENERGY); BIND_ENUM_CONSTANT(PARAM_INDIRECT_ENERGY); + BIND_ENUM_CONSTANT(PARAM_VOLUMETRIC_FOG_ENERGY); BIND_ENUM_CONSTANT(PARAM_SPECULAR); BIND_ENUM_CONSTANT(PARAM_RANGE); BIND_ENUM_CONSTANT(PARAM_SIZE); @@ -329,8 +402,8 @@ void Light3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_SHADOW_PANCAKE_SIZE); BIND_ENUM_CONSTANT(PARAM_SHADOW_OPACITY); BIND_ENUM_CONSTANT(PARAM_SHADOW_BLUR); - BIND_ENUM_CONSTANT(PARAM_SHADOW_VOLUMETRIC_FOG_FADE); BIND_ENUM_CONSTANT(PARAM_TRANSMITTANCE_BIAS); + BIND_ENUM_CONSTANT(PARAM_INTENSITY); BIND_ENUM_CONSTANT(PARAM_MAX); BIND_ENUM_CONSTANT(BAKE_DISABLED); @@ -363,6 +436,7 @@ Light3D::Light3D(RenderingServer::LightType p_type) { set_param(PARAM_ENERGY, 1); set_param(PARAM_INDIRECT_ENERGY, 1); + set_param(PARAM_VOLUMETRIC_FOG_ENERGY, 1); set_param(PARAM_SPECULAR, 0.5); set_param(PARAM_RANGE, 5); set_param(PARAM_SIZE, 0); @@ -380,8 +454,10 @@ Light3D::Light3D(RenderingServer::LightType p_type) { set_param(PARAM_SHADOW_BIAS, 0.03); set_param(PARAM_SHADOW_NORMAL_BIAS, 1.0); set_param(PARAM_TRANSMITTANCE_BIAS, 0.05); - set_param(PARAM_SHADOW_VOLUMETRIC_FOG_FADE, 0.1); set_param(PARAM_SHADOW_FADE_START, 1); + // For OmniLight3D and SpotLight3D, specified in Lumens. + set_param(PARAM_INTENSITY, 1000.0); + set_temperature(6500.0); // Nearly white. set_disable_scale(true); } @@ -487,6 +563,7 @@ DirectionalLight3D::DirectionalLight3D() : set_param(PARAM_SHADOW_FADE_START, 0.8); // Increase the default shadow bias to better suit most scenes. set_param(PARAM_SHADOW_BIAS, 0.1); + set_param(PARAM_INTENSITY, 100000.0); // Specified in Lux, approximate mid-day sun. set_shadow_mode(SHADOW_PARALLEL_4_SPLITS); blend_splits = false; set_sky_mode(SKY_MODE_LIGHT_AND_SKY); diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index 035ba50e42..e43d6f0419 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -40,6 +40,7 @@ public: enum Param { PARAM_ENERGY = RS::LIGHT_PARAM_ENERGY, PARAM_INDIRECT_ENERGY = RS::LIGHT_PARAM_INDIRECT_ENERGY, + PARAM_VOLUMETRIC_FOG_ENERGY = RS::LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY, PARAM_SPECULAR = RS::LIGHT_PARAM_SPECULAR, PARAM_RANGE = RS::LIGHT_PARAM_RANGE, PARAM_SIZE = RS::LIGHT_PARAM_SIZE, @@ -56,8 +57,8 @@ public: PARAM_SHADOW_PANCAKE_SIZE = RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE, PARAM_SHADOW_OPACITY = RS::LIGHT_PARAM_SHADOW_OPACITY, PARAM_SHADOW_BLUR = RS::LIGHT_PARAM_SHADOW_BLUR, - PARAM_SHADOW_VOLUMETRIC_FOG_FADE = RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE, PARAM_TRANSMITTANCE_BIAS = RS::LIGHT_PARAM_TRANSMITTANCE_BIAS, + PARAM_INTENSITY = RS::LIGHT_PARAM_INTENSITY, PARAM_MAX = RS::LIGHT_PARAM_MAX }; @@ -83,6 +84,8 @@ private: void _update_visibility(); BakeMode bake_mode = BAKE_DYNAMIC; Ref<Texture2D> projector; + Color correlated_color = Color(1.0, 1.0, 1.0); + float temperature = 6500.0; // bind helpers @@ -139,6 +142,10 @@ public: void set_projector(const Ref<Texture2D> &p_texture); Ref<Texture2D> get_projector() const; + void set_temperature(const float p_temperature); + float get_temperature() const; + Color get_correlated_color() const; + virtual AABB get_aabb() const override; Light3D(); diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 7efda6db32..41a537f7cb 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -30,10 +30,14 @@ #include "lightmap_gi.h" +#include "core/config/project_settings.h" #include "core/io/config_file.h" #include "core/math/delaunay_3d.h" #include "lightmap_probe.h" #include "scene/3d/mesh_instance_3d.h" +#include "scene/resources/camera_attributes.h" +#include "scene/resources/environment.h" +#include "scene/resources/sky.h" void LightmapGIData::add_user(const NodePath &p_path, const Rect2 &p_uv_scale, int p_slice_index, int32_t p_sub_instance) { User user; @@ -207,7 +211,7 @@ bool LightmapGIData::is_using_spherical_harmonics() const { return uses_spherical_harmonics; } -void LightmapGIData::set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { +void LightmapGIData::set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree, float p_baked_exposure) { if (p_points.size()) { int pc = p_points.size(); ERR_FAIL_COND(pc * 9 != p_point_sh.size()); @@ -221,6 +225,8 @@ void LightmapGIData::set_capture_data(const AABB &p_bounds, bool p_interior, con RS::get_singleton()->lightmap_set_probe_bounds(lightmap, AABB()); RS::get_singleton()->lightmap_set_probe_interior(lightmap, false); } + RS::get_singleton()->lightmap_set_baked_exposure_normalization(lightmap, p_baked_exposure); + baked_exposure = p_baked_exposure; interior = p_interior; bounds = p_bounds; } @@ -249,6 +255,10 @@ bool LightmapGIData::is_interior() const { return interior; } +float LightmapGIData::get_baked_exposure() const { + return baked_exposure; +} + void LightmapGIData::_set_probe_data(const Dictionary &p_data) { ERR_FAIL_COND(!p_data.has("bounds")); ERR_FAIL_COND(!p_data.has("points")); @@ -256,7 +266,8 @@ void LightmapGIData::_set_probe_data(const Dictionary &p_data) { ERR_FAIL_COND(!p_data.has("bsp")); ERR_FAIL_COND(!p_data.has("sh")); ERR_FAIL_COND(!p_data.has("interior")); - set_capture_data(p_data["bounds"], p_data["interior"], p_data["points"], p_data["sh"], p_data["tetrahedra"], p_data["bsp"]); + ERR_FAIL_COND(!p_data.has("baked_exposure")); + set_capture_data(p_data["bounds"], p_data["interior"], p_data["points"], p_data["sh"], p_data["tetrahedra"], p_data["bsp"], p_data["baked_exposure"]); } Dictionary LightmapGIData::_get_probe_data() const { @@ -267,6 +278,7 @@ Dictionary LightmapGIData::_get_probe_data() const { d["bsp"] = get_capture_bsp_tree(); d["sh"] = get_capture_sh(); d["interior"] = is_interior(); + d["baked_exposure"] = get_baked_exposure(); return d; } @@ -977,15 +989,21 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa Transform3D xf = lights_found[i].xform; Color linear_color = light->get_color().srgb_to_linear(); + float energy = light->get_param(Light3D::PARAM_ENERGY); + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + energy *= light->get_param(Light3D::PARAM_INTENSITY); + linear_color *= light->get_correlated_color().srgb_to_linear(); + } + if (Object::cast_to<DirectionalLight3D>(light)) { DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light); - lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); + lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } else if (Object::cast_to<OmniLight3D>(light)) { OmniLight3D *l = Object::cast_to<OmniLight3D>(light); - lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); + lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, energy * (1.0 / (Math_PI * 4.0)), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } else if (Object::cast_to<SpotLight3D>(light)) { SpotLight3D *l = Object::cast_to<SpotLight3D>(light); - lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); + lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy * (1.0 / Math_PI), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } } for (int i = 0; i < probes_found.size(); i++) { @@ -1040,7 +1058,12 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } } - Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, bounces, bias, max_texture_size, directional, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud); + float exposure_normalization = 1.0; + if (camera_attributes.is_valid()) { + exposure_normalization = camera_attributes->calculate_exposure_normalization() * camera_attributes->get_exposure_multiplier(); + } + + Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, bounces, bias, max_texture_size, directional, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud, exposure_normalization); if (bake_err == Lightmapper::BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES) { return BAKE_ERROR_MESHES_INVALID; @@ -1214,7 +1237,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa /* Obtain the colors from the images, they will be re-created as cubemaps on the server, depending on the driver */ - data->set_capture_data(bounds, interior, points, sh, tetrahedrons, bsp_array); + data->set_capture_data(bounds, interior, points, sh, tetrahedrons, bsp_array, exposure_normalization); /* Compute a BSP tree of the simplices, so it's easy to find the exact one */ } @@ -1410,6 +1433,14 @@ LightmapGI::GenerateProbes LightmapGI::get_generate_probes() const { return gen_probes; } +void LightmapGI::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { + camera_attributes = p_camera_attributes; +} + +Ref<CameraAttributes> LightmapGI::get_camera_attributes() const { + return camera_attributes; +} + void LightmapGI::_validate_property(PropertyInfo &p_property) const { if (p_property.name == "environment_custom_sky" && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { p_property.usage = PROPERTY_USAGE_NONE; @@ -1462,6 +1493,9 @@ void LightmapGI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_directional", "directional"), &LightmapGI::set_directional); ClassDB::bind_method(D_METHOD("is_directional"), &LightmapGI::is_directional); + ClassDB::bind_method(D_METHOD("set_camera_attributes", "camera_attributes"), &LightmapGI::set_camera_attributes); + ClassDB::bind_method(D_METHOD("get_camera_attributes"), &LightmapGI::get_camera_attributes); + // ClassDB::bind_method(D_METHOD("bake", "from_node"), &LightmapGI::bake, DEFVAL(Variant())); ADD_GROUP("Tweaks", ""); @@ -1477,6 +1511,7 @@ void LightmapGI::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment_custom_sky", PROPERTY_HINT_RESOURCE_TYPE, "Sky"), "set_environment_custom_sky", "get_environment_custom_sky"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "environment_custom_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_environment_custom_color", "get_environment_custom_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "environment_custom_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_environment_custom_energy", "get_environment_custom_energy"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); ADD_GROUP("Gen Probes", "generate_probes_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "generate_probes_subdiv", PROPERTY_HINT_ENUM, "Disabled,4,8,16,32"), "set_generate_probes", "get_generate_probes"); ADD_GROUP("Data", ""); diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index 87add9facc..0062a4a093 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -36,6 +36,9 @@ #include "scene/3d/lightmapper.h" #include "scene/3d/visual_instance_3d.h" +class Sky; +class CameraAttributes; + class LightmapGIData : public Resource { GDCLASS(LightmapGIData, Resource); RES_BASE_EXTENSION("lmbake") @@ -47,6 +50,7 @@ class LightmapGIData : public Resource { RID lightmap; AABB bounds; + float baked_exposure = 1.0; struct User { NodePath path; @@ -83,8 +87,9 @@ public: bool is_using_spherical_harmonics() const; bool is_interior() const; + float get_baked_exposure() const; - void set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree); + void set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree, float p_baked_exposure); PackedVector3Array get_capture_points() const; PackedColorArray get_capture_sh() const; PackedInt32Array get_capture_tetrahedra() const; @@ -147,6 +152,7 @@ private: float environment_custom_energy = 1.0; bool directional = false; GenerateProbes gen_probes = GENERATE_PROBES_DISABLED; + Ref<CameraAttributes> camera_attributes; Ref<LightmapGIData> light_data; @@ -260,6 +266,9 @@ public: void set_generate_probes(GenerateProbes p_generate_probes); GenerateProbes get_generate_probes() const; + void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); + Ref<CameraAttributes> get_camera_attributes() const; + AABB get_aabb() const override; BakeError bake(Node *p_from_node, String p_image_data_path = "", Lightmapper::BakeStepFunc p_bake_step = nullptr, void *p_bake_userdata = nullptr); diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h index 9b973fd6bc..5b5c6cf53a 100644 --- a/scene/3d/lightmapper.h +++ b/scene/3d/lightmapper.h @@ -180,7 +180,7 @@ public: virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) = 0; virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) = 0; virtual void add_probe(const Vector3 &p_position) = 0; - virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr) = 0; + virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr, float p_exposure_normalization = 1.0) = 0; virtual int get_bake_texture_count() const = 0; virtual Ref<Image> get_bake_texture(int p_index) const = 0; diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 7d79d9b4fd..3d23206e6b 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -198,17 +198,17 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { } } - Vector3 pos = c->interpolate_baked(progress, cubic); + Vector3 pos = c->sample_baked(progress, cubic); Transform3D t = get_transform(); // Vector3 pos_offset = Vector3(h_offset, v_offset, 0); not used in all cases // will be replaced by "Vector3(h_offset, v_offset, 0)" where it was formerly used if (rotation_mode == ROTATION_ORIENTED) { - Vector3 forward = c->interpolate_baked(o_next, cubic) - pos; + Vector3 forward = c->sample_baked(o_next, cubic) - pos; // Try with the previous position if (forward.length_squared() < CMP_EPSILON2) { - forward = pos - c->interpolate_baked(o_prev, cubic); + forward = pos - c->sample_baked(o_prev, cubic); } if (forward.length_squared() < CMP_EPSILON2) { @@ -217,10 +217,10 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { forward.normalize(); } - Vector3 up = c->interpolate_baked_up_vector(progress, true); + Vector3 up = c->sample_baked_up_vector(progress, true); if (o_next < progress) { - Vector3 up1 = c->interpolate_baked_up_vector(o_next, true); + Vector3 up1 = c->sample_baked_up_vector(o_next, true); Vector3 axis = up.cross(up1); if (axis.length_squared() < CMP_EPSILON2) { @@ -249,10 +249,10 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { t.origin = pos; if (p_update_xyz_rot && prev_offset != progress) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree. real_t sample_distance = bi * 0.01; - Vector3 t_prev_pos_a = c->interpolate_baked(prev_offset - sample_distance, cubic); - Vector3 t_prev_pos_b = c->interpolate_baked(prev_offset + sample_distance, cubic); - Vector3 t_cur_pos_a = c->interpolate_baked(progress - sample_distance, cubic); - Vector3 t_cur_pos_b = c->interpolate_baked(progress + sample_distance, cubic); + Vector3 t_prev_pos_a = c->sample_baked(prev_offset - sample_distance, cubic); + Vector3 t_prev_pos_b = c->sample_baked(prev_offset + sample_distance, cubic); + Vector3 t_cur_pos_a = c->sample_baked(progress - sample_distance, cubic); + Vector3 t_cur_pos_b = c->sample_baked(progress + sample_distance, cubic); Vector3 t_prev = (t_prev_pos_a - t_prev_pos_b).normalized(); Vector3 t_cur = (t_cur_pos_a - t_cur_pos_b).normalized(); @@ -277,7 +277,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { } // do the additional tilting - real_t tilt_angle = c->interpolate_baked_tilt(progress); + real_t tilt_angle = c->sample_baked_tilt(progress); Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct?? if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) { diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index ae231026a7..c97af087bf 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -30,8 +30,10 @@ #include "voxel_gi.h" +#include "core/core_string_names.h" #include "mesh_instance_3d.h" #include "multimesh_instance_3d.h" +#include "scene/resources/camera_attributes.h" #include "voxelizer.h" void VoxelGIData::_set_data(const Dictionary &p_data) { @@ -281,6 +283,14 @@ Vector3 VoxelGI::get_extents() const { return extents; } +void VoxelGI::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { + camera_attributes = p_camera_attributes; +} + +Ref<CameraAttributes> VoxelGI::get_camera_attributes() const { + return camera_attributes; +} + void VoxelGI::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) { MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node); if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_STATIC && mi->is_visible_in_tree()) { @@ -370,9 +380,14 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { p_from_node = p_from_node ? p_from_node : get_parent(); ERR_FAIL_NULL(p_from_node); + float exposure_normalization = 1.0; + if (camera_attributes.is_valid()) { + exposure_normalization = camera_attributes->calculate_exposure_normalization() * camera_attributes->get_exposure_multiplier(); + } + Voxelizer baker; - baker.begin_bake(subdiv_value[subdiv], AABB(-extents, extents * 2.0)); + baker.begin_bake(subdiv_value[subdiv], AABB(-extents, extents * 2.0), exposure_normalization); List<PlotMesh> mesh_list; @@ -428,6 +443,8 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { Vector<uint8_t> df = baker.get_sdf_3d_image(); + RS::get_singleton()->voxel_gi_set_baked_exposure_normalization(probe_data->get_rid(), exposure_normalization); + probe_data->allocate(baker.get_to_cell_space_xform(), AABB(-extents, extents * 2.0), baker.get_voxel_gi_octree_size(), baker.get_voxel_gi_octree_cells(), baker.get_voxel_gi_data_cells(), df, baker.get_voxel_gi_level_cell_count()); set_probe_data(probe_data); @@ -472,12 +489,16 @@ void VoxelGI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_extents", "extents"), &VoxelGI::set_extents); ClassDB::bind_method(D_METHOD("get_extents"), &VoxelGI::get_extents); + ClassDB::bind_method(D_METHOD("set_camera_attributes", "camera_attributes"), &VoxelGI::set_camera_attributes); + ClassDB::bind_method(D_METHOD("get_camera_attributes"), &VoxelGI::get_camera_attributes); + ClassDB::bind_method(D_METHOD("bake", "from_node", "create_visual_debug"), &VoxelGI::bake, DEFVAL(Variant()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("debug_bake"), &VoxelGI::_debug_bake); ClassDB::set_method_flags(get_class_static(), _scs_create("debug_bake"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdiv", PROPERTY_HINT_ENUM, "64,128,256,512"), "set_subdiv", "get_subdiv"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_NONE, "suffix:m"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "VoxelGIData", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_probe_data", "get_probe_data"); BIND_ENUM_CONSTANT(SUBDIV_64); diff --git a/scene/3d/voxel_gi.h b/scene/3d/voxel_gi.h index 6d173dea87..b31ae4cd95 100644 --- a/scene/3d/voxel_gi.h +++ b/scene/3d/voxel_gi.h @@ -33,6 +33,8 @@ #include "scene/3d/visual_instance_3d.h" +class CameraAttributes; + class VoxelGIData : public Resource { GDCLASS(VoxelGIData, Resource); @@ -117,6 +119,7 @@ private: Subdiv subdiv = SUBDIV_128; Vector3 extents = Vector3(10, 10, 10); + Ref<CameraAttributes> camera_attributes; struct PlotMesh { Ref<Material> override_material; @@ -144,6 +147,10 @@ public: void set_extents(const Vector3 &p_extents); Vector3 get_extents() const; + + void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); + Ref<CameraAttributes> get_camera_attributes() const; + Vector3i get_estimated_cell_size() const; void bake(Node *p_from_node = nullptr, bool p_create_visual_debug = false); diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index 9380d1cf32..6daa9e0aec 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -30,6 +30,8 @@ #include "voxelizer.h" +#include "core/config/project_settings.h" + static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv, const Vector3 *p_normal, Vector2 &r_uv, Vector3 &r_normal) { if (p_pos.is_equal_approx(p_vtx[0])) { r_uv = p_uv[0]; @@ -348,7 +350,10 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material Ref<Texture2D> emission_tex = mat->get_texture(BaseMaterial3D::TEXTURE_EMISSION); Color emission_col = mat->get_emission(); - float emission_energy = mat->get_emission_energy(); + float emission_energy = mat->get_emission_energy_multiplier() * exposure_normalization; + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + emission_energy *= mat->get_emission_intensity(); + } Ref<Image> img_emission; @@ -608,10 +613,11 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) { } } -void Voxelizer::begin_bake(int p_subdiv, const AABB &p_bounds) { +void Voxelizer::begin_bake(int p_subdiv, const AABB &p_bounds, float p_exposure_normalization) { sorted = false; original_bounds = p_bounds; cell_subdiv = p_subdiv; + exposure_normalization = p_exposure_normalization; bake_cells.resize(1); material_cache.clear(); diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h index 68bce768b7..f5bb9af107 100644 --- a/scene/3d/voxelizer.h +++ b/scene/3d/voxelizer.h @@ -87,6 +87,7 @@ private: }; HashMap<Ref<Material>, MaterialCache> material_cache; + float exposure_normalization = 1.0; AABB original_bounds; AABB po2_bounds; int axis_cell_size[3] = {}; @@ -111,7 +112,7 @@ private: void _sort(); public: - void begin_bake(int p_subdiv, const AABB &p_bounds); + void begin_bake(int p_subdiv, const AABB &p_bounds, float p_exposure_normalization); void plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material); void end_bake(); diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp index fe9d9ae4dd..ae7d79e8b0 100644 --- a/scene/3d/world_environment.cpp +++ b/scene/3d/world_environment.cpp @@ -42,9 +42,9 @@ void WorldEnvironment::_notification(int p_what) { _update_current_environment(); } - if (camera_effects.is_valid()) { - add_to_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); - _update_current_camera_effects(); + if (camera_attributes.is_valid()) { + add_to_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + _update_current_camera_attributes(); } } break; @@ -55,9 +55,9 @@ void WorldEnvironment::_notification(int p_what) { _update_current_environment(); } - if (camera_effects.is_valid()) { - remove_from_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); - _update_current_camera_effects(); + if (camera_attributes.is_valid()) { + remove_from_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + _update_current_camera_attributes(); } } break; } @@ -74,15 +74,15 @@ void WorldEnvironment::_update_current_environment() { get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } -void WorldEnvironment::_update_current_camera_effects() { - WorldEnvironment *first = Object::cast_to<WorldEnvironment>(get_tree()->get_first_node_in_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()))); +void WorldEnvironment::_update_current_camera_attributes() { + WorldEnvironment *first = Object::cast_to<WorldEnvironment>(get_tree()->get_first_node_in_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()))); if (first) { - get_viewport()->find_world_3d()->set_camera_effects(first->camera_effects); + get_viewport()->find_world_3d()->set_camera_attributes(first->camera_attributes); } else { - get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>()); + get_viewport()->find_world_3d()->set_camera_attributes(Ref<CameraAttributes>()); } - get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, "_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); } void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) { @@ -110,36 +110,36 @@ Ref<Environment> WorldEnvironment::get_environment() const { return environment; } -void WorldEnvironment::set_camera_effects(const Ref<CameraEffects> &p_camera_effects) { - if (camera_effects == p_camera_effects) { +void WorldEnvironment::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { + if (camera_attributes == p_camera_attributes) { return; } - if (is_inside_tree() && camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() == camera_effects) { - remove_from_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + if (is_inside_tree() && camera_attributes.is_valid() && get_viewport()->find_world_3d()->get_camera_attributes() == camera_attributes) { + remove_from_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); } - camera_effects = p_camera_effects; - if (is_inside_tree() && camera_effects.is_valid()) { - add_to_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + camera_attributes = p_camera_attributes; + if (is_inside_tree() && camera_attributes.is_valid()) { + add_to_group("_world_camera_attributes_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); } if (is_inside_tree()) { - _update_current_camera_effects(); + _update_current_camera_attributes(); } else { update_configuration_warnings(); } } -Ref<CameraEffects> WorldEnvironment::get_camera_effects() const { - return camera_effects; +Ref<CameraAttributes> WorldEnvironment::get_camera_attributes() const { + return camera_attributes; } TypedArray<String> WorldEnvironment::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); - if (!environment.is_valid() && !camera_effects.is_valid()) { - warnings.push_back(RTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Effects\" property to contain a CameraEffects resource, or both.")); + if (!environment.is_valid() && !camera_attributes.is_valid()) { + warnings.push_back(RTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Attributes\" property to contain a CameraAttributes resource, or both.")); } if (!is_inside_tree()) { @@ -150,7 +150,7 @@ TypedArray<String> WorldEnvironment::get_configuration_warnings() const { warnings.push_back(("Only the first Environment has an effect in a scene (or set of instantiated scenes).")); } - if (camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() != camera_effects) { + if (camera_attributes.is_valid() && get_viewport()->find_world_3d()->get_camera_attributes() != camera_attributes) { warnings.push_back(RTR("Only one WorldEnvironment is allowed per scene (or set of instantiated scenes).")); } @@ -162,9 +162,9 @@ void WorldEnvironment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_environment"), &WorldEnvironment::get_environment); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); - ClassDB::bind_method(D_METHOD("set_camera_effects", "env"), &WorldEnvironment::set_camera_effects); - ClassDB::bind_method(D_METHOD("get_camera_effects"), &WorldEnvironment::get_camera_effects); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_camera_effects", "get_camera_effects"); + ClassDB::bind_method(D_METHOD("set_camera_attributes", "camera_attributes"), &WorldEnvironment::set_camera_attributes); + ClassDB::bind_method(D_METHOD("get_camera_attributes"), &WorldEnvironment::get_camera_attributes); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); } WorldEnvironment::WorldEnvironment() { diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h index 9955aa72a8..07f243c750 100644 --- a/scene/3d/world_environment.h +++ b/scene/3d/world_environment.h @@ -32,17 +32,17 @@ #define WORLD_ENVIRONMENT_H #include "scene/main/node.h" -#include "scene/resources/camera_effects.h" +#include "scene/resources/camera_attributes.h" #include "scene/resources/environment.h" class WorldEnvironment : public Node { GDCLASS(WorldEnvironment, Node); Ref<Environment> environment; - Ref<CameraEffects> camera_effects; + Ref<CameraAttributes> camera_attributes; void _update_current_environment(); - void _update_current_camera_effects(); + void _update_current_camera_attributes(); protected: void _notification(int p_what); @@ -52,8 +52,8 @@ public: void set_environment(const Ref<Environment> &p_environment); Ref<Environment> get_environment() const; - void set_camera_effects(const Ref<CameraEffects> &p_camera_effects); - Ref<CameraEffects> get_camera_effects() const; + void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); + Ref<CameraAttributes> get_camera_attributes() const; TypedArray<String> get_configuration_warnings() const override; diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 61f068408c..3d81b6b9e8 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -750,7 +750,7 @@ double AnimationNodeTransition::process(double p_time, bool p_seek, bool p_seek_ float blend = xfade_time == 0 ? 0 : (prev_xfading / xfade_time); if (xfade_curve.is_valid()) { - blend = xfade_curve->interpolate(blend); + blend = xfade_curve->sample(blend); } if (from_start && !p_seek && switched) { //just switched, seek to start of current @@ -810,7 +810,7 @@ void AnimationNodeTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_from_start", "from_start"), &AnimationNodeTransition::set_from_start); ClassDB::bind_method(D_METHOD("is_from_start"), &AnimationNodeTransition::is_from_start); - ADD_PROPERTY(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_inputs", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01,suffix:s"), "set_xfade_time", "get_xfade_time"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "xfade_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_xfade_curve", "get_xfade_curve"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "from_start"), "set_from_start", "is_from_start"); diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index afb52de307..49a59de9b2 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -344,6 +344,15 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta } double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_state_machine, double p_time, bool p_seek, bool p_seek_root) { + if (p_time == -1) { + Ref<AnimationNodeStateMachine> anodesm = p_state_machine->states[current].node; + if (anodesm.is_valid()) { + p_state_machine->blend_node(current, p_state_machine->states[current].node, -1, p_seek, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); + } + playing = false; + return 0; + } + //if not playing and it can restart, then restart if (!playing && start_request == StringName()) { if (!stop_request && p_state_machine->start_node) { @@ -433,7 +442,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } if (current_curve.is_valid()) { - fade_blend = current_curve->interpolate(fade_blend); + fade_blend = current_curve->sample(fade_blend); } float rem = p_state_machine->blend_node(current, p_state_machine->states[current].node, p_time, p_seek, p_seek_root, fade_blend, AnimationNode::FILTER_IGNORE, true); @@ -491,7 +500,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s // handles start_node: if previous state machine is pointing to a node inside the current state machine, starts the current machine from start_node to prev_local_to if (p_state_machine->start_node == current && p_state_machine->transitions[i].local_from == current) { if (p_state_machine->prev_state_machine != nullptr) { - Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback"); + Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter(p_state_machine->playback); if (prev_playback.is_valid()) { StringName prev_local_to = String(prev_playback->current_transition.next).replace_first(String(p_state_machine->state_machine_name) + "/", ""); @@ -530,7 +539,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s AnimationNodeStateMachine *prev_state_machine = p_state_machine->prev_state_machine; if (prev_state_machine != nullptr) { - Ref<AnimationNodeStateMachinePlayback> prev_playback = prev_state_machine->get_parameter("playback"); + Ref<AnimationNodeStateMachinePlayback> prev_playback = prev_state_machine->get_parameter(p_state_machine->playback); if (prev_playback.is_valid()) { if (next_xfade) { @@ -578,7 +587,6 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } if (goto_next) { //end_loop should be used because fade time may be too small or zero and animation may have looped - if (next_xfade) { //time to fade, baby fading_from = current; @@ -592,7 +600,16 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s if (path.size()) { //if it came from path, remove path path.remove_at(0); } + + { // if the current node is a state machine, update the "playing" variable to false by passing -1 in p_time + Ref<AnimationNodeStateMachine> anodesm = p_state_machine->states[current].node; + if (anodesm.is_valid()) { + p_state_machine->blend_node(current, p_state_machine->states[current].node, -1, p_seek, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); + } + } + current = next; + if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { len_current = p_state_machine->blend_node(current, p_state_machine->states[current].node, 0, true, p_seek_root, 0, AnimationNode::FILTER_IGNORE, true); pos_current = MIN(pos_current, len_current); @@ -607,15 +624,16 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s } } - // time left must always be 1 because the end node don't length to compute - if (p_state_machine->end_node != current) { - rem = 1; + if (current != p_state_machine->end_node) { + rem = 1; // the time remaining must always be 1 because there is no way to predict how long it takes for the entire state machine to complete } else { - Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback"); + if (p_state_machine->prev_state_machine != nullptr) { + Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter(p_state_machine->playback); - if (prev_playback.is_valid()) { - prev_playback->current_transition = current_transition; - prev_playback->force_auto_advance = true; + if (prev_playback.is_valid()) { + prev_playback->current_transition = current_transition; + prev_playback->force_auto_advance = true; + } } } @@ -690,23 +708,6 @@ void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) c for (const StringName &E : advance_conditions) { r_list->push_back(PropertyInfo(Variant::BOOL, E)); } - - // for (const KeyValue<StringName, State> &E : states) { - // if (E->node == ansm) { - // for (int i = 0; i < E->node->transitions.size(); i++) { - // StringName ac = E->node->transitions[i].transition->get_advance_condition_name(); - // if (ac != StringName() && advance_conditions.find(ac) == nullptr) { - // advance_conditions.push_back(ac); - // } - // } - - // advance_conditions.sort_custom<StringName::AlphCompare>(); - - // for (const StringName &E : advance_conditions) { - // r_list->push_back(PropertyInfo(Variant::BOOL, E)); - // } - // } - // } } Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName &p_parameter) const { @@ -900,10 +901,6 @@ void AnimationNodeStateMachine::_rename_transitions(const StringName &p_name, co void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { List<StringName> nodes; for (const KeyValue<StringName, State> &E : states) { - if (E.key == end_node && prev_state_machine == nullptr) { - continue; - } - nodes.push_back(E.key); } nodes.sort_custom<StringName::AlphCompare>(); diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 0b7280619e..c7b64ba6c6 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -544,7 +544,7 @@ void Button::_bind_methods() { Button::Button(const String &p_text) { text_buf.instantiate(); - text_buf->set_break_flags(TextServer::BREAK_MANDATORY); + text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_EDGE_SPACES); set_mouse_filter(MOUSE_FILTER_STOP); set_text(p_text); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index e3a27ba7d5..308dbe33f2 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -45,7 +45,7 @@ void ItemList::_shape(int p_idx) { } item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.language); if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { - item.text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); + item.text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES); } else { item.text_buf->set_break_flags(TextServer::BREAK_NONE); } @@ -532,7 +532,7 @@ void ItemList::set_max_text_lines(int p_lines) { max_text_lines = p_lines; for (int i = 0; i < items.size(); i++) { if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { - items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); + items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES); items.write[i].text_buf->set_max_lines_visible(p_lines); } else { items.write[i].text_buf->set_break_flags(TextServer::BREAK_NONE); @@ -582,7 +582,7 @@ void ItemList::set_icon_mode(IconMode p_mode) { icon_mode = p_mode; for (int i = 0; i < items.size(); i++) { if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { - items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); + items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES); } else { items.write[i].text_buf->set_break_flags(TextServer::BREAK_NONE); } diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index cd68d1f001..fd174619d5 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -143,8 +143,9 @@ void Label::_shape() { case TextServer::AUTOWRAP_OFF: break; } - PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); + autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); for (int i = 0; i < line_breaks.size(); i = i + 2) { RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); lines_rid.push_back(line); diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 8aad14e1ec..db06fca587 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -377,6 +377,11 @@ void MenuBar::_notification(int p_what) { MutexLock lock(mutex); Vector2 pos = DisplayServer::get_singleton()->mouse_get_position() - mouse_pos_adjusted - get_global_position(); + if (pos == old_mouse_pos) { + return; + } + old_mouse_pos = pos; + int index = _get_index_at_point(pos); if (index >= 0 && index != active_menu) { selected_menu = index; diff --git a/scene/gui/menu_bar.h b/scene/gui/menu_bar.h index b7d9933ab2..5aa8ac7324 100644 --- a/scene/gui/menu_bar.h +++ b/scene/gui/menu_bar.h @@ -73,6 +73,7 @@ class MenuBar : public Control { int active_menu = -1; Vector2i mouse_pos_adjusted; + Vector2i old_mouse_pos; ObjectID shortcut_context; int _get_index_at_point(const Point2 &p_point) const; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index c9a903153d..c5fe218a15 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -453,6 +453,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> case TextServer::AUTOWRAP_OFF: break; } + autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; // Clear cache. l.text_buf->clear(); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index ec98ff36a0..268b381029 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -46,6 +46,7 @@ #include "scene/debugger/scene_debugger.h" #include "scene/main/multiplayer_api.h" #include "scene/main/viewport.h" +#include "scene/resources/environment.h" #include "scene/resources/font.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" @@ -1472,18 +1473,18 @@ SceneTree::SceneTree() { } } - int shadowmap_size = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_size", 4096); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_size", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_size", PROPERTY_HINT_RANGE, "256,16384")); - GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_size.mobile", 2048); - bool shadowmap_16_bits = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_16_bits", true); - int atlas_q0 = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv", 2); - int atlas_q1 = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv", 2); - int atlas_q2 = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv", 3); - int atlas_q3 = GLOBAL_DEF("rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv", 4); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_quadrant_0_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_quadrant_1_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_quadrant_2_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/atlas_quadrant_3_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + int shadowmap_size = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_size", 4096); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_size", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_size", PROPERTY_HINT_RANGE, "256,16384")); + GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_size.mobile", 2048); + bool shadowmap_16_bits = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_16_bits", true); + int atlas_q0 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv", 2); + int atlas_q1 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv", 2); + int atlas_q2 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv", 3); + int atlas_q3 = GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv", 4); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_1_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_2_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/atlas_quadrant_3_subdiv", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows")); root->set_positional_shadow_atlas_size(shadowmap_size); root->set_positional_shadow_atlas_16_bits(shadowmap_16_bits); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index a738c6eabc..1bb1faacdd 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -190,14 +190,7 @@ void Viewport::_sub_window_register(Window *p_window) { } void Viewport::_sub_window_update(Window *p_window) { - int index = -1; - for (int i = 0; i < gui.sub_windows.size(); i++) { - if (gui.sub_windows[i].window == p_window) { - index = i; - break; - } - } - + int index = _sub_window_find(p_window); ERR_FAIL_COND(index == -1); const SubWindow &sw = gui.sub_windows[index]; @@ -257,14 +250,7 @@ void Viewport::_sub_window_grab_focus(Window *p_window) { return; } - int index = -1; - for (int i = 0; i < gui.sub_windows.size(); i++) { - if (gui.sub_windows[i].window == p_window) { - index = i; - break; - } - } - + int index = _sub_window_find(p_window); ERR_FAIL_COND(index == -1); if (p_window->get_flag(Window::FLAG_NO_FOCUS)) { @@ -312,13 +298,11 @@ void Viewport::_sub_window_grab_focus(Window *p_window) { } void Viewport::_sub_window_remove(Window *p_window) { - for (int i = 0; i < gui.sub_windows.size(); i++) { - if (gui.sub_windows[i].window == p_window) { - RS::get_singleton()->free(gui.sub_windows[i].canvas_item); - gui.sub_windows.remove_at(i); - break; - } - } + int index = _sub_window_find(p_window); + ERR_FAIL_COND(index == -1); + + RS::get_singleton()->free(gui.sub_windows[index].canvas_item); + gui.sub_windows.remove_at(index); if (gui.sub_windows.size() == 0) { RS::get_singleton()->free(subwindow_canvas); @@ -326,27 +310,46 @@ void Viewport::_sub_window_remove(Window *p_window) { } if (gui.subwindow_focused == p_window) { + Window *new_focused_window; Window *parent_visible = p_window->get_parent_visible_window(); gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED; gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT); - if (parent_visible && parent_visible != this) { - gui.subwindow_focused = parent_visible; - gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + if (parent_visible) { + new_focused_window = parent_visible; } else { - gui.subwindow_focused = nullptr; - Window *this_window = Object::cast_to<Window>(this); - if (this_window) { - this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + new_focused_window = Object::cast_to<Window>(this); + } + + if (new_focused_window) { + int new_focused_index = _sub_window_find(new_focused_window); + if (new_focused_index != -1) { + gui.subwindow_focused = new_focused_window; + } else { + gui.subwindow_focused = nullptr; } + + new_focused_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN); + } else { + gui.subwindow_focused = nullptr; } } RenderingServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, p_window->parent ? p_window->parent->viewport : RID()); } +int Viewport::_sub_window_find(Window *p_window) { + for (int i = 0; i < gui.sub_windows.size(); i++) { + if (gui.sub_windows[i].window == p_window) { + return i; + } + } + + return -1; +} + void Viewport::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 0b3853ba79..afea3ea56c 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -461,6 +461,7 @@ private: void _sub_window_update(Window *p_window); void _sub_window_grab_focus(Window *p_window); void _sub_window_remove(Window *p_window); + int _sub_window_find(Window *p_window); bool _sub_windows_forward_input(const Ref<InputEvent> &p_event); SubWindowResize _sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 6c7bc552bf..cc40d36fa3 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -142,7 +142,7 @@ #include "scene/resources/bit_map.h" #include "scene/resources/bone_map.h" #include "scene/resources/box_shape_3d.h" -#include "scene/resources/camera_effects.h" +#include "scene/resources/camera_attributes.h" #include "scene/resources/capsule_shape_2d.h" #include "scene/resources/capsule_shape_3d.h" #include "scene/resources/circle_shape_2d.h" @@ -152,6 +152,7 @@ #include "scene/resources/convex_polygon_shape_3d.h" #include "scene/resources/cylinder_shape_3d.h" #include "scene/resources/default_theme/default_theme.h" +#include "scene/resources/environment.h" #include "scene/resources/font.h" #include "scene/resources/gradient.h" #include "scene/resources/height_map_shape_3d.h" @@ -836,7 +837,9 @@ void register_scene_types() { GDREGISTER_CLASS(PhysicsMaterial); GDREGISTER_CLASS(World3D); GDREGISTER_CLASS(Environment); - GDREGISTER_CLASS(CameraEffects); + GDREGISTER_VIRTUAL_CLASS(CameraAttributes); + GDREGISTER_CLASS(CameraAttributesPhysical); + GDREGISTER_CLASS(CameraAttributesPractical); GDREGISTER_CLASS(World2D); GDREGISTER_VIRTUAL_CLASS(Texture); GDREGISTER_VIRTUAL_CLASS(Texture2D); diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp new file mode 100644 index 0000000000..3c322f32b6 --- /dev/null +++ b/scene/resources/camera_attributes.cpp @@ -0,0 +1,493 @@ +/*************************************************************************/ +/* camera_attributes.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "camera_attributes.h" + +#include "core/config/project_settings.h" +#include "servers/rendering_server.h" + +void CameraAttributes::set_exposure_multiplier(float p_multiplier) { + exposure_multiplier = p_multiplier; + _update_exposure(); + emit_changed(); +} + +float CameraAttributes::get_exposure_multiplier() const { + return exposure_multiplier; +} + +void CameraAttributes::set_exposure_sensitivity(float p_sensitivity) { + exposure_sensitivity = p_sensitivity; + _update_exposure(); + emit_changed(); +} + +float CameraAttributes::get_exposure_sensitivity() const { + return exposure_sensitivity; +} + +void CameraAttributes::_update_exposure() { + float exposure_normalization = 1.0; + // Ignore physical properties if not using physical light units. + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + exposure_normalization = calculate_exposure_normalization(); + } + + RS::get_singleton()->camera_attributes_set_exposure(camera_attributes, exposure_multiplier, exposure_normalization); +} + +void CameraAttributes::set_auto_exposure_enabled(bool p_enabled) { + auto_exposure_enabled = p_enabled; + _update_auto_exposure(); + notify_property_list_changed(); +} + +bool CameraAttributes::is_auto_exposure_enabled() const { + return auto_exposure_enabled; +} + +void CameraAttributes::set_auto_exposure_speed(float p_auto_exposure_speed) { + auto_exposure_speed = p_auto_exposure_speed; + _update_auto_exposure(); +} + +float CameraAttributes::get_auto_exposure_speed() const { + return auto_exposure_speed; +} + +void CameraAttributes::set_auto_exposure_scale(float p_auto_exposure_scale) { + auto_exposure_scale = p_auto_exposure_scale; + _update_auto_exposure(); +} + +float CameraAttributes::get_auto_exposure_scale() const { + return auto_exposure_scale; +} + +RID CameraAttributes::get_rid() const { + return camera_attributes; +} + +void CameraAttributes::_validate_property(PropertyInfo &p_property) const { + if (!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units") && p_property.name == "exposure_sensitivity") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + return; + } + + if (p_property.name.begins_with("auto_exposure_") && p_property.name != "auto_exposure_enabled" && !auto_exposure_enabled) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + return; + } +} + +void CameraAttributes::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_exposure_multiplier", "multiplier"), &CameraAttributes::set_exposure_multiplier); + ClassDB::bind_method(D_METHOD("get_exposure_multiplier"), &CameraAttributes::get_exposure_multiplier); + ClassDB::bind_method(D_METHOD("set_exposure_sensitivity", "sensitivity"), &CameraAttributes::set_exposure_sensitivity); + ClassDB::bind_method(D_METHOD("get_exposure_sensitivity"), &CameraAttributes::get_exposure_sensitivity); + + ClassDB::bind_method(D_METHOD("set_auto_exposure_enabled", "enabled"), &CameraAttributes::set_auto_exposure_enabled); + ClassDB::bind_method(D_METHOD("is_auto_exposure_enabled"), &CameraAttributes::is_auto_exposure_enabled); + ClassDB::bind_method(D_METHOD("set_auto_exposure_speed", "exposure_speed"), &CameraAttributes::set_auto_exposure_speed); + ClassDB::bind_method(D_METHOD("get_auto_exposure_speed"), &CameraAttributes::get_auto_exposure_speed); + ClassDB::bind_method(D_METHOD("set_auto_exposure_scale", "exposure_grey"), &CameraAttributes::set_auto_exposure_scale); + ClassDB::bind_method(D_METHOD("get_auto_exposure_scale"), &CameraAttributes::get_auto_exposure_scale); + + ADD_GROUP("Exposure", "exposure"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_sensitivity", PROPERTY_HINT_RANGE, "0.1,32000.0,0.1,suffix:ISO"), "set_exposure_sensitivity", "get_exposure_sensitivity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_multiplier", PROPERTY_HINT_RANGE, "0.0,2048.0,0.001"), "set_exposure_multiplier", "get_exposure_multiplier"); + + ADD_GROUP("Auto Exposure", "auto_exposure_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_exposure_enabled"), "set_auto_exposure_enabled", "is_auto_exposure_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_auto_exposure_scale", "get_auto_exposure_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_speed", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_auto_exposure_speed", "get_auto_exposure_speed"); +} + +CameraAttributes::CameraAttributes() { + camera_attributes = RS::get_singleton()->camera_attributes_create(); +} + +CameraAttributes::~CameraAttributes() { + RS::get_singleton()->free(camera_attributes); +} + +////////////////////////////////////////////////////// +/* CameraAttributesPractical */ + +void CameraAttributesPractical::set_dof_blur_far_enabled(bool p_enabled) { + dof_blur_far_enabled = p_enabled; + _update_dof_blur(); + notify_property_list_changed(); +} + +bool CameraAttributesPractical::is_dof_blur_far_enabled() const { + return dof_blur_far_enabled; +} + +void CameraAttributesPractical::set_dof_blur_far_distance(float p_distance) { + dof_blur_far_distance = p_distance; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_far_distance() const { + return dof_blur_far_distance; +} + +void CameraAttributesPractical::set_dof_blur_far_transition(float p_distance) { + dof_blur_far_transition = p_distance; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_far_transition() const { + return dof_blur_far_transition; +} + +void CameraAttributesPractical::set_dof_blur_near_enabled(bool p_enabled) { + dof_blur_near_enabled = p_enabled; + _update_dof_blur(); + notify_property_list_changed(); +} + +bool CameraAttributesPractical::is_dof_blur_near_enabled() const { + return dof_blur_near_enabled; +} + +void CameraAttributesPractical::set_dof_blur_near_distance(float p_distance) { + dof_blur_near_distance = p_distance; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_near_distance() const { + return dof_blur_near_distance; +} + +void CameraAttributesPractical::set_dof_blur_near_transition(float p_distance) { + dof_blur_near_transition = p_distance; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_near_transition() const { + return dof_blur_near_transition; +} + +void CameraAttributesPractical::set_dof_blur_amount(float p_amount) { + dof_blur_amount = p_amount; + _update_dof_blur(); +} + +float CameraAttributesPractical::get_dof_blur_amount() const { + return dof_blur_amount; +} + +void CameraAttributesPractical::_update_dof_blur() { + RS::get_singleton()->camera_attributes_set_dof_blur( + get_rid(), + dof_blur_far_enabled, + dof_blur_far_distance, + dof_blur_far_transition, + dof_blur_near_enabled, + dof_blur_near_distance, + dof_blur_near_transition, + dof_blur_amount); +} + +float CameraAttributesPractical::calculate_exposure_normalization() const { + return exposure_sensitivity / 3072007.0; // Matches exposure normalization for default CameraAttributesPhysical at ISO 100. +} + +void CameraAttributesPractical::set_auto_exposure_min_sensitivity(float p_min) { + auto_exposure_min = p_min; + _update_auto_exposure(); +} + +float CameraAttributesPractical::get_auto_exposure_min_sensitivity() const { + return auto_exposure_min; +} + +void CameraAttributesPractical::set_auto_exposure_max_sensitivity(float p_max) { + auto_exposure_max = p_max; + _update_auto_exposure(); +} + +float CameraAttributesPractical::get_auto_exposure_max_sensitivity() const { + return auto_exposure_max; +} + +void CameraAttributesPractical::_update_auto_exposure() { + RS::get_singleton()->camera_attributes_set_auto_exposure( + get_rid(), + auto_exposure_enabled, + auto_exposure_min * ((12.5 / 100.0) / exposure_sensitivity), // Convert from Sensitivity to Luminance + auto_exposure_max * ((12.5 / 100.0) / exposure_sensitivity), // Convert from Sensitivity to Luminance + auto_exposure_speed, + auto_exposure_scale); + emit_changed(); +} + +void CameraAttributesPractical::_validate_property(PropertyInfo &p_property) const { + if ((!dof_blur_far_enabled && (p_property.name == "dof_blur_far_distance" || p_property.name == "dof_blur_far_transition")) || + (!dof_blur_near_enabled && (p_property.name == "dof_blur_near_distance" || p_property.name == "dof_blur_near_transition"))) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + } +} + +void CameraAttributesPractical::_bind_methods() { + // DOF blur + + ClassDB::bind_method(D_METHOD("set_dof_blur_far_enabled", "enabled"), &CameraAttributesPractical::set_dof_blur_far_enabled); + ClassDB::bind_method(D_METHOD("is_dof_blur_far_enabled"), &CameraAttributesPractical::is_dof_blur_far_enabled); + ClassDB::bind_method(D_METHOD("set_dof_blur_far_distance", "distance"), &CameraAttributesPractical::set_dof_blur_far_distance); + ClassDB::bind_method(D_METHOD("get_dof_blur_far_distance"), &CameraAttributesPractical::get_dof_blur_far_distance); + ClassDB::bind_method(D_METHOD("set_dof_blur_far_transition", "distance"), &CameraAttributesPractical::set_dof_blur_far_transition); + ClassDB::bind_method(D_METHOD("get_dof_blur_far_transition"), &CameraAttributesPractical::get_dof_blur_far_transition); + + ClassDB::bind_method(D_METHOD("set_dof_blur_near_enabled", "enabled"), &CameraAttributesPractical::set_dof_blur_near_enabled); + ClassDB::bind_method(D_METHOD("is_dof_blur_near_enabled"), &CameraAttributesPractical::is_dof_blur_near_enabled); + ClassDB::bind_method(D_METHOD("set_dof_blur_near_distance", "distance"), &CameraAttributesPractical::set_dof_blur_near_distance); + ClassDB::bind_method(D_METHOD("get_dof_blur_near_distance"), &CameraAttributesPractical::get_dof_blur_near_distance); + ClassDB::bind_method(D_METHOD("set_dof_blur_near_transition", "distance"), &CameraAttributesPractical::set_dof_blur_near_transition); + ClassDB::bind_method(D_METHOD("get_dof_blur_near_transition"), &CameraAttributesPractical::get_dof_blur_near_transition); + ClassDB::bind_method(D_METHOD("set_dof_blur_amount", "amount"), &CameraAttributesPractical::set_dof_blur_amount); + ClassDB::bind_method(D_METHOD("get_dof_blur_amount"), &CameraAttributesPractical::get_dof_blur_amount); + + ClassDB::bind_method(D_METHOD("set_auto_exposure_max_sensitivity", "max_sensitivity"), &CameraAttributesPractical::set_auto_exposure_max_sensitivity); + ClassDB::bind_method(D_METHOD("get_auto_exposure_max_sensitivity"), &CameraAttributesPractical::get_auto_exposure_max_sensitivity); + ClassDB::bind_method(D_METHOD("set_auto_exposure_min_sensitivity", "min_sensitivity"), &CameraAttributesPractical::set_auto_exposure_min_sensitivity); + ClassDB::bind_method(D_METHOD("get_auto_exposure_min_sensitivity"), &CameraAttributesPractical::get_auto_exposure_min_sensitivity); + + ADD_GROUP("DOF Blur", "dof_blur_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_far_enabled"), "set_dof_blur_far_enabled", "is_dof_blur_far_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_far_distance", "get_dof_blur_far_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_near_distance", "get_dof_blur_near_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_amount", "get_dof_blur_amount"); + + ADD_GROUP("Auto Exposure", "auto_exposure_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_min_sensitivity", PROPERTY_HINT_RANGE, "0,1600,0.01,or_greater,suffic:ISO"), "set_auto_exposure_min_sensitivity", "get_auto_exposure_min_sensitivity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_max_sensitivity", PROPERTY_HINT_RANGE, "0,64000,0.1,or_greater,suffic:ISO"), "set_auto_exposure_max_sensitivity", "get_auto_exposure_max_sensitivity"); +} + +CameraAttributesPractical::CameraAttributesPractical() { + _update_dof_blur(); + _update_exposure(); + set_auto_exposure_min_sensitivity(0.0); + set_auto_exposure_max_sensitivity(800.0); + notify_property_list_changed(); +} + +CameraAttributesPractical::~CameraAttributesPractical() { +} + +////////////////////////////////////////////////////// +/* CameraAttributesPhysical */ + +void CameraAttributesPhysical::set_aperture(float p_aperture) { + exposure_aperture = p_aperture; + _update_exposure(); + _update_frustum(); +} + +float CameraAttributesPhysical::get_aperture() const { + return exposure_aperture; +} + +void CameraAttributesPhysical::set_shutter_speed(float p_shutter_speed) { + exposure_shutter_speed = p_shutter_speed; + _update_exposure(); +} + +float CameraAttributesPhysical::get_shutter_speed() const { + return exposure_shutter_speed; +} + +void CameraAttributesPhysical::set_focal_length(float p_focal_length) { + frustum_focal_length = p_focal_length; + _update_frustum(); + emit_changed(); +} + +float CameraAttributesPhysical::get_focal_length() const { + return frustum_focal_length; +} + +void CameraAttributesPhysical::set_focus_distance(float p_focus_distance) { + frustum_focus_distance = p_focus_distance; + _update_frustum(); +} + +float CameraAttributesPhysical::get_focus_distance() const { + return frustum_focus_distance; +} + +void CameraAttributesPhysical::set_near(real_t p_near) { + frustum_near = p_near; + _update_frustum(); + emit_changed(); +} + +real_t CameraAttributesPhysical::get_near() const { + return frustum_near; +} + +void CameraAttributesPhysical::set_far(real_t p_far) { + frustum_far = p_far; + _update_frustum(); + emit_changed(); +} + +real_t CameraAttributesPhysical::get_far() const { + return frustum_far; +} + +real_t CameraAttributesPhysical::get_fov() const { + return frustum_fov; +} + +void CameraAttributesPhysical::_update_frustum() { + //https://en.wikipedia.org/wiki/Circle_of_confusion#Circle_of_confusion_diameter_limit_based_on_d/1500 + Vector2i sensor_size = Vector2i(36, 24); // Matches high-end DSLR, could be made variable if there is demand. + float CoC = sensor_size.length() / 1500.0; + + frustum_fov = Math::rad_to_deg(2 * atan(sensor_size.height / (2 * frustum_focal_length))); + + // Based on https://en.wikipedia.org/wiki/Depth_of_field. + float u = MAX(frustum_focus_distance * 1000.0, frustum_focal_length + 1.0); // Focus distance expressed in mm and clamped to at least 1 mm away from lens. + float hyperfocal_length = frustum_focal_length + ((frustum_focal_length * frustum_focal_length) / (exposure_aperture * CoC)); + + // This computes the start and end of the depth of field. Anything between these two points has a Circle of Confusino so small + // that it is not picked up by the camera sensors. + // To be properly physically-based, we would run the DoF shader at all depths. To be efficient, we are only running it where the CoC + // will be visible, this introduces some value shifts in the near field that we have to compensate for below. + float near = ((hyperfocal_length * u) / (hyperfocal_length + (u - frustum_focal_length))) / 1000.0; // In meters. + float far = ((hyperfocal_length * u) / (hyperfocal_length - (u - frustum_focal_length))) / 1000.0; // In meters. + float scale = (frustum_focal_length / (u - frustum_focal_length)) * (frustum_focal_length / exposure_aperture); + + bool use_far = (far < frustum_far) && (far > 0.0); + bool use_near = near > frustum_near; + RS::get_singleton()->camera_attributes_set_dof_blur( + get_rid(), + use_far, + u / 1000.0, // Focus distance clampd to focal length expressed in meters. + -1.0, // Negative to tell Bokeh effect to use physically-based scaling. + use_near, + u / 1000.0, + -1.0, + scale / 5.0); // Arbitrary scaling to get close to how much blur there should be. +} + +float CameraAttributesPhysical::calculate_exposure_normalization() const { + const float e = (exposure_aperture * exposure_aperture) * exposure_shutter_speed * (100.0 / exposure_sensitivity); + return 1.0 / (e * 1.2); +} + +void CameraAttributesPhysical::set_auto_exposure_min_exposure_value(float p_min) { + auto_exposure_min = p_min; + _update_auto_exposure(); +} + +float CameraAttributesPhysical::get_auto_exposure_min_exposure_value() const { + return auto_exposure_min; +} + +void CameraAttributesPhysical::set_auto_exposure_max_exposure_value(float p_max) { + auto_exposure_max = p_max; + _update_auto_exposure(); +} + +float CameraAttributesPhysical::get_auto_exposure_max_exposure_value() const { + return auto_exposure_max; +} + +void CameraAttributesPhysical::_update_auto_exposure() { + RS::get_singleton()->camera_attributes_set_auto_exposure( + get_rid(), + auto_exposure_enabled, + pow(2.0, auto_exposure_min) * (12.5 / exposure_sensitivity), // Convert from EV100 to Luminance + pow(2.0, auto_exposure_max) * (12.5 / exposure_sensitivity), // Convert from EV100 to Luminance + auto_exposure_speed, + auto_exposure_scale); + emit_changed(); +} + +void CameraAttributesPhysical::_validate_property(PropertyInfo &property) const { + if (!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units") && (property.name == "exposure_aperture" || property.name == "exposure_shutter_speed")) { + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + return; + } +} + +void CameraAttributesPhysical::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_aperture", "aperture"), &CameraAttributesPhysical::set_aperture); + ClassDB::bind_method(D_METHOD("get_aperture"), &CameraAttributesPhysical::get_aperture); + ClassDB::bind_method(D_METHOD("set_shutter_speed", "shutter_speed"), &CameraAttributesPhysical::set_shutter_speed); + ClassDB::bind_method(D_METHOD("get_shutter_speed"), &CameraAttributesPhysical::get_shutter_speed); + + ClassDB::bind_method(D_METHOD("set_focal_length", "focal_length"), &CameraAttributesPhysical::set_focal_length); + ClassDB::bind_method(D_METHOD("get_focal_length"), &CameraAttributesPhysical::get_focal_length); + ClassDB::bind_method(D_METHOD("set_focus_distance", "focus_distance"), &CameraAttributesPhysical::set_focus_distance); + ClassDB::bind_method(D_METHOD("get_focus_distance"), &CameraAttributesPhysical::get_focus_distance); + ClassDB::bind_method(D_METHOD("set_near", "near"), &CameraAttributesPhysical::set_near); + ClassDB::bind_method(D_METHOD("get_near"), &CameraAttributesPhysical::get_near); + ClassDB::bind_method(D_METHOD("set_far", "far"), &CameraAttributesPhysical::set_far); + ClassDB::bind_method(D_METHOD("get_far"), &CameraAttributesPhysical::get_far); + ClassDB::bind_method(D_METHOD("get_fov"), &CameraAttributesPhysical::get_fov); + + ClassDB::bind_method(D_METHOD("set_auto_exposure_max_exposure_value", "exposure_value_max"), &CameraAttributesPhysical::set_auto_exposure_max_exposure_value); + ClassDB::bind_method(D_METHOD("get_auto_exposure_max_exposure_value"), &CameraAttributesPhysical::get_auto_exposure_max_exposure_value); + ClassDB::bind_method(D_METHOD("set_auto_exposure_min_exposure_value", "exposure_value_min"), &CameraAttributesPhysical::set_auto_exposure_min_exposure_value); + ClassDB::bind_method(D_METHOD("get_auto_exposure_min_exposure_value"), &CameraAttributesPhysical::get_auto_exposure_min_exposure_value); + + ADD_GROUP("Frustum", "frustum_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_focus_distance", PROPERTY_HINT_RANGE, "0.01,4000.0,0.01,suffix:m"), "set_focus_distance", "get_focus_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_focal_length", PROPERTY_HINT_RANGE, "1.0,800.0,0.01,exp,suffix:mm"), "set_focal_length", "get_focal_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp,suffix:m"), "set_near", "get_near"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp,suffix:m"), "set_far", "get_far"); + + ADD_GROUP("Exposure", "exposure"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_aperture", PROPERTY_HINT_RANGE, "0.5,64.0,0.01,exp,suffix:f-stop"), "set_aperture", "get_aperture"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_shutter_speed", PROPERTY_HINT_RANGE, "0.1,8000.0,0.001,suffix:1/s"), "set_shutter_speed", "get_shutter_speed"); + + ADD_GROUP("Auto Exposure", "auto_exposure_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_min_exposure_value", PROPERTY_HINT_RANGE, "-16.0,16.0,0.01,or_greater,suffix:EV100"), "set_auto_exposure_min_exposure_value", "get_auto_exposure_min_exposure_value"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_max_exposure_value", PROPERTY_HINT_RANGE, "-16.0,16.0,0.01,or_greater,suffix:EV100"), "set_auto_exposure_max_exposure_value", "get_auto_exposure_max_exposure_value"); +}; + +CameraAttributesPhysical::CameraAttributesPhysical() { + _update_exposure(); + _update_frustum(); + set_auto_exposure_min_exposure_value(-8); + set_auto_exposure_max_exposure_value(10); // Use a wide range by default to feel more like a real camera. + notify_property_list_changed(); +} + +CameraAttributesPhysical::~CameraAttributesPhysical() { +} diff --git a/scene/resources/camera_effects.h b/scene/resources/camera_attributes.h index 7353931d16..c4c783af29 100644 --- a/scene/resources/camera_effects.h +++ b/scene/resources/camera_attributes.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* camera_effects.h */ +/* camera_attributes.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,18 +28,57 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CAMERA_EFFECTS_H -#define CAMERA_EFFECTS_H +#ifndef CAMERA_ATTRIBUTES_H +#define CAMERA_ATTRIBUTES_H #include "core/io/resource.h" #include "core/templates/rid.h" -class CameraEffects : public Resource { - GDCLASS(CameraEffects, Resource); +class CameraAttributes : public Resource { + GDCLASS(CameraAttributes, Resource); private: - RID camera_effects; + RID camera_attributes; +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; + + float exposure_multiplier = 1.0; + float exposure_sensitivity = 100.0; // In ISO. + void _update_exposure(); + + bool auto_exposure_enabled = false; + float auto_exposure_min = 0.01; + float auto_exposure_max = 64.0; + float auto_exposure_speed = 0.5; + float auto_exposure_scale = 0.4; + virtual void _update_auto_exposure(){}; + +public: + virtual RID get_rid() const override; + virtual float calculate_exposure_normalization() const { return 1.0; } + + void set_exposure_multiplier(float p_multiplier); + float get_exposure_multiplier() const; + void set_exposure_sensitivity(float p_sensitivity); + float get_exposure_sensitivity() const; + + void set_auto_exposure_enabled(bool p_enabled); + bool is_auto_exposure_enabled() const; + void set_auto_exposure_speed(float p_auto_exposure_speed); + float get_auto_exposure_speed() const; + void set_auto_exposure_scale(float p_auto_exposure_scale); + float get_auto_exposure_scale() const; + + CameraAttributes(); + ~CameraAttributes(); +}; + +class CameraAttributesPractical : public CameraAttributes { + GDCLASS(CameraAttributesPractical, CameraAttributes); + +private: // DOF blur bool dof_blur_far_enabled = false; float dof_blur_far_distance = 10.0; @@ -52,18 +91,13 @@ private: float dof_blur_amount = 0.1; void _update_dof_blur(); - // Override exposure - bool override_exposure_enabled = false; - float override_exposure = 1.0; - void _update_override_exposure(); + virtual void _update_auto_exposure() override; protected: static void _bind_methods(); void _validate_property(PropertyInfo &p_property) const; public: - virtual RID get_rid() const override; - // DOF blur void set_dof_blur_far_enabled(bool p_enabled); bool is_dof_blur_far_enabled() const; @@ -78,18 +112,72 @@ public: float get_dof_blur_near_distance() const; void set_dof_blur_near_transition(float p_distance); float get_dof_blur_near_transition() const; - void set_dof_blur_amount(float p_amount); float get_dof_blur_amount() const; - // Override exposure - void set_override_exposure_enabled(bool p_enabled); - bool is_override_exposure_enabled() const; - void set_override_exposure(float p_exposure); - float get_override_exposure() const; + void set_auto_exposure_min_sensitivity(float p_min); + float get_auto_exposure_min_sensitivity() const; + void set_auto_exposure_max_sensitivity(float p_max); + float get_auto_exposure_max_sensitivity() const; + + virtual float calculate_exposure_normalization() const override; + + CameraAttributesPractical(); + ~CameraAttributesPractical(); +}; + +class CameraAttributesPhysical : public CameraAttributes { + GDCLASS(CameraAttributesPhysical, CameraAttributes); + +private: + // Exposure + float exposure_aperture = 16.0; // In f-stops; + float exposure_shutter_speed = 100.0; // In 1 / seconds; + + // Camera properties. + float frustum_focal_length = 35.0; // In millimeters. + float frustum_focus_distance = 10.0; // In Meters. + real_t frustum_near = 0.05; + real_t frustum_far = 4000.0; + real_t frustum_fov = 75.0; + void _update_frustum(); + + virtual void _update_auto_exposure() override; + +protected: + static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; + +public: + void set_aperture(float p_aperture); + float get_aperture() const; + + void set_shutter_speed(float p_shutter_speed); + float get_shutter_speed() const; + + void set_focal_length(float p_focal_length); + float get_focal_length() const; + + void set_focus_distance(float p_focus_distance); + float get_focus_distance() const; + + void set_near(real_t p_near); + real_t get_near() const; + + void set_far(real_t p_far); + real_t get_far() const; + + real_t get_fov() const; + + void set_auto_exposure_min_exposure_value(float p_min); + float get_auto_exposure_min_exposure_value() const; + void set_auto_exposure_max_exposure_value(float p_max); + float get_auto_exposure_max_exposure_value() const; + + virtual float calculate_exposure_normalization() const override; - CameraEffects(); - ~CameraEffects(); + CameraAttributesPhysical(); + ~CameraAttributesPhysical(); }; -#endif // CAMERA_EFFECTS_H +#endif // CAMERA_ATTRIBUTES_H diff --git a/scene/resources/camera_effects.cpp b/scene/resources/camera_effects.cpp deleted file mode 100644 index 0b11366591..0000000000 --- a/scene/resources/camera_effects.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/*************************************************************************/ -/* camera_effects.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "camera_effects.h" - -#include "servers/rendering_server.h" - -RID CameraEffects::get_rid() const { - return camera_effects; -} - -// DOF blur - -void CameraEffects::set_dof_blur_far_enabled(bool p_enabled) { - dof_blur_far_enabled = p_enabled; - _update_dof_blur(); - notify_property_list_changed(); -} - -bool CameraEffects::is_dof_blur_far_enabled() const { - return dof_blur_far_enabled; -} - -void CameraEffects::set_dof_blur_far_distance(float p_distance) { - dof_blur_far_distance = p_distance; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_far_distance() const { - return dof_blur_far_distance; -} - -void CameraEffects::set_dof_blur_far_transition(float p_distance) { - dof_blur_far_transition = p_distance; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_far_transition() const { - return dof_blur_far_transition; -} - -void CameraEffects::set_dof_blur_near_enabled(bool p_enabled) { - dof_blur_near_enabled = p_enabled; - _update_dof_blur(); - notify_property_list_changed(); -} - -bool CameraEffects::is_dof_blur_near_enabled() const { - return dof_blur_near_enabled; -} - -void CameraEffects::set_dof_blur_near_distance(float p_distance) { - dof_blur_near_distance = p_distance; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_near_distance() const { - return dof_blur_near_distance; -} - -void CameraEffects::set_dof_blur_near_transition(float p_distance) { - dof_blur_near_transition = p_distance; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_near_transition() const { - return dof_blur_near_transition; -} - -void CameraEffects::set_dof_blur_amount(float p_amount) { - dof_blur_amount = p_amount; - _update_dof_blur(); -} - -float CameraEffects::get_dof_blur_amount() const { - return dof_blur_amount; -} - -void CameraEffects::_update_dof_blur() { - RS::get_singleton()->camera_effects_set_dof_blur( - camera_effects, - dof_blur_far_enabled, - dof_blur_far_distance, - dof_blur_far_transition, - dof_blur_near_enabled, - dof_blur_near_distance, - dof_blur_near_transition, - dof_blur_amount); -} - -// Custom exposure - -void CameraEffects::set_override_exposure_enabled(bool p_enabled) { - override_exposure_enabled = p_enabled; - _update_override_exposure(); - notify_property_list_changed(); -} - -bool CameraEffects::is_override_exposure_enabled() const { - return override_exposure_enabled; -} - -void CameraEffects::set_override_exposure(float p_exposure) { - override_exposure = p_exposure; - _update_override_exposure(); -} - -float CameraEffects::get_override_exposure() const { - return override_exposure; -} - -void CameraEffects::_update_override_exposure() { - RS::get_singleton()->camera_effects_set_custom_exposure( - camera_effects, - override_exposure_enabled, - override_exposure); -} - -// Private methods, constructor and destructor - -void CameraEffects::_validate_property(PropertyInfo &p_property) const { - if ((!dof_blur_far_enabled && (p_property.name == "dof_blur_far_distance" || p_property.name == "dof_blur_far_transition")) || - (!dof_blur_near_enabled && (p_property.name == "dof_blur_near_distance" || p_property.name == "dof_blur_near_transition")) || - (!override_exposure_enabled && p_property.name == "override_exposure")) { - p_property.usage = PROPERTY_USAGE_NO_EDITOR; - } -} - -void CameraEffects::_bind_methods() { - // DOF blur - - ClassDB::bind_method(D_METHOD("set_dof_blur_far_enabled", "enabled"), &CameraEffects::set_dof_blur_far_enabled); - ClassDB::bind_method(D_METHOD("is_dof_blur_far_enabled"), &CameraEffects::is_dof_blur_far_enabled); - ClassDB::bind_method(D_METHOD("set_dof_blur_far_distance", "distance"), &CameraEffects::set_dof_blur_far_distance); - ClassDB::bind_method(D_METHOD("get_dof_blur_far_distance"), &CameraEffects::get_dof_blur_far_distance); - ClassDB::bind_method(D_METHOD("set_dof_blur_far_transition", "distance"), &CameraEffects::set_dof_blur_far_transition); - ClassDB::bind_method(D_METHOD("get_dof_blur_far_transition"), &CameraEffects::get_dof_blur_far_transition); - - ClassDB::bind_method(D_METHOD("set_dof_blur_near_enabled", "enabled"), &CameraEffects::set_dof_blur_near_enabled); - ClassDB::bind_method(D_METHOD("is_dof_blur_near_enabled"), &CameraEffects::is_dof_blur_near_enabled); - ClassDB::bind_method(D_METHOD("set_dof_blur_near_distance", "distance"), &CameraEffects::set_dof_blur_near_distance); - ClassDB::bind_method(D_METHOD("get_dof_blur_near_distance"), &CameraEffects::get_dof_blur_near_distance); - ClassDB::bind_method(D_METHOD("set_dof_blur_near_transition", "distance"), &CameraEffects::set_dof_blur_near_transition); - ClassDB::bind_method(D_METHOD("get_dof_blur_near_transition"), &CameraEffects::get_dof_blur_near_transition); - - ClassDB::bind_method(D_METHOD("set_dof_blur_amount", "amount"), &CameraEffects::set_dof_blur_amount); - ClassDB::bind_method(D_METHOD("get_dof_blur_amount"), &CameraEffects::get_dof_blur_amount); - - ADD_GROUP("DOF Blur", "dof_blur_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_far_enabled"), "set_dof_blur_far_enabled", "is_dof_blur_far_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_far_distance", "get_dof_blur_far_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_far_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_far_transition", "get_dof_blur_far_transition"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dof_blur_near_enabled"), "set_dof_blur_near_enabled", "is_dof_blur_near_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_distance", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp,suffix:m"), "set_dof_blur_near_distance", "get_dof_blur_near_distance"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_near_transition", PROPERTY_HINT_RANGE, "0.01,8192,0.01,exp"), "set_dof_blur_near_transition", "get_dof_blur_near_transition"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dof_blur_amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dof_blur_amount", "get_dof_blur_amount"); - - // Override exposure - - ClassDB::bind_method(D_METHOD("set_override_exposure_enabled", "enabled"), &CameraEffects::set_override_exposure_enabled); - ClassDB::bind_method(D_METHOD("is_override_exposure_enabled"), &CameraEffects::is_override_exposure_enabled); - ClassDB::bind_method(D_METHOD("set_override_exposure", "exposure"), &CameraEffects::set_override_exposure); - ClassDB::bind_method(D_METHOD("get_override_exposure"), &CameraEffects::get_override_exposure); - - ADD_GROUP("Override Exposure", "override_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_exposure_enabled"), "set_override_exposure_enabled", "is_override_exposure_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "override_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_override_exposure", "get_override_exposure"); -} - -CameraEffects::CameraEffects() { - camera_effects = RS::get_singleton()->camera_effects_create(); - - _update_dof_blur(); - _update_override_exposure(); -} - -CameraEffects::~CameraEffects() { - RS::get_singleton()->free(camera_effects); -} diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index ee53578517..0ea5264935 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -313,7 +313,7 @@ void Curve::set_max_value(real_t p_max) { emit_signal(SNAME(SIGNAL_RANGE_CHANGED)); } -real_t Curve::interpolate(real_t p_offset) const { +real_t Curve::sample(real_t p_offset) const { if (_points.size() == 0) { return 0; } @@ -333,10 +333,10 @@ real_t Curve::interpolate(real_t p_offset) const { return _points[0].position.y; } - return interpolate_local_nocheck(i, local); + return sample_local_nocheck(i, local); } -real_t Curve::interpolate_local_nocheck(int p_index, real_t p_local_offset) const { +real_t Curve::sample_local_nocheck(int p_index, real_t p_local_offset) const { const Point a = _points[p_index]; const Point b = _points[p_index + 1]; @@ -440,7 +440,7 @@ void Curve::bake() { for (int i = 1; i < _bake_resolution - 1; ++i) { real_t x = i / static_cast<real_t>(_bake_resolution); - real_t y = interpolate(x); + real_t y = sample(x); _baked_cache.write[i] = y; } @@ -459,7 +459,7 @@ void Curve::set_bake_resolution(int p_resolution) { _baked_cache_dirty = true; } -real_t Curve::interpolate_baked(real_t p_offset) const { +real_t Curve::sample_baked(real_t p_offset) const { if (_baked_cache_dirty) { // Last-second bake if not done already const_cast<Curve *>(this)->bake(); @@ -486,7 +486,7 @@ real_t Curve::interpolate_baked(real_t p_offset) const { fi = 0; } - // Interpolate + // Sample if (i + 1 < _baked_cache.size()) { real_t t = fi - i; return Math::lerp(_baked_cache[i], _baked_cache[i + 1], t); @@ -595,8 +595,8 @@ void Curve::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_position", "index"), &Curve::get_point_position); ClassDB::bind_method(D_METHOD("set_point_value", "index", "y"), &Curve::set_point_value); ClassDB::bind_method(D_METHOD("set_point_offset", "index", "offset"), &Curve::set_point_offset); - ClassDB::bind_method(D_METHOD("interpolate", "offset"), &Curve::interpolate); - ClassDB::bind_method(D_METHOD("interpolate_baked", "offset"), &Curve::interpolate_baked); + ClassDB::bind_method(D_METHOD("sample", "offset"), &Curve::sample); + ClassDB::bind_method(D_METHOD("sample_baked", "offset"), &Curve::sample_baked); ClassDB::bind_method(D_METHOD("get_point_left_tangent", "index"), &Curve::get_point_left_tangent); ClassDB::bind_method(D_METHOD("get_point_right_tangent", "index"), &Curve::get_point_right_tangent); ClassDB::bind_method(D_METHOD("get_point_left_mode", "index"), &Curve::get_point_left_mode); @@ -720,7 +720,7 @@ void Curve2D::clear_points() { } } -Vector2 Curve2D::interpolate(int p_index, const real_t p_offset) const { +Vector2 Curve2D::sample(int p_index, const real_t p_offset) const { int pc = points.size(); ERR_FAIL_COND_V(pc == 0, Vector2()); @@ -738,14 +738,14 @@ Vector2 Curve2D::interpolate(int p_index, const real_t p_offset) const { return p0.bezier_interpolate(p1, p2, p3, p_offset); } -Vector2 Curve2D::interpolatef(real_t p_findex) const { +Vector2 Curve2D::samplef(real_t p_findex) const { if (p_findex < 0) { p_findex = 0; } else if (p_findex >= points.size()) { p_findex = points.size(); } - return interpolate((int)p_findex, Math::fmod(p_findex, (real_t)1.0)); + return sample((int)p_findex, Math::fmod(p_findex, (real_t)1.0)); } void Curve2D::mark_dirty() { @@ -883,7 +883,7 @@ real_t Curve2D::get_baked_length() const { return baked_max_ofs; } -Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const { +Vector2 Curve2D::sample_baked(real_t p_offset, bool p_cubic) const { if (baked_cache_dirty) { _bake(); } @@ -923,7 +923,7 @@ Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const { real_t offset_end = baked_dist_cache[idx + 1]; real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "failed to find baked segment"); + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "Couldn't find baked segment."); real_t frac = (p_offset - offset_begin) / idx_interval; @@ -1176,14 +1176,14 @@ void Curve2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_out", "idx"), &Curve2D::get_point_out); ClassDB::bind_method(D_METHOD("remove_point", "idx"), &Curve2D::remove_point); ClassDB::bind_method(D_METHOD("clear_points"), &Curve2D::clear_points); - ClassDB::bind_method(D_METHOD("interpolate", "idx", "t"), &Curve2D::interpolate); - ClassDB::bind_method(D_METHOD("interpolatef", "fofs"), &Curve2D::interpolatef); + ClassDB::bind_method(D_METHOD("sample", "idx", "t"), &Curve2D::sample); + ClassDB::bind_method(D_METHOD("samplef", "fofs"), &Curve2D::samplef); //ClassDB::bind_method(D_METHOD("bake","subdivs"),&Curve2D::bake,DEFVAL(10)); ClassDB::bind_method(D_METHOD("set_bake_interval", "distance"), &Curve2D::set_bake_interval); ClassDB::bind_method(D_METHOD("get_bake_interval"), &Curve2D::get_bake_interval); ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve2D::get_baked_length); - ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve2D::interpolate_baked, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve2D::sample_baked, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve2D::get_baked_points); ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve2D::get_closest_point); ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve2D::get_closest_offset); @@ -1309,7 +1309,7 @@ void Curve3D::clear_points() { } } -Vector3 Curve3D::interpolate(int p_index, real_t p_offset) const { +Vector3 Curve3D::sample(int p_index, real_t p_offset) const { int pc = points.size(); ERR_FAIL_COND_V(pc == 0, Vector3()); @@ -1327,14 +1327,14 @@ Vector3 Curve3D::interpolate(int p_index, real_t p_offset) const { return p0.bezier_interpolate(p1, p2, p3, p_offset); } -Vector3 Curve3D::interpolatef(real_t p_findex) const { +Vector3 Curve3D::samplef(real_t p_findex) const { if (p_findex < 0) { p_findex = 0; } else if (p_findex >= points.size()) { p_findex = points.size(); } - return interpolate((int)p_findex, Math::fmod(p_findex, (real_t)1.0)); + return sample((int)p_findex, Math::fmod(p_findex, (real_t)1.0)); } void Curve3D::mark_dirty() { @@ -1536,7 +1536,7 @@ real_t Curve3D::get_baked_length() const { return baked_max_ofs; } -Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const { +Vector3 Curve3D::sample_baked(real_t p_offset, bool p_cubic) const { if (baked_cache_dirty) { _bake(); } @@ -1576,7 +1576,7 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const { real_t offset_end = baked_dist_cache[idx + 1]; real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(), "failed to find baked segment"); + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(), "Couldn't find baked segment."); real_t frac = (p_offset - offset_begin) / idx_interval; @@ -1589,7 +1589,7 @@ Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const { } } -real_t Curve3D::interpolate_baked_tilt(real_t p_offset) const { +real_t Curve3D::sample_baked_tilt(real_t p_offset) const { if (baked_cache_dirty) { _bake(); } @@ -1629,14 +1629,14 @@ real_t Curve3D::interpolate_baked_tilt(real_t p_offset) const { real_t offset_end = baked_dist_cache[idx + 1]; real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, 0, "failed to find baked segment"); + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, 0, "Couldn't find baked segment."); real_t frac = (p_offset - offset_begin) / idx_interval; return Math::lerp(r[idx], r[idx + 1], (real_t)frac); } -Vector3 Curve3D::interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt) const { +Vector3 Curve3D::sample_baked_up_vector(real_t p_offset, bool p_apply_tilt) const { if (baked_cache_dirty) { _bake(); } @@ -1671,7 +1671,7 @@ Vector3 Curve3D::interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt) real_t offset_end = baked_dist_cache[idx + 1]; real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(0, 1, 0), "failed to find baked segment"); + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(0, 1, 0), "Couldn't find baked segment."); real_t frac = (p_offset - offset_begin) / idx_interval; @@ -1983,8 +1983,8 @@ void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_out", "idx"), &Curve3D::get_point_out); ClassDB::bind_method(D_METHOD("remove_point", "idx"), &Curve3D::remove_point); ClassDB::bind_method(D_METHOD("clear_points"), &Curve3D::clear_points); - ClassDB::bind_method(D_METHOD("interpolate", "idx", "t"), &Curve3D::interpolate); - ClassDB::bind_method(D_METHOD("interpolatef", "fofs"), &Curve3D::interpolatef); + ClassDB::bind_method(D_METHOD("sample", "idx", "t"), &Curve3D::sample); + ClassDB::bind_method(D_METHOD("samplef", "fofs"), &Curve3D::samplef); //ClassDB::bind_method(D_METHOD("bake","subdivs"),&Curve3D::bake,DEFVAL(10)); ClassDB::bind_method(D_METHOD("set_bake_interval", "distance"), &Curve3D::set_bake_interval); ClassDB::bind_method(D_METHOD("get_bake_interval"), &Curve3D::get_bake_interval); @@ -1992,8 +1992,8 @@ void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_up_vector_enabled"), &Curve3D::is_up_vector_enabled); ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve3D::get_baked_length); - ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve3D::interpolate_baked, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("interpolate_baked_up_vector", "offset", "apply_tilt"), &Curve3D::interpolate_baked_up_vector, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve3D::sample_baked, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("sample_baked_up_vector", "offset", "apply_tilt"), &Curve3D::sample_baked_up_vector, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve3D::get_baked_points); ClassDB::bind_method(D_METHOD("get_baked_tilts"), &Curve3D::get_baked_tilts); ClassDB::bind_method(D_METHOD("get_baked_up_vectors"), &Curve3D::get_baked_up_vectors); diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 08807b1b6e..88b6dda096 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -100,8 +100,8 @@ public: real_t get_max_value() const { return _max_value; } void set_max_value(real_t p_max); - real_t interpolate(real_t p_offset) const; - real_t interpolate_local_nocheck(int p_index, real_t p_local_offset) const; + real_t sample(real_t p_offset) const; + real_t sample_local_nocheck(int p_index, real_t p_local_offset) const; void clean_dupes(); @@ -123,7 +123,7 @@ public: void bake(); int get_bake_resolution() const { return _bake_resolution; } void set_bake_resolution(int p_resolution); - real_t interpolate_baked(real_t p_offset) const; + real_t sample_baked(real_t p_offset) const; void ensure_default_setup(real_t p_min, real_t p_max); @@ -208,14 +208,14 @@ public: void remove_point(int p_index); void clear_points(); - Vector2 interpolate(int p_index, real_t p_offset) const; - Vector2 interpolatef(real_t p_findex) const; + Vector2 sample(int p_index, real_t p_offset) const; + Vector2 samplef(real_t p_findex) const; void set_bake_interval(real_t p_tolerance); real_t get_bake_interval() const; real_t get_baked_length() const; - Vector2 interpolate_baked(real_t p_offset, bool p_cubic = false) const; + Vector2 sample_baked(real_t p_offset, bool p_cubic = false) const; PackedVector2Array get_baked_points() const; //useful for going through Vector2 get_closest_point(const Vector2 &p_to_point) const; real_t get_closest_offset(const Vector2 &p_to_point) const; @@ -285,8 +285,8 @@ public: void remove_point(int p_index); void clear_points(); - Vector3 interpolate(int p_index, real_t p_offset) const; - Vector3 interpolatef(real_t p_findex) const; + Vector3 sample(int p_index, real_t p_offset) const; + Vector3 samplef(real_t p_findex) const; void set_bake_interval(real_t p_tolerance); real_t get_bake_interval() const; @@ -294,9 +294,9 @@ public: bool is_up_vector_enabled() const; real_t get_baked_length() const; - Vector3 interpolate_baked(real_t p_offset, bool p_cubic = false) const; - real_t interpolate_baked_tilt(real_t p_offset) const; - Vector3 interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt = false) const; + Vector3 sample_baked(real_t p_offset, bool p_cubic = false) const; + real_t sample_baked_tilt(real_t p_offset) const; + Vector3 sample_baked_up_vector(real_t p_offset, bool p_apply_tilt = false) const; PackedVector3Array get_baked_points() const; //useful for going through Vector<real_t> get_baked_tilts() const; //useful for going through PackedVector3Array get_baked_up_vectors() const; diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index f7a7818b3b..8c23471e73 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -94,13 +94,30 @@ Color Environment::get_bg_color() const { return bg_color; } -void Environment::set_bg_energy(float p_energy) { - bg_energy = p_energy; - RS::get_singleton()->environment_set_bg_energy(environment, p_energy); +void Environment::set_bg_energy_multiplier(float p_multiplier) { + bg_energy_multiplier = p_multiplier; + _update_bg_energy(); } -float Environment::get_bg_energy() const { - return bg_energy; +float Environment::get_bg_energy_multiplier() const { + return bg_energy_multiplier; +} + +void Environment::set_bg_intensity(float p_exposure_value) { + bg_intensity = p_exposure_value; + _update_bg_energy(); +} + +float Environment::get_bg_intensity() const { + return bg_intensity; +} + +void Environment::_update_bg_energy() { + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + RS::get_singleton()->environment_set_bg_energy(environment, bg_energy_multiplier, bg_intensity); + } else { + RS::get_singleton()->environment_set_bg_energy(environment, bg_energy_multiplier, 1.0); + } } void Environment::set_canvas_max_layer(int p_max_layer) { @@ -214,63 +231,12 @@ float Environment::get_tonemap_white() const { return tonemap_white; } -void Environment::set_tonemap_auto_exposure_enabled(bool p_enabled) { - tonemap_auto_exposure_enabled = p_enabled; - _update_tonemap(); - notify_property_list_changed(); -} - -bool Environment::is_tonemap_auto_exposure_enabled() const { - return tonemap_auto_exposure_enabled; -} - -void Environment::set_tonemap_auto_exposure_min(float p_auto_exposure_min) { - tonemap_auto_exposure_min = p_auto_exposure_min; - _update_tonemap(); -} - -float Environment::get_tonemap_auto_exposure_min() const { - return tonemap_auto_exposure_min; -} - -void Environment::set_tonemap_auto_exposure_max(float p_auto_exposure_max) { - tonemap_auto_exposure_max = p_auto_exposure_max; - _update_tonemap(); -} - -float Environment::get_tonemap_auto_exposure_max() const { - return tonemap_auto_exposure_max; -} - -void Environment::set_tonemap_auto_exposure_speed(float p_auto_exposure_speed) { - tonemap_auto_exposure_speed = p_auto_exposure_speed; - _update_tonemap(); -} - -float Environment::get_tonemap_auto_exposure_speed() const { - return tonemap_auto_exposure_speed; -} - -void Environment::set_tonemap_auto_exposure_grey(float p_auto_exposure_grey) { - tonemap_auto_exposure_grey = p_auto_exposure_grey; - _update_tonemap(); -} - -float Environment::get_tonemap_auto_exposure_grey() const { - return tonemap_auto_exposure_grey; -} - void Environment::_update_tonemap() { RS::get_singleton()->environment_set_tonemap( environment, RS::EnvironmentToneMapper(tone_mapper), tonemap_exposure, - tonemap_white, - tonemap_auto_exposure_enabled, - tonemap_auto_exposure_min, - tonemap_auto_exposure_max, - tonemap_auto_exposure_speed, - tonemap_auto_exposure_grey); + tonemap_white); } // SSR @@ -1080,10 +1046,13 @@ void Environment::_validate_property(PropertyInfo &p_property) const { } } + if (p_property.name == "background_intensity" && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + static const char *hide_prefixes[] = { "fog_", "volumetric_fog_", - "auto_exposure_", "ssr_", "ssao_", "ssil_", @@ -1095,7 +1064,6 @@ void Environment::_validate_property(PropertyInfo &p_property) const { }; static const char *high_end_prefixes[] = { - "auto_exposure_", "ssr_", "ssao_", nullptr @@ -1162,8 +1130,10 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_sky_rotation"), &Environment::get_sky_rotation); ClassDB::bind_method(D_METHOD("set_bg_color", "color"), &Environment::set_bg_color); ClassDB::bind_method(D_METHOD("get_bg_color"), &Environment::get_bg_color); - ClassDB::bind_method(D_METHOD("set_bg_energy", "energy"), &Environment::set_bg_energy); - ClassDB::bind_method(D_METHOD("get_bg_energy"), &Environment::get_bg_energy); + ClassDB::bind_method(D_METHOD("set_bg_energy_multiplier", "energy"), &Environment::set_bg_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_bg_energy_multiplier"), &Environment::get_bg_energy_multiplier); + ClassDB::bind_method(D_METHOD("set_bg_intensity", "energy"), &Environment::set_bg_intensity); + ClassDB::bind_method(D_METHOD("get_bg_intensity"), &Environment::get_bg_intensity); ClassDB::bind_method(D_METHOD("set_canvas_max_layer", "layer"), &Environment::set_canvas_max_layer); ClassDB::bind_method(D_METHOD("get_canvas_max_layer"), &Environment::get_canvas_max_layer); ClassDB::bind_method(D_METHOD("set_camera_feed_id", "id"), &Environment::set_camera_feed_id); @@ -1172,7 +1142,9 @@ void Environment::_bind_methods() { ADD_GROUP("Background", "background_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "background_mode", PROPERTY_HINT_ENUM, "Clear Color,Custom Color,Sky,Canvas,Keep,Camera Feed"), "set_background", "get_background"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "background_color"), "set_bg_color", "get_bg_color"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "background_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_bg_energy", "get_bg_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "background_energy_multiplier", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_bg_energy_multiplier", "get_bg_energy_multiplier"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "background_intensity", PROPERTY_HINT_RANGE, "0,100000,0.01,suffix:nt"), "set_bg_intensity", "get_bg_intensity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "background_canvas_max_layer", PROPERTY_HINT_RANGE, "-1000,1000,1"), "set_canvas_max_layer", "get_canvas_max_layer"); ADD_PROPERTY(PropertyInfo(Variant::INT, "background_camera_feed_id", PROPERTY_HINT_RANGE, "1,10,1"), "set_camera_feed_id", "get_camera_feed_id"); @@ -1211,27 +1183,11 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tonemap_exposure"), &Environment::get_tonemap_exposure); ClassDB::bind_method(D_METHOD("set_tonemap_white", "white"), &Environment::set_tonemap_white); ClassDB::bind_method(D_METHOD("get_tonemap_white"), &Environment::get_tonemap_white); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_enabled", "enabled"), &Environment::set_tonemap_auto_exposure_enabled); - ClassDB::bind_method(D_METHOD("is_tonemap_auto_exposure_enabled"), &Environment::is_tonemap_auto_exposure_enabled); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_max", "exposure_max"), &Environment::set_tonemap_auto_exposure_max); - ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_max"), &Environment::get_tonemap_auto_exposure_max); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_min", "exposure_min"), &Environment::set_tonemap_auto_exposure_min); - ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_min"), &Environment::get_tonemap_auto_exposure_min); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_speed", "exposure_speed"), &Environment::set_tonemap_auto_exposure_speed); - ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_speed"), &Environment::get_tonemap_auto_exposure_speed); - ClassDB::bind_method(D_METHOD("set_tonemap_auto_exposure_grey", "exposure_grey"), &Environment::set_tonemap_auto_exposure_grey); - ClassDB::bind_method(D_METHOD("get_tonemap_auto_exposure_grey"), &Environment::get_tonemap_auto_exposure_grey); ADD_GROUP("Tonemap", "tonemap_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tonemap_mode", PROPERTY_HINT_ENUM, "Linear,Reinhard,Filmic,ACES"), "set_tonemapper", "get_tonemapper"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_exposure", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_exposure", "get_tonemap_exposure"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "tonemap_white", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_white", "get_tonemap_white"); - ADD_GROUP("Auto Exposure", "auto_exposure_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_exposure_enabled"), "set_tonemap_auto_exposure_enabled", "is_tonemap_auto_exposure_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_tonemap_auto_exposure_grey", "get_tonemap_auto_exposure_grey"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_min_luma", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_auto_exposure_min", "get_tonemap_auto_exposure_min"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_max_luma", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_tonemap_auto_exposure_max", "get_tonemap_auto_exposure_max"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "auto_exposure_speed", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_tonemap_auto_exposure_speed", "get_tonemap_auto_exposure_speed"); // SSR @@ -1549,6 +1505,7 @@ Environment::Environment() { _update_fog(); _update_adjustment(); _update_volumetric_fog(); + _update_bg_energy(); notify_property_list_changed(); } diff --git a/scene/resources/environment.h b/scene/resources/environment.h index d39cb1acd8..6d8330f74b 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -92,9 +92,11 @@ private: float bg_sky_custom_fov = 0.0; Vector3 bg_sky_rotation; Color bg_color; - float bg_energy = 1.0; int bg_canvas_max_layer = 0; int bg_camera_feed_id = 1; + float bg_energy_multiplier = 1.0; + float bg_intensity = 30000.0; // Measured in nits or candela/m^2 + void _update_bg_energy(); // Ambient light Color ambient_color; @@ -108,11 +110,6 @@ private: ToneMapper tone_mapper = TONE_MAPPER_LINEAR; float tonemap_exposure = 1.0; float tonemap_white = 1.0; - bool tonemap_auto_exposure_enabled = false; - float tonemap_auto_exposure_min = 0.05; - float tonemap_auto_exposure_max = 8.0; - float tonemap_auto_exposure_speed = 0.5; - float tonemap_auto_exposure_grey = 0.4; void _update_tonemap(); // SSR @@ -231,8 +228,10 @@ public: Vector3 get_sky_rotation() const; void set_bg_color(const Color &p_color); Color get_bg_color() const; - void set_bg_energy(float p_energy); - float get_bg_energy() const; + void set_bg_energy_multiplier(float p_energy); + float get_bg_energy_multiplier() const; + void set_bg_intensity(float p_energy); + float get_bg_intensity() const; void set_canvas_max_layer(int p_max_layer); int get_canvas_max_layer() const; void set_camera_feed_id(int p_id); @@ -257,16 +256,6 @@ public: float get_tonemap_exposure() const; void set_tonemap_white(float p_white); float get_tonemap_white() const; - void set_tonemap_auto_exposure_enabled(bool p_enabled); - bool is_tonemap_auto_exposure_enabled() const; - void set_tonemap_auto_exposure_min(float p_auto_exposure_min); - float get_tonemap_auto_exposure_min() const; - void set_tonemap_auto_exposure_max(float p_auto_exposure_max); - float get_tonemap_auto_exposure_max() const; - void set_tonemap_auto_exposure_speed(float p_auto_exposure_speed); - float get_tonemap_auto_exposure_speed() const; - void set_tonemap_auto_exposure_grey(float p_auto_exposure_grey); - float get_tonemap_auto_exposure_grey() const; // SSR void set_ssr_enabled(bool p_enabled); diff --git a/scene/resources/gradient.cpp b/scene/resources/gradient.cpp index a9c44dc6bf..f04eb75d86 100644 --- a/scene/resources/gradient.cpp +++ b/scene/resources/gradient.cpp @@ -56,7 +56,7 @@ void Gradient::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color", "point", "color"), &Gradient::set_color); ClassDB::bind_method(D_METHOD("get_color", "point"), &Gradient::get_color); - ClassDB::bind_method(D_METHOD("interpolate", "offset"), &Gradient::get_color_at_offset); + ClassDB::bind_method(D_METHOD("sample", "offset"), &Gradient::get_color_at_offset); ClassDB::bind_method(D_METHOD("get_point_count"), &Gradient::get_points_count); diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h index e4bac15e4b..2b91331ab0 100644 --- a/scene/resources/gradient.h +++ b/scene/resources/gradient.h @@ -122,7 +122,7 @@ public: } } - // Return interpolated value. + // Return sampled value. if (points[middle].offset > p_offset) { middle--; } diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 32ddef1693..9a1b784ec4 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -31,6 +31,8 @@ #include "material.h" #include "core/config/engine.h" +#include "core/config/project_settings.h" +#include "core/error/error_macros.h" #include "core/version.h" #include "scene/main/scene_tree.h" #include "scene/scene_string_names.h" @@ -1504,13 +1506,27 @@ Color BaseMaterial3D::get_emission() const { return emission; } -void BaseMaterial3D::set_emission_energy(float p_emission_energy) { - emission_energy = p_emission_energy; - RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy); +void BaseMaterial3D::set_emission_energy_multiplier(float p_emission_energy_multiplier) { + emission_energy_multiplier = p_emission_energy_multiplier; + if (GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy_multiplier * emission_intensity); + } else { + RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, p_emission_energy_multiplier); + } +} + +float BaseMaterial3D::get_emission_energy_multiplier() const { + return emission_energy_multiplier; +} + +void BaseMaterial3D::set_emission_intensity(float p_emission_intensity) { + ERR_FAIL_COND_EDMSG(!GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units"), "Cannot set material emission intensity when Physical Light Units disabled."); + emission_intensity = p_emission_intensity; + RS::get_singleton()->material_set_param(_get_material(), shader_names->emission_energy, emission_energy_multiplier * emission_intensity); } -float BaseMaterial3D::get_emission_energy() const { - return emission_energy; +float BaseMaterial3D::get_emission_intensity() const { + return emission_intensity; } void BaseMaterial3D::set_normal_scale(float p_normal_scale) { @@ -1884,6 +1900,10 @@ void BaseMaterial3D::_validate_property(PropertyInfo &p_property) const { _validate_high_end("subsurf_scatter", p_property); _validate_high_end("heightmap", p_property); + if (p_property.name == "emission_intensity" && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + p_property.usage = PROPERTY_USAGE_NONE; + } + if (p_property.name.begins_with("particles_anim_") && billboard_mode != BILLBOARD_PARTICLES) { p_property.usage = PROPERTY_USAGE_NONE; } @@ -2463,8 +2483,11 @@ void BaseMaterial3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emission", "emission"), &BaseMaterial3D::set_emission); ClassDB::bind_method(D_METHOD("get_emission"), &BaseMaterial3D::get_emission); - ClassDB::bind_method(D_METHOD("set_emission_energy", "emission_energy"), &BaseMaterial3D::set_emission_energy); - ClassDB::bind_method(D_METHOD("get_emission_energy"), &BaseMaterial3D::get_emission_energy); + ClassDB::bind_method(D_METHOD("set_emission_energy_multiplier", "emission_energy_multiplier"), &BaseMaterial3D::set_emission_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_emission_energy_multiplier"), &BaseMaterial3D::get_emission_energy_multiplier); + + ClassDB::bind_method(D_METHOD("set_emission_intensity", "emission_energy_multiplier"), &BaseMaterial3D::set_emission_intensity); + ClassDB::bind_method(D_METHOD("get_emission_intensity"), &BaseMaterial3D::get_emission_intensity); ClassDB::bind_method(D_METHOD("set_normal_scale", "normal_scale"), &BaseMaterial3D::set_normal_scale); ClassDB::bind_method(D_METHOD("get_normal_scale"), &BaseMaterial3D::get_normal_scale); @@ -2681,7 +2704,9 @@ void BaseMaterial3D::_bind_methods() { ADD_GROUP("Emission", "emission_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "emission_enabled"), "set_feature", "get_feature", FEATURE_EMISSION); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "emission", PROPERTY_HINT_COLOR_NO_ALPHA), "set_emission", "get_emission"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_emission_energy", "get_emission_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy_multiplier", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_emission_energy_multiplier", "get_emission_energy_multiplier"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_intensity", PROPERTY_HINT_RANGE, "0,100000.0,0.01,or_greater,suffix:nt"), "set_emission_intensity", "get_emission_intensity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_operator", PROPERTY_HINT_ENUM, "Add,Multiply"), "set_emission_operator", "get_emission_operator"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "emission_on_uv2"), "set_flag", "get_flag", FLAG_EMISSION_ON_UV2); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "emission_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_EMISSION); @@ -2943,7 +2968,7 @@ BaseMaterial3D::BaseMaterial3D(bool p_orm) : set_roughness(1.0); set_metallic(0.0); set_emission(Color(0, 0, 0)); - set_emission_energy(1.0); + set_emission_energy_multiplier(1.0); set_normal_scale(1); set_rim(1.0); set_rim_tint(0.5); @@ -3096,6 +3121,8 @@ bool StandardMaterial3D::_set(const StringName &p_name, const Variant &p_value) { "depth_flip_binormal", "heightmap_flip_binormal" }, { "depth_texture", "heightmap_texture" }, + { "emission_energy", "emission_energy_multiplier" }, + { nullptr, nullptr }, }; diff --git a/scene/resources/material.h b/scene/resources/material.h index c6be1b8766..9458e859f0 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -467,7 +467,8 @@ private: float metallic = 0.0f; float roughness = 0.0f; Color emission; - float emission_energy = 0.0f; + float emission_energy_multiplier = 1.0f; + float emission_intensity = 1000.0f; // In nits, equivalent to indoor lighting. float normal_scale = 0.0f; float rim = 0.0f; float rim_tint = 0.0f; @@ -573,8 +574,11 @@ public: void set_emission(const Color &p_emission); Color get_emission() const; - void set_emission_energy(float p_emission_energy); - float get_emission_energy() const; + void set_emission_energy_multiplier(float p_emission_energy_multiplier); + float get_emission_energy_multiplier() const; + + void set_emission_intensity(float p_emission_intensity); + float get_emission_intensity() const; void set_normal_scale(float p_normal_scale); float get_normal_scale() const; diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index fc5cf2a028..727f7a4e09 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -1822,7 +1822,7 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { float r = radius; if (curve.is_valid() && curve->get_point_count() > 0) { - r *= curve->interpolate_baked(v); + r *= curve->sample_baked(v); } float x = sin(u * Math_TAU); float z = cos(u * Math_TAU); @@ -1863,7 +1863,7 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { // add top float scale_pos = 1.0; if (curve.is_valid() && curve->get_point_count() > 0) { - scale_pos = curve->interpolate_baked(0); + scale_pos = curve->sample_baked(0); } if (scale_pos > CMP_EPSILON) { @@ -1925,7 +1925,7 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { float scale_neg = 1.0; if (curve.is_valid() && curve->get_point_count() > 0) { - scale_neg = curve->interpolate_baked(1.0); + scale_neg = curve->sample_baked(1.0); } // add bottom @@ -2138,7 +2138,7 @@ void RibbonTrailMesh::_create_mesh_array(Array &p_arr) const { float s = size; if (curve.is_valid() && curve->get_point_count() > 0) { - s *= curve->interpolate_baked(v); + s *= curve->sample_baked(v); } points.push_back(Vector3(-s * 0.5, y, 0)); diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index 737c50e570..fc999d5fcb 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -30,6 +30,7 @@ #include "sky_material.h" +#include "core/config/project_settings.h" #include "core/version.h" Mutex ProceduralSkyMaterial::shader_mutex; @@ -62,13 +63,13 @@ float ProceduralSkyMaterial::get_sky_curve() const { return sky_curve; } -void ProceduralSkyMaterial::set_sky_energy(float p_energy) { - sky_energy = p_energy; - RS::get_singleton()->material_set_param(_get_material(), "sky_energy", sky_energy); +void ProceduralSkyMaterial::set_sky_energy_multiplier(float p_multiplier) { + sky_energy_multiplier = p_multiplier; + RS::get_singleton()->material_set_param(_get_material(), "sky_energy", sky_energy_multiplier); } -float ProceduralSkyMaterial::get_sky_energy() const { - return sky_energy; +float ProceduralSkyMaterial::get_sky_energy_multiplier() const { + return sky_energy_multiplier; } void ProceduralSkyMaterial::set_sky_cover(const Ref<Texture2D> &p_sky_cover) { @@ -117,13 +118,13 @@ float ProceduralSkyMaterial::get_ground_curve() const { return ground_curve; } -void ProceduralSkyMaterial::set_ground_energy(float p_energy) { - ground_energy = p_energy; - RS::get_singleton()->material_set_param(_get_material(), "ground_energy", ground_energy); +void ProceduralSkyMaterial::set_ground_energy_multiplier(float p_multiplier) { + ground_energy_multiplier = p_multiplier; + RS::get_singleton()->material_set_param(_get_material(), "ground_energy", ground_energy_multiplier); } -float ProceduralSkyMaterial::get_ground_energy() const { - return ground_energy; +float ProceduralSkyMaterial::get_ground_energy_multiplier() const { + return ground_energy_multiplier; } void ProceduralSkyMaterial::set_sun_angle_max(float p_angle) { @@ -171,6 +172,12 @@ RID ProceduralSkyMaterial::get_shader_rid() const { return shader; } +void ProceduralSkyMaterial::_validate_property(PropertyInfo &p_property) const { + if ((p_property.name == "sky_luminance" || p_property.name == "ground_luminance") && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } +} + void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sky_top_color", "color"), &ProceduralSkyMaterial::set_sky_top_color); ClassDB::bind_method(D_METHOD("get_sky_top_color"), &ProceduralSkyMaterial::get_sky_top_color); @@ -181,8 +188,8 @@ void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_sky_curve", "curve"), &ProceduralSkyMaterial::set_sky_curve); ClassDB::bind_method(D_METHOD("get_sky_curve"), &ProceduralSkyMaterial::get_sky_curve); - ClassDB::bind_method(D_METHOD("set_sky_energy", "energy"), &ProceduralSkyMaterial::set_sky_energy); - ClassDB::bind_method(D_METHOD("get_sky_energy"), &ProceduralSkyMaterial::get_sky_energy); + ClassDB::bind_method(D_METHOD("set_sky_energy_multiplier", "multiplier"), &ProceduralSkyMaterial::set_sky_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_sky_energy_multiplier"), &ProceduralSkyMaterial::get_sky_energy_multiplier); ClassDB::bind_method(D_METHOD("set_sky_cover", "sky_cover"), &ProceduralSkyMaterial::set_sky_cover); ClassDB::bind_method(D_METHOD("get_sky_cover"), &ProceduralSkyMaterial::get_sky_cover); @@ -199,8 +206,8 @@ void ProceduralSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ground_curve", "curve"), &ProceduralSkyMaterial::set_ground_curve); ClassDB::bind_method(D_METHOD("get_ground_curve"), &ProceduralSkyMaterial::get_ground_curve); - ClassDB::bind_method(D_METHOD("set_ground_energy", "energy"), &ProceduralSkyMaterial::set_ground_energy); - ClassDB::bind_method(D_METHOD("get_ground_energy"), &ProceduralSkyMaterial::get_ground_energy); + ClassDB::bind_method(D_METHOD("set_ground_energy_multiplier", "energy"), &ProceduralSkyMaterial::set_ground_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_ground_energy_multiplier"), &ProceduralSkyMaterial::get_ground_energy_multiplier); ClassDB::bind_method(D_METHOD("set_sun_angle_max", "degrees"), &ProceduralSkyMaterial::set_sun_angle_max); ClassDB::bind_method(D_METHOD("get_sun_angle_max"), &ProceduralSkyMaterial::get_sun_angle_max); @@ -215,7 +222,7 @@ void ProceduralSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_top_color", "get_sky_top_color"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_horizon_color", "get_sky_horizon_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_curve", PROPERTY_HINT_EXP_EASING), "set_sky_curve", "get_sky_curve"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy", "get_sky_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_energy_multiplier", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy_multiplier", "get_sky_energy_multiplier"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "sky_cover", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_sky_cover", "get_sky_cover"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_cover_modulate"), "set_sky_cover_modulate", "get_sky_cover_modulate"); @@ -223,7 +230,7 @@ void ProceduralSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_bottom_color", "get_ground_bottom_color"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_horizon_color", "get_ground_horizon_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_curve", PROPERTY_HINT_EXP_EASING), "set_ground_curve", "get_ground_curve"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_ground_energy", "get_ground_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_energy_multiplier", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_ground_energy_multiplier", "get_ground_energy_multiplier"); ADD_GROUP("Sun", "sun_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_angle_max", PROPERTY_HINT_RANGE, "0,360,0.01,degrees"), "set_sun_angle_max", "get_sun_angle_max"); @@ -253,7 +260,7 @@ shader_type sky; uniform vec4 sky_top_color : source_color = vec4(0.385, 0.454, 0.55, 1.0); uniform vec4 sky_horizon_color : source_color = vec4(0.646, 0.656, 0.67, 1.0); uniform float sky_curve : hint_range(0, 1) = 0.15; -uniform float sky_energy = 1.0; +uniform float sky_energy = 1.0; // In Lux. uniform sampler2D sky_cover : source_color, hint_default_black; uniform vec4 sky_cover_modulate : source_color = vec4(1.0, 1.0, 1.0, 1.0); uniform vec4 ground_bottom_color : source_color = vec4(0.2, 0.169, 0.133, 1.0); @@ -338,13 +345,13 @@ ProceduralSkyMaterial::ProceduralSkyMaterial() { set_sky_top_color(Color(0.385, 0.454, 0.55)); set_sky_horizon_color(Color(0.6463, 0.6558, 0.6708)); set_sky_curve(0.15); - set_sky_energy(1.0); + set_sky_energy_multiplier(1.0); set_sky_cover_modulate(Color(1, 1, 1)); set_ground_bottom_color(Color(0.2, 0.169, 0.133)); set_ground_horizon_color(Color(0.6463, 0.6558, 0.6708)); set_ground_curve(0.02); - set_ground_energy(1.0); + set_ground_energy_multiplier(1.0); set_sun_angle_max(30.0); set_sun_curve(0.15); @@ -528,13 +535,13 @@ Color PhysicalSkyMaterial::get_ground_color() const { return ground_color; } -void PhysicalSkyMaterial::set_exposure(float p_exposure) { - exposure = p_exposure; - RS::get_singleton()->material_set_param(_get_material(), "exposure", exposure); +void PhysicalSkyMaterial::set_energy_multiplier(float p_multiplier) { + energy_multiplier = p_multiplier; + RS::get_singleton()->material_set_param(_get_material(), "exposure", energy_multiplier); } -float PhysicalSkyMaterial::get_exposure() const { - return exposure; +float PhysicalSkyMaterial::get_energy_multiplier() const { + return energy_multiplier; } void PhysicalSkyMaterial::set_use_debanding(bool p_use_debanding) { @@ -574,6 +581,12 @@ RID PhysicalSkyMaterial::get_shader_rid() const { return shader; } +void PhysicalSkyMaterial::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "exposure_value" && !GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units")) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } +} + Mutex PhysicalSkyMaterial::shader_mutex; RID PhysicalSkyMaterial::shader; @@ -602,8 +615,8 @@ void PhysicalSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ground_color", "color"), &PhysicalSkyMaterial::set_ground_color); ClassDB::bind_method(D_METHOD("get_ground_color"), &PhysicalSkyMaterial::get_ground_color); - ClassDB::bind_method(D_METHOD("set_exposure", "exposure"), &PhysicalSkyMaterial::set_exposure); - ClassDB::bind_method(D_METHOD("get_exposure"), &PhysicalSkyMaterial::get_exposure); + ClassDB::bind_method(D_METHOD("set_energy_multiplier", "multiplier"), &PhysicalSkyMaterial::set_energy_multiplier); + ClassDB::bind_method(D_METHOD("get_energy_multiplier"), &PhysicalSkyMaterial::get_energy_multiplier); ClassDB::bind_method(D_METHOD("set_use_debanding", "use_debanding"), &PhysicalSkyMaterial::set_use_debanding); ClassDB::bind_method(D_METHOD("get_use_debanding"), &PhysicalSkyMaterial::get_use_debanding); @@ -623,7 +636,7 @@ void PhysicalSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "turbidity", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_turbidity", "get_turbidity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_disk_scale", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_disk_scale", "get_sun_disk_scale"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_color", "get_ground_color"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_exposure", "get_exposure"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "energy_multiplier", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_energy_multiplier", "get_energy_multiplier"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "get_use_debanding"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "night_sky", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_night_sky", "get_night_sky"); } @@ -654,16 +667,13 @@ uniform vec4 mie_color : source_color = vec4(0.69, 0.729, 0.812, 1.0); uniform float turbidity : hint_range(0, 1000) = 10.0; uniform float sun_disk_scale : hint_range(0, 360) = 1.0; uniform vec4 ground_color : source_color = vec4(0.1, 0.07, 0.034, 1.0); -uniform float exposure : hint_range(0, 128) = 0.1; +uniform float exposure : hint_range(0, 128) = 1.0; uniform bool use_debanding = true; uniform sampler2D night_sky : source_color, hint_default_black; const vec3 UP = vec3( 0.0, 1.0, 0.0 ); -// Sun constants -const float SUN_ENERGY = 1000.0; - // Optical length at zenith for molecules. const float rayleigh_zenith_size = 8.4e3; const float mie_zenith_size = 1.25e3; @@ -683,7 +693,7 @@ vec3 interleaved_gradient_noise(vec2 pos) { void sky() { if (LIGHT0_ENABLED) { float zenith_angle = clamp( dot(UP, normalize(LIGHT0_DIRECTION)), -1.0, 1.0 ); - float sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * SUN_ENERGY * LIGHT0_ENERGY; + float sun_energy = max(0.0, 1.0 - exp(-((PI * 0.5) - acos(zenith_angle)))) * LIGHT0_ENERGY; float sun_fade = 1.0 - clamp(1.0 - exp(LIGHT0_DIRECTION.y), 0.0, 1.0); // Rayleigh coefficients. @@ -721,10 +731,10 @@ void sky() { float sunAngularDiameterCos = cos(LIGHT0_SIZE * sun_disk_scale); float sunAngularDiameterCos2 = cos(LIGHT0_SIZE * sun_disk_scale*0.5); float sundisk = smoothstep(sunAngularDiameterCos, sunAngularDiameterCos2, cos_theta); - vec3 L0 = (sun_energy * 1900.0 * extinction) * sundisk * LIGHT0_COLOR; + vec3 L0 = (sun_energy * extinction) * sundisk * LIGHT0_COLOR; L0 += texture(night_sky, SKY_COORDS).xyz * extinction; - vec3 color = (Lin + L0) * 0.04; + vec3 color = Lin + L0; COLOR = pow(color, vec3(1.0 / (1.2 + (1.2 * sun_fade)))); COLOR *= exposure; if (use_debanding) { @@ -732,7 +742,7 @@ void sky() { } } else { // There is no sun, so display night_sky and nothing else. - COLOR = texture(night_sky, SKY_COORDS).xyz * 0.04; + COLOR = texture(night_sky, SKY_COORDS).xyz; COLOR *= exposure; } } @@ -751,7 +761,7 @@ PhysicalSkyMaterial::PhysicalSkyMaterial() { set_turbidity(10.0); set_sun_disk_scale(1.0); set_ground_color(Color(0.1, 0.07, 0.034)); - set_exposure(0.1); + set_energy_multiplier(1.0); set_use_debanding(true); } diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h index 61999af3c4..b517fd806b 100644 --- a/scene/resources/sky_material.h +++ b/scene/resources/sky_material.h @@ -41,14 +41,14 @@ private: Color sky_top_color; Color sky_horizon_color; float sky_curve = 0.0f; - float sky_energy = 0.0f; + float sky_energy_multiplier = 0.0f; Ref<Texture2D> sky_cover; Color sky_cover_modulate; Color ground_bottom_color; Color ground_horizon_color; float ground_curve = 0.0f; - float ground_energy = 0.0f; + float ground_energy_multiplier = 0.0f; float sun_angle_max = 0.0f; float sun_curve = 0.0f; @@ -61,6 +61,7 @@ private: protected: static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; public: void set_sky_top_color(const Color &p_sky_top); @@ -72,8 +73,8 @@ public: void set_sky_curve(float p_curve); float get_sky_curve() const; - void set_sky_energy(float p_energy); - float get_sky_energy() const; + void set_sky_energy_multiplier(float p_multiplier); + float get_sky_energy_multiplier() const; void set_sky_cover(const Ref<Texture2D> &p_sky_cover); Ref<Texture2D> get_sky_cover() const; @@ -90,8 +91,8 @@ public: void set_ground_curve(float p_curve); float get_ground_curve() const; - void set_ground_energy(float p_energy); - float get_ground_energy() const; + void set_ground_energy_multiplier(float p_energy); + float get_ground_energy_multiplier() const; void set_sun_angle_max(float p_angle); float get_sun_angle_max() const; @@ -138,6 +139,9 @@ public: void set_filtering_enabled(bool p_enabled); bool is_filtering_enabled() const; + void set_energy_multiplier(float p_multiplier); + float get_energy_multiplier() const; + virtual Shader::Mode get_shader_mode() const override; virtual RID get_shader_rid() const override; virtual RID get_rid() const override; @@ -166,7 +170,7 @@ private: float turbidity = 0.0f; float sun_disk_scale = 0.0f; Color ground_color; - float exposure = 0.0f; + float energy_multiplier = 1.0f; bool use_debanding = true; Ref<Texture2D> night_sky; static void _update_shader(); @@ -174,6 +178,7 @@ private: protected: static void _bind_methods(); + void _validate_property(PropertyInfo &property) const; public: void set_rayleigh_coefficient(float p_rayleigh); @@ -200,8 +205,11 @@ public: void set_ground_color(Color p_ground_color); Color get_ground_color() const; - void set_exposure(float p_exposure); - float get_exposure() const; + void set_energy_multiplier(float p_multiplier); + float get_energy_multiplier() const; + + void set_exposure_value(float p_exposure); + float get_exposure_value() const; void set_use_debanding(bool p_use_debanding); bool get_use_debanding() const; diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 43d3f329fa..7e9a2591e4 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -77,7 +77,7 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("set_break_flags", "flags"), &TextParagraph::set_break_flags); ClassDB::bind_method(D_METHOD("get_break_flags"), &TextParagraph::get_break_flags); - ADD_PROPERTY(PropertyInfo(Variant::INT, "break_flags", PROPERTY_HINT_FLAGS, "Mandatory,Word Bound,Grapheme Bound,Adaptive"), "set_break_flags", "get_break_flags"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "break_flags", PROPERTY_HINT_FLAGS, "Mandatory,Word Bound,Grapheme Bound,Adaptive,Trim Spaces"), "set_break_flags", "get_break_flags"); ClassDB::bind_method(D_METHOD("set_justification_flags", "flags"), &TextParagraph::set_justification_flags); ClassDB::bind_method(D_METHOD("get_justification_flags"), &TextParagraph::get_justification_flags); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 25f5006c4f..f8f6900976 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -1866,11 +1866,11 @@ void CurveTexture::_update() { for (int i = 0; i < _width; ++i) { float t = i / static_cast<float>(_width); if (texture_mode == TEXTURE_MODE_RGB) { - wd[i * 3 + 0] = curve.interpolate_baked(t); + wd[i * 3 + 0] = curve.sample_baked(t); wd[i * 3 + 1] = wd[i * 3 + 0]; wd[i * 3 + 2] = wd[i * 3 + 0]; } else { - wd[i] = curve.interpolate_baked(t); + wd[i] = curve.sample_baked(t); } } @@ -2054,7 +2054,7 @@ void CurveXYZTexture::_update() { Curve &curve_x = **_curve_x; for (int i = 0; i < _width; ++i) { float t = i / static_cast<float>(_width); - wd[i * 3 + 0] = curve_x.interpolate_baked(t); + wd[i * 3 + 0] = curve_x.sample_baked(t); } } else { @@ -2067,7 +2067,7 @@ void CurveXYZTexture::_update() { Curve &curve_y = **_curve_y; for (int i = 0; i < _width; ++i) { float t = i / static_cast<float>(_width); - wd[i * 3 + 1] = curve_y.interpolate_baked(t); + wd[i * 3 + 1] = curve_y.sample_baked(t); } } else { @@ -2080,7 +2080,7 @@ void CurveXYZTexture::_update() { Curve &curve_z = **_curve_z; for (int i = 0; i < _width; ++i) { float t = i / static_cast<float>(_width); - wd[i * 3 + 2] = curve_z.interpolate_baked(t); + wd[i * 3 + 2] = curve_z.sample_baked(t); } } else { diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 552d856034..376b53b75c 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -4716,14 +4716,19 @@ void TileSetScenesCollectionSource::set_scene_tile_id(int p_id, int p_new_id) { void TileSetScenesCollectionSource::set_scene_tile_scene(int p_id, Ref<PackedScene> p_packed_scene) { ERR_FAIL_COND(!scenes.has(p_id)); if (p_packed_scene.is_valid()) { - // Make sure we have a root node. Supposed to be at 0 index because find_node_by_path() does not seem to work. - ERR_FAIL_COND(!p_packed_scene->get_state().is_valid()); - ERR_FAIL_COND(p_packed_scene->get_state()->get_node_count() < 1); - // Check if it extends CanvasItem. - String type = p_packed_scene->get_state()->get_node_type(0); + Ref<SceneState> scene_state = p_packed_scene->get_state(); + String type; + while (scene_state.is_valid() && type.is_empty()) { + // Make sure we have a root node. Supposed to be at 0 index because find_node_by_path() does not seem to work. + ERR_FAIL_COND(scene_state->get_node_count() < 1); + + type = scene_state->get_node_type(0); + scene_state = scene_state->get_base_scene_state(); + } + ERR_FAIL_COND_MSG(type.is_empty(), vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Could not get the type of the root node.", p_packed_scene->get_path())); bool extends_correct_class = ClassDB::is_parent_class(type, "Control") || ClassDB::is_parent_class(type, "Node2D"); - ERR_FAIL_COND_MSG(!extends_correct_class, vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Root node should extend Control or Node2D.", p_packed_scene->get_path())); + ERR_FAIL_COND_MSG(!extends_correct_class, vformat("Invalid PackedScene for TileSetScenesCollectionSource: %s. Root node should extend Control or Node2D. Found %s instead.", p_packed_scene->get_path(), type)); scenes[p_id].scene = p_packed_scene; } else { diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 3b2b58516d..a968aebdaa 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -7247,7 +7247,11 @@ String VisualShaderNodeProximityFade::generate_code(Shader::Mode p_mode, VisualS String proximity_fade_distance = vformat("%s", p_input_vars[0]); code += " float __depth_tex = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r;\n"; - code += " vec4 __depth_world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, __depth_tex, 1.0);\n"; + if (!RenderingServer::get_singleton()->is_low_end()) { + code += " vec4 __depth_world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, __depth_tex, 1.0);\n"; + } else { + code += " vec4 __depth_world_pos = INV_PROJECTION_MATRIX * vec4(vec3(SCREEN_UV, __depth_tex) * 2.0 - 1.0, 1.0);\n"; + } code += " __depth_world_pos.xyz /= __depth_world_pos.z;\n"; code += vformat(" %s = clamp(1.0 - smoothstep(__depth_world_pos.z + %s, __depth_world_pos.z, VERTEX.z), 0.0, 1.0);\n", p_output_vars[0], p_input_vars[0]); diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index fb6dcd3d57..945b6af614 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -33,6 +33,8 @@ #include "core/config/project_settings.h" #include "scene/3d/camera_3d.h" #include "scene/3d/visible_on_screen_notifier_3d.h" +#include "scene/resources/camera_attributes.h" +#include "scene/resources/environment.h" #include "scene/scene_string_names.h" #include "servers/navigation_server_3d.h" @@ -98,17 +100,17 @@ Ref<Environment> World3D::get_fallback_environment() const { return fallback_environment; } -void World3D::set_camera_effects(const Ref<CameraEffects> &p_camera_effects) { - camera_effects = p_camera_effects; - if (camera_effects.is_valid()) { - RS::get_singleton()->scenario_set_camera_effects(scenario, camera_effects->get_rid()); +void World3D::set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes) { + camera_attributes = p_camera_attributes; + if (camera_attributes.is_valid()) { + RS::get_singleton()->scenario_set_camera_attributes(scenario, camera_attributes->get_rid()); } else { - RS::get_singleton()->scenario_set_camera_effects(scenario, RID()); + RS::get_singleton()->scenario_set_camera_attributes(scenario, RID()); } } -Ref<CameraEffects> World3D::get_camera_effects() const { - return camera_effects; +Ref<CameraAttributes> World3D::get_camera_attributes() const { + return camera_attributes; } PhysicsDirectSpaceState3D *World3D::get_direct_space_state() { @@ -123,12 +125,12 @@ void World3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_environment"), &World3D::get_environment); ClassDB::bind_method(D_METHOD("set_fallback_environment", "env"), &World3D::set_fallback_environment); ClassDB::bind_method(D_METHOD("get_fallback_environment"), &World3D::get_fallback_environment); - ClassDB::bind_method(D_METHOD("set_camera_effects", "effects"), &World3D::set_camera_effects); - ClassDB::bind_method(D_METHOD("get_camera_effects"), &World3D::get_camera_effects); + ClassDB::bind_method(D_METHOD("set_camera_attributes", "attributes"), &World3D::set_camera_attributes); + ClassDB::bind_method(D_METHOD("get_camera_attributes"), &World3D::get_camera_attributes); ClassDB::bind_method(D_METHOD("get_direct_space_state"), &World3D::get_direct_space_state); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_environment", "get_environment"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "fallback_environment", PROPERTY_HINT_RESOURCE_TYPE, "Environment"), "set_fallback_environment", "get_fallback_environment"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_effects", PROPERTY_HINT_RESOURCE_TYPE, "CameraEffects"), "set_camera_effects", "get_camera_effects"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "camera_attributes", PROPERTY_HINT_RESOURCE_TYPE, "CameraAttributesPractical,CameraAttributesPhysical"), "set_camera_attributes", "get_camera_attributes"); ADD_PROPERTY(PropertyInfo(Variant::RID, "space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_space"); ADD_PROPERTY(PropertyInfo(Variant::RID, "navigation_map", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_navigation_map"); ADD_PROPERTY(PropertyInfo(Variant::RID, "scenario", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_scenario"); diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index 08bc050349..411b9aab37 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -32,11 +32,11 @@ #define WORLD_3D_H #include "core/io/resource.h" -#include "scene/resources/camera_effects.h" #include "scene/resources/environment.h" #include "servers/physics_server_3d.h" #include "servers/rendering_server.h" +class CameraAttributes; class Camera3D; class VisibleOnScreenNotifier3D; struct SpatialIndexer; @@ -51,7 +51,7 @@ private: Ref<Environment> environment; Ref<Environment> fallback_environment; - Ref<CameraEffects> camera_effects; + Ref<CameraAttributes> camera_attributes; HashSet<Camera3D *> cameras; @@ -74,8 +74,8 @@ public: void set_fallback_environment(const Ref<Environment> &p_environment); Ref<Environment> get_fallback_environment() const; - void set_camera_effects(const Ref<CameraEffects> &p_camera_effects); - Ref<CameraEffects> get_camera_effects() const; + void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes); + Ref<CameraAttributes> get_camera_attributes() const; _FORCE_INLINE_ const HashSet<Camera3D *> &get_cameras() const { return cameras; } diff --git a/servers/rendering/dummy/environment/gi.h b/servers/rendering/dummy/environment/gi.h index 76d34cd14e..fea7994cfe 100644 --- a/servers/rendering/dummy/environment/gi.h +++ b/servers/rendering/dummy/environment/gi.h @@ -62,6 +62,9 @@ public: virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override {} virtual float voxel_gi_get_energy(RID p_voxel_gi) const override { return 0.0; } + virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) override {} + virtual float voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const override { return 1.0; } + virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override {} virtual float voxel_gi_get_bias(RID p_voxel_gi) const override { return 0.0; } diff --git a/servers/rendering/dummy/rasterizer_scene_dummy.h b/servers/rendering/dummy/rasterizer_scene_dummy.h index 8d3a45d696..0d368b765a 100644 --- a/servers/rendering/dummy/rasterizer_scene_dummy.h +++ b/servers/rendering/dummy/rasterizer_scene_dummy.h @@ -140,14 +140,6 @@ public: Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override { return Ref<Image>(); } - RID camera_effects_allocate() override { return RID(); } - void camera_effects_initialize(RID p_rid) override {} - void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) override {} - void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) override {} - - void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) override {} - void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override {} - void positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override {} void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override {} @@ -188,7 +180,7 @@ public: void voxel_gi_set_quality(RS::VoxelGIQuality) override {} - void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_info = nullptr) override {} + void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_info = nullptr) override {} void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override {} void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) override {} @@ -212,6 +204,9 @@ public: if (is_environment(p_rid)) { environment_free(p_rid); return true; + } else if (RSG::camera_attributes->owns_camera_attributes(p_rid)) { + RSG::camera_attributes->camera_attributes_free(p_rid); + return true; } else { return false; } diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h index 0c0ea61df5..79f484d513 100644 --- a/servers/rendering/dummy/storage/light_storage.h +++ b/servers/rendering/dummy/storage/light_storage.h @@ -119,6 +119,7 @@ public: virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override {} virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override {} virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override {} + virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) override {} virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override { return PackedVector3Array(); } virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override { return PackedColorArray(); } virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override { return PackedInt32Array(); } diff --git a/servers/rendering/dummy/storage/mesh_storage.h b/servers/rendering/dummy/storage/mesh_storage.h index a884892832..1eb4fd854f 100644 --- a/servers/rendering/dummy/storage/mesh_storage.h +++ b/servers/rendering/dummy/storage/mesh_storage.h @@ -105,7 +105,6 @@ public: virtual int mesh_get_surface_count(RID p_mesh) const override { DummyMesh *m = mesh_owner.get_or_null(p_mesh); ERR_FAIL_COND_V(!m, 0); - print_line(m->surfaces.size()); return m->surfaces.size(); } diff --git a/servers/rendering/environment/renderer_gi.h b/servers/rendering/environment/renderer_gi.h index 70d2bb3a9c..0faf683015 100644 --- a/servers/rendering/environment/renderer_gi.h +++ b/servers/rendering/environment/renderer_gi.h @@ -63,6 +63,9 @@ public: virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_energy) = 0; virtual float voxel_gi_get_energy(RID p_voxel_gi) const = 0; + virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) = 0; + virtual float voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const = 0; + virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_bias) = 0; virtual float voxel_gi_get_bias(RID p_voxel_gi) const = 0; diff --git a/servers/rendering/renderer_compositor.h b/servers/rendering/renderer_compositor.h index 4cfded8460..78d4ded617 100644 --- a/servers/rendering/renderer_compositor.h +++ b/servers/rendering/renderer_compositor.h @@ -35,6 +35,7 @@ #include "servers/rendering/environment/renderer_gi.h" #include "servers/rendering/renderer_canvas_render.h" #include "servers/rendering/renderer_scene.h" +#include "servers/rendering/storage/camera_attributes_storage.h" #include "servers/rendering/storage/light_storage.h" #include "servers/rendering/storage/material_storage.h" #include "servers/rendering/storage/mesh_storage.h" diff --git a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp index cc7441776d..27850695b0 100644 --- a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp +++ b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp @@ -33,6 +33,8 @@ #include "servers/rendering/renderer_rd/renderer_compositor_rd.h" #include "servers/rendering/renderer_rd/storage_rd/material_storage.h" #include "servers/rendering/renderer_rd/uniform_set_cache_rd.h" +#include "servers/rendering/rendering_server_default.h" +#include "servers/rendering/storage/camera_attributes_storage.h" using namespace RendererRD; @@ -84,7 +86,7 @@ BokehDOF::~BokehDOF() { } } -void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) { +void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) { ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use compute version of bokeh depth of field with the mobile renderer."); UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); @@ -92,22 +94,39 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, MaterialStorage *material_storage = MaterialStorage::get_singleton(); ERR_FAIL_NULL(material_storage); + bool dof_far = RSG::camera_attributes->camera_attributes_get_dof_far_enabled(p_camera_attributes); + float dof_far_begin = RSG::camera_attributes->camera_attributes_get_dof_far_distance(p_camera_attributes); + float dof_far_size = RSG::camera_attributes->camera_attributes_get_dof_far_transition(p_camera_attributes); + bool dof_near = RSG::camera_attributes->camera_attributes_get_dof_near_enabled(p_camera_attributes); + float dof_near_begin = RSG::camera_attributes->camera_attributes_get_dof_near_distance(p_camera_attributes); + float dof_near_size = RSG::camera_attributes->camera_attributes_get_dof_near_transition(p_camera_attributes); + float bokeh_size = RSG::camera_attributes->camera_attributes_get_dof_blur_amount(p_camera_attributes) * 64; // Base 64 pixel radius. + + bool use_jitter = RSG::camera_attributes->camera_attributes_get_dof_blur_use_jitter(); + RS::DOFBokehShape bokeh_shape = RSG::camera_attributes->camera_attributes_get_dof_blur_bokeh_shape(); + RS::DOFBlurQuality blur_quality = RSG::camera_attributes->camera_attributes_get_dof_blur_quality(); + // setup our push constant memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant)); - bokeh.push_constant.blur_far_active = p_dof_far; - bokeh.push_constant.blur_far_begin = p_dof_far_begin; - bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size; - - bokeh.push_constant.blur_near_active = p_dof_near; - bokeh.push_constant.blur_near_begin = p_dof_near_begin; - bokeh.push_constant.blur_near_end = MAX(0, p_dof_near_begin - p_dof_near_size); - bokeh.push_constant.use_jitter = p_use_jitter; + bokeh.push_constant.blur_far_active = dof_far; + bokeh.push_constant.blur_far_begin = dof_far_begin; + bokeh.push_constant.blur_far_end = dof_far_begin + dof_far_size; // Only used with non-physically-based. + bokeh.push_constant.use_physical_far = dof_far_size < 0.0; + bokeh.push_constant.blur_size_far = bokeh_size; // Only used with physically-based. + + bokeh.push_constant.blur_near_active = dof_near; + bokeh.push_constant.blur_near_begin = dof_near_begin; + bokeh.push_constant.blur_near_end = dof_near_begin - dof_near_size; // Only used with non-physically-based. + bokeh.push_constant.use_physical_near = dof_near_size < 0.0; + bokeh.push_constant.blur_size_near = bokeh_size; // Only used with physically-based. + + bokeh.push_constant.use_jitter = use_jitter; bokeh.push_constant.jitter_seed = Math::randf() * 1000.0; bokeh.push_constant.z_near = p_cam_znear; bokeh.push_constant.z_far = p_cam_zfar; bokeh.push_constant.orthogonal = p_cam_orthogonal; - bokeh.push_constant.blur_size = p_bokeh_size; + bokeh.push_constant.blur_size = (dof_near_size < 0.0 && dof_far_size < 0.0) ? 32 : bokeh_size; // Cap with physically-based to keep performance reasonable. bokeh.push_constant.second_pass = false; bokeh.push_constant.half_size = false; @@ -150,9 +169,9 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_buffers.base_texture_size.x, p_buffers.base_texture_size.y, 1); RD::get_singleton()->compute_list_add_barrier(compute_list); - if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) { + if (bokeh_shape == RS::DOF_BOKEH_BOX || bokeh_shape == RS::DOF_BOKEH_HEXAGON) { //second pass - BokehMode mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL; + BokehMode mode = bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL; shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, mode); ERR_FAIL_COND(shader.is_null()); @@ -160,9 +179,9 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, static const int quality_samples[4] = { 6, 12, 12, 24 }; - bokeh.push_constant.steps = quality_samples[p_quality]; + bokeh.push_constant.steps = quality_samples[blur_quality]; - if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) { + if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) { //box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes) RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image0), 0); @@ -187,7 +206,7 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, //third pass bokeh.push_constant.second_pass = true; - if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) { + if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_half_image1), 0); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_half_texture0), 1); } else { @@ -200,7 +219,7 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, RD::get_singleton()->compute_list_dispatch_threads(compute_list, bokeh.push_constant.size[0], bokeh.push_constant.size[1], 1); RD::get_singleton()->compute_list_add_barrier(compute_list); - if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) { + if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) { //forth pass, upscale for low quality shader = bokeh.compute_shader.version_get_shader(bokeh.shader_version, BOKEH_COMPOSITE); @@ -232,7 +251,7 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 }; bokeh.push_constant.steps = 0; - bokeh.push_constant.blur_scale = quality_scale[p_quality]; + bokeh.push_constant.blur_scale = quality_scale[blur_quality]; //circle always runs in half size, otherwise too expensive @@ -273,7 +292,7 @@ void BokehDOF::bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, RD::get_singleton()->compute_list_end(); } -void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) { +void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal) { ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't blur-based depth of field with the clustered renderer."); UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); @@ -281,6 +300,17 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f MaterialStorage *material_storage = MaterialStorage::get_singleton(); ERR_FAIL_NULL(material_storage); + bool dof_far = RSG::camera_attributes->camera_attributes_get_dof_far_enabled(p_camera_attributes); + float dof_far_begin = RSG::camera_attributes->camera_attributes_get_dof_far_distance(p_camera_attributes); + float dof_far_size = RSG::camera_attributes->camera_attributes_get_dof_far_transition(p_camera_attributes); + bool dof_near = RSG::camera_attributes->camera_attributes_get_dof_near_enabled(p_camera_attributes); + float dof_near_begin = RSG::camera_attributes->camera_attributes_get_dof_near_distance(p_camera_attributes); + float dof_near_size = RSG::camera_attributes->camera_attributes_get_dof_near_transition(p_camera_attributes); + float bokeh_size = RSG::camera_attributes->camera_attributes_get_dof_blur_amount(p_camera_attributes) * 64; // Base 64 pixel radius. + + RS::DOFBokehShape bokeh_shape = RSG::camera_attributes->camera_attributes_get_dof_blur_bokeh_shape(); + RS::DOFBlurQuality blur_quality = RSG::camera_attributes->camera_attributes_get_dof_blur_quality(); + // setup our base push constant memset(&bokeh.push_constant, 0, sizeof(BokehPushConstant)); @@ -292,7 +322,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f bokeh.push_constant.second_pass = false; bokeh.push_constant.half_size = false; - bokeh.push_constant.blur_size = p_dof_blur_amount; + bokeh.push_constant.blur_size = bokeh_size; // setup our uniforms RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); @@ -307,17 +337,17 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f RD::Uniform u_weight_texture2(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[2] })); RD::Uniform u_weight_texture3(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_buffers.weight_texture[3] })); - if (p_dof_far || p_dof_near) { - if (p_dof_far) { + if (dof_far || dof_near) { + if (dof_far) { bokeh.push_constant.blur_far_active = true; - bokeh.push_constant.blur_far_begin = p_dof_far_begin; - bokeh.push_constant.blur_far_end = p_dof_far_begin + p_dof_far_size; + bokeh.push_constant.blur_far_begin = dof_far_begin; + bokeh.push_constant.blur_far_end = dof_far_begin + dof_far_size; } - if (p_dof_near) { + if (dof_near) { bokeh.push_constant.blur_near_active = true; - bokeh.push_constant.blur_near_begin = p_dof_near_begin; - bokeh.push_constant.blur_near_end = p_dof_near_begin - p_dof_near_size; + bokeh.push_constant.blur_near_begin = dof_near_begin; + bokeh.push_constant.blur_near_end = dof_near_begin - dof_near_size; } { @@ -337,14 +367,14 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f RD::get_singleton()->draw_list_end(); } - if (p_bokeh_shape == RS::DOF_BOKEH_BOX || p_bokeh_shape == RS::DOF_BOKEH_HEXAGON) { + if (bokeh_shape == RS::DOF_BOKEH_BOX || bokeh_shape == RS::DOF_BOKEH_HEXAGON) { // double pass approach - BokehMode mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL; + BokehMode mode = bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX : BOKEH_GEN_BOKEH_HEXAGONAL; RID shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - if (p_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || p_quality == RS::DOF_BLUR_QUALITY_LOW) { + if (blur_quality == RS::DOF_BLUR_QUALITY_VERY_LOW || blur_quality == RS::DOF_BLUR_QUALITY_LOW) { //box and hexagon are more or less the same, and they can work in either half (very low and low quality) or full (medium and high quality_ sizes) bokeh.push_constant.size[0] = p_buffers.base_texture_size.x >> 1; bokeh.push_constant.size[1] = p_buffers.base_texture_size.y >> 1; @@ -354,7 +384,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f static const int quality_samples[4] = { 6, 12, 12, 24 }; bokeh.push_constant.blur_scale = 0.5; - bokeh.push_constant.steps = quality_samples[p_quality]; + bokeh.push_constant.steps = quality_samples[blur_quality]; RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb; @@ -373,7 +403,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f // Pass 2 if (!bokeh.push_constant.half_size) { // do not output weight, we're writing back into our base buffer - mode = p_bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX_NOWEIGHT : BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT; + mode = bokeh_shape == RS::DOF_BOKEH_BOX ? BOKEH_GEN_BOKEH_BOX_NOWEIGHT : BOKEH_GEN_BOKEH_HEXAGONAL_NOWEIGHT; shader = bokeh.raster_shader.version_get_shader(bokeh.shader_version, mode); ERR_FAIL_COND(shader.is_null()); @@ -432,7 +462,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, f } static const float quality_scale[4] = { 8.0, 4.0, 1.0, 0.5 }; - bokeh.push_constant.blur_scale = quality_scale[p_quality]; + bokeh.push_constant.blur_scale = quality_scale[blur_quality]; bokeh.push_constant.steps = 0.0; RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb; diff --git a/servers/rendering/renderer_rd/effects/bokeh_dof.h b/servers/rendering/renderer_rd/effects/bokeh_dof.h index 30b33be168..33dbdfcdc1 100644 --- a/servers/rendering/renderer_rd/effects/bokeh_dof.h +++ b/servers/rendering/renderer_rd/effects/bokeh_dof.h @@ -66,6 +66,11 @@ private: uint32_t use_jitter; float jitter_seed; + uint32_t use_physical_near; + uint32_t use_physical_far; + + float blur_size_near; + float blur_size_far; uint32_t pad[2]; }; @@ -111,8 +116,8 @@ public: BokehDOF(bool p_prefer_raster_effects); ~BokehDOF(); - void bokeh_dof_compute(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_bokeh_size, RS::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, bool p_use_jitter, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal); - void bokeh_dof_raster(const BokehBuffers &p_buffers, bool p_dof_far, float p_dof_far_begin, float p_dof_far_size, bool p_dof_near, float p_dof_near_begin, float p_dof_near_size, float p_dof_blur_amount, RenderingServer::DOFBokehShape p_bokeh_shape, RS::DOFBlurQuality p_quality, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal); + void bokeh_dof_compute(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal); + void bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attributes, float p_cam_znear, float p_cam_zfar, bool p_cam_orthogonal); }; } // namespace RendererRD diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp index 5507483cee..70f5fc4a6a 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.cpp +++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp @@ -654,7 +654,7 @@ void CopyEffects::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Re RD::get_singleton()->compute_list_end(); } -void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) { +void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_scale) { ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian glow with the mobile renderer."); UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); @@ -678,7 +678,7 @@ void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, con copy.push_constant.glow_white = 0; //actually unused copy.push_constant.glow_luminance_cap = p_luminance_cap; - copy.push_constant.glow_auto_exposure_grey = p_auto_exposure_grey; //unused also + copy.push_constant.glow_auto_exposure_scale = p_auto_exposure_scale; //unused also // setup our uniforms RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); @@ -705,7 +705,7 @@ void CopyEffects::gaussian_glow(RID p_source_rd_texture, RID p_back_texture, con RD::get_singleton()->compute_list_end(); } -void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminance_multiplier, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_grey) { +void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminance_multiplier, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Size2i &p_size, float p_strength, bool p_high_quality, bool p_first_pass, float p_luminance_cap, float p_exposure, float p_bloom, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, RID p_auto_exposure, float p_auto_exposure_scale) { ERR_FAIL_COND_MSG(!prefer_raster_effects, "Can't use the raster version of the gaussian glow with the clustered renderer."); UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); @@ -729,7 +729,7 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, float p_luminanc blur_raster.push_constant.glow_white = 0; //actually unused blur_raster.push_constant.glow_luminance_cap = p_luminance_cap; - blur_raster.push_constant.glow_auto_exposure_grey = p_auto_exposure_grey; //unused also + blur_raster.push_constant.glow_auto_exposure_scale = p_auto_exposure_scale; //unused also blur_raster.push_constant.luminance_multiplier = p_luminance_multiplier; diff --git a/servers/rendering/renderer_rd/effects/copy_effects.h b/servers/rendering/renderer_rd/effects/copy_effects.h index d25555eee5..f82726d654 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.h +++ b/servers/rendering/renderer_rd/effects/copy_effects.h @@ -86,7 +86,7 @@ private: float glow_exposure; float glow_white; float glow_luminance_cap; - float glow_auto_exposure_grey; + float glow_auto_exposure_scale; float luminance_multiplier; float res1; @@ -148,7 +148,7 @@ private: float glow_exposure; float glow_white; float glow_luminance_cap; - float glow_auto_exposure_grey; + float glow_auto_exposure_scale; // DOF. float camera_z_far; float camera_z_near; @@ -321,8 +321,8 @@ public: void copy_raster(RID p_source_texture, RID p_dest_framebuffer); void gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst = false); - void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0); - void gaussian_glow_raster(RID p_source_rd_texture, float p_luminance_multiplier, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0); + void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_scale = 1.0); + void gaussian_glow_raster(RID p_source_rd_texture, float p_luminance_multiplier, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_scale = 1.0); void make_mipmap(RID p_source_rd_texture, RID p_dest_texture, const Size2i &p_size); void make_mipmap_raster(RID p_source_rd_texture, RID p_dest_framebuffer, const Size2i &p_size); diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/servers/rendering/renderer_rd/effects/tone_mapper.cpp index 38a4a37b8a..3a47b1420b 100644 --- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp +++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp @@ -117,7 +117,7 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure; tonemap.push_constant.exposure = p_settings.exposure; tonemap.push_constant.white = p_settings.white; - tonemap.push_constant.auto_exposure_grey = p_settings.auto_exposure_grey; + tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale; tonemap.push_constant.luminance_multiplier = p_settings.luminance_multiplier; tonemap.push_constant.use_color_correction = p_settings.use_color_correction; @@ -203,7 +203,7 @@ void ToneMapper::tonemapper(RD::DrawListID p_subpass_draw_list, RID p_source_col tonemap.push_constant.use_auto_exposure = p_settings.use_auto_exposure; tonemap.push_constant.exposure = p_settings.exposure; tonemap.push_constant.white = p_settings.white; - tonemap.push_constant.auto_exposure_grey = p_settings.auto_exposure_grey; + tonemap.push_constant.auto_exposure_scale = p_settings.auto_exposure_scale; tonemap.push_constant.use_color_correction = p_settings.use_color_correction; diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.h b/servers/rendering/renderer_rd/effects/tone_mapper.h index 05db4a0cbe..e91118e241 100644 --- a/servers/rendering/renderer_rd/effects/tone_mapper.h +++ b/servers/rendering/renderer_rd/effects/tone_mapper.h @@ -77,7 +77,7 @@ private: float exposure; // 4 - 84 float white; // 4 - 88 - float auto_exposure_grey; // 4 - 92 + float auto_exposure_scale; // 4 - 92 float luminance_multiplier; // 4 - 96 float pixel_size[2]; // 8 - 104 @@ -124,7 +124,7 @@ public: float white = 1.0; bool use_auto_exposure = false; - float auto_exposure_grey = 0.5; + float auto_exposure_scale = 0.5; RID exposure_texture; float luminance_multiplier = 1.0; diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index 66e984174c..bc4f8d5855 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -287,6 +287,19 @@ float GI::voxel_gi_get_energy(RID p_voxel_gi) const { return voxel_gi->energy; } +void GI::voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) { + VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); + ERR_FAIL_COND(!voxel_gi); + + voxel_gi->baked_exposure = p_baked_exposure; +} + +float GI::voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const { + VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); + ERR_FAIL_COND_V(!voxel_gi, 0); + return voxel_gi->baked_exposure; +} + void GI::voxel_gi_set_bias(RID p_voxel_gi, float p_bias) { VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi); ERR_FAIL_COND(!voxel_gi); @@ -1292,7 +1305,7 @@ void GI::SDFGI::update_probes(RID p_env, SkyRD::Sky *p_sky) { push_constant.y_mult = y_mult; if (reads_sky && p_env.is_valid()) { - push_constant.sky_energy = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy(p_env); + push_constant.sky_energy = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy_multiplier(p_env); if (RendererSceneRenderRD::get_singleton()->environment_get_background(p_env) == RS::ENV_BG_CLEAR_COLOR) { push_constant.sky_mode = SDFGIShader::IntegratePushConstant::SKY_MODE_COLOR; @@ -1840,6 +1853,11 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r c.probe_world_offset[2] = probe_ofs.z; c.to_cell = 1.0 / cascades[i].cell_size; + c.exposure_normalization = 1.0; + if (p_render_data->camera_attributes.is_valid()) { + float exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + c.exposure_normalization = exposure_normalization / cascades[i].baked_exposure_normalization; + } } RD::get_singleton()->buffer_update(gi->sdfgi_ubo, 0, sizeof(SDFGIData), &sdfgi_data, RD::BARRIER_MASK_COMPUTE); @@ -1876,6 +1894,14 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r lights[idx].color[2] = color.b; lights[idx].type = RS::LIGHT_DIRECTIONAL; lights[idx].energy = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY) * RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_INDIRECT_ENERGY); + if (p_scene_render->is_using_physical_light_units()) { + lights[idx].energy *= RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_INTENSITY); + } + + if (p_render_data->camera_attributes.is_valid()) { + lights[idx].energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + lights[idx].has_shadow = RSG::light_storage->light_has_shadow(li->light); idx++; @@ -1920,7 +1946,25 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r lights[idx].color[1] = color.g; lights[idx].color[2] = color.b; lights[idx].type = RSG::light_storage->light_get_type(li->light); + lights[idx].energy = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY) * RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_INDIRECT_ENERGY); + if (p_scene_render->is_using_physical_light_units()) { + lights[idx].energy *= RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_INTENSITY); + + // Convert from Luminous Power to Luminous Intensity + if (lights[idx].type == RS::LIGHT_OMNI) { + lights[idx].energy *= 1.0 / (Math_PI * 4.0); + } else if (lights[idx].type == RS::LIGHT_SPOT) { + // Spot Lights are not physically accurate, Luminous Intensity should change in relation to the cone angle. + // We make this assumption to keep them easy to control. + lights[idx].energy *= 1.0 / Math_PI; + } + } + + if (p_render_data->camera_attributes.is_valid()) { + lights[idx].energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + lights[idx].has_shadow = RSG::light_storage->light_has_shadow(li->light); lights[idx].attenuation = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION); lights[idx].radius = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE); @@ -1938,7 +1982,7 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r } } -void GI::SDFGI::render_region(RID p_render_buffers, int p_region, const PagedArray<RenderGeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render) { +void GI::SDFGI::render_region(RID p_render_buffers, int p_region, const PagedArray<RenderGeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render, float p_exposure_normalization) { //print_line("rendering region " + itos(p_region)); RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.get_or_null(p_render_buffers); ERR_FAIL_COND(!rb); // we wouldn't be here if this failed but... @@ -1960,7 +2004,7 @@ void GI::SDFGI::render_region(RID p_render_buffers, int p_region, const PagedArr } //print_line("rendering cascade " + itos(p_region) + " objects: " + itos(p_cull_count) + " bounds: " + bounds + " from: " + from + " size: " + size + " cell size: " + rtos(cascades[cascade].cell_size)); - p_scene_render->_render_sdfgi(p_render_buffers, from, size, bounds, p_instances, render_albedo, render_emission, render_emission_aniso, render_geom_facing); + p_scene_render->_render_sdfgi(p_render_buffers, from, size, bounds, p_instances, render_albedo, render_emission, render_emission_aniso, render_geom_facing, p_exposure_normalization); if (cascade_next != cascade) { RD::get_singleton()->draw_command_begin_label("SDFGI Pre-Process Cascade"); @@ -1989,6 +2033,7 @@ void GI::SDFGI::render_region(RID p_render_buffers, int p_region, const PagedArr } cascades[cascade].all_dynamic_lights_dirty = true; + cascades[cascade].baked_exposure_normalization = p_exposure_normalization; push_constant.grid_size = cascade_size; push_constant.cascade = cascade; @@ -2297,7 +2342,7 @@ void GI::SDFGI::render_region(RID p_render_buffers, int p_region, const PagedArr } } -void GI::SDFGI::render_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result, RendererSceneRenderRD *p_scene_render) { +void GI::SDFGI::render_static_lights(RenderDataRD *p_render_data, RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result, RendererSceneRenderRD *p_scene_render) { RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.get_or_null(p_render_buffers); ERR_FAIL_COND(!rb); // we wouldn't be here if this failed but... @@ -2358,7 +2403,25 @@ void GI::SDFGI::render_static_lights(RID p_render_buffers, uint32_t p_cascade_co lights[idx].color[0] = color.r; lights[idx].color[1] = color.g; lights[idx].color[2] = color.b; + lights[idx].energy = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY) * RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_INDIRECT_ENERGY); + if (p_scene_render->is_using_physical_light_units()) { + lights[idx].energy *= RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_INTENSITY); + + // Convert from Luminous Power to Luminous Intensity + if (lights[idx].type == RS::LIGHT_OMNI) { + lights[idx].energy *= 1.0 / (Math_PI * 4.0); + } else if (lights[idx].type == RS::LIGHT_SPOT) { + // Spot Lights are not physically accurate, Luminous Intensity should change in relation to the cone angle. + // We make this assumption to keep them easy to control. + lights[idx].energy *= 1.0 / Math_PI; + } + } + + if (p_render_data->camera_attributes.is_valid()) { + lights[idx].energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + lights[idx].has_shadow = RSG::light_storage->light_has_shadow(li->light); lights[idx].attenuation = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION); lights[idx].radius = RSG::light_storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE); @@ -2794,6 +2857,22 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID l.attenuation = RSG::light_storage->light_get_param(light, RS::LIGHT_PARAM_ATTENUATION); l.energy = RSG::light_storage->light_get_param(light, RS::LIGHT_PARAM_ENERGY) * RSG::light_storage->light_get_param(light, RS::LIGHT_PARAM_INDIRECT_ENERGY); + + if (p_scene_render->is_using_physical_light_units()) { + l.energy *= RSG::light_storage->light_get_param(light, RS::LIGHT_PARAM_INTENSITY); + + l.energy *= gi->voxel_gi_get_baked_exposure_normalization(probe); + + // Convert from Luminous Power to Luminous Intensity + if (l.type == RS::LIGHT_OMNI) { + l.energy *= 1.0 / (Math_PI * 4.0); + } else if (l.type == RS::LIGHT_SPOT) { + // Spot Lights are not physically accurate, Luminous Intensity should change in relation to the cone angle. + // We make this assumption to keep them easy to control. + l.energy *= 1.0 / Math_PI; + } + } + l.radius = to_cell.basis.xform(Vector3(RSG::light_storage->light_get_param(light, RS::LIGHT_PARAM_RANGE), 0, 0)).length(); Color color = RSG::light_storage->light_get_color(light).srgb_to_linear(); l.color[0] = color.r; @@ -3003,7 +3082,12 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID } p_scene_render->cull_argument[0] = instance; - p_scene_render->_render_material(to_world_xform * xform, cm, true, p_scene_render->cull_argument, dynamic_maps[0].fb, Rect2i(Vector2i(), rect.size)); + float exposure_normalization = 1.0; + if (p_scene_render->is_using_physical_light_units()) { + exposure_normalization = gi->voxel_gi_get_baked_exposure_normalization(probe); + } + + p_scene_render->_render_material(to_world_xform * xform, cm, true, p_scene_render->cull_argument, dynamic_maps[0].fb, Rect2i(Vector2i(), rect.size), exposure_normalization); VoxelGIDynamicPushConstant push_constant; memset(&push_constant, 0, sizeof(VoxelGIDynamicPushConstant)); @@ -3496,7 +3580,7 @@ GI::SDFGI *GI::create_sdfgi(RID p_env, const Vector3 &p_world_position, uint32_t return sdfgi; } -void GI::setup_voxel_gi_instances(RID p_render_buffers, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used, RendererSceneRenderRD *p_scene_render) { +void GI::setup_voxel_gi_instances(RenderDataRD *p_render_data, RID p_render_buffers, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used, RendererSceneRenderRD *p_scene_render) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); r_voxel_gi_instances_used = 0; @@ -3555,6 +3639,11 @@ void GI::setup_voxel_gi_instances(RID p_render_buffers, const Transform3D &p_tra gipd.normal_bias = voxel_gi_get_normal_bias(base_probe); gipd.blend_ambient = !voxel_gi_is_interior(base_probe); gipd.mipmaps = gipi->mipmaps.size(); + gipd.exposure_normalization = 1.0; + if (p_render_data->camera_attributes.is_valid()) { + float exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + gipd.exposure_normalization = exposure_normalization / voxel_gi_get_baked_exposure_normalization(base_probe); + } } r_voxel_gi_instances_used++; diff --git a/servers/rendering/renderer_rd/environment/gi.h b/servers/rendering/renderer_rd/environment/gi.h index 8860445c3b..9ef1914333 100644 --- a/servers/rendering/renderer_rd/environment/gi.h +++ b/servers/rendering/renderer_rd/environment/gi.h @@ -76,6 +76,7 @@ public: float dynamic_range = 2.0; float energy = 1.0; + float baked_exposure = 1.0; float bias = 1.4; float normal_bias = 0.0; float propagation = 0.5; @@ -398,6 +399,9 @@ public: virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_energy) override; virtual float voxel_gi_get_energy(RID p_voxel_gi) const override; + virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) override; + virtual float voxel_gi_get_baked_exposure_normalization(RID p_voxel_gi) const override; + virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_bias) override; virtual float voxel_gi_get_bias(RID p_voxel_gi) const override; @@ -512,6 +516,7 @@ public: float to_cell; int32_t probe_offset[3]; uint32_t pad; + float pad2[4]; }; //cascade blocks are full-size for volume (128^3), half size for albedo/emission @@ -551,6 +556,8 @@ public: RID integrate_uniform_set; RID lights_buffer; + float baked_exposure_normalization = 1.0; + bool all_dynamic_lights_dirty = true; }; @@ -630,8 +637,8 @@ public: void debug_probes(RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth); void pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_render_data, RendererSceneRenderRD *p_scene_render); - void render_region(RID p_render_buffers, int p_region, const PagedArray<RenderGeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render); - void render_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result, RendererSceneRenderRD *p_scene_render); + void render_region(RID p_render_buffers, int p_region, const PagedArray<RenderGeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render, float p_exposure_normalization); + void render_static_lights(RenderDataRD *p_render_data, RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result, RendererSceneRenderRD *p_scene_render); }; RS::EnvironmentSDFGIRayCount sdfgi_ray_count = RS::ENV_SDFGI_RAY_COUNT_16; @@ -705,6 +712,8 @@ public: float to_probe; // 1/bounds * grid_size int32_t probe_world_offset[3]; float to_cell; // 1/bounds * grid_size + float pad[3]; + float exposure_normalization; }; ProbeCascadeData cascades[SDFGI::MAX_CASCADES]; @@ -720,6 +729,9 @@ public: float normal_bias; // 4 - 88 uint32_t blend_ambient; // 4 - 92 uint32_t mipmaps; // 4 - 96 + + float pad[3]; // 12 - 108 + float exposure_normalization; // 4 - 112 }; struct SceneData { @@ -777,7 +789,7 @@ public: SDFGI *create_sdfgi(RID p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size); - void setup_voxel_gi_instances(RID p_render_buffers, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used, RendererSceneRenderRD *p_scene_render); + void setup_voxel_gi_instances(RenderDataRD *p_render_data, RID p_render_buffers, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used, RendererSceneRenderRD *p_scene_render); void process_gi(RID p_render_buffers, const RID *p_normal_roughness_slices, RID p_voxel_gi_buffer, const RID *p_vrs_slices, RID p_environment, uint32_t p_view_count, const Projection *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_cam_transform, const PagedArray<RID> &p_voxel_gi_instances, RendererSceneRenderRD *p_scene_render); RID voxel_gi_instance_create(RID p_base); diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 1d6b158d65..d1d18cdd83 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -292,7 +292,7 @@ static _FORCE_INLINE_ void store_transform_3x3(const Basis &p_basis, float *p_ar p_array[11] = 0; } -void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const Projection *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position, float p_luminance_multiplier) { +void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const Projection *p_projections, const Basis &p_orientation, const Vector3 &p_position, float p_luminance_multiplier) { SkyPushConstant sky_push_constant; memset(&sky_push_constant, 0, sizeof(SkyPushConstant)); @@ -307,7 +307,6 @@ void SkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineC sky_push_constant.position[0] = p_position.x; sky_push_constant.position[1] = p_position.y; sky_push_constant.position[2] = p_position.z; - sky_push_constant.multiplier = p_multiplier; sky_push_constant.time = p_time; sky_push_constant.luminance_multiplier = p_luminance_multiplier; store_transform_3x3(p_orientation, sky_push_constant.orientation); @@ -762,7 +761,7 @@ Ref<Image> SkyRD::Sky::bake_panorama(float p_energy, int p_roughness_layers, con RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton(); RD::TextureFormat tf; - tf.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; + tf.format = RD::DATA_FORMAT_R32G32B32A32_SFLOAT; // Could be RGBA16 tf.width = p_size.width; tf.height = p_size.height; tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; @@ -868,7 +867,7 @@ void SkyRD::init() { actions.renames["COLOR"] = "color"; actions.renames["ALPHA"] = "alpha"; actions.renames["EYEDIR"] = "cube_normal"; - actions.renames["POSITION"] = "params.position_multiplier.xyz"; + actions.renames["POSITION"] = "params.position"; actions.renames["SKY_COORDS"] = "panorama_coords"; actions.renames["SCREEN_UV"] = "uv"; actions.renames["FRAGCOORD"] = "gl_FragCoord"; @@ -1110,7 +1109,7 @@ SkyRD::~SkyRD() { RD::get_singleton()->free(index_buffer); //array gets freed as dependency } -void SkyRD::setup(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render) { +void SkyRD::setup(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, RID p_camera_attributes, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); ERR_FAIL_COND(p_env.is_null()); @@ -1220,6 +1219,14 @@ void SkyRD::setup(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_ligh float sign = light_storage->light_is_negative(base) ? -1 : 1; sky_light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); + if (p_scene_render->is_using_physical_light_units()) { + sky_light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + } + + if (p_camera_attributes.is_valid()) { + sky_light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_camera_attributes); + } + Color linear_col = light_storage->light_get_color(base).srgb_to_linear(); sky_light_data.color[0] = linear_col.r; sky_light_data.color[1] = linear_col.g; @@ -1351,8 +1358,6 @@ void SkyRD::update(RID p_env, const Projection &p_projection, const Transform3D ERR_FAIL_COND(!shader_data); - float multiplier = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy(p_env); - bool update_single_frame = sky->mode == RS::SKY_MODE_REALTIME || sky->mode == RS::SKY_MODE_QUALITY; RS::SkyMode sky_mode = sky->mode; @@ -1415,7 +1420,7 @@ void SkyRD::update(RID p_env, const Projection &p_projection, const Transform3D RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin, p_luminance_multiplier); + _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); @@ -1434,7 +1439,7 @@ void SkyRD::update(RID p_env, const Projection &p_projection, const Transform3D RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin, p_luminance_multiplier); + _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); @@ -1449,7 +1454,7 @@ void SkyRD::update(RID p_env, const Projection &p_projection, const Transform3D RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd); cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); - _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, multiplier, p_transform.origin, p_luminance_multiplier); + _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, 1, &cm, local_view, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); @@ -1475,7 +1480,7 @@ void SkyRD::update(RID p_env, const Projection &p_projection, const Transform3D } sky->processing_layer = 1; } - + sky->baked_exposure = p_luminance_multiplier; sky->reflection.dirty = false; } else { @@ -1491,7 +1496,7 @@ void SkyRD::update(RID p_env, const Projection &p_projection, const Transform3D } } -void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, double p_time) { +void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier) { RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); ERR_FAIL_COND(p_env.is_null()); @@ -1536,7 +1541,6 @@ void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth Basis sky_transform = RendererSceneRenderRD::get_singleton()->environment_get_sky_orientation(p_env); sky_transform.invert(); - float multiplier = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy(p_env); float custom_fov = RendererSceneRenderRD::get_singleton()->environment_get_sky_custom_fov(p_env); // Camera @@ -1567,7 +1571,7 @@ void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth clear_colors.push_back(Color(0.0, 0.0, 0.0)); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); - _render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, 1.0); + _render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1580,7 +1584,7 @@ void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth clear_colors.push_back(Color(0.0, 0.0, 0.0)); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); - _render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, 1.0); + _render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1594,7 +1598,7 @@ void SkyRD::draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth } RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_fb, RD::INITIAL_ACTION_CONTINUE, p_can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ); - _render_sky(draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, 1.0); + _render_sky(draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1634,7 +1638,6 @@ void SkyRD::update_res_buffers(RID p_env, uint32_t p_view_count, const Projectio Basis sky_transform = RendererSceneRenderRD::get_singleton()->environment_get_sky_orientation(p_env); sky_transform.invert(); - float multiplier = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy(p_env); float custom_fov = RendererSceneRenderRD::get_singleton()->environment_get_sky_custom_fov(p_env); // Camera @@ -1665,7 +1668,7 @@ void SkyRD::update_res_buffers(RID p_env, uint32_t p_view_count, const Projectio clear_colors.push_back(Color(0.0, 0.0, 0.0)); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->quarter_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); - _render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, p_luminance_multiplier); + _render_sky(draw_list, p_time, sky->quarter_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1678,7 +1681,7 @@ void SkyRD::update_res_buffers(RID p_env, uint32_t p_view_count, const Projectio clear_colors.push_back(Color(0.0, 0.0, 0.0)); RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(sky->half_res_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); - _render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, p_luminance_multiplier); + _render_sky(draw_list, p_time, sky->half_res_framebuffer, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, p_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } } @@ -1728,7 +1731,6 @@ void SkyRD::draw(RD::DrawListID p_draw_list, RID p_env, RID p_fb, uint32_t p_vie Basis sky_transform = RendererSceneRenderRD::get_singleton()->environment_get_sky_orientation(p_env); sky_transform.invert(); - float multiplier = RendererSceneRenderRD::get_singleton()->environment_get_bg_energy(p_env); float custom_fov = RendererSceneRenderRD::get_singleton()->environment_get_sky_custom_fov(p_env); // Camera @@ -1759,7 +1761,7 @@ void SkyRD::draw(RD::DrawListID p_draw_list, RID p_env, RID p_fb, uint32_t p_vie texture_uniform_set = sky_scene_state.fog_only_texture_uniform_set; } - _render_sky(p_draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, multiplier, p_transform.origin, p_luminance_multiplier); + _render_sky(p_draw_list, p_time, p_fb, pipeline, material->uniform_set, texture_uniform_set, view_count, projections, sky_transform, p_transform.origin, p_luminance_multiplier); } void SkyRD::invalidate_sky(Sky *p_sky) { @@ -1881,6 +1883,13 @@ RID SkyRD::sky_get_material(RID p_sky) const { return sky->material; } +float SkyRD::sky_get_baked_exposure(RID p_sky) const { + Sky *sky = get_sky(p_sky); + ERR_FAIL_COND_V(!sky, 1.0); + + return sky->baked_exposure; +} + RID SkyRD::allocate_sky_rid() { return sky_owner.allocate_rid(); } diff --git a/servers/rendering/renderer_rd/environment/sky.h b/servers/rendering/renderer_rd/environment/sky.h index 080165c112..bac8f44ef7 100644 --- a/servers/rendering/renderer_rd/environment/sky.h +++ b/servers/rendering/renderer_rd/environment/sky.h @@ -100,10 +100,9 @@ private: float orientation[12]; // 48 - 48 float projections[RendererSceneRender::MAX_RENDER_VIEWS][4]; // 2 x 16 - 80 float position[3]; // 12 - 92 - float multiplier; // 4 - 96 - float time; // 4 - 100 - float luminance_multiplier; // 4 - 104 - float pad[2]; // 8 - 112 // Using pad to align on 16 bytes + float time; // 4 - 96 + float pad[3]; // 12 - 108 + float luminance_multiplier; // 4 - 112 // 128 is the max size of a push constant. We can replace "pad" but we can't add any more. }; @@ -143,7 +142,7 @@ private: virtual ~SkyShaderData(); }; - void _render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const Projection *p_projections, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position, float p_luminance_multiplier); + void _render_sky(RD::DrawListID p_list, float p_time, RID p_fb, PipelineCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, uint32_t p_view_count, const Projection *p_projections, const Basis &p_orientation, const Vector3 &p_position, float p_luminance_multiplier); public: struct SkySceneState { @@ -264,6 +263,7 @@ public: bool dirty = false; int processing_layer = 0; Sky *dirty_list = nullptr; + float baked_exposure = 1.0; //State to track when radiance cubemap needs updating SkyMaterialData *prev_material = nullptr; @@ -296,9 +296,9 @@ public: void set_texture_format(RD::DataFormat p_texture_format); ~SkyRD(); - void setup(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render); + void setup(RID p_env, RID p_render_buffers, const PagedArray<RID> &p_lights, RID p_camera_attributes, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size, RendererSceneRenderRD *p_scene_render); void update(RID p_env, const Projection &p_projection, const Transform3D &p_transform, double p_time, float p_luminance_multiplier = 1.0); - void draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, double p_time); // only called by clustered renderer + void draw(RID p_env, bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier = 1.0); // only called by clustered renderer void update_res_buffers(RID p_env, uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier = 1.0); void draw(RD::DrawListID p_draw_list, RID p_env, RID p_fb, uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, double p_time, float p_luminance_multiplier = 1.0); @@ -306,6 +306,8 @@ public: void update_dirty_skys(); RID sky_get_material(RID p_sky) const; + RID sky_get_radiance_texture_rd(RID p_sky) const; + float sky_get_baked_exposure(RID p_sky) const; RID allocate_sky_rid(); void initialize_sky_rid(RID p_rid); @@ -315,8 +317,6 @@ public: void sky_set_mode(RID p_sky, RS::SkyMode p_mode); void sky_set_material(RID p_sky, RID p_material); Ref<Image> sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size); - - RID sky_get_radiance_texture_rd(RID p_sky) const; }; } // namespace RendererRD 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 8754e90647..8421598275 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -906,8 +906,9 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat RS::EnvironmentBG env_bg = environment_get_background(p_render_data->environment); RS::EnvironmentAmbientSource ambient_src = environment_get_ambient_source(p_render_data->environment); - float bg_energy = environment_get_bg_energy(p_render_data->environment); - scene_state.ubo.ambient_light_color_energy[3] = bg_energy; + float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); + + scene_state.ubo.ambient_light_color_energy[3] = bg_energy_multiplier; scene_state.ubo.ambient_color_sky_mix = environment_get_ambient_sky_contribution(p_render_data->environment); @@ -916,9 +917,9 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat Color color = env_bg == RS::ENV_BG_CLEAR_COLOR ? p_default_bg_color : environment_get_bg_color(p_render_data->environment); color = color.srgb_to_linear(); - scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy; - scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy; - scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy; + scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy_multiplier; scene_state.ubo.use_ambient_light = true; scene_state.ubo.use_ambient_cubemap = false; } else { @@ -987,6 +988,25 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat scene_state.ubo.ss_effects_flags = 0; } + if (p_render_data->camera_attributes.is_valid()) { + scene_state.ubo.emissive_exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + scene_state.ubo.IBL_exposure_normalization = 1.0; + if (is_environment(p_render_data->environment)) { + RID sky_rid = environment_get_sky(p_render_data->environment); + if (sky_rid.is_valid()) { + float current_exposure = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes) * environment_get_bg_intensity(p_render_data->environment) / _render_buffers_get_luminance_multiplier(); + scene_state.ubo.IBL_exposure_normalization = current_exposure / MAX(0.001, sky.sky_get_baked_exposure(sky_rid)); + } + } + } else if (scene_state.ubo.emissive_exposure_normalization > 0.0) { + // This branch is triggered when using render_material(). + // Emissive is set outside the function, so don't set it. + // IBL isn't used don't set it. + } else { + scene_state.ubo.emissive_exposure_normalization = 1.0; + scene_state.ubo.IBL_exposure_normalization = 1.0; + } + scene_state.ubo.roughness_limiter_enabled = p_opaque_render_buffers && screen_space_roughness_limiter_is_active(); scene_state.ubo.roughness_limiter_amount = screen_space_roughness_limiter_get_amount(); scene_state.ubo.roughness_limiter_limit = screen_space_roughness_limiter_get_limit(); @@ -1383,7 +1403,7 @@ void RenderForwardClustered::_setup_voxelgis(const PagedArray<RID> &p_voxelgis) } } -void RenderForwardClustered::_setup_lightmaps(const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform) { +void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform) { scene_state.lightmaps_used = 0; for (int i = 0; i < (int)p_lightmaps.size(); i++) { if (i >= (int)scene_state.max_lightmaps) { @@ -1395,6 +1415,13 @@ void RenderForwardClustered::_setup_lightmaps(const PagedArray<RID> &p_lightmaps Basis to_lm = lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis; to_lm = to_lm.inverse().transposed(); //will transform normals RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform); + scene_state.lightmaps[i].exposure_normalization = 1.0; + if (p_render_data->camera_attributes.is_valid()) { + float baked_exposure = RendererRD::LightStorage::get_singleton()->lightmap_get_baked_exposure_normalization(lightmap); + float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + scene_state.lightmaps[i].exposure_normalization = enf / baked_exposure; + } + scene_state.lightmap_ids[i] = p_lightmaps[i]; scene_state.lightmap_has_sh[i] = RendererRD::LightStorage::get_singleton()->lightmap_uses_spherical_harmonics(lightmap); @@ -1513,9 +1540,11 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co scene_state.ubo.viewport_size[0] = screen_size.x; scene_state.ubo.viewport_size[1] = screen_size.y; + scene_state.ubo.emissive_exposure_normalization = -1.0; + RD::get_singleton()->draw_command_begin_label("Render Setup"); - _setup_lightmaps(*p_render_data->lightmaps, p_render_data->cam_transform); + _setup_lightmaps(p_render_data, *p_render_data->lightmaps, p_render_data->cam_transform); _setup_voxelgis(*p_render_data->voxel_gi_instances); _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false); @@ -1539,6 +1568,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RID radiance_texture; bool draw_sky = false; bool draw_sky_fog_only = false; + // We invert luminance_multiplier for sky so that we can combine it with exposure value. + float sky_energy_multiplier = 1.0 / _render_buffers_get_luminance_multiplier(); Color clear_color; bool keep_color = false; @@ -1547,13 +1578,19 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black } else if (is_environment(p_render_data->environment)) { RS::EnvironmentBG bg_mode = environment_get_background(p_render_data->environment); - float bg_energy = environment_get_bg_energy(p_render_data->environment); + float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); + bg_energy_multiplier *= environment_get_bg_intensity(p_render_data->environment); + + if (p_render_data->camera_attributes.is_valid()) { + bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + switch (bg_mode) { case RS::ENV_BG_CLEAR_COLOR: { clear_color = p_default_bg_color; - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; if ((p_render_data->render_buffers.is_valid() && render_buffers_has_volumetric_fog(p_render_data->render_buffers)) || environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); @@ -1561,9 +1598,9 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } break; case RS::ENV_BG_COLOR: { clear_color = environment_get_bg_color(p_render_data->environment); - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; if ((p_render_data->render_buffers.is_valid() && render_buffers_has_volumetric_fog(p_render_data->render_buffers)) || environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear())); @@ -1594,11 +1631,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co projection = correction * p_render_data->cam_projection; } - sky.setup(p_render_data->environment, p_render_data->render_buffers, *p_render_data->lights, projection, p_render_data->cam_transform, screen_size, this); + sky.setup(p_render_data->environment, p_render_data->render_buffers, *p_render_data->lights, p_render_data->camera_attributes, projection, p_render_data->cam_transform, screen_size, this); + + sky_energy_multiplier *= bg_energy_multiplier; RID sky_rid = environment_get_sky(p_render_data->environment); if (sky_rid.is_valid()) { - sky.update(p_render_data->environment, projection, p_render_data->cam_transform, time); + sky.update(p_render_data->environment, projection, p_render_data->cam_transform, time, sky_energy_multiplier); radiance_texture = sky.sky_get_radiance_texture_rd(sky_rid); } else { // do not try to draw sky if invalid @@ -1753,9 +1792,9 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co Projection correction; correction.set_depth_correction(true); Projection projection = correction * p_render_data->cam_projection; - sky.draw(p_render_data->environment, can_continue_color, can_continue_depth, color_only_framebuffer, 1, &projection, p_render_data->cam_transform, time); + sky.draw(p_render_data->environment, can_continue_color, can_continue_depth, color_only_framebuffer, 1, &projection, p_render_data->cam_transform, time, sky_energy_multiplier); } else { - sky.draw(p_render_data->environment, can_continue_color, can_continue_depth, color_only_framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time); + sky.draw(p_render_data->environment, can_continue_color, can_continue_depth, color_only_framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time, sky_energy_multiplier); } RD::get_singleton()->draw_command_end_label(); } @@ -2000,7 +2039,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con RD::get_singleton()->draw_command_end_label(); } -void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) { +void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) { RENDER_TIMESTAMP("Setup Rendering 3D Material"); RD::get_singleton()->draw_command_begin_label("Render 3D Material"); @@ -2018,6 +2057,7 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform scene_state.ubo.dual_paraboloid_side = 0; scene_state.ubo.material_uv2_mode = false; scene_state.ubo.opaque_prepass_threshold = 0.0f; + scene_state.ubo.emissive_exposure_normalization = p_exposure_normalization; _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); @@ -2064,6 +2104,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance scene_state.ubo.dual_paraboloid_side = 0; scene_state.ubo.material_uv2_mode = true; scene_state.ubo.opaque_prepass_threshold = 0.0; + scene_state.ubo.emissive_exposure_normalization = -1.0; _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); @@ -2119,7 +2160,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance RD::get_singleton()->draw_command_end_label(); } -void RenderForwardClustered::_render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) { +void RenderForwardClustered::_render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) { RENDER_TIMESTAMP("Render SDFGI"); RD::get_singleton()->draw_command_begin_label("Render SDFGI Voxel"); @@ -2187,6 +2228,7 @@ void RenderForwardClustered::_render_sdfgi(RID p_render_buffers, const Vector3i RendererRD::MaterialStorage::store_transform(to_bounds.affine_inverse() * render_data.cam_transform, scene_state.ubo.sdf_to_bounds); + scene_state.ubo.emissive_exposure_normalization = p_exposure_normalization; _setup_environment(&render_data, true, Vector2(1, 1), false, Color()); RID rp_uniform_set = _setup_sdfgi_render_pass_uniform_set(p_albedo_texture, p_emission_texture, p_emission_aniso_texture, p_geom_facing_texture); 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 7e71406af8..3d74f6769e 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -218,6 +218,8 @@ class RenderForwardClustered : public RendererSceneRenderRD { struct LightmapData { float normal_xform[12]; + float pad[3]; + float exposure_normalization; }; struct LightmapCaptureData { @@ -324,7 +326,8 @@ class RenderForwardClustered : public RendererSceneRenderRD { uint32_t pancake_shadows; float taa_jitter[2]; - uint32_t pad[2]; + float emissive_exposure_normalization; // Needed to normalize emissive when using physical units. + float IBL_exposure_normalization; }; struct PushConstant { @@ -397,7 +400,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { void _setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers = false, bool p_pancake_shadows = false, int p_index = 0); void _setup_voxelgis(const PagedArray<RID> &p_voxelgis); - void _setup_lightmaps(const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform); + void _setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform); struct RenderElementInfo { enum { MAX_REPEATS = (1 << 20) - 1 }; @@ -618,9 +621,9 @@ protected: virtual void _render_shadow_process() override; virtual void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL) override; - virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; + virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override; virtual void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; - virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) override; + virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) override; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray<RenderGeometryInstance *> &p_instances) override; public: diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index ffd47cc163..67d001dcb7 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -469,7 +469,7 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_ return render_pass_uniform_sets[p_index]; } -void RenderForwardMobile::_setup_lightmaps(const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform) { +void RenderForwardMobile::_setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform) { // This probably needs to change... scene_state.lightmaps_used = 0; for (int i = 0; i < (int)p_lightmaps.size(); i++) { @@ -482,6 +482,13 @@ void RenderForwardMobile::_setup_lightmaps(const PagedArray<RID> &p_lightmaps, c Basis to_lm = lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis; to_lm = to_lm.inverse().transposed(); //will transform normals RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform); + scene_state.lightmaps[i].exposure_normalization = 1.0; + if (p_render_data->camera_attributes.is_valid()) { + float baked_exposure = RendererRD::LightStorage::get_singleton()->lightmap_get_baked_exposure_normalization(lightmap); + float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + scene_state.lightmaps[i].exposure_normalization = enf / baked_exposure; + } + scene_state.lightmap_ids[i] = p_lightmaps[i]; scene_state.lightmap_has_sh[i] = RendererRD::LightStorage::get_singleton()->lightmap_uses_spherical_harmonics(lightmap); @@ -539,7 +546,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color if (render_buffer->color_fbs[FB_CONFIG_FOUR_SUBPASSES].is_null()) { // can't do blit subpass using_subpass_post_process = false; - } else if (p_render_data->environment.is_valid() && (environment_get_glow_enabled(p_render_data->environment) || environment_get_auto_exposure(p_render_data->environment) || camera_effects_uses_dof(p_render_data->camera_effects))) { + } else if (p_render_data->environment.is_valid() && (environment_get_glow_enabled(p_render_data->environment) || RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) || RSG::camera_attributes->camera_attributes_uses_dof(p_render_data->camera_attributes))) { // can't do blit subpass using_subpass_post_process = false; } @@ -580,10 +587,11 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color scene_state.ubo.viewport_size[0] = screen_size.x; scene_state.ubo.viewport_size[1] = screen_size.y; + scene_state.ubo.emissive_exposure_normalization = -1.0; RD::get_singleton()->draw_command_begin_label("Render Setup"); - _setup_lightmaps(*p_render_data->lightmaps, p_render_data->cam_transform); + _setup_lightmaps(p_render_data, *p_render_data->lightmaps, p_render_data->cam_transform); _setup_environment(p_render_data, p_render_data->reflection_probe.is_valid(), screen_size, !p_render_data->reflection_probe.is_valid(), p_default_bg_color, false); _update_render_base_uniform_set(); //may have changed due to the above (light buffer enlarged, as an example) @@ -594,6 +602,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RID radiance_texture; bool draw_sky = false; bool draw_sky_fog_only = false; + // We invert luminance_multiplier for sky so that we can combine it with exposure value. + float sky_energy_multiplier = 1.0 / _render_buffers_get_luminance_multiplier(); Color clear_color = p_default_bg_color; bool keep_color = false; @@ -602,13 +612,19 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black } else if (is_environment(p_render_data->environment)) { RS::EnvironmentBG bg_mode = environment_get_background(p_render_data->environment); - float bg_energy = environment_get_bg_energy(p_render_data->environment); + float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); + bg_energy_multiplier *= environment_get_bg_intensity(p_render_data->environment); + + if (p_render_data->camera_attributes.is_valid()) { + bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } + switch (bg_mode) { case RS::ENV_BG_CLEAR_COLOR: { clear_color = p_default_bg_color; - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; /* if (render_buffers_has_volumetric_fog(p_render_data->render_buffers) || environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; @@ -618,9 +634,9 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } break; case RS::ENV_BG_COLOR: { clear_color = environment_get_bg_color(p_render_data->environment); - clear_color.r *= bg_energy; - clear_color.g *= bg_energy; - clear_color.b *= bg_energy; + clear_color.r *= bg_energy_multiplier; + clear_color.g *= bg_energy_multiplier; + clear_color.b *= bg_energy_multiplier; /* if (render_buffers_has_volumetric_fog(p_render_data->render_buffers) || environment_get_fog_enabled(p_render_data->environment)) { draw_sky_fog_only = true; @@ -653,11 +669,13 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color projection = correction * p_render_data->cam_projection; } - sky.setup(p_render_data->environment, p_render_data->render_buffers, *p_render_data->lights, projection, p_render_data->cam_transform, screen_size, this); + sky.setup(p_render_data->environment, p_render_data->render_buffers, *p_render_data->lights, p_render_data->camera_attributes, projection, p_render_data->cam_transform, screen_size, this); + + sky_energy_multiplier *= bg_energy_multiplier; RID sky_rid = environment_get_sky(p_render_data->environment); if (sky_rid.is_valid()) { - sky.update(p_render_data->environment, projection, p_render_data->cam_transform, time, _render_buffers_get_luminance_multiplier()); + sky.update(p_render_data->environment, projection, p_render_data->cam_transform, time, sky_energy_multiplier); radiance_texture = sky.sky_get_radiance_texture_rd(sky_rid); } else { // do not try to draw sky if invalid @@ -681,9 +699,9 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color Projection correction; correction.set_depth_correction(true); Projection projection = correction * p_render_data->cam_projection; - sky.update_res_buffers(p_render_data->environment, 1, &projection, p_render_data->cam_transform, time); + sky.update_res_buffers(p_render_data->environment, 1, &projection, p_render_data->cam_transform, time, sky_energy_multiplier); } else { - sky.update_res_buffers(p_render_data->environment, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time); + sky.update_res_buffers(p_render_data->environment, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time, sky_energy_multiplier); } RD::get_singleton()->draw_command_end_label(); // Setup Sky resolution buffers @@ -780,9 +798,9 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color Projection correction; correction.set_depth_correction(true); Projection projection = correction * p_render_data->cam_projection; - sky.draw(draw_list, p_render_data->environment, framebuffer, 1, &projection, p_render_data->cam_transform, time, _render_buffers_get_luminance_multiplier()); + sky.draw(draw_list, p_render_data->environment, framebuffer, 1, &projection, p_render_data->cam_transform, time, sky_energy_multiplier); } else { - sky.draw(draw_list, p_render_data->environment, framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time, _render_buffers_get_luminance_multiplier()); + sky.draw(draw_list, p_render_data->environment, framebuffer, p_render_data->view_count, p_render_data->view_projection, p_render_data->cam_transform, time, sky_energy_multiplier); } RD::get_singleton()->draw_command_end_label(); // Draw Sky Subpass @@ -999,7 +1017,7 @@ void RenderForwardMobile::_render_shadow_end(uint32_t p_barrier) { /* */ -void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) { +void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) { RENDER_TIMESTAMP("Setup Rendering 3D Material"); RD::get_singleton()->draw_command_begin_label("Render 3D Material"); @@ -1009,6 +1027,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c scene_state.ubo.dual_paraboloid_side = 0; scene_state.ubo.material_uv2_mode = false; scene_state.ubo.opaque_prepass_threshold = 0.0f; + scene_state.ubo.emissive_exposure_normalization = p_exposure_normalization; RenderDataRD render_data; render_data.cam_projection = p_cam_projection; @@ -1054,6 +1073,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *> scene_state.ubo.dual_paraboloid_side = 0; scene_state.ubo.material_uv2_mode = true; + scene_state.ubo.emissive_exposure_normalization = -1.0; RenderDataRD render_data; render_data.instances = &p_instances; @@ -1112,7 +1132,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *> RD::get_singleton()->draw_command_end_label(); } -void RenderForwardMobile::_render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) { +void RenderForwardMobile::_render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) { // we don't do GI in low end.. } @@ -1650,8 +1670,9 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, RS::EnvironmentBG env_bg = environment_get_background(p_render_data->environment); RS::EnvironmentAmbientSource ambient_src = environment_get_ambient_source(p_render_data->environment); - float bg_energy = environment_get_bg_energy(p_render_data->environment); - scene_state.ubo.ambient_light_color_energy[3] = bg_energy; + float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment); + + scene_state.ubo.ambient_light_color_energy[3] = bg_energy_multiplier; scene_state.ubo.ambient_color_sky_mix = environment_get_ambient_sky_contribution(p_render_data->environment); @@ -1660,9 +1681,9 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, Color color = env_bg == RS::ENV_BG_CLEAR_COLOR ? p_default_bg_color : environment_get_bg_color(p_render_data->environment); color = color.srgb_to_linear(); - scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy; - scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy; - scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy; + scene_state.ubo.ambient_light_color_energy[0] = color.r * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[1] = color.g * bg_energy_multiplier; + scene_state.ubo.ambient_light_color_energy[2] = color.b * bg_energy_multiplier; scene_state.ubo.use_ambient_light = true; scene_state.ubo.use_ambient_cubemap = false; } else { @@ -1726,6 +1747,25 @@ void RenderForwardMobile::_setup_environment(const RenderDataRD *p_render_data, scene_state.ubo.ssao_enabled = false; } + if (p_render_data->camera_attributes.is_valid()) { + scene_state.ubo.emissive_exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + scene_state.ubo.IBL_exposure_normalization = 1.0; + if (is_environment(p_render_data->environment)) { + RID sky_rid = environment_get_sky(p_render_data->environment); + if (sky_rid.is_valid()) { + float current_exposure = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes) * environment_get_bg_intensity(p_render_data->environment) / _render_buffers_get_luminance_multiplier(); + scene_state.ubo.IBL_exposure_normalization = current_exposure / MAX(0.001, sky.sky_get_baked_exposure(sky_rid)); + } + } + } else if (scene_state.ubo.emissive_exposure_normalization > 0.0) { + // This branch is triggered when using render_material(). + // Emissive is set outside the function, so don't set it. + // IBL isn't used don't set it. + } else { + scene_state.ubo.emissive_exposure_normalization = 1.0; + scene_state.ubo.IBL_exposure_normalization = 1.0; + } + scene_state.ubo.roughness_limiter_enabled = p_opaque_render_buffers && screen_space_roughness_limiter_is_active(); scene_state.ubo.roughness_limiter_amount = screen_space_roughness_limiter_get_amount(); scene_state.ubo.roughness_limiter_limit = screen_space_roughness_limiter_get_limit(); diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 4a7112eb81..cc3e245f2f 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -216,9 +216,9 @@ protected: virtual void _render_shadow_process() override; virtual void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL) override; - virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; + virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) override; virtual void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; - virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) override; + virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) override; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray<RenderGeometryInstance *> &p_instances) override; uint64_t lightmap_texture_array_version = 0xFFFFFFFF; @@ -235,7 +235,7 @@ protected: static RenderForwardMobile *singleton; void _setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers = false, bool p_pancake_shadows = false, int p_index = 0); - void _setup_lightmaps(const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform); + void _setup_lightmaps(const RenderDataRD *p_render_data, const PagedArray<RID> &p_lightmaps, const Transform3D &p_cam_transform); RID render_base_uniform_set; LocalVector<RID> render_pass_uniform_sets; @@ -244,6 +244,8 @@ protected: struct LightmapData { float normal_xform[12]; + float pad[3]; + float exposure_normalization; }; struct LightmapCaptureData { @@ -315,8 +317,8 @@ protected: float reflection_multiplier; uint32_t pancake_shadows; - uint32_t pad1; - uint32_t pad2; + float emissive_exposure_normalization; // Needed to normalize emissive when using physical units. + float IBL_exposure_normalization; // Adjusts for baked exposure. uint32_t pad3; }; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 2d004baf48..a95dbbe779 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -37,6 +37,7 @@ #include "servers/rendering/renderer_rd/storage_rd/material_storage.h" #include "servers/rendering/renderer_rd/storage_rd/texture_storage.h" #include "servers/rendering/rendering_server_default.h" +#include "servers/rendering/storage/camera_attributes_storage.h" void get_vogel_disk(float *r_kernel, int p_sample_count) { const float golden_angle = 2.4; @@ -246,7 +247,7 @@ Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_ba } if (use_cube_map) { - Ref<Image> panorama = sky_bake_panorama(environment_get_sky(p_env), environment_get_bg_energy(p_env), p_bake_irradiance, p_size); + Ref<Image> panorama = sky_bake_panorama(environment_get_sky(p_env), environment_get_bg_energy_multiplier(p_env), p_bake_irradiance, p_size); if (use_ambient_light) { for (int x = 0; x < p_size.width; x++) { for (int y = 0; y < p_size.height; y++) { @@ -256,12 +257,12 @@ Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_ba } return panorama; } else { - const float bg_energy = environment_get_bg_energy(p_env); + const float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_env); Color panorama_color = ((environment_background == RS::ENV_BG_CLEAR_COLOR) ? RSG::texture_storage->get_default_clear_color() : environment_get_bg_color(p_env)); panorama_color = panorama_color.srgb_to_linear(); - panorama_color.r *= bg_energy; - panorama_color.g *= bg_energy; - panorama_color.b *= bg_energy; + panorama_color.r *= bg_energy_multiplier; + panorama_color.g *= bg_energy_multiplier; + panorama_color.b *= bg_energy_multiplier; if (use_ambient_light) { panorama_color = ambient_color.lerp(panorama_color, ambient_color_sky_mix); @@ -1083,45 +1084,6 @@ int RendererSceneRenderRD::get_directional_light_shadow_size(RID p_light_intance ////////////////////////////////////////////////// -RID RendererSceneRenderRD::camera_effects_allocate() { - return camera_effects_owner.allocate_rid(); -} -void RendererSceneRenderRD::camera_effects_initialize(RID p_rid) { - camera_effects_owner.initialize_rid(p_rid, CameraEffects()); -} - -void RendererSceneRenderRD::camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) { - dof_blur_quality = p_quality; - dof_blur_use_jitter = p_use_jitter; -} - -void RendererSceneRenderRD::camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) { - dof_blur_bokeh_shape = p_shape; -} - -void RendererSceneRenderRD::camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) { - CameraEffects *camfx = camera_effects_owner.get_or_null(p_camera_effects); - ERR_FAIL_COND(!camfx); - - camfx->dof_blur_far_enabled = p_far_enable; - camfx->dof_blur_far_distance = p_far_distance; - camfx->dof_blur_far_transition = p_far_transition; - - camfx->dof_blur_near_enabled = p_near_enable; - camfx->dof_blur_near_distance = p_near_distance; - camfx->dof_blur_near_transition = p_near_transition; - - camfx->dof_blur_amount = p_amount; -} - -void RendererSceneRenderRD::camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) { - CameraEffects *camfx = camera_effects_owner.get_or_null(p_camera_effects); - ERR_FAIL_COND(!camfx); - - camfx->override_exposure_enabled = p_enable; - camfx->override_exposure = p_exposure; -} - RID RendererSceneRenderRD::light_instance_create(RID p_light) { RID li = light_instance_owner.make_rid(LightInstance()); @@ -1860,13 +1822,11 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_data->render_buffers); ERR_FAIL_COND(!rb); - // Glow and override exposure (if enabled). - CameraEffects *camfx = camera_effects_owner.get_or_null(p_render_data->camera_effects); - + // Glow, auto exposure and DoF (if enabled). bool can_use_effects = rb->width >= 8 && rb->height >= 8; bool can_use_storage = _render_buffers_can_be_storage(); - if (can_use_effects && camfx && (camfx->dof_blur_near_enabled || camfx->dof_blur_far_enabled) && camfx->dof_blur_amount > 0.0) { + if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_dof(p_render_data->camera_attributes)) { RENDER_TIMESTAMP("Depth of Field"); RD::get_singleton()->draw_command_begin_label("DOF"); if (rb->blur[0].texture.is_null()) { @@ -1881,7 +1841,6 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende buffers.half_texture[0] = rb->blur[1].layers[0].mipmaps[0].texture; buffers.half_texture[1] = rb->blur[0].layers[0].mipmaps[1].texture; - float bokeh_size = camfx->dof_blur_amount * 64.0; if (can_use_storage) { for (uint32_t i = 0; i < rb->view_count; i++) { buffers.base_texture = rb->views[i].view_texture; @@ -1890,7 +1849,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustrum float z_near = p_render_data->view_projection[i].get_z_near(); float z_far = p_render_data->view_projection[i].get_z_far(); - bokeh_dof->bokeh_dof_compute(buffers, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, dof_blur_use_jitter, z_near, z_far, p_render_data->cam_orthogonal); + bokeh_dof->bokeh_dof_compute(buffers, p_render_data->camera_attributes, z_near, z_far, p_render_data->cam_orthogonal); }; } else { // Set framebuffers. @@ -1913,27 +1872,32 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende // In stereo p_render_data->z_near and p_render_data->z_far can be offset for our combined frustrum float z_near = p_render_data->view_projection[i].get_z_near(); float z_far = p_render_data->view_projection[i].get_z_far(); - bokeh_dof->bokeh_dof_raster(buffers, camfx->dof_blur_far_enabled, camfx->dof_blur_far_distance, camfx->dof_blur_far_transition, camfx->dof_blur_near_enabled, camfx->dof_blur_near_distance, camfx->dof_blur_near_transition, bokeh_size, dof_blur_bokeh_shape, dof_blur_quality, z_near, z_far, p_render_data->cam_orthogonal); + bokeh_dof->bokeh_dof_raster(buffers, p_render_data->camera_attributes, z_near, z_far, p_render_data->cam_orthogonal); } } RD::get_singleton()->draw_command_end_label(); } - if (can_use_effects && p_render_data->environment.is_valid() && environment_get_auto_exposure(p_render_data->environment)) { + float auto_exposure_scale = 1.0; + + if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes)) { RENDER_TIMESTAMP("Auto exposure"); + RD::get_singleton()->draw_command_begin_label("Auto exposure"); if (rb->luminance.current.is_null()) { _allocate_luminance_textures(rb); } + uint64_t auto_exposure_version = RSG::camera_attributes->camera_attributes_get_auto_exposure_version(p_render_data->camera_attributes); + bool set_immediate = auto_exposure_version != rb->auto_exposure_version; + rb->auto_exposure_version = auto_exposure_version; - bool set_immediate = environment_get_auto_exposure_version(p_render_data->environment) != rb->auto_exposure_version; - rb->auto_exposure_version = environment_get_auto_exposure_version(p_render_data->environment); - - double step = environment_get_auto_exp_speed(p_render_data->environment) * time_step; + double step = RSG::camera_attributes->camera_attributes_get_auto_exposure_adjust_speed(p_render_data->camera_attributes) * time_step; + float auto_exposure_min_sensitivity = RSG::camera_attributes->camera_attributes_get_auto_exposure_min_sensitivity(p_render_data->camera_attributes); + float auto_exposure_max_sensitivity = RSG::camera_attributes->camera_attributes_get_auto_exposure_max_sensitivity(p_render_data->camera_attributes); if (can_use_storage) { - RendererCompositorRD::singleton->get_effects()->luminance_reduction(rb->internal_texture, Size2i(rb->internal_width, rb->internal_height), rb->luminance.reduce, rb->luminance.current, environment_get_min_luminance(p_render_data->environment), environment_get_max_luminance(p_render_data->environment), step, set_immediate); + RendererCompositorRD::singleton->get_effects()->luminance_reduction(rb->internal_texture, Size2i(rb->internal_width, rb->internal_height), rb->luminance.reduce, rb->luminance.current, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate); } else { - RendererCompositorRD::singleton->get_effects()->luminance_reduction_raster(rb->internal_texture, Size2i(rb->internal_width, rb->internal_height), rb->luminance.reduce, rb->luminance.fb, rb->luminance.current, environment_get_min_luminance(p_render_data->environment), environment_get_max_luminance(p_render_data->environment), step, set_immediate); + RendererCompositorRD::singleton->get_effects()->luminance_reduction_raster(rb->internal_texture, Size2i(rb->internal_width, rb->internal_height), rb->luminance.reduce, rb->luminance.fb, rb->luminance.current, auto_exposure_min_sensitivity, auto_exposure_max_sensitivity, step, set_immediate); } // Swap final reduce with prev luminance. SWAP(rb->luminance.current, rb->luminance.reduce.write[rb->luminance.reduce.size() - 1]); @@ -1941,6 +1905,8 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende SWAP(rb->luminance.current_fb, rb->luminance.fb.write[rb->luminance.fb.size() - 1]); } + auto_exposure_scale = RSG::camera_attributes->camera_attributes_get_auto_exposure_scale(p_render_data->camera_attributes); + RenderingServerDefault::redraw_request(); // Redraw all the time if auto exposure rendering is on. RD::get_singleton()->draw_command_end_label(); } @@ -1975,13 +1941,13 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende if (i == 0) { RID luminance_texture; - if (environment_get_auto_exposure(p_render_data->environment) && rb->luminance.current.is_valid()) { + if (RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && rb->luminance.current.is_valid()) { luminance_texture = rb->luminance.current; } if (can_use_storage) { - copy_effects->gaussian_glow(rb->views[l].view_texture, rb->blur[1].layers[l].mipmaps[i].texture, Size2i(vp_w, vp_h), environment_get_glow_strength(p_render_data->environment), glow_high_quality, true, environment_get_glow_hdr_luminance_cap(p_render_data->environment), environment_get_exposure(p_render_data->environment), environment_get_glow_bloom(p_render_data->environment), environment_get_glow_hdr_bleed_threshold(p_render_data->environment), environment_get_glow_hdr_bleed_scale(p_render_data->environment), luminance_texture, environment_get_auto_exp_scale(p_render_data->environment)); + copy_effects->gaussian_glow(rb->views[l].view_texture, rb->blur[1].layers[l].mipmaps[i].texture, Size2i(vp_w, vp_h), environment_get_glow_strength(p_render_data->environment), glow_high_quality, true, environment_get_glow_hdr_luminance_cap(p_render_data->environment), environment_get_exposure(p_render_data->environment), environment_get_glow_bloom(p_render_data->environment), environment_get_glow_hdr_bleed_threshold(p_render_data->environment), environment_get_glow_hdr_bleed_scale(p_render_data->environment), luminance_texture, auto_exposure_scale); } else { - copy_effects->gaussian_glow_raster(rb->views[l].view_texture, luminance_multiplier, rb->blur[1].layers[l].mipmaps[i].half_fb, rb->blur[1].layers[l].mipmaps[i].half_texture, rb->blur[1].layers[l].mipmaps[i].fb, Size2i(vp_w, vp_h), environment_get_glow_strength(p_render_data->environment), glow_high_quality, true, environment_get_glow_hdr_luminance_cap(p_render_data->environment), environment_get_exposure(p_render_data->environment), environment_get_glow_bloom(p_render_data->environment), environment_get_glow_hdr_bleed_threshold(p_render_data->environment), environment_get_glow_hdr_bleed_scale(p_render_data->environment), luminance_texture, environment_get_auto_exp_scale(p_render_data->environment)); + copy_effects->gaussian_glow_raster(rb->views[l].view_texture, luminance_multiplier, rb->blur[1].layers[l].mipmaps[i].half_fb, rb->blur[1].layers[l].mipmaps[i].half_texture, rb->blur[1].layers[l].mipmaps[i].fb, Size2i(vp_w, vp_h), environment_get_glow_strength(p_render_data->environment), glow_high_quality, true, environment_get_glow_hdr_luminance_cap(p_render_data->environment), environment_get_exposure(p_render_data->environment), environment_get_glow_bloom(p_render_data->environment), environment_get_glow_hdr_bleed_threshold(p_render_data->environment), environment_get_glow_hdr_bleed_scale(p_render_data->environment), luminance_texture, auto_exposure_scale); } } else { if (can_use_storage) { @@ -2002,10 +1968,10 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende RendererRD::ToneMapper::TonemapSettings tonemap; - if (can_use_effects && p_render_data->environment.is_valid() && environment_get_auto_exposure(p_render_data->environment) && rb->luminance.current.is_valid()) { + if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes) && rb->luminance.current.is_valid()) { tonemap.use_auto_exposure = true; tonemap.exposure_texture = rb->luminance.current; - tonemap.auto_exposure_grey = environment_get_auto_exp_scale(p_render_data->environment); + tonemap.auto_exposure_scale = auto_exposure_scale; } else { tonemap.exposure_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_WHITE); } @@ -2047,10 +2013,6 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende tonemap.exposure = environment_get_exposure(p_render_data->environment); } - if (camfx && camfx->override_exposure_enabled) { - tonemap.exposure = camfx->override_exposure; - } - tonemap.use_color_correction = false; tonemap.use_1d_color_correction = false; tonemap.color_correction_texture = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_3D_WHITE); @@ -2093,9 +2055,6 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_data->render_buffers); ERR_FAIL_COND(!rb); - // Override exposure (if enabled). - CameraEffects *camfx = camera_effects_owner.get_or_null(p_render_data->camera_effects); - bool can_use_effects = rb->width >= 8 && rb->height >= 8; RD::DrawListID draw_list = RD::get_singleton()->draw_list_switch_to_next_pass(); @@ -2108,18 +2067,15 @@ void RendererSceneRenderRD::_post_process_subpass(RID p_source_texture, RID p_fr tonemap.white = environment_get_white(p_render_data->environment); } - if (camfx && camfx->override_exposure_enabled) { - tonemap.exposure = camfx->override_exposure; - } - // We don't support glow or auto exposure here, if they are needed, don't use subpasses! // The problem is that we need to use the result so far and process them before we can // apply this to our results. if (can_use_effects && p_render_data->environment.is_valid() && environment_get_glow_enabled(p_render_data->environment)) { ERR_FAIL_MSG("Glow is not supported when using subpasses."); } - if (can_use_effects && p_render_data->environment.is_valid() && environment_get_auto_exposure(p_render_data->environment)) { - ERR_FAIL_MSG("Glow is not supported when using subpasses."); + + if (can_use_effects && RSG::camera_attributes->camera_attributes_uses_auto_exposure(p_render_data->camera_attributes)) { + ERR_FAIL_MSG("Auto Exposure is not supported when using subpasses."); } tonemap.use_glow = false; @@ -2739,7 +2695,7 @@ RendererSceneRenderRD::RenderBufferData *RendererSceneRenderRD::render_buffers_g return rb->data; } -void RendererSceneRenderRD::_setup_reflections(const PagedArray<RID> &p_reflections, const Transform3D &p_camera_inverse_transform, RID p_environment) { +void RendererSceneRenderRD::_setup_reflections(RenderDataRD *p_render_data, const PagedArray<RID> &p_reflections, const Transform3D &p_camera_inverse_transform, RID p_environment) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); cluster.reflection_count = 0; @@ -2796,6 +2752,12 @@ void RendererSceneRenderRD::_setup_reflections(const PagedArray<RID> &p_reflecti reflection_ubo.exterior = !light_storage->reflection_probe_is_interior(base_probe); reflection_ubo.box_project = light_storage->reflection_probe_is_box_projection(base_probe); + reflection_ubo.exposure_normalization = 1.0; + + if (p_render_data->camera_attributes.is_valid()) { + float exposure = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + reflection_ubo.exposure_normalization = exposure / light_storage->reflection_probe_get_baked_exposure(base_probe); + } Color ambient_linear = light_storage->reflection_probe_get_ambient_color(base_probe).srgb_to_linear(); float interior_ambient_energy = light_storage->reflection_probe_get_ambient_color_energy(base_probe); @@ -2819,7 +2781,7 @@ void RendererSceneRenderRD::_setup_reflections(const PagedArray<RID> &p_reflecti } } -void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const Transform3D &p_camera_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count, bool &r_directional_light_soft_shadows) { +void RendererSceneRenderRD::_setup_lights(RenderDataRD *p_render_data, const PagedArray<RID> &p_lights, const Transform3D &p_camera_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count, bool &r_directional_light_soft_shadows) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); @@ -2863,7 +2825,17 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const float sign = light_storage->light_is_negative(base) ? -1 : 1; - light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI; + light_data.energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY); + + if (is_using_physical_light_units()) { + light_data.energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + } else { + light_data.energy *= Math_PI; + } + + if (p_render_data->camera_attributes.is_valid()) { + light_data.energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } Color linear_col = light_storage->light_get_color(base).srgb_to_linear(); light_data.color[0] = linear_col.r; @@ -2871,6 +2843,7 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const light_data.color[2] = linear_col.b; light_data.specular = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR); + light_data.volumetric_fog_energy = light_storage->light_get_param(base, RS::LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY); light_data.mask = light_storage->light_get_cull_mask(base); float size = light_storage->light_get_param(base, RS::LIGHT_PARAM_SIZE); @@ -2952,7 +2925,6 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const float fade_start = light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_FADE_START); light_data.fade_from = -light_data.shadow_split_offsets[3] * MIN(fade_start, 0.999); //using 1.0 would break smoothstep light_data.fade_to = -light_data.shadow_split_offsets[3]; - light_data.shadow_volumetric_fog_fade = 1.0 / light_storage->light_get_shadow_volumetric_fog_fade(base); light_data.soft_shadow_scale = light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BLUR); light_data.softshadow_angle = angular_diameter; @@ -3076,12 +3048,32 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const } } - float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * Math_PI * fade; + float energy = sign * light_storage->light_get_param(base, RS::LIGHT_PARAM_ENERGY) * fade; + + if (is_using_physical_light_units()) { + energy *= light_storage->light_get_param(base, RS::LIGHT_PARAM_INTENSITY); + + // Convert from Luminous Power to Luminous Intensity + if (type == RS::LIGHT_OMNI) { + energy *= 1.0 / (Math_PI * 4.0); + } else { + // Spot Lights are not physically accurate, Luminous Intensity should change in relation to the cone angle. + // We make this assumption to keep them easy to control. + energy *= 1.0 / Math_PI; + } + } else { + energy *= Math_PI; + } + + if (p_render_data->camera_attributes.is_valid()) { + energy *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); + } light_data.color[0] = linear_col.r * energy; light_data.color[1] = linear_col.g * energy; light_data.color[2] = linear_col.b * energy; light_data.specular_amount = light_storage->light_get_param(base, RS::LIGHT_PARAM_SPECULAR) * 2.0; + light_data.volumetric_fog_energy = light_storage->light_get_param(base, RS::LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY); light_data.bake_mode = light_storage->light_get_bake_mode(base); float radius = MAX(0.001, light_storage->light_get_param(base, RS::LIGHT_PARAM_RANGE)); @@ -3176,7 +3168,6 @@ void RendererSceneRenderRD::_setup_lights(const PagedArray<RID> &p_lights, const light_data.atlas_rect[3] = rect.size.height; light_data.soft_shadow_scale = light_storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BLUR); - light_data.shadow_volumetric_fog_fade = 1.0 / light_storage->light_get_shadow_volumetric_fog_fade(base); if (type == RS::LIGHT_OMNI) { Transform3D proj = (inverse_transform * light_transform).inverse(); @@ -3645,12 +3636,12 @@ void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool } } else { //do not render reflections when rendering a reflection probe - _setup_reflections(*p_render_data->reflection_probes, p_render_data->cam_transform.affine_inverse(), p_render_data->environment); + _setup_reflections(p_render_data, *p_render_data->reflection_probes, p_render_data->cam_transform.affine_inverse(), p_render_data->environment); } uint32_t directional_light_count = 0; uint32_t positional_light_count = 0; - _setup_lights(*p_render_data->lights, p_render_data->cam_transform, p_render_data->shadow_atlas, using_shadows, directional_light_count, positional_light_count, p_render_data->directional_light_soft_shadows); + _setup_lights(p_render_data, *p_render_data->lights, p_render_data->cam_transform, p_render_data->shadow_atlas, using_shadows, directional_light_count, positional_light_count, p_render_data->directional_light_soft_shadows); _setup_decals(*p_render_data->decals, p_render_data->cam_transform.affine_inverse()); p_render_data->directional_light_count = directional_light_count; @@ -3673,7 +3664,7 @@ void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool } } -void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) { +void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) { RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); // getting this here now so we can direct call a bunch of things more easily @@ -3719,7 +3710,7 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData render_data.lightmaps = &p_lightmaps; render_data.fog_volumes = &p_fog_volumes; render_data.environment = p_environment; - render_data.camera_effects = p_camera_effects; + render_data.camera_attributes = p_camera_attributes; render_data.shadow_atlas = p_shadow_atlas; render_data.reflection_atlas = p_reflection_atlas; render_data.reflection_probe = p_reflection_probe; @@ -3753,11 +3744,15 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData //sdfgi first if (rb != nullptr && rb->sdfgi != nullptr) { + float exposure_normalization = 1.0; + if (p_camera_attributes.is_valid()) { + exposure_normalization = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_camera_attributes); + } for (int i = 0; i < render_state.render_sdfgi_region_count; i++) { - rb->sdfgi->render_region(p_render_buffers, render_state.render_sdfgi_regions[i].region, render_state.render_sdfgi_regions[i].instances, this); + rb->sdfgi->render_region(p_render_buffers, render_state.render_sdfgi_regions[i].region, render_state.render_sdfgi_regions[i].instances, this, exposure_normalization); } if (render_state.sdfgi_update_data->update_static) { - rb->sdfgi->render_static_lights(p_render_buffers, render_state.sdfgi_update_data->static_cascade_count, p_sdfgi_update_data->static_cascade_indices, render_state.sdfgi_update_data->static_positional_lights, this); + rb->sdfgi->render_static_lights(&render_data, p_render_buffers, render_state.sdfgi_update_data->static_cascade_count, p_sdfgi_update_data->static_cascade_indices, render_state.sdfgi_update_data->static_positional_lights, this); } } @@ -3790,6 +3785,9 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData } else { current_cluster_builder = ra->cluster_builder; } + if (p_camera_attributes.is_valid()) { + RendererRD::LightStorage::get_singleton()->reflection_probe_set_baked_exposure(rpi->probe, RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_camera_attributes)); + } } else { ERR_PRINT("No render buffer nor reflection atlas, bug"); //should never happen, will crash current_cluster_builder = nullptr; @@ -3804,7 +3802,7 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData rb->sdfgi->update_light(); } - gi.setup_voxel_gi_instances(render_data.render_buffers, render_data.cam_transform, *render_data.voxel_gi_instances, render_state.voxel_gi_count, this); + gi.setup_voxel_gi_instances(&render_data, render_data.render_buffers, render_data.cam_transform, *render_data.voxel_gi_instances, render_state.voxel_gi_count, this); } render_state.depth_prepass_used = false; @@ -4041,7 +4039,7 @@ void RendererSceneRenderRD::_render_shadow_pass(RID p_light, RID p_shadow_atlas, } void RendererSceneRenderRD::render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) { - _render_material(p_cam_transform, p_cam_projection, p_cam_orthogonal, p_instances, p_framebuffer, p_region); + _render_material(p_cam_transform, p_cam_projection, p_cam_orthogonal, p_instances, p_framebuffer, p_region, 1.0); } void RendererSceneRenderRD::render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) { @@ -4083,9 +4081,8 @@ bool RendererSceneRenderRD::free(RID p_rid) { render_buffers_owner.free(p_rid); } else if (is_environment(p_rid)) { environment_free(p_rid); - } else if (camera_effects_owner.owns(p_rid)) { - //not much to delete, just free it - camera_effects_owner.free(p_rid); + } else if (RSG::camera_attributes->owns_camera_attributes(p_rid)) { + RSG::camera_attributes->camera_attributes_free(p_rid); } else if (reflection_atlas_owner.owns(p_rid)) { reflection_atlas_set_size(p_rid, 0, 0); ReflectionAtlas *ra = reflection_atlas_owner.get_or_null(p_rid); @@ -4335,8 +4332,8 @@ RendererSceneRenderRD::RendererSceneRenderRD() { void RendererSceneRenderRD::init() { max_cluster_elements = get_max_elements(); - directional_shadow.size = GLOBAL_GET("rendering/shadows/directional_shadow/size"); - directional_shadow.use_16_bits = GLOBAL_GET("rendering/shadows/directional_shadow/16_bits"); + directional_shadow.size = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/size"); + directional_shadow.use_16_bits = GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/16_bits"); /* SKY SHADER */ @@ -4395,8 +4392,10 @@ void RendererSceneRenderRD::init() { shadow_sampler = RD::get_singleton()->sampler_create(sampler); } - camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_shape")))); - camera_effects_set_dof_blur_quality(RS::DOFBlurQuality(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_quality"))), GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_use_jitter")); + RSG::camera_attributes->camera_attributes_set_dof_blur_bokeh_shape(RS::DOFBokehShape(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_shape")))); + RSG::camera_attributes->camera_attributes_set_dof_blur_quality(RS::DOFBlurQuality(int(GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_bokeh_quality"))), GLOBAL_GET("rendering/camera/depth_of_field/depth_of_field_use_jitter")); + use_physical_light_units = GLOBAL_GET("rendering/lights_and_shadows/use_physical_light_units"); + environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/environment/ssao/quality"))), GLOBAL_GET("rendering/environment/ssao/half_size"), GLOBAL_GET("rendering/environment/ssao/adaptive_target"), GLOBAL_GET("rendering/environment/ssao/blur_passes"), GLOBAL_GET("rendering/environment/ssao/fadeout_from"), GLOBAL_GET("rendering/environment/ssao/fadeout_to")); screen_space_roughness_limiter = GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/enabled"); screen_space_roughness_limiter_amount = GLOBAL_GET("rendering/anti_aliasing/screen_space_roughness_limiter/amount"); @@ -4414,8 +4413,8 @@ void RendererSceneRenderRD::init() { directional_soft_shadow_kernel = memnew_arr(float, 128); penumbra_shadow_kernel = memnew_arr(float, 128); soft_shadow_kernel = memnew_arr(float, 128); - positional_soft_shadow_filter_set_quality(RS::ShadowQuality(int(GLOBAL_GET("rendering/shadows/positional_shadow/soft_shadow_filter_quality")))); - directional_soft_shadow_filter_set_quality(RS::ShadowQuality(int(GLOBAL_GET("rendering/shadows/directional_shadow/soft_shadow_filter_quality")))); + positional_soft_shadow_filter_set_quality(RS::ShadowQuality(int(GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality")))); + directional_soft_shadow_filter_set_quality(RS::ShadowQuality(int(GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality")))); environment_set_volumetric_fog_volume_size(GLOBAL_GET("rendering/environment/volumetric_fog/volume_size"), GLOBAL_GET("rendering/environment/volumetric_fog/volume_depth")); environment_set_volumetric_fog_filter_active(GLOBAL_GET("rendering/environment/volumetric_fog/use_filter")); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 8e59b21aa8..70109b29da 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -76,7 +76,7 @@ struct RenderDataRD { const PagedArray<RID> *lightmaps = nullptr; const PagedArray<RID> *fog_volumes = nullptr; RID environment; - RID camera_effects; + RID camera_attributes; RID shadow_atlas; RID reflection_atlas; RID reflection_probe; @@ -114,9 +114,9 @@ protected: }; virtual RenderBufferData *_create_render_buffer_data() = 0; - void _setup_lights(const PagedArray<RID> &p_lights, const Transform3D &p_camera_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count, bool &r_directional_light_soft_shadows); + void _setup_lights(RenderDataRD *p_render_data, const PagedArray<RID> &p_lights, const Transform3D &p_camera_transform, RID p_shadow_atlas, bool p_using_shadows, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count, bool &r_directional_light_soft_shadows); void _setup_decals(const PagedArray<RID> &p_decals, const Transform3D &p_camera_inverse_xform); - void _setup_reflections(const PagedArray<RID> &p_reflections, const Transform3D &p_camera_inverse_transform, RID p_environment); + void _setup_reflections(RenderDataRD *p_render_data, const PagedArray<RID> &p_reflections, const Transform3D &p_camera_inverse_transform, RID p_environment); virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_color) = 0; @@ -125,9 +125,9 @@ protected: virtual void _render_shadow_process() = 0; virtual void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL) = 0; - virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; + virtual void _render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region, float p_exposure_normalization) = 0; virtual void _render_uv2(const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; - virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) = 0; + virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) = 0; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray<RenderGeometryInstance *> &p_instances) = 0; void _debug_sdfgi_probes(RID p_render_buffers, RID p_framebuffer, uint32_t p_view_count, const Projection *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth); @@ -418,31 +418,11 @@ private: bool glow_high_quality = false; RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW; - /* CAMERA EFFECTS */ - - struct CameraEffects { - bool dof_blur_far_enabled = false; - float dof_blur_far_distance = 10; - float dof_blur_far_transition = 5; - - bool dof_blur_near_enabled = false; - float dof_blur_near_distance = 2; - float dof_blur_near_transition = 1; - - float dof_blur_amount = 0.1; - - bool override_exposure_enabled = false; - float override_exposure = 1; - }; - - RS::DOFBlurQuality dof_blur_quality = RS::DOF_BLUR_QUALITY_MEDIUM; - RS::DOFBokehShape dof_blur_bokeh_shape = RS::DOF_BOKEH_HEXAGON; - bool dof_blur_use_jitter = false; RS::SubSurfaceScatteringQuality sss_quality = RS::SUB_SURFACE_SCATTERING_QUALITY_MEDIUM; float sss_scale = 0.05; float sss_depth_scale = 0.01; - mutable RID_Owner<CameraEffects, true> camera_effects_owner; + bool use_physical_light_units = false; /* RENDER BUFFERS */ @@ -592,7 +572,7 @@ private: uint32_t exterior; uint32_t box_project; uint32_t ambient_mode; - uint32_t pad; + float exposure_normalization; float local_matrix[16]; // up to here for spot and omni, rest is for directional }; @@ -618,7 +598,7 @@ private: float soft_shadow_size; float soft_shadow_scale; uint32_t mask; - float shadow_volumetric_fog_fade; + float volumetric_fog_energy; uint32_t bake_mode; float projector_rect[4]; }; @@ -638,7 +618,7 @@ private: float fade_to; uint32_t pad[2]; uint32_t bake_mode; - float shadow_volumetric_fog_fade; + float volumetric_fog_energy; float shadow_bias[4]; float shadow_normal_bias[4]; float shadow_transmittance_bias[4]; @@ -831,21 +811,8 @@ public: virtual Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) override; - /* CAMERA EFFECTS */ - - virtual RID camera_effects_allocate() override; - virtual void camera_effects_initialize(RID p_rid) override; - - virtual void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) override; - virtual void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) override; - - virtual void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) override; - virtual void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) override; - - bool camera_effects_uses_dof(RID p_camera_effects) { - CameraEffects *camfx = camera_effects_owner.get_or_null(p_camera_effects); - - return camfx && (camfx->dof_blur_near_enabled || camfx->dof_blur_far_enabled) && camfx->dof_blur_amount > 0.0; + _FORCE_INLINE_ bool is_using_physical_light_units() { + return use_physical_light_units; } /* LIGHT INSTANCE API */ @@ -1139,7 +1106,7 @@ public: virtual void update_uniform_sets(){}; - virtual void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) override; + virtual void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) override; virtual void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; diff --git a/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl b/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl index 96f5c3e9f2..cb06250cf2 100644 --- a/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/blur_raster.glsl @@ -129,7 +129,7 @@ void main() { #ifdef GLOW_USE_AUTO_EXPOSURE - frag_color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / blur.glow_auto_exposure_grey; + frag_color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / blur.glow_auto_exposure_scale; #endif frag_color *= blur.glow_exposure; diff --git a/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl index 730504571a..06ca198f37 100644 --- a/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/blur_raster_inc.glsl @@ -16,7 +16,7 @@ layout(push_constant, std430) uniform Blur { float glow_exposure; // 04 - 36 float glow_white; // 04 - 40 float glow_luminance_cap; // 04 - 44 - float glow_auto_exposure_grey; // 04 - 48 + float glow_auto_exposure_scale; // 04 - 48 float luminance_multiplier; // 04 - 52 float res1; // 04 - 56 diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl index 0b43af7738..bdf84bb03a 100644 --- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof.glsl @@ -41,11 +41,25 @@ float get_depth_at_pos(vec2 uv) { float get_blur_size(float depth) { if (params.blur_near_active && depth < params.blur_near_begin) { - return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; //near blur is negative + if (params.use_physical_near) { + // Physically-based. + float d = abs(params.blur_near_begin - depth); + return -(d / (params.blur_near_begin - d)) * params.blur_size_near - DEPTH_GAP; // Near blur is negative. + } else { + // Non-physically-based. + return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; // Near blur is negative. + } } if (params.blur_far_active && depth > params.blur_far_begin) { - return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP; + if (params.use_physical_far) { + // Physically-based. + float d = abs(params.blur_far_begin - depth); + return (d / (params.blur_far_begin + d)) * params.blur_size_far + DEPTH_GAP; + } else { + // Non-physically-based. + return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP; + } } return 0.0; diff --git a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl index b90a527554..4a2b0edc18 100644 --- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_inc.glsl @@ -20,6 +20,11 @@ layout(push_constant, std430) uniform Params { bool use_jitter; float jitter_seed; + bool use_physical_near; + bool use_physical_far; + + float blur_size_near; + float blur_size_far; uint pad[2]; } params; 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 a06cacfabe..a2bdc2e90e 100644 --- a/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/bokeh_dof_raster.glsl @@ -63,11 +63,25 @@ float get_depth_at_pos(vec2 uv) { float get_blur_size(float depth) { if (params.blur_near_active && depth < params.blur_near_begin) { - return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; //near blur is negative + if (params.use_physical_near) { + // Physically-based. + float d = abs(params.blur_near_begin - depth); + return -(d / (params.blur_near_begin - d)) * params.blur_size_near - DEPTH_GAP; // Near blur is negative. + } else { + // Non-physically-based. + return -(1.0 - smoothstep(params.blur_near_end, params.blur_near_begin, depth)) * params.blur_size - DEPTH_GAP; // Near blur is negative. + } } if (params.blur_far_active && depth > params.blur_far_begin) { - return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP; + if (params.use_physical_far) { + // Physically-based. + float d = abs(params.blur_far_begin - depth); + return (d / (params.blur_far_begin + d)) * params.blur_size_far + DEPTH_GAP; + } else { + // Non-physically-based. + return smoothstep(params.blur_far_begin, params.blur_far_end, depth) * params.blur_size + DEPTH_GAP; + } } return 0.0; diff --git a/servers/rendering/renderer_rd/shaders/effects/copy.glsl b/servers/rendering/renderer_rd/shaders/effects/copy.glsl index 3a4ef86ef0..bfe329b8ec 100644 --- a/servers/rendering/renderer_rd/shaders/effects/copy.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/copy.glsl @@ -31,7 +31,7 @@ layout(push_constant, std430) uniform Params { float glow_exposure; float glow_white; float glow_luminance_cap; - float glow_auto_exposure_grey; + float glow_auto_exposure_scale; // DOF. float camera_z_far; float camera_z_near; @@ -185,7 +185,7 @@ void main() { if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) { #ifdef GLOW_USE_AUTO_EXPOSURE - color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.glow_auto_exposure_grey; + color /= texelFetch(source_auto_exposure, ivec2(0, 0), 0).r / params.glow_auto_exposure_scale; #endif color *= params.glow_exposure; diff --git a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl index 62a7b0e7d7..e459756c6a 100644 --- a/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl +++ b/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl @@ -75,7 +75,7 @@ layout(push_constant, std430) uniform Params { float exposure; float white; - float auto_exposure_grey; + float auto_exposure_scale; float luminance_multiplier; vec2 pixel_size; @@ -440,7 +440,7 @@ void main() { #ifndef SUBPASS if (params.use_auto_exposure) { - exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r * params.luminance_multiplier / params.auto_exposure_grey); + exposure *= 1.0 / (texelFetch(source_auto_exposure, ivec2(0, 0), 0).r * params.luminance_multiplier / params.auto_exposure_scale); } #endif diff --git a/servers/rendering/renderer_rd/shaders/environment/gi.glsl b/servers/rendering/renderer_rd/shaders/environment/gi.glsl index 6ea8cb1377..ab927df678 100644 --- a/servers/rendering/renderer_rd/shaders/environment/gi.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/gi.glsl @@ -32,6 +32,8 @@ struct ProbeCascadeData { float to_probe; ivec3 probe_world_offset; float to_cell; // 1/bounds * grid_size + vec3 pad; + float exposure_normalization; }; layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image2D ambient_buffer; @@ -83,6 +85,9 @@ struct VoxelGIData { float normal_bias; // 4 - 88 bool blend_ambient; // 4 - 92 uint mipmaps; // 4 - 96 + + vec3 pad; // 12 - 108 + float exposure_normalization; // 4 - 112 }; layout(set = 0, binding = 16, std140) uniform VoxelGIs { @@ -241,7 +246,7 @@ void sdfvoxel_gi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_ pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z; diffuse = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb; - diffuse_accum += vec4(diffuse * weight, weight); + diffuse_accum += vec4(diffuse * weight * sdfgi.cascades[cascade].exposure_normalization, weight); { vec3 specular = vec3(0.0); @@ -255,7 +260,7 @@ void sdfvoxel_gi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_ specular = mix(specular, textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb, (roughness - 0.2) * 1.25); } - specular_accum += specular * weight; + specular_accum += specular * weight * sdfgi.cascades[cascade].exposure_normalization; } } @@ -574,7 +579,7 @@ void voxel_gi_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 } } - light.rgb *= voxel_gi_instances.data[index].dynamic_range; + light.rgb *= voxel_gi_instances.data[index].dynamic_range * voxel_gi_instances.data[index].exposure_normalization; if (!voxel_gi_instances.data[index].blend_ambient) { light.a = 1.0; } @@ -583,7 +588,7 @@ void voxel_gi_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 //radiance vec4 irr_light = voxel_cone_trace(voxel_gi_textures[index], cell_size, position, ref_vec, tan(roughness * 0.5 * M_PI * 0.99), max_distance, voxel_gi_instances.data[index].bias); - irr_light.rgb *= voxel_gi_instances.data[index].dynamic_range; + irr_light.rgb *= voxel_gi_instances.data[index].dynamic_range * voxel_gi_instances.data[index].exposure_normalization; if (!voxel_gi_instances.data[index].blend_ambient) { irr_light.a = 1.0; } diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl index 9640d30e78..177dab16c7 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl @@ -21,6 +21,7 @@ struct CascadeData { float to_cell; // 1/bounds * grid_size ivec3 probe_world_offset; uint pad; + vec4 pad2; }; layout(set = 0, binding = 9, std140) uniform Cascades { diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl index 75b1ad2130..a0ef169f03 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl @@ -73,6 +73,7 @@ struct CascadeData { float to_cell; // 1/bounds * grid_size ivec3 probe_world_offset; uint pad; + vec4 pad2; }; layout(set = 0, binding = 1, std140) uniform Cascades { diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl index b95fad650e..9f7449b8aa 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl @@ -45,6 +45,7 @@ struct CascadeData { float to_cell; // 1/bounds * grid_size ivec3 probe_world_offset; uint pad; + vec4 pad2; }; layout(set = 0, binding = 8, std140) uniform Cascades { diff --git a/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl index 9c03297f5c..4bdb0dcc72 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl @@ -20,6 +20,7 @@ struct CascadeData { float to_cell; // 1/bounds * grid_size ivec3 probe_world_offset; uint pad; + vec4 pad2; }; layout(set = 0, binding = 7, std140) uniform Cascades { diff --git a/servers/rendering/renderer_rd/shaders/environment/sky.glsl b/servers/rendering/renderer_rd/shaders/environment/sky.glsl index e825020a4e..7a0b2af3ce 100644 --- a/servers/rendering/renderer_rd/shaders/environment/sky.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/sky.glsl @@ -15,10 +15,10 @@ layout(location = 0) out vec2 uv_interp; layout(push_constant, std430) uniform Params { mat3 orientation; vec4 projections[MAX_VIEWS]; - vec4 position_multiplier; + vec3 position; float time; + vec3 pad; float luminance_multiplier; - float pad[2]; } params; @@ -55,10 +55,10 @@ layout(location = 0) in vec2 uv_interp; layout(push_constant, std430) uniform Params { mat3 orientation; vec4 projections[MAX_VIEWS]; - vec4 position_multiplier; + vec3 position; float time; + vec3 pad; float luminance_multiplier; - float pad[2]; } params; @@ -200,17 +200,17 @@ void main() { #ifdef USE_CUBEMAP_PASS #ifdef USES_HALF_RES_COLOR - half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal) * params.luminance_multiplier; + half_res_color = texture(samplerCube(half_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal) / params.luminance_multiplier; #endif #ifdef USES_QUARTER_RES_COLOR - quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal) * params.luminance_multiplier; + quarter_res_color = texture(samplerCube(quarter_res, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), cube_normal) / params.luminance_multiplier; #endif #else #ifdef USES_HALF_RES_COLOR - half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) * params.luminance_multiplier; + half_res_color = textureLod(sampler2D(half_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) / params.luminance_multiplier; #endif #ifdef USES_QUARTER_RES_COLOR - quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) * params.luminance_multiplier; + quarter_res_color = textureLod(sampler2D(quarter_res, material_samplers[SAMPLER_LINEAR_CLAMP]), uv, 0.0) / params.luminance_multiplier; #endif #endif @@ -220,7 +220,7 @@ void main() { } - frag_color.rgb = color * params.position_multiplier.w; + frag_color.rgb = color; frag_color.a = alpha; #if !defined(DISABLE_FOG) && !defined(USE_CUBEMAP_PASS) @@ -242,12 +242,13 @@ void main() { #endif // DISABLE_FOG - // Blending is disabled for Sky, so alpha doesn't blend - // alpha is used for subsurface scattering so make sure it doesn't get applied to Sky + // Blending is disabled for Sky, so alpha doesn't blend. + // Alpha is used for subsurface scattering so make sure it doesn't get applied to Sky. if (!AT_CUBEMAP_PASS && !AT_HALF_RES_PASS && !AT_QUARTER_RES_PASS) { frag_color.a = 0.0; } - // For mobile renderer we're dividing by 2.0 as we're using a UNORM buffer - frag_color.rgb = frag_color.rgb / params.luminance_multiplier; + // For mobile renderer we're multiplying by 0.5 as we're using a UNORM buffer. + // For both mobile and clustered, we also bake in the exposure value for the environment and camera. + frag_color.rgb = frag_color.rgb * params.luminance_multiplier; } diff --git a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl index 6f79b9e771..eed9038502 100644 --- a/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl +++ b/servers/rendering/renderer_rd/shaders/environment/volumetric_fog_process.glsl @@ -84,6 +84,9 @@ struct VoxelGIData { float normal_bias; // 4 - 88 bool blend_ambient; // 4 - 92 uint mipmaps; // 4 - 96 + + vec3 pad; // 12 - 108 + float exposure_normalization; // 4 - 112 }; layout(set = 0, binding = 11, std140) uniform VoxelGIs { @@ -105,6 +108,8 @@ struct SDFVoxelGICascadeData { float to_probe; ivec3 probe_world_offset; float to_cell; // 1/bounds * grid_size + vec3 pad; + float exposure_normalization; }; layout(set = 1, binding = 0, std140) uniform SDFGI { @@ -270,6 +275,9 @@ const vec3 halton_map[TEMPORAL_FRAMES] = vec3[]( vec3(0.9375, 0.25925926, 0.12), vec3(0.03125, 0.59259259, 0.32)); +// Higher values will make light in volumetric fog fade out sooner when it's occluded by shadow. +const float INV_FOG_FADE = 10.0; + void main() { vec3 fog_cell_size = 1.0 / vec3(params.fog_volume_size); @@ -375,46 +383,48 @@ void main() { if (total_density > 0.001) { for (uint i = 0; i < params.directional_light_count; i++) { - vec3 shadow_attenuation = vec3(1.0); - - if (directional_lights.data[i].shadow_opacity > 0.001) { - float depth_z = -view_pos.z; - - vec4 pssm_coord; - vec3 light_dir = directional_lights.data[i].direction; - vec4 v = vec4(view_pos, 1.0); - float z_range; - - if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { - pssm_coord = (directional_lights.data[i].shadow_matrix1 * v); - pssm_coord /= pssm_coord.w; - z_range = directional_lights.data[i].shadow_z_range.x; - - } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { - pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); - pssm_coord /= pssm_coord.w; - z_range = directional_lights.data[i].shadow_z_range.y; - - } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { - pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); - pssm_coord /= pssm_coord.w; - z_range = directional_lights.data[i].shadow_z_range.z; - - } else { - pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); - pssm_coord /= pssm_coord.w; - z_range = directional_lights.data[i].shadow_z_range.w; - } + if (directional_lights.data[i].volumetric_fog_energy > 0.001) { + vec3 shadow_attenuation = vec3(1.0); + + if (directional_lights.data[i].shadow_opacity > 0.001) { + float depth_z = -view_pos.z; + + vec4 pssm_coord; + vec3 light_dir = directional_lights.data[i].direction; + vec4 v = vec4(view_pos, 1.0); + float z_range; + + if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { + pssm_coord = (directional_lights.data[i].shadow_matrix1 * v); + pssm_coord /= pssm_coord.w; + z_range = directional_lights.data[i].shadow_z_range.x; + + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { + pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); + pssm_coord /= pssm_coord.w; + z_range = directional_lights.data[i].shadow_z_range.y; + + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { + pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); + pssm_coord /= pssm_coord.w; + z_range = directional_lights.data[i].shadow_z_range.z; + + } else { + pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); + pssm_coord /= pssm_coord.w; + z_range = directional_lights.data[i].shadow_z_range.w; + } - float depth = texture(sampler2D(directional_shadow_atlas, linear_sampler), pssm_coord.xy).r; - float shadow = exp(min(0.0, (depth - pssm_coord.z)) * z_range * directional_lights.data[i].shadow_volumetric_fog_fade); + float depth = texture(sampler2D(directional_shadow_atlas, linear_sampler), pssm_coord.xy).r; + float shadow = exp(min(0.0, (depth - pssm_coord.z)) * z_range * INV_FOG_FADE); - shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, view_pos.z)); //done with negative values for performance + shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, view_pos.z)); //done with negative values for performance - shadow_attenuation = mix(vec3(0.0), vec3(1.0), shadow); - } + shadow_attenuation = mix(vec3(1.0 - directional_lights.data[i].shadow_opacity), vec3(1.0), shadow); + } - total_light += shadow_attenuation * directional_lights.data[i].color * directional_lights.data[i].energy * henyey_greenstein(dot(normalize(view_pos), normalize(directional_lights.data[i].direction)), params.phase_g); + total_light += shadow_attenuation * directional_lights.data[i].color * directional_lights.data[i].energy * henyey_greenstein(dot(normalize(view_pos), normalize(directional_lights.data[i].direction)), params.phase_g) * directional_lights.data[i].volumetric_fog_energy; + } } // Compute light from sky @@ -481,7 +491,7 @@ void main() { float d = distance(omni_lights.data[light_index].position, view_pos); float shadow_attenuation = 1.0; - if (d * omni_lights.data[light_index].inv_radius < 1.0) { + if (omni_lights.data[light_index].volumetric_fog_energy > 0.001 && d * omni_lights.data[light_index].inv_radius < 1.0) { float attenuation = get_omni_attenuation(d, omni_lights.data[light_index].inv_radius, omni_lights.data[light_index].attenuation); vec3 light = omni_lights.data[light_index].color; @@ -509,9 +519,9 @@ void main() { float depth = texture(sampler2D(shadow_atlas, linear_sampler), pos.xy).r; - shadow_attenuation = exp(min(0.0, (depth - pos.z)) / omni_lights.data[light_index].inv_radius * omni_lights.data[light_index].shadow_volumetric_fog_fade); + shadow_attenuation = mix(1.0 - omni_lights.data[light_index].shadow_opacity, 1.0, exp(min(0.0, (depth - pos.z)) / omni_lights.data[light_index].inv_radius * INV_FOG_FADE)); } - total_light += light * attenuation * shadow_attenuation * henyey_greenstein(dot(normalize(light_pos - view_pos), normalize(view_pos)), params.phase_g); + total_light += light * attenuation * shadow_attenuation * henyey_greenstein(dot(normalize(light_pos - view_pos), normalize(view_pos)), params.phase_g) * omni_lights.data[light_index].volumetric_fog_energy; } } } @@ -562,7 +572,7 @@ void main() { float d = length(light_rel_vec); float shadow_attenuation = 1.0; - if (d * spot_lights.data[light_index].inv_radius < 1.0) { + if (spot_lights.data[light_index].volumetric_fog_energy > 0.001 && d * spot_lights.data[light_index].inv_radius < 1.0) { float attenuation = get_omni_attenuation(d, spot_lights.data[light_index].inv_radius, spot_lights.data[light_index].attenuation); vec3 spot_dir = spot_lights.data[light_index].direction; @@ -595,9 +605,9 @@ void main() { float depth = texture(sampler2D(shadow_atlas, linear_sampler), pos.xy).r; - shadow_attenuation = exp(min(0.0, (depth - pos.z)) / spot_lights.data[light_index].inv_radius * spot_lights.data[light_index].shadow_volumetric_fog_fade); + shadow_attenuation = mix(1.0 - spot_lights.data[light_index].shadow_opacity, 1.0, exp(min(0.0, (depth - pos.z)) / spot_lights.data[light_index].inv_radius * INV_FOG_FADE)); } - total_light += light * attenuation * shadow_attenuation * henyey_greenstein(dot(normalize(light_rel_vec), normalize(view_pos)), params.phase_g); + total_light += light * attenuation * shadow_attenuation * henyey_greenstein(dot(normalize(light_rel_vec), normalize(view_pos)), params.phase_g) * spot_lights.data[light_index].volumetric_fog_energy; } } } @@ -619,7 +629,7 @@ void main() { light += a * slight; } - light.rgb *= voxel_gi_instances.data[i].dynamic_range * params.gi_inject; + light.rgb *= voxel_gi_instances.data[i].dynamic_range * params.gi_inject * voxel_gi_instances.data[i].exposure_normalization; total_light += light.rgb; } @@ -686,7 +696,7 @@ void main() { vec3 ambient = texelFetch(sampler2DArray(sdfgi_ambient_texture, linear_sampler), uvw, 0).rgb; - ambient_accum.rgb += ambient * weight; + ambient_accum.rgb += ambient * weight * sdfgi.cascades[i].exposure_normalization; ambient_accum.a += weight; } diff --git a/servers/rendering/renderer_rd/shaders/light_data_inc.glsl b/servers/rendering/renderer_rd/shaders/light_data_inc.glsl index 799f7087b6..7488a3f2c7 100644 --- a/servers/rendering/renderer_rd/shaders/light_data_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/light_data_inc.glsl @@ -25,7 +25,7 @@ struct LightData { //this structure needs to be as packed as possible highp float soft_shadow_size; // for spot, it's the size in uv coordinates of the light, for omni it's the span angle highp float soft_shadow_scale; // scales the shadow kernel for blurrier shadows uint mask; - mediump float shadow_volumetric_fog_fade; + mediump float volumetric_fog_energy; uint bake_mode; highp vec4 projector_rect; //projector rect in srgb decal atlas }; @@ -44,7 +44,7 @@ struct ReflectionData { bool exterior; bool box_project; uint ambient_mode; - uint pad; + float exposure_normalization; //0-8 is intensity,8-9 is ambient, mode highp mat4 local_matrix; // up to here for spot and omni, rest is for directional // notes: for ambientblend, use distance to edge to blend between already existing global environment @@ -52,7 +52,7 @@ struct ReflectionData { struct DirectionalLightData { mediump vec3 direction; - mediump float energy; + highp float energy; // needs to be highp to avoid NaNs being created with high energy values (i.e. when using physical light units and over-exposing the image) mediump vec3 color; mediump float size; mediump float specular; @@ -65,7 +65,7 @@ struct DirectionalLightData { highp float fade_to; uvec2 pad; uint bake_mode; - mediump float shadow_volumetric_fog_fade; + mediump float volumetric_fog_energy; highp vec4 shadow_bias; highp vec4 shadow_normal_bias; highp vec4 shadow_transmittance_bias; diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl index 6b4e4a5a16..26b96b358f 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl @@ -986,6 +986,11 @@ void fragment_shader(in SceneData scene_data) { vec3 diffuse_light = vec3(0.0, 0.0, 0.0); vec3 ambient_light = vec3(0.0, 0.0, 0.0); +#ifndef MODE_UNSHADED + // Used in regular draw pass and when drawing SDFs for SDFGI and materials for VoxelGI. + emission *= scene_data.emissive_exposure_normalization; +#endif + #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) if (scene_data.use_reflection_cubemap) { @@ -1015,6 +1020,7 @@ void fragment_shader(in SceneData scene_data) { specular_light = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ref_vec, roughness * MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY + specular_light *= scene_data.IBL_exposure_normalization; specular_light *= horizon * horizon; specular_light *= scene_data.ambient_light_color_energy.a; } @@ -1035,7 +1041,7 @@ void fragment_shader(in SceneData scene_data) { #else vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ambient_dir, MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY - + cubemap_ambient *= scene_data.IBL_exposure_normalization; ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix); } } @@ -1094,15 +1100,16 @@ void fragment_shader(in SceneData scene_data) { const float c4 = 0.886227; const float c5 = 0.247708; ambient_light += (c1 * lightmap_captures.data[index].sh[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + - c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z + - c4 * lightmap_captures.data[index].sh[0].rgb - - c5 * lightmap_captures.data[index].sh[6].rgb + - 2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y + - 2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z + - 2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z + - 2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x + - 2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y + - 2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z); + c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z + + c4 * lightmap_captures.data[index].sh[0].rgb - + c5 * lightmap_captures.data[index].sh[6].rgb + + 2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y + + 2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z + + 2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z + + 2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x + + 2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y + + 2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z) * + scene_data.emissive_exposure_normalization; } else if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { // has actual lightmap bool uses_sh = bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_SH_LIGHTMAP); @@ -1120,20 +1127,22 @@ void fragment_shader(in SceneData scene_data) { uint idx = instances.data[instance_index].gi_offset >> 20; vec3 n = normalize(lightmaps.data[idx].normal_xform * normal); + float en = lightmaps.data[idx].exposure_normalization; - ambient_light += lm_light_l0 * 0.282095f; - ambient_light += lm_light_l1n1 * 0.32573 * n.y; - ambient_light += lm_light_l1_0 * 0.32573 * n.z; - ambient_light += lm_light_l1p1 * 0.32573 * n.x; + ambient_light += lm_light_l0 * 0.282095f * en; + ambient_light += lm_light_l1n1 * 0.32573 * n.y * en; + ambient_light += lm_light_l1_0 * 0.32573 * n.z * en; + ambient_light += lm_light_l1p1 * 0.32573 * n.x * en; if (metallic > 0.01) { // since the more direct bounced light is lost, we can kind of fake it with this trick vec3 r = reflect(normalize(-vertex), normal); - specular_light += lm_light_l1n1 * 0.32573 * r.y; - specular_light += lm_light_l1_0 * 0.32573 * r.z; - specular_light += lm_light_l1p1 * 0.32573 * r.x; + specular_light += lm_light_l1n1 * 0.32573 * r.y * en; + specular_light += lm_light_l1_0 * 0.32573 * r.z * en; + specular_light += lm_light_l1p1 * 0.32573 * r.x * en; } } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb; + uint idx = instances.data[instance_index].gi_offset >> 20; + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb * lightmaps.data[idx].exposure_normalization; } } #else 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 f0717294ef..45484b8c47 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl @@ -105,6 +105,8 @@ directional_lights; struct Lightmap { mat3 normal_xform; + vec3 pad; + float exposure_normalization; }; layout(set = 0, binding = 9, std140) restrict readonly buffer Lightmaps { @@ -139,6 +141,8 @@ struct SDFVoxelGICascadeData { float to_probe; ivec3 probe_world_offset; float to_cell; // 1/bounds * grid_size + vec3 pad; + float exposure_normalization; }; layout(set = 0, binding = 15, std140) uniform SDFGI { @@ -251,7 +255,8 @@ struct SceneData { bool pancake_shadows; vec2 taa_jitter; - uvec2 pad2; + float emissive_exposure_normalization; + float IBL_exposure_normalization; }; layout(set = 1, binding = 0, std140) uniform SceneDataBlock { @@ -340,6 +345,9 @@ struct VoxelGIData { float normal_bias; // 4 - 88 bool blend_ambient; // 4 - 92 uint mipmaps; // 4 - 96 + + vec3 pad; // 12 - 108 + float exposure_normalization; // 4 - 112 }; layout(set = 1, binding = 17, std140) uniform VoxelGIs { diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl index c88bd0a14b..ae5e1b7251 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_gi_inc.glsl @@ -94,7 +94,7 @@ void voxel_gi_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 light += cone_weights[i] * cone_light.rgb; } - light *= voxel_gi_instances.data[index].dynamic_range; + light *= voxel_gi_instances.data[index].dynamic_range * voxel_gi_instances.data[index].exposure_normalization; out_diff += vec4(light * blend, blend); //irradiance @@ -102,7 +102,7 @@ void voxel_gi_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 if (voxel_gi_instances.data[index].blend_ambient) { irr_light.rgb = mix(environment, irr_light.rgb, min(1.0, irr_light.a / 0.95)); } - irr_light.rgb *= voxel_gi_instances.data[index].dynamic_range; + irr_light.rgb *= voxel_gi_instances.data[index].dynamic_range * voxel_gi_instances.data[index].exposure_normalization; //irr_light=vec3(0.0); out_spec += vec4(irr_light.rgb * blend, blend); @@ -189,7 +189,7 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z; diffuse = textureLod(sampler2DArray(sdfgi_lightprobe_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), pos_uvw, 0.0).rgb; - diffuse_accum += vec4(diffuse * weight, weight); + diffuse_accum += vec4(diffuse * weight * sdfgi.cascades[cascade].exposure_normalization, weight); if (use_specular) { vec3 specular = vec3(0.0); @@ -203,7 +203,7 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal specular = mix(specular, textureLod(sampler2DArray(sdfgi_lightprobe_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), pos_uvw, 0.0).rgb, (roughness - 0.5) * 2.0); } - specular_accum += specular * weight; + specular_accum += specular * weight * sdfgi.cascades[cascade].exposure_normalization; } } diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl index 7299bb0576..4e6e29b315 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl @@ -909,7 +909,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 ref_vec, vec3 normal, vec4 reflection; reflection.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_ref_vec, reflections.data[ref_index].index), roughness * MAX_ROUGHNESS_LOD).rgb * sc_luminance_multiplier; - + reflection.rgb *= reflections.data[ref_index].exposure_normalization; if (reflections.data[ref_index].exterior) { reflection.rgb = mix(specular_light, reflection.rgb, blend); } @@ -932,6 +932,7 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 ref_vec, vec3 normal, vec4 ambient_out; ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb; + ambient_out.rgb *= reflections.data[ref_index].exposure_normalization; ambient_out.a = blend; if (reflections.data[ref_index].exterior) { ambient_out.rgb = mix(ambient_light, ambient_out.rgb, blend); diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl index 0960533917..5a5ada7231 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl @@ -240,7 +240,7 @@ void main() { #endif #if defined(TANGENT_USED) || defined(NORMAL_MAP_USED) || defined(LIGHT_ANISOTROPY_USED) - vec3 signed_tangent_attrib = tangent_attrib * 2.0 - 1.0; + vec2 signed_tangent_attrib = tangent_attrib * 2.0 - 1.0; vec3 tangent = oct_to_vec3(vec2(signed_tangent_attrib.x, abs(signed_tangent_attrib.y) * 2.0 - 1.0)); float binormalf = sign(signed_tangent_attrib.y); vec3 binormal = normalize(cross(normal, tangent) * binormalf); @@ -471,8 +471,10 @@ layout(location = 8) highp in float dp_clip; #define model_matrix draw_call.transform #ifdef USE_MULTIVIEW #define projection_matrix scene_data.projection_matrix_view[ViewIndex] +#define inv_projection_matrix scene_data.inv_projection_matrix_view[ViewIndex] #else #define projection_matrix scene_data.projection_matrix +#define inv_projection_matrix scene_data.inv_projection_matrix #endif #if defined(ENABLE_SSS) && defined(ENABLE_TRANSMITTANCE) @@ -887,6 +889,11 @@ void main() { vec3 diffuse_light = vec3(0.0, 0.0, 0.0); vec3 ambient_light = vec3(0.0, 0.0, 0.0); +#ifndef MODE_UNSHADED + // Used in regular draw pass and when drawing SDFs for SDFGI and materials for VoxelGI. + emission *= scene_data.emissive_exposure_normalization; +#endif + #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) if (scene_data.use_reflection_cubemap) { @@ -915,6 +922,8 @@ void main() { specular_light = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ref_vec, roughness * MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY + specular_light *= sc_luminance_multiplier; + specular_light *= scene_data.IBL_exposure_normalization; specular_light *= horizon * horizon; specular_light *= scene_data.ambient_light_color_energy.a; } @@ -935,7 +944,8 @@ void main() { #else vec3 cubemap_ambient = textureLod(samplerCube(radiance_cubemap, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ambient_dir, MAX_ROUGHNESS_LOD).rgb; #endif //USE_RADIANCE_CUBEMAP_ARRAY - + cubemap_ambient *= sc_luminance_multiplier; + cubemap_ambient *= scene_data.IBL_exposure_normalization; ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix); } } @@ -993,15 +1003,16 @@ void main() { const float c4 = 0.886227; const float c5 = 0.247708; ambient_light += (c1 * lightmap_captures.data[index].sh[8].rgb * (wnormal.x * wnormal.x - wnormal.y * wnormal.y) + - c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z + - c4 * lightmap_captures.data[index].sh[0].rgb - - c5 * lightmap_captures.data[index].sh[6].rgb + - 2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y + - 2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z + - 2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z + - 2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x + - 2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y + - 2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z); + c3 * lightmap_captures.data[index].sh[6].rgb * wnormal.z * wnormal.z + + c4 * lightmap_captures.data[index].sh[0].rgb - + c5 * lightmap_captures.data[index].sh[6].rgb + + 2.0 * c1 * lightmap_captures.data[index].sh[4].rgb * wnormal.x * wnormal.y + + 2.0 * c1 * lightmap_captures.data[index].sh[7].rgb * wnormal.x * wnormal.z + + 2.0 * c1 * lightmap_captures.data[index].sh[5].rgb * wnormal.y * wnormal.z + + 2.0 * c2 * lightmap_captures.data[index].sh[3].rgb * wnormal.x + + 2.0 * c2 * lightmap_captures.data[index].sh[1].rgb * wnormal.y + + 2.0 * c2 * lightmap_captures.data[index].sh[2].rgb * wnormal.z) * + scene_data.emissive_exposure_normalization; } else if (bool(draw_call.flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { // has actual lightmap bool uses_sh = bool(draw_call.flags & INSTANCE_FLAGS_USE_SH_LIGHTMAP); @@ -1010,6 +1021,8 @@ void main() { uvw.xy = uv2 * draw_call.lightmap_uv_scale.zw + draw_call.lightmap_uv_scale.xy; uvw.z = float((draw_call.gi_offset >> 16) & 0xFFFF); + uint idx = draw_call.gi_offset >> 20; + if (uses_sh) { uvw.z *= 4.0; //SH textures use 4 times more data vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; @@ -1017,22 +1030,22 @@ void main() { vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; - uint idx = draw_call.gi_offset >> 20; vec3 n = normalize(lightmaps.data[idx].normal_xform * normal); + float exposure_normalization = lightmaps.data[idx].exposure_normalization; ambient_light += lm_light_l0 * 0.282095f; - ambient_light += lm_light_l1n1 * 0.32573 * n.y; - ambient_light += lm_light_l1_0 * 0.32573 * n.z; - ambient_light += lm_light_l1p1 * 0.32573 * n.x; + ambient_light += lm_light_l1n1 * 0.32573 * n.y * exposure_normalization; + ambient_light += lm_light_l1_0 * 0.32573 * n.z * exposure_normalization; + ambient_light += lm_light_l1p1 * 0.32573 * n.x * exposure_normalization; if (metallic > 0.01) { // since the more direct bounced light is lost, we can kind of fake it with this trick vec3 r = reflect(normalize(-vertex), normal); - specular_light += lm_light_l1n1 * 0.32573 * r.y; - specular_light += lm_light_l1_0 * 0.32573 * r.z; - specular_light += lm_light_l1p1 * 0.32573 * r.x; + specular_light += lm_light_l1n1 * 0.32573 * r.y * exposure_normalization; + specular_light += lm_light_l1_0 * 0.32573 * r.z * exposure_normalization; + specular_light += lm_light_l1p1 * 0.32573 * r.x * exposure_normalization; } } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb; + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb * lightmaps.data[idx].exposure_normalization; } } diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl index 98ad674ce0..3a9c52f5bc 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile_inc.glsl @@ -94,6 +94,8 @@ directional_lights; struct Lightmap { mediump mat3 normal_xform; + vec3 pad; + float exposure_normalization; }; layout(set = 0, binding = 9, std140) restrict readonly buffer Lightmaps { @@ -187,8 +189,8 @@ struct SceneData { mediump float reflection_multiplier; // one normally, zero when rendering reflections bool pancake_shadows; - uint pad1; - uint pad2; + float emissive_exposure_normalization; + float IBL_exposure_normalization; uint pad3; }; diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index 7b58cc08dd..81b0661481 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -75,6 +75,7 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) { light.param[RS::LIGHT_PARAM_ENERGY] = 1.0; light.param[RS::LIGHT_PARAM_INDIRECT_ENERGY] = 1.0; + light.param[RS::LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY] = 1.0; light.param[RS::LIGHT_PARAM_SPECULAR] = 0.5; light.param[RS::LIGHT_PARAM_RANGE] = 1.0; light.param[RS::LIGHT_PARAM_SIZE] = 0.0; @@ -91,8 +92,8 @@ void LightStorage::_light_initialize(RID p_light, RS::LightType p_type) { light.param[RS::LIGHT_PARAM_SHADOW_OPACITY] = 1.0; light.param[RS::LIGHT_PARAM_SHADOW_BLUR] = 0; light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0; - light.param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE] = 0.1; light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05; + light.param[RS::LIGHT_PARAM_INTENSITY] = p_type == RS::LIGHT_DIRECTIONAL ? 100000.0 : 1000.0; light_owner.initialize_rid(p_light, light); } @@ -502,6 +503,13 @@ void LightStorage::reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE); } +void LightStorage::reflection_probe_set_baked_exposure(RID p_probe, float p_exposure) { + ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); + ERR_FAIL_COND(!reflection_probe); + + reflection_probe->baked_exposure = p_exposure; +} + AABB LightStorage::reflection_probe_get_aabb(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, AABB()); @@ -569,6 +577,13 @@ int LightStorage::reflection_probe_get_resolution(RID p_probe) const { return reflection_probe->resolution; } +float LightStorage::reflection_probe_get_baked_exposure(RID p_probe) const { + const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); + ERR_FAIL_COND_V(!reflection_probe, 1.0); + + return reflection_probe->baked_exposure; +} + float LightStorage::reflection_probe_get_intensity(RID p_probe) const { const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe); ERR_FAIL_COND_V(!reflection_probe, 0); @@ -711,6 +726,13 @@ void LightStorage::lightmap_set_probe_capture_data(RID p_lightmap, const PackedV lm->tetrahedra = p_tetrahedra; } +void LightStorage::lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) { + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_COND(!lm); + + lm->baked_exposure = p_exposure; +} + PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_COND_V(!lm, PackedVector3Array()); diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h index 3e3246e8e9..82d609291c 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h @@ -87,6 +87,7 @@ private: bool enable_shadows = false; uint32_t cull_mask = (1 << 20) - 1; float mesh_lod_threshold = 0.01; + float baked_exposure = 1.0; Dependency dependency; }; @@ -99,6 +100,7 @@ private: bool uses_spherical_harmonics = false; bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); + float baked_exposure = 1.0; int32_t array_index = -1; //unassigned PackedVector3Array points; PackedColorArray point_sh; @@ -250,13 +252,6 @@ public: return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS]; } - _FORCE_INLINE_ float light_get_shadow_volumetric_fog_fade(RID p_light) const { - const Light *light = light_owner.get_or_null(p_light); - ERR_FAIL_COND_V(!light, 0.0); - - return light->param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE]; - } - virtual RS::LightBakeMode light_get_bake_mode(RID p_light) override; virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override; virtual uint64_t light_get_version(RID p_light) const override; @@ -286,6 +281,8 @@ public: virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) override; virtual void reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) override; + void reflection_probe_set_baked_exposure(RID p_probe, float p_exposure); + virtual AABB reflection_probe_get_aabb(RID p_probe) const override; virtual RS::ReflectionProbeUpdateMode reflection_probe_get_update_mode(RID p_probe) const override; virtual uint32_t reflection_probe_get_cull_mask(RID p_probe) const override; @@ -295,6 +292,7 @@ public: virtual float reflection_probe_get_mesh_lod_threshold(RID p_probe) const override; int reflection_probe_get_resolution(RID p_probe) const; + float reflection_probe_get_baked_exposure(RID p_probe) const; virtual bool reflection_probe_renders_shadows(RID p_probe) const override; float reflection_probe_get_intensity(RID p_probe) const; @@ -318,6 +316,7 @@ public: virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) override; virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override; + virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) override; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override; @@ -337,6 +336,11 @@ public: ERR_FAIL_COND_V(!lm, RID()); return lm->light_texture; } + _FORCE_INLINE_ float lightmap_get_baked_exposure_normalization(RID p_lightmap) const { + const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_COND_V(!lm, 1.0); + return lm->baked_exposure; + } _FORCE_INLINE_ int32_t lightmap_get_array_index(RID p_lightmap) const { ERR_FAIL_COND_V(!using_lightmap_array, -1); //only for arrays const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); diff --git a/servers/rendering/renderer_scene.h b/servers/rendering/renderer_scene.h index ba6fb71e67..ceb205a2f6 100644 --- a/servers/rendering/renderer_scene.h +++ b/servers/rendering/renderer_scene.h @@ -45,7 +45,7 @@ public: virtual void camera_set_transform(RID p_camera, const Transform3D &p_transform) = 0; virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers) = 0; virtual void camera_set_environment(RID p_camera, RID p_env) = 0; - virtual void camera_set_camera_effects(RID p_camera, RID p_fx) = 0; + virtual void camera_set_camera_attributes(RID p_camera, RID p_attributes) = 0; virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable) = 0; virtual bool is_camera(RID p_camera) const = 0; @@ -57,7 +57,7 @@ public: virtual void scenario_initialize(RID p_rid) = 0; virtual void scenario_set_environment(RID p_scenario, RID p_environment) = 0; - virtual void scenario_set_camera_effects(RID p_scenario, RID p_fx) = 0; + virtual void scenario_set_camera_attributes(RID p_scenario, RID p_attributes) = 0; virtual void scenario_set_fallback_environment(RID p_scenario, RID p_environment) = 0; virtual void scenario_set_reflection_atlas_size(RID p_scenario, int p_reflection_size, int p_reflection_count) = 0; virtual bool is_scenario(RID p_scenario) const = 0; @@ -128,7 +128,7 @@ public: virtual void environment_set_sky_custom_fov(RID p_env, float p_scale) = 0; virtual void environment_set_sky_orientation(RID p_env, const Basis &p_orientation) = 0; virtual void environment_set_bg_color(RID p_env, const Color &p_color) = 0; - virtual void environment_set_bg_energy(RID p_env, float p_energy) = 0; + virtual void environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value) = 0; virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer) = 0; virtual void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG) = 0; @@ -137,7 +137,8 @@ public: virtual float environment_get_sky_custom_fov(RID p_env) const = 0; virtual Basis environment_get_sky_orientation(RID p_env) const = 0; virtual Color environment_get_bg_color(RID p_env) const = 0; - virtual float environment_get_bg_energy(RID p_env) const = 0; + virtual float environment_get_bg_energy_multiplier(RID p_env) const = 0; + virtual float environment_get_bg_intensity(RID p_env) const = 0; virtual int environment_get_canvas_max_layer(RID p_env) const = 0; virtual RS::EnvironmentAmbientSource environment_get_ambient_source(RID p_env) const = 0; virtual Color environment_get_ambient_light(RID p_env) const = 0; @@ -146,17 +147,11 @@ public: virtual RS::EnvironmentReflectionSource environment_get_reflection_source(RID p_env) const = 0; // Tonemap - virtual void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) = 0; + virtual void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) = 0; virtual RS::EnvironmentToneMapper environment_get_tone_mapper(RID p_env) const = 0; virtual float environment_get_exposure(RID p_env) const = 0; virtual float environment_get_white(RID p_env) const = 0; - virtual bool environment_get_auto_exposure(RID p_env) const = 0; - virtual float environment_get_min_luminance(RID p_env) const = 0; - virtual float environment_get_max_luminance(RID p_env) const = 0; - virtual float environment_get_auto_exp_speed(RID p_env) const = 0; - virtual float environment_get_auto_exp_scale(RID p_env) const = 0; - virtual uint64_t environment_get_auto_exposure_version(RID p_env) const = 0; // Fog virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) = 0; @@ -266,7 +261,6 @@ public: virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) = 0; virtual void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) = 0; - // Adjustment virtual void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) = 0; virtual bool environment_get_adjustments_enabled(RID p_env) const = 0; @@ -284,17 +278,6 @@ public: virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) = 0; virtual void sub_surface_scattering_set_scale(float p_scale, float p_depth_scale) = 0; - /* Camera Effects */ - - virtual RID camera_effects_allocate() = 0; - virtual void camera_effects_initialize(RID p_rid) = 0; - - virtual void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) = 0; - virtual void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) = 0; - - virtual void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) = 0; - virtual void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) = 0; - virtual void positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) = 0; virtual void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) = 0; diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index 6846916f3f..5276322651 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -93,10 +93,10 @@ void RendererSceneCull::camera_set_environment(RID p_camera, RID p_env) { camera->env = p_env; } -void RendererSceneCull::camera_set_camera_effects(RID p_camera, RID p_fx) { +void RendererSceneCull::camera_set_camera_attributes(RID p_camera, RID p_attributes) { Camera *camera = camera_owner.get_or_null(p_camera); ERR_FAIL_COND(!camera); - camera->effects = p_fx; + camera->attributes = p_attributes; } void RendererSceneCull::camera_set_use_vertical_aspect(RID p_camera, bool p_enable) { @@ -378,10 +378,10 @@ void RendererSceneCull::scenario_set_environment(RID p_scenario, RID p_environme scenario->environment = p_environment; } -void RendererSceneCull::scenario_set_camera_effects(RID p_scenario, RID p_camera_effects) { +void RendererSceneCull::scenario_set_camera_attributes(RID p_scenario, RID p_camera_attributes) { Scenario *scenario = scenario_owner.get_or_null(p_scenario); ERR_FAIL_COND(!scenario); - scenario->camera_effects = p_camera_effects; + scenario->camera_attributes = p_camera_attributes; } void RendererSceneCull::scenario_set_fallback_environment(RID p_scenario, RID p_environment) { @@ -2537,7 +2537,7 @@ void RendererSceneCull::render_camera(RID p_render_buffers, RID p_camera, RID p_ // For now just cull on the first camera RendererSceneOcclusionCull::get_singleton()->buffer_update(p_viewport, camera_data.main_transform, camera_data.main_projection, camera_data.is_orthogonal); - _render_scene(&camera_data, p_render_buffers, environment, camera->effects, camera->visible_layers, p_scenario, p_viewport, p_shadow_atlas, RID(), -1, p_screen_mesh_lod_threshold, true, r_render_info); + _render_scene(&camera_data, p_render_buffers, environment, camera->attributes, camera->visible_layers, p_scenario, p_viewport, p_shadow_atlas, RID(), -1, p_screen_mesh_lod_threshold, true, r_render_info); #endif } @@ -2910,7 +2910,7 @@ void RendererSceneCull::_scene_cull(CullData &cull_data, InstanceCullResult &cul } } -void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_camera_data, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, bool p_using_shadows, RendererScene::RenderInfo *r_render_info) { +void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_camera_data, RID p_render_buffers, RID p_environment, RID p_force_camera_attributes, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, bool p_using_shadows, RendererScene::RenderInfo *r_render_info) { Instance *render_reflection_probe = instance_owner.get_or_null(p_reflection_probe); //if null, not rendering to it Scenario *scenario = scenario_owner.get_or_null(p_scenario); @@ -3240,11 +3240,11 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c scene_cull_result.light_instances.push_back(directional_lights[i]); } - RID camera_effects; - if (p_force_camera_effects.is_valid()) { - camera_effects = p_force_camera_effects; + RID camera_attributes; + if (p_force_camera_attributes.is_valid()) { + camera_attributes = p_force_camera_attributes; } else { - camera_effects = scenario->camera_effects; + camera_attributes = scenario->camera_attributes; } /* PROCESS GEOMETRY AND DRAW SCENE */ @@ -3256,7 +3256,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c } RENDER_TIMESTAMP("Render 3D Scene"); - scene_render->render_scene(p_render_buffers, p_camera_data, prev_camera_data, scene_cull_result.geometry_instances, scene_cull_result.light_instances, scene_cull_result.reflections, scene_cull_result.voxel_gi_instances, scene_cull_result.decals, scene_cull_result.lightmaps, scene_cull_result.fog_volumes, p_environment, camera_effects, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_mesh_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data, r_render_info); + scene_render->render_scene(p_render_buffers, p_camera_data, prev_camera_data, scene_cull_result.geometry_instances, scene_cull_result.light_instances, scene_cull_result.reflections, scene_cull_result.voxel_gi_instances, scene_cull_result.decals, scene_cull_result.lightmaps, scene_cull_result.fog_volumes, p_environment, camera_attributes, p_shadow_atlas, occluders_tex, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass, p_screen_mesh_lod_threshold, render_shadow_data, max_shadows_used, render_sdfgi_data, cull.sdfgi.region_count, &sdfgi_update_data, r_render_info); if (p_viewport.is_valid()) { RSG::viewport->viewport_set_prev_camera_data(p_viewport, p_camera_data); @@ -3271,7 +3271,7 @@ void RendererSceneCull::_render_scene(const RendererSceneRender::CameraData *p_c render_sdfgi_data[i].instances.clear(); } - // virtual void render_scene(RID p_render_buffers, const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold,const RenderShadowData *p_render_shadows,int p_render_shadow_count,const RenderSDFGIData *p_render_sdfgi_regions,int p_render_sdfgi_region_count,const RenderSDFGIStaticLightData *p_render_sdfgi_static_lights=nullptr) = 0; + // virtual void render_scene(RID p_render_buffers, const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold,const RenderShadowData *p_render_shadows,int p_render_shadow_count,const RenderSDFGIData *p_render_sdfgi_regions,int p_render_sdfgi_region_count,const RenderSDFGIStaticLightData *p_render_sdfgi_static_lights=nullptr) = 0; } RID RendererSceneCull::_render_get_environment(RID p_camera, RID p_scenario) { @@ -3479,6 +3479,7 @@ void RendererSceneCull::render_probes() { cache->transform != instance->transform || cache->color != RSG::light_storage->light_get_color(instance->base) || cache->energy != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ENERGY) || + cache->intensity != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INTENSITY) || cache->bake_energy != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INDIRECT_ENERGY) || cache->radius != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_RANGE) || cache->attenuation != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ATTENUATION) || @@ -3510,6 +3511,7 @@ void RendererSceneCull::render_probes() { cache->transform != instance->transform || cache->color != RSG::light_storage->light_get_color(instance->base) || cache->energy != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ENERGY) || + cache->intensity != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INTENSITY) || cache->bake_energy != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INDIRECT_ENERGY) || cache->radius != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_RANGE) || cache->attenuation != RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ATTENUATION) || @@ -3556,6 +3558,7 @@ void RendererSceneCull::render_probes() { cache->transform = instance->transform; cache->color = RSG::light_storage->light_get_color(instance->base); cache->energy = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ENERGY); + cache->intensity = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INTENSITY); cache->bake_energy = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INDIRECT_ENERGY); cache->radius = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_RANGE); cache->attenuation = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ATTENUATION); @@ -3578,6 +3581,7 @@ void RendererSceneCull::render_probes() { cache->transform = instance->transform; cache->color = RSG::light_storage->light_get_color(instance->base); cache->energy = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ENERGY); + cache->intensity = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INTENSITY); cache->bake_energy = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_INDIRECT_ENERGY); cache->radius = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_RANGE); cache->attenuation = RSG::light_storage->light_get_param(instance->base, RS::LIGHT_PARAM_ATTENUATION); diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 540fb0e27a..6fff6827c0 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -76,7 +76,7 @@ public: uint32_t visible_layers; bool vaspect; RID env; - RID effects; + RID attributes; Transform3D transform; @@ -103,7 +103,7 @@ public: virtual void camera_set_transform(RID p_camera, const Transform3D &p_transform); virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers); virtual void camera_set_environment(RID p_camera, RID p_env); - virtual void camera_set_camera_effects(RID p_camera, RID p_fx); + virtual void camera_set_camera_attributes(RID p_camera, RID p_attributes); virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable); virtual bool is_camera(RID p_camera) const; @@ -320,7 +320,7 @@ public: List<Instance *> directional_lights; RID environment; RID fallback_environment; - RID camera_effects; + RID camera_attributes; RID reflection_probe_shadow_atlas; RID reflection_atlas; uint64_t used_viewport_visibility_bits; @@ -354,7 +354,7 @@ public: virtual void scenario_initialize(RID p_rid); virtual void scenario_set_environment(RID p_scenario, RID p_environment); - virtual void scenario_set_camera_effects(RID p_scenario, RID p_fx); + virtual void scenario_set_camera_attributes(RID p_scenario, RID p_attributes); virtual void scenario_set_fallback_environment(RID p_scenario, RID p_environment); virtual void scenario_set_reflection_atlas_size(RID p_scenario, int p_reflection_size, int p_reflection_count); virtual bool is_scenario(RID p_scenario) const; @@ -683,6 +683,7 @@ public: Transform3D transform; Color color; float energy; + float intensity; float bake_energy; float radius; float attenuation; @@ -1054,7 +1055,7 @@ public: _FORCE_INLINE_ bool _visibility_parent_check(const CullData &p_cull_data, const InstanceData &p_instance_data); bool _render_reflection_probe_step(Instance *p_instance, int p_step); - void _render_scene(const RendererSceneRender::CameraData *p_camera_data, RID p_render_buffers, RID p_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, bool p_using_shadows = true, RenderInfo *r_render_info = nullptr); + void _render_scene(const RendererSceneRender::CameraData *p_camera_data, RID p_render_buffers, RID p_environment, RID p_force_camera_attributes, uint32_t p_visible_layers, RID p_scenario, RID p_viewport, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, bool p_using_shadows = true, RenderInfo *r_render_info = nullptr); void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas); void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, RID p_viewport, Size2 p_viewport_size, bool p_use_taa, float p_screen_mesh_lod_threshold, RID p_shadow_atlas, Ref<XRInterface> &p_xr_interface, RendererScene::RenderInfo *r_render_info = nullptr); @@ -1099,7 +1100,7 @@ public: PASS2(environment_set_sky_custom_fov, RID, float) PASS2(environment_set_sky_orientation, RID, const Basis &) PASS2(environment_set_bg_color, RID, const Color &) - PASS2(environment_set_bg_energy, RID, float) + PASS3(environment_set_bg_energy, RID, float, float) PASS2(environment_set_canvas_max_layer, RID, int) PASS6(environment_set_ambient_light, RID, const Color &, RS::EnvironmentAmbientSource, float, float, RS::EnvironmentReflectionSource) @@ -1108,7 +1109,8 @@ public: PASS1RC(float, environment_get_sky_custom_fov, RID) PASS1RC(Basis, environment_get_sky_orientation, RID) PASS1RC(Color, environment_get_bg_color, RID) - PASS1RC(float, environment_get_bg_energy, RID) + PASS1RC(float, environment_get_bg_energy_multiplier, RID) + PASS1RC(float, environment_get_bg_intensity, RID) PASS1RC(int, environment_get_canvas_max_layer, RID) PASS1RC(RS::EnvironmentAmbientSource, environment_get_ambient_source, RID) PASS1RC(Color, environment_get_ambient_light, RID) @@ -1117,16 +1119,10 @@ public: PASS1RC(RS::EnvironmentReflectionSource, environment_get_reflection_source, RID) // Tonemap - PASS9(environment_set_tonemap, RID, RS::EnvironmentToneMapper, float, float, bool, float, float, float, float) + PASS4(environment_set_tonemap, RID, RS::EnvironmentToneMapper, float, float) PASS1RC(RS::EnvironmentToneMapper, environment_get_tone_mapper, RID) PASS1RC(float, environment_get_exposure, RID) PASS1RC(float, environment_get_white, RID) - PASS1RC(bool, environment_get_auto_exposure, RID) - PASS1RC(float, environment_get_min_luminance, RID) - PASS1RC(float, environment_get_max_luminance, RID) - PASS1RC(float, environment_get_auto_exp_speed, RID) - PASS1RC(float, environment_get_auto_exp_scale, RID) - PASS1RC(uint64_t, environment_get_auto_exposure_version, RID) // Fog PASS9(environment_set_fog, RID, bool, const Color &, float, float, float, float, float, float) @@ -1250,17 +1246,6 @@ public: PASS1(sub_surface_scattering_set_quality, RS::SubSurfaceScatteringQuality) PASS2(sub_surface_scattering_set_scale, float, float) - /* CAMERA EFFECTS */ - - PASS0R(RID, camera_effects_allocate) - PASS1(camera_effects_initialize, RID) - - PASS2(camera_effects_set_dof_blur_quality, RS::DOFBlurQuality, bool) - PASS1(camera_effects_set_dof_blur_bokeh_shape, RS::DOFBokehShape) - - PASS8(camera_effects_set_dof_blur, RID, bool, float, float, bool, float, float, float) - PASS3(camera_effects_set_custom_exposure, RID, bool, float) - PASS1(positional_soft_shadow_filter_set_quality, RS::ShadowQuality) PASS1(directional_soft_shadow_filter_set_quality, RS::ShadowQuality) diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp index e024e59ec3..29a0002a32 100644 --- a/servers/rendering/renderer_scene_render.cpp +++ b/servers/rendering/renderer_scene_render.cpp @@ -224,8 +224,8 @@ void RendererSceneRender::environment_set_bg_color(RID p_env, const Color &p_col environment_storage.environment_set_bg_color(p_env, p_color); } -void RendererSceneRender::environment_set_bg_energy(RID p_env, float p_energy) { - environment_storage.environment_set_bg_energy(p_env, p_energy); +void RendererSceneRender::environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value) { + environment_storage.environment_set_bg_energy(p_env, p_multiplier, p_exposure_value); } void RendererSceneRender::environment_set_canvas_max_layer(RID p_env, int p_max_layer) { @@ -256,8 +256,12 @@ Color RendererSceneRender::environment_get_bg_color(RID p_env) const { return environment_storage.environment_get_bg_color(p_env); } -float RendererSceneRender::environment_get_bg_energy(RID p_env) const { - return environment_storage.environment_get_bg_energy(p_env); +float RendererSceneRender::environment_get_bg_energy_multiplier(RID p_env) const { + return environment_storage.environment_get_bg_energy_multiplier(p_env); +} + +float RendererSceneRender::environment_get_bg_intensity(RID p_env) const { + return environment_storage.environment_get_bg_intensity(p_env); } int RendererSceneRender::environment_get_canvas_max_layer(RID p_env) const { @@ -286,8 +290,8 @@ RS::EnvironmentReflectionSource RendererSceneRender::environment_get_reflection_ // Tonemap -void RendererSceneRender::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) { - environment_storage.environment_set_tonemap(p_env, p_tone_mapper, p_exposure, p_white, p_auto_exposure, p_min_luminance, p_max_luminance, p_auto_exp_speed, p_auto_exp_scale); +void RendererSceneRender::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) { + environment_storage.environment_set_tonemap(p_env, p_tone_mapper, p_exposure, p_white); } RS::EnvironmentToneMapper RendererSceneRender::environment_get_tone_mapper(RID p_env) const { @@ -302,30 +306,6 @@ float RendererSceneRender::environment_get_white(RID p_env) const { return environment_storage.environment_get_white(p_env); } -bool RendererSceneRender::environment_get_auto_exposure(RID p_env) const { - return environment_storage.environment_get_auto_exposure(p_env); -} - -float RendererSceneRender::environment_get_min_luminance(RID p_env) const { - return environment_storage.environment_get_min_luminance(p_env); -} - -float RendererSceneRender::environment_get_max_luminance(RID p_env) const { - return environment_storage.environment_get_max_luminance(p_env); -} - -float RendererSceneRender::environment_get_auto_exp_speed(RID p_env) const { - return environment_storage.environment_get_auto_exp_speed(p_env); -} - -float RendererSceneRender::environment_get_auto_exp_scale(RID p_env) const { - return environment_storage.environment_get_auto_exp_scale(p_env); -} - -uint64_t RendererSceneRender::environment_get_auto_exposure_version(RID p_env) const { - return environment_storage.environment_get_auto_exposure_version(p_env); -} - // Fog void RendererSceneRender::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective) { diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h index 7f70f4b939..6684773cbc 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -97,7 +97,7 @@ public: void environment_set_sky_custom_fov(RID p_env, float p_scale); void environment_set_sky_orientation(RID p_env, const Basis &p_orientation); void environment_set_bg_color(RID p_env, const Color &p_color); - void environment_set_bg_energy(RID p_env, float p_energy); + void environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value); void environment_set_canvas_max_layer(RID p_env, int p_max_layer); void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG); // FIXME: Disabled during Vulkan refactoring, should be ported. @@ -110,7 +110,8 @@ public: float environment_get_sky_custom_fov(RID p_env) const; Basis environment_get_sky_orientation(RID p_env) const; Color environment_get_bg_color(RID p_env) const; - float environment_get_bg_energy(RID p_env) const; + float environment_get_bg_energy_multiplier(RID p_env) const; + float environment_get_bg_intensity(RID p_env) const; int environment_get_canvas_max_layer(RID p_env) const; RS::EnvironmentAmbientSource environment_get_ambient_source(RID p_env) const; Color environment_get_ambient_light(RID p_env) const; @@ -119,16 +120,10 @@ public: RS::EnvironmentReflectionSource environment_get_reflection_source(RID p_env) const; // Tonemap - void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale); + void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white); RS::EnvironmentToneMapper environment_get_tone_mapper(RID p_env) const; float environment_get_exposure(RID p_env) const; float environment_get_white(RID p_env) const; - bool environment_get_auto_exposure(RID p_env) const; - float environment_get_min_luminance(RID p_env) const; - float environment_get_max_luminance(RID p_env) const; - float environment_get_auto_exp_speed(RID p_env) const; - float environment_get_auto_exp_scale(RID p_env) const; - uint64_t environment_get_auto_exposure_version(RID p_env) const; // Fog void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective); @@ -239,15 +234,6 @@ public: virtual Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) = 0; - virtual RID camera_effects_allocate() = 0; - virtual void camera_effects_initialize(RID p_rid) = 0; - - virtual void camera_effects_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) = 0; - virtual void camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) = 0; - - virtual void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) = 0; - virtual void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) = 0; - virtual void positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) = 0; virtual void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) = 0; @@ -331,7 +317,7 @@ public: void set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_vaspect); }; - virtual void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) = 0; + virtual void render_scene(RID p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) = 0; virtual void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) = 0; virtual void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) = 0; diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 8071b7e416..1f686069bd 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -397,6 +397,7 @@ RenderingServerDefault::RenderingServerDefault(bool p_create_thread) : RSG::canvas = memnew(RendererCanvasCull); RSG::viewport = memnew(RendererViewport); RendererSceneCull *sr = memnew(RendererSceneCull); + RSG::camera_attributes = memnew(RendererCameraAttributes); RSG::scene = sr; RSG::rasterizer = RendererCompositor::create(); RSG::utilities = RSG::rasterizer->get_utilities(); @@ -418,4 +419,5 @@ RenderingServerDefault::~RenderingServerDefault() { memdelete(RSG::viewport); memdelete(RSG::rasterizer); memdelete(RSG::scene); + memdelete(RSG::camera_attributes); } diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index ba70f02ca5..40912805dc 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -403,6 +403,7 @@ public: FUNC2(lightmap_set_probe_bounds, RID, const AABB &) FUNC2(lightmap_set_probe_interior, RID, bool) FUNC5(lightmap_set_probe_capture_data, RID, const PackedVector3Array &, const PackedColorArray &, const PackedInt32Array &, const PackedInt32Array &) + FUNC2(lightmap_set_baked_exposure_normalization, RID, float) FUNC1RC(PackedVector3Array, lightmap_get_probe_capture_points, RID) FUNC1RC(PackedColorArray, lightmap_get_probe_capture_sh, RID) FUNC1RC(PackedInt32Array, lightmap_get_probe_capture_tetrahedra, RID) @@ -453,6 +454,7 @@ public: FUNC2(voxel_gi_set_dynamic_range, RID, float) FUNC2(voxel_gi_set_propagation, RID, float) FUNC2(voxel_gi_set_energy, RID, float) + FUNC2(voxel_gi_set_baked_exposure_normalization, RID, float) FUNC2(voxel_gi_set_bias, RID, float) FUNC2(voxel_gi_set_normal_bias, RID, float) FUNC2(voxel_gi_set_interior, RID, bool) @@ -560,7 +562,7 @@ public: FUNC2(camera_set_transform, RID, const Transform3D &) FUNC2(camera_set_cull_mask, RID, uint32_t) FUNC2(camera_set_environment, RID, RID) - FUNC2(camera_set_camera_effects, RID, RID) + FUNC2(camera_set_camera_attributes, RID, RID) FUNC2(camera_set_use_vertical_aspect, RID, bool) /* OCCLUDER */ @@ -668,7 +670,7 @@ public: FUNC2(environment_set_sky_custom_fov, RID, float) FUNC2(environment_set_sky_orientation, RID, const Basis &) FUNC2(environment_set_bg_color, RID, const Color &) - FUNC2(environment_set_bg_energy, RID, float) + FUNC3(environment_set_bg_energy, RID, float, float) FUNC2(environment_set_canvas_max_layer, RID, int) FUNC6(environment_set_ambient_light, RID, const Color &, EnvironmentAmbientSource, float, float, EnvironmentReflectionSource) @@ -689,7 +691,7 @@ public: FUNC1(environment_glow_set_use_bicubic_upscale, bool) FUNC1(environment_glow_set_use_high_quality, bool) - FUNC9(environment_set_tonemap, RID, EnvironmentToneMapper, float, float, bool, float, float, float, float) + FUNC4(environment_set_tonemap, RID, EnvironmentToneMapper, float, float) FUNC7(environment_set_adjustment, RID, bool, float, float, float, bool, RID) @@ -710,21 +712,28 @@ public: FUNC1(sub_surface_scattering_set_quality, SubSurfaceScatteringQuality) FUNC2(sub_surface_scattering_set_scale, float, float) - /* CAMERA EFFECTS */ - - FUNCRIDSPLIT(camera_effects) - - FUNC2(camera_effects_set_dof_blur_quality, DOFBlurQuality, bool) - FUNC1(camera_effects_set_dof_blur_bokeh_shape, DOFBokehShape) - - FUNC8(camera_effects_set_dof_blur, RID, bool, float, float, bool, float, float, float) - FUNC3(camera_effects_set_custom_exposure, RID, bool, float) - FUNC1(positional_soft_shadow_filter_set_quality, ShadowQuality); FUNC1(directional_soft_shadow_filter_set_quality, ShadowQuality); FUNC1(decals_set_filter, RS::DecalFilter); FUNC1(light_projectors_set_filter, RS::LightProjectorFilter); + /* CAMERA ATTRIBUTES */ + +#undef server_name +#undef ServerName +//from now on, calls forwarded to this singleton +#define ServerName RendererCameraAttributes +#define server_name RSG::camera_attributes + + FUNCRIDSPLIT(camera_attributes) + + FUNC2(camera_attributes_set_dof_blur_quality, DOFBlurQuality, bool) + FUNC1(camera_attributes_set_dof_blur_bokeh_shape, DOFBokehShape) + + FUNC8(camera_attributes_set_dof_blur, RID, bool, float, float, bool, float, float, float) + FUNC3(camera_attributes_set_exposure, RID, float, float) + FUNC6(camera_attributes_set_auto_exposure, RID, bool, float, float, float, float) + /* SCENARIO API */ #undef server_name @@ -736,7 +745,7 @@ public: FUNCRIDSPLIT(scenario) FUNC2(scenario_set_environment, RID, RID) - FUNC2(scenario_set_camera_effects, RID, RID) + FUNC2(scenario_set_camera_attributes, RID, RID) FUNC2(scenario_set_fallback_environment, RID, RID) /* INSTANCING API */ diff --git a/servers/rendering/rendering_server_globals.cpp b/servers/rendering/rendering_server_globals.cpp index 46fa49e683..ce7383a03f 100644 --- a/servers/rendering/rendering_server_globals.cpp +++ b/servers/rendering/rendering_server_globals.cpp @@ -40,6 +40,7 @@ RendererParticlesStorage *RenderingServerGlobals::particles_storage = nullptr; RendererTextureStorage *RenderingServerGlobals::texture_storage = nullptr; RendererGI *RenderingServerGlobals::gi = nullptr; RendererFog *RenderingServerGlobals::fog = nullptr; +RendererCameraAttributes *RenderingServerGlobals::camera_attributes = nullptr; RendererCanvasRender *RenderingServerGlobals::canvas_render = nullptr; RendererCompositor *RenderingServerGlobals::rasterizer = nullptr; diff --git a/servers/rendering/rendering_server_globals.h b/servers/rendering/rendering_server_globals.h index 87d69984f8..6c4ab5a26e 100644 --- a/servers/rendering/rendering_server_globals.h +++ b/servers/rendering/rendering_server_globals.h @@ -36,6 +36,7 @@ #include "servers/rendering/renderer_canvas_cull.h" #include "servers/rendering/renderer_canvas_render.h" #include "servers/rendering/renderer_scene.h" +#include "servers/rendering/storage/camera_attributes_storage.h" #include "servers/rendering/storage/light_storage.h" #include "servers/rendering/storage/material_storage.h" #include "servers/rendering/storage/mesh_storage.h" @@ -59,6 +60,7 @@ public: static RendererTextureStorage *texture_storage; static RendererGI *gi; static RendererFog *fog; + static RendererCameraAttributes *camera_attributes; static RendererCanvasRender *canvas_render; static RendererCompositor *rasterizer; diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index f14350305a..7b33a13ec3 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -1239,7 +1239,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene } code += ")"; if (is_screen_texture && actions.apply_luminance_multiplier) { - code = "(" + code + " * vec4(vec3(sc_luminance_multiplier), 1.0))"; + code = "(" + code + " / vec4(vec3(sc_luminance_multiplier), 1.0))"; } } break; case SL::OP_INDEX: { diff --git a/servers/rendering/storage/camera_attributes_storage.cpp b/servers/rendering/storage/camera_attributes_storage.cpp new file mode 100644 index 0000000000..570fefb9de --- /dev/null +++ b/servers/rendering/storage/camera_attributes_storage.cpp @@ -0,0 +1,177 @@ +/*************************************************************************/ +/* camera_attributes_storage.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "camera_attributes_storage.h" + +RendererCameraAttributes *RendererCameraAttributes::singleton = nullptr; +uint64_t RendererCameraAttributes::auto_exposure_counter = 2; + +RendererCameraAttributes::RendererCameraAttributes() { + singleton = this; +} + +RendererCameraAttributes::~RendererCameraAttributes() { + singleton = nullptr; +} + +RID RendererCameraAttributes::camera_attributes_allocate() { + return camera_attributes_owner.allocate_rid(); +} + +void RendererCameraAttributes::camera_attributes_initialize(RID p_rid) { + camera_attributes_owner.initialize_rid(p_rid, CameraAttributes()); +} + +void RendererCameraAttributes::camera_attributes_free(RID p_rid) { + camera_attributes_owner.free(p_rid); +} + +void RendererCameraAttributes::camera_attributes_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter) { + dof_blur_quality = p_quality; + dof_blur_use_jitter = p_use_jitter; +} + +void RendererCameraAttributes::camera_attributes_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape) { + dof_blur_bokeh_shape = p_shape; +} + +void RendererCameraAttributes::camera_attributes_set_dof_blur(RID p_camera_attributes, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND(!cam_attributes); + + cam_attributes->dof_blur_far_enabled = p_far_enable; + cam_attributes->dof_blur_far_distance = p_far_distance; + cam_attributes->dof_blur_far_transition = p_far_transition; + + cam_attributes->dof_blur_near_enabled = p_near_enable; + cam_attributes->dof_blur_near_distance = p_near_distance; + cam_attributes->dof_blur_near_transition = p_near_transition; + + cam_attributes->dof_blur_amount = p_amount; +} + +bool RendererCameraAttributes::camera_attributes_get_dof_far_enabled(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, false); + return cam_attributes->dof_blur_far_enabled; +} + +float RendererCameraAttributes::camera_attributes_get_dof_far_distance(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->dof_blur_far_distance; +} + +float RendererCameraAttributes::camera_attributes_get_dof_far_transition(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->dof_blur_far_transition; +} + +bool RendererCameraAttributes::camera_attributes_get_dof_near_enabled(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, false); + return cam_attributes->dof_blur_near_enabled; +} + +float RendererCameraAttributes::camera_attributes_get_dof_near_distance(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->dof_blur_near_distance; +} + +float RendererCameraAttributes::camera_attributes_get_dof_near_transition(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->dof_blur_near_transition; +} + +float RendererCameraAttributes::camera_attributes_get_dof_blur_amount(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->dof_blur_amount; +} + +void RendererCameraAttributes::camera_attributes_set_exposure(RID p_camera_attributes, float p_multiplier, float p_exposure_normalization) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND(!cam_attributes); + cam_attributes->exposure_multiplier = p_multiplier; + cam_attributes->exposure_normalization = p_exposure_normalization; +} + +float RendererCameraAttributes::camera_attributes_get_exposure_normalization_factor(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 1.0); + + return cam_attributes->exposure_multiplier * cam_attributes->exposure_normalization; +} + +void RendererCameraAttributes::camera_attributes_set_auto_exposure(RID p_camera_attributes, bool p_enable, float p_min_sensitivity, float p_max_sensitivity, float p_speed, float p_scale) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND(!cam_attributes); + if (!cam_attributes->use_auto_exposure && p_enable) { + cam_attributes->auto_exposure_version = ++auto_exposure_counter; + } + cam_attributes->use_auto_exposure = p_enable; + cam_attributes->auto_exposure_min_sensitivity = p_min_sensitivity; + cam_attributes->auto_exposure_max_sensitivity = p_max_sensitivity; + cam_attributes->auto_exposure_adjust_speed = p_speed; + cam_attributes->auto_exposure_scale = p_scale; +} + +float RendererCameraAttributes::camera_attributes_get_auto_exposure_min_sensitivity(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->auto_exposure_min_sensitivity; +} + +float RendererCameraAttributes::camera_attributes_get_auto_exposure_max_sensitivity(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->auto_exposure_max_sensitivity; +} + +float RendererCameraAttributes::camera_attributes_get_auto_exposure_adjust_speed(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->auto_exposure_adjust_speed; +} + +float RendererCameraAttributes::camera_attributes_get_auto_exposure_scale(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0.0); + return cam_attributes->auto_exposure_scale; +} + +uint64_t RendererCameraAttributes::camera_attributes_get_auto_exposure_version(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + ERR_FAIL_COND_V(!cam_attributes, 0); + return cam_attributes->auto_exposure_version; +} diff --git a/servers/rendering/storage/camera_attributes_storage.h b/servers/rendering/storage/camera_attributes_storage.h new file mode 100644 index 0000000000..6c7b364b10 --- /dev/null +++ b/servers/rendering/storage/camera_attributes_storage.h @@ -0,0 +1,129 @@ +/*************************************************************************/ +/* camera_attributes_storage.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef CAMERA_ATTRIBUTES_STORAGE_H +#define CAMERA_ATTRIBUTES_STORAGE_H + +#include "core/templates/rid_owner.h" +#include "servers/rendering_server.h" + +class RendererCameraAttributes { +private: + static RendererCameraAttributes *singleton; + + struct CameraAttributes { + float exposure_multiplier = 1.0; + float exposure_normalization = 1.0; + float exposure_sensitivity = 100.0; // In ISO. + + bool use_auto_exposure = false; + float auto_exposure_min_sensitivity = 50.0; + float auto_exposure_max_sensitivity = 800.0; + float auto_exposure_adjust_speed = 1.0; + float auto_exposure_scale = 1.0; + uint64_t auto_exposure_version = 0; + + bool dof_blur_far_enabled = false; + float dof_blur_far_distance = 10; + float dof_blur_far_transition = 5; + bool dof_blur_near_enabled = false; + float dof_blur_near_distance = 2; + float dof_blur_near_transition = 1; + float dof_blur_amount = 0.1; + }; + + RS::DOFBlurQuality dof_blur_quality = RS::DOF_BLUR_QUALITY_MEDIUM; + RS::DOFBokehShape dof_blur_bokeh_shape = RS::DOF_BOKEH_HEXAGON; + bool dof_blur_use_jitter = false; + static uint64_t auto_exposure_counter; + + mutable RID_Owner<CameraAttributes, true> camera_attributes_owner; + +public: + static RendererCameraAttributes *get_singleton() { return singleton; } + + RendererCameraAttributes(); + ~RendererCameraAttributes(); + + CameraAttributes *get_camera_attributes(RID p_rid) { return camera_attributes_owner.get_or_null(p_rid); }; + bool owns_camera_attributes(RID p_rid) { return camera_attributes_owner.owns(p_rid); }; + + RID camera_attributes_allocate(); + void camera_attributes_initialize(RID p_rid); + void camera_attributes_free(RID p_rid); + + void camera_attributes_set_dof_blur_quality(RS::DOFBlurQuality p_quality, bool p_use_jitter); + void camera_attributes_set_dof_blur_bokeh_shape(RS::DOFBokehShape p_shape); + + void camera_attributes_set_dof_blur(RID p_camera_attributes, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount); + bool camera_attributes_get_dof_far_enabled(RID p_camera_attributes); + float camera_attributes_get_dof_far_distance(RID p_camera_attributes); + float camera_attributes_get_dof_far_transition(RID p_camera_attributes); + bool camera_attributes_get_dof_near_enabled(RID p_camera_attributes); + float camera_attributes_get_dof_near_distance(RID p_camera_attributes); + float camera_attributes_get_dof_near_transition(RID p_camera_attributes); + float camera_attributes_get_dof_blur_amount(RID p_camera_attributes); + + _FORCE_INLINE_ bool camera_attributes_uses_dof(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + + return cam_attributes && (cam_attributes->dof_blur_near_enabled || cam_attributes->dof_blur_far_enabled) && cam_attributes->dof_blur_amount > 0.0; + } + + void camera_attributes_set_exposure(RID p_camera_attributes, float p_multiplier, float p_exposure_normalization); + float camera_attributes_get_exposure_normalization_factor(RID p_camera_attributes); + + void camera_attributes_set_auto_exposure(RID p_camera_attributes, bool p_enable, float p_min_sensitivity, float p_max_sensitivity, float p_speed, float p_scale); + float camera_attributes_get_auto_exposure_min_sensitivity(RID p_camera_attributes); + float camera_attributes_get_auto_exposure_max_sensitivity(RID p_camera_attributes); + float camera_attributes_get_auto_exposure_adjust_speed(RID p_camera_attributes); + float camera_attributes_get_auto_exposure_scale(RID p_camera_attributes); + uint64_t camera_attributes_get_auto_exposure_version(RID p_camera_attributes); + + _FORCE_INLINE_ bool camera_attributes_uses_auto_exposure(RID p_camera_attributes) { + CameraAttributes *cam_attributes = camera_attributes_owner.get_or_null(p_camera_attributes); + + return cam_attributes && cam_attributes->use_auto_exposure; + } + + _FORCE_INLINE_ RS::DOFBlurQuality camera_attributes_get_dof_blur_quality() { + return dof_blur_quality; + } + + _FORCE_INLINE_ RS::DOFBokehShape camera_attributes_get_dof_blur_bokeh_shape() { + return dof_blur_bokeh_shape; + } + + _FORCE_INLINE_ bool camera_attributes_get_dof_blur_use_jitter() { + return dof_blur_use_jitter; + } +}; + +#endif // CAMERA_ATTRIBUTES_STORAGE_H diff --git a/servers/rendering/storage/environment_storage.cpp b/servers/rendering/storage/environment_storage.cpp index 1d4dc55e98..19cb067025 100644 --- a/servers/rendering/storage/environment_storage.cpp +++ b/servers/rendering/storage/environment_storage.cpp @@ -30,8 +30,6 @@ #include "environment_storage.h" -uint64_t RendererEnvironmentStorage::auto_exposure_counter = 2; - RID RendererEnvironmentStorage::environment_allocate() { return environment_owner.allocate_rid(); } @@ -76,10 +74,11 @@ void RendererEnvironmentStorage::environment_set_bg_color(RID p_env, const Color env->bg_color = p_color; } -void RendererEnvironmentStorage::environment_set_bg_energy(RID p_env, float p_energy) { +void RendererEnvironmentStorage::environment_set_bg_energy(RID p_env, float p_multiplier, float p_intensity) { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_COND(!env); - env->bg_energy = p_energy; + env->bg_energy_multiplier = p_multiplier; + env->bg_intensity = p_intensity; } void RendererEnvironmentStorage::environment_set_canvas_max_layer(RID p_env, int p_max_layer) { @@ -128,10 +127,16 @@ Color RendererEnvironmentStorage::environment_get_bg_color(RID p_env) const { return env->bg_color; } -float RendererEnvironmentStorage::environment_get_bg_energy(RID p_env) const { +float RendererEnvironmentStorage::environment_get_bg_energy_multiplier(RID p_env) const { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_COND_V(!env, 1.0); - return env->bg_energy; + return env->bg_energy_multiplier; +} + +float RendererEnvironmentStorage::environment_get_bg_intensity(RID p_env) const { + Environment *env = environment_owner.get_or_null(p_env); + ERR_FAIL_COND_V(!env, 1.0); + return env->bg_intensity; } int RendererEnvironmentStorage::environment_get_canvas_max_layer(RID p_env) const { @@ -172,20 +177,12 @@ RS::EnvironmentReflectionSource RendererEnvironmentStorage::environment_get_refl // Tonemap -void RendererEnvironmentStorage::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) { +void RendererEnvironmentStorage::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) { Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_COND(!env); env->exposure = p_exposure; env->tone_mapper = p_tone_mapper; - if (!env->auto_exposure && p_auto_exposure) { - env->auto_exposure_version = ++auto_exposure_counter; - } - env->auto_exposure = p_auto_exposure; env->white = p_white; - env->min_luminance = p_min_luminance; - env->max_luminance = p_max_luminance; - env->auto_exp_speed = p_auto_exp_speed; - env->auto_exp_scale = p_auto_exp_scale; } RS::EnvironmentToneMapper RendererEnvironmentStorage::environment_get_tone_mapper(RID p_env) const { @@ -206,42 +203,6 @@ float RendererEnvironmentStorage::environment_get_white(RID p_env) const { return env->white; } -bool RendererEnvironmentStorage::environment_get_auto_exposure(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, false); - return env->auto_exposure; -} - -float RendererEnvironmentStorage::environment_get_min_luminance(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, 0.2); - return env->min_luminance; -} - -float RendererEnvironmentStorage::environment_get_max_luminance(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, 8.0); - return env->max_luminance; -} - -float RendererEnvironmentStorage::environment_get_auto_exp_speed(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, 0.2); - return env->auto_exp_speed; -} - -float RendererEnvironmentStorage::environment_get_auto_exp_scale(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, 0.5); - return env->auto_exp_scale; -} - -uint64_t RendererEnvironmentStorage::environment_get_auto_exposure_version(RID p_env) const { - Environment *env = environment_owner.get_or_null(p_env); - ERR_FAIL_COND_V(!env, 0); - return env->auto_exposure_version; -} - // Fog void RendererEnvironmentStorage::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_fog_aerial_perspective) { diff --git a/servers/rendering/storage/environment_storage.h b/servers/rendering/storage/environment_storage.h index 29bba86930..2821d299a8 100644 --- a/servers/rendering/storage/environment_storage.h +++ b/servers/rendering/storage/environment_storage.h @@ -46,7 +46,8 @@ private: float sky_custom_fov = 0.0; Basis sky_orientation; Color bg_color; - float bg_energy = 1.0; + float bg_energy_multiplier = 1.0; + float bg_intensity = 1.0; // Measured in nits or candela/m^2. Default to 1.0 so this doesn't impact rendering when Physical Light Units disabled. int canvas_max_layer = 0; RS::EnvironmentAmbientSource ambient_source = RS::ENV_AMBIENT_SOURCE_BG; Color ambient_light; @@ -58,12 +59,6 @@ private: RS::EnvironmentToneMapper tone_mapper; float exposure = 1.0; float white = 1.0; - bool auto_exposure = false; - float min_luminance = 0.2; - float max_luminance = 8.0; - float auto_exp_speed = 0.2; - float auto_exp_scale = 0.5; - uint64_t auto_exposure_version = 0; // Fog bool fog_enabled = false; @@ -149,8 +144,6 @@ private: RID color_correction = RID(); }; - static uint64_t auto_exposure_counter; - mutable RID_Owner<Environment, true> environment_owner; public: @@ -168,7 +161,7 @@ public: void environment_set_sky_custom_fov(RID p_env, float p_scale); void environment_set_sky_orientation(RID p_env, const Basis &p_orientation); void environment_set_bg_color(RID p_env, const Color &p_color); - void environment_set_bg_energy(RID p_env, float p_energy); + void environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value); void environment_set_canvas_max_layer(RID p_env, int p_max_layer); void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG); // FIXME: Disabled during Vulkan refactoring, should be ported. @@ -181,7 +174,8 @@ public: float environment_get_sky_custom_fov(RID p_env) const; Basis environment_get_sky_orientation(RID p_env) const; Color environment_get_bg_color(RID p_env) const; - float environment_get_bg_energy(RID p_env) const; + float environment_get_bg_energy_multiplier(RID p_env) const; + float environment_get_bg_intensity(RID p_env) const; int environment_get_canvas_max_layer(RID p_env) const; RS::EnvironmentAmbientSource environment_get_ambient_source(RID p_env) const; Color environment_get_ambient_light(RID p_env) const; @@ -190,16 +184,10 @@ public: RS::EnvironmentReflectionSource environment_get_reflection_source(RID p_env) const; // Tonemap - void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale); + void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white); RS::EnvironmentToneMapper environment_get_tone_mapper(RID p_env) const; float environment_get_exposure(RID p_env) const; float environment_get_white(RID p_env) const; - bool environment_get_auto_exposure(RID p_env) const; - float environment_get_min_luminance(RID p_env) const; - float environment_get_max_luminance(RID p_env) const; - float environment_get_auto_exp_speed(RID p_env) const; - float environment_get_auto_exp_scale(RID p_env) const; - uint64_t environment_get_auto_exposure_version(RID p_env) const; // Fog void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective); diff --git a/servers/rendering/storage/light_storage.h b/servers/rendering/storage/light_storage.h index 087ea1a025..b04bc671ee 100644 --- a/servers/rendering/storage/light_storage.h +++ b/servers/rendering/storage/light_storage.h @@ -124,6 +124,7 @@ public: virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) = 0; virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) = 0; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) = 0; + virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) = 0; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const = 0; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const = 0; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const = 0; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 3e850a9798..a840375393 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1898,6 +1898,7 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(LIGHT_PARAM_ENERGY); BIND_ENUM_CONSTANT(LIGHT_PARAM_INDIRECT_ENERGY); + BIND_ENUM_CONSTANT(LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY); BIND_ENUM_CONSTANT(LIGHT_PARAM_SPECULAR); BIND_ENUM_CONSTANT(LIGHT_PARAM_RANGE); BIND_ENUM_CONSTANT(LIGHT_PARAM_SIZE); @@ -1914,7 +1915,6 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_PANCAKE_SIZE); BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_OPACITY); BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_BLUR); - BIND_ENUM_CONSTANT(LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE); BIND_ENUM_CONSTANT(LIGHT_PARAM_TRANSMITTANCE_BIAS); BIND_ENUM_CONSTANT(LIGHT_PARAM_MAX); @@ -2016,6 +2016,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("voxel_gi_set_dynamic_range", "voxel_gi", "range"), &RenderingServer::voxel_gi_set_dynamic_range); ClassDB::bind_method(D_METHOD("voxel_gi_set_propagation", "voxel_gi", "amount"), &RenderingServer::voxel_gi_set_propagation); ClassDB::bind_method(D_METHOD("voxel_gi_set_energy", "voxel_gi", "energy"), &RenderingServer::voxel_gi_set_energy); + ClassDB::bind_method(D_METHOD("voxel_gi_set_baked_exposure_normalization", "voxel_gi", "baked_exposure"), &RenderingServer::voxel_gi_set_baked_exposure_normalization); ClassDB::bind_method(D_METHOD("voxel_gi_set_bias", "voxel_gi", "bias"), &RenderingServer::voxel_gi_set_bias); ClassDB::bind_method(D_METHOD("voxel_gi_set_normal_bias", "voxel_gi", "bias"), &RenderingServer::voxel_gi_set_normal_bias); ClassDB::bind_method(D_METHOD("voxel_gi_set_interior", "voxel_gi", "enable"), &RenderingServer::voxel_gi_set_interior); @@ -2037,6 +2038,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("lightmap_get_probe_capture_sh", "lightmap"), &RenderingServer::lightmap_get_probe_capture_sh); ClassDB::bind_method(D_METHOD("lightmap_get_probe_capture_tetrahedra", "lightmap"), &RenderingServer::lightmap_get_probe_capture_tetrahedra); ClassDB::bind_method(D_METHOD("lightmap_get_probe_capture_bsp_tree", "lightmap"), &RenderingServer::lightmap_get_probe_capture_bsp_tree); + ClassDB::bind_method(D_METHOD("lightmap_set_baked_exposure_normalization", "lightmap", "baked_exposure"), &RenderingServer::lightmap_set_baked_exposure_normalization); ClassDB::bind_method(D_METHOD("lightmap_set_probe_capture_update_speed", "speed"), &RenderingServer::lightmap_set_probe_capture_update_speed); @@ -2161,7 +2163,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("camera_set_transform", "camera", "transform"), &RenderingServer::camera_set_transform); ClassDB::bind_method(D_METHOD("camera_set_cull_mask", "camera", "layers"), &RenderingServer::camera_set_cull_mask); ClassDB::bind_method(D_METHOD("camera_set_environment", "camera", "env"), &RenderingServer::camera_set_environment); - ClassDB::bind_method(D_METHOD("camera_set_camera_effects", "camera", "effects"), &RenderingServer::camera_set_camera_effects); + ClassDB::bind_method(D_METHOD("camera_set_camera_attributes", "camera", "effects"), &RenderingServer::camera_set_camera_attributes); ClassDB::bind_method(D_METHOD("camera_set_use_vertical_aspect", "camera", "enable"), &RenderingServer::camera_set_use_vertical_aspect); /* VIEWPORT */ @@ -2325,11 +2327,11 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("environment_set_sky_custom_fov", "env", "scale"), &RenderingServer::environment_set_sky_custom_fov); ClassDB::bind_method(D_METHOD("environment_set_sky_orientation", "env", "orientation"), &RenderingServer::environment_set_sky_orientation); ClassDB::bind_method(D_METHOD("environment_set_bg_color", "env", "color"), &RenderingServer::environment_set_bg_color); - ClassDB::bind_method(D_METHOD("environment_set_bg_energy", "env", "energy"), &RenderingServer::environment_set_bg_energy); + ClassDB::bind_method(D_METHOD("environment_set_bg_energy", "env", "multiplier", "exposure_value"), &RenderingServer::environment_set_bg_energy); ClassDB::bind_method(D_METHOD("environment_set_canvas_max_layer", "env", "max_layer"), &RenderingServer::environment_set_canvas_max_layer); ClassDB::bind_method(D_METHOD("environment_set_ambient_light", "env", "color", "ambient", "energy", "sky_contibution", "reflection_source"), &RenderingServer::environment_set_ambient_light, DEFVAL(RS::ENV_AMBIENT_SOURCE_BG), DEFVAL(1.0), DEFVAL(0.0), DEFVAL(RS::ENV_REFLECTION_SOURCE_BG)); ClassDB::bind_method(D_METHOD("environment_set_glow", "env", "enable", "levels", "intensity", "strength", "mix", "bloom_threshold", "blend_mode", "hdr_bleed_threshold", "hdr_bleed_scale", "hdr_luminance_cap", "glow_map_strength", "glow_map"), &RenderingServer::environment_set_glow); - ClassDB::bind_method(D_METHOD("environment_set_tonemap", "env", "tone_mapper", "exposure", "white", "auto_exposure", "min_luminance", "max_luminance", "auto_exp_speed", "auto_exp_grey"), &RenderingServer::environment_set_tonemap); + ClassDB::bind_method(D_METHOD("environment_set_tonemap", "env", "tone_mapper", "exposure", "white"), &RenderingServer::environment_set_tonemap); ClassDB::bind_method(D_METHOD("environment_set_adjustment", "env", "enable", "brightness", "contrast", "saturation", "use_1d_color_correction", "color_correction"), &RenderingServer::environment_set_adjustment); ClassDB::bind_method(D_METHOD("environment_set_ssr", "env", "enable", "max_steps", "fade_in", "fade_out", "depth_tolerance"), &RenderingServer::environment_set_ssr); ClassDB::bind_method(D_METHOD("environment_set_ssao", "env", "enable", "radius", "intensity", "power", "detail", "horizon", "sharpness", "light_affect", "ao_channel_affect"), &RenderingServer::environment_set_ssao); @@ -2434,13 +2436,14 @@ void RenderingServer::_bind_methods() { /* CAMERA EFFECTS */ - ClassDB::bind_method(D_METHOD("camera_effects_create"), &RenderingServer::camera_effects_create); + ClassDB::bind_method(D_METHOD("camera_attributes_create"), &RenderingServer::camera_attributes_create); - ClassDB::bind_method(D_METHOD("camera_effects_set_dof_blur_quality", "quality", "use_jitter"), &RenderingServer::camera_effects_set_dof_blur_quality); - ClassDB::bind_method(D_METHOD("camera_effects_set_dof_blur_bokeh_shape", "shape"), &RenderingServer::camera_effects_set_dof_blur_bokeh_shape); + ClassDB::bind_method(D_METHOD("camera_attributes_set_dof_blur_quality", "quality", "use_jitter"), &RenderingServer::camera_attributes_set_dof_blur_quality); + ClassDB::bind_method(D_METHOD("camera_attributes_set_dof_blur_bokeh_shape", "shape"), &RenderingServer::camera_attributes_set_dof_blur_bokeh_shape); - ClassDB::bind_method(D_METHOD("camera_effects_set_dof_blur", "camera_effects", "far_enable", "far_distance", "far_transition", "near_enable", "near_distance", "near_transition", "amount"), &RenderingServer::camera_effects_set_dof_blur); - ClassDB::bind_method(D_METHOD("camera_effects_set_custom_exposure", "camera_effects", "enable", "exposure"), &RenderingServer::camera_effects_set_custom_exposure); + ClassDB::bind_method(D_METHOD("camera_attributes_set_dof_blur", "camera_attributes", "far_enable", "far_distance", "far_transition", "near_enable", "near_distance", "near_transition", "amount"), &RenderingServer::camera_attributes_set_dof_blur); + ClassDB::bind_method(D_METHOD("camera_attributes_set_exposure", "camera_attributes", "multiplier", "normalization"), &RenderingServer::camera_attributes_set_exposure); + ClassDB::bind_method(D_METHOD("camera_attributes_set_auto_exposure", "camera_attributes", "enable", "min_sensitivity", "max_sensitivity", "speed", "scale"), &RenderingServer::camera_attributes_set_auto_exposure); BIND_ENUM_CONSTANT(DOF_BOKEH_BOX); BIND_ENUM_CONSTANT(DOF_BOKEH_HEXAGON); @@ -2456,7 +2459,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("scenario_create"), &RenderingServer::scenario_create); ClassDB::bind_method(D_METHOD("scenario_set_environment", "scenario", "environment"), &RenderingServer::scenario_set_environment); ClassDB::bind_method(D_METHOD("scenario_set_fallback_environment", "scenario", "environment"), &RenderingServer::scenario_set_fallback_environment); - ClassDB::bind_method(D_METHOD("scenario_set_camera_effects", "scenario", "effects"), &RenderingServer::scenario_set_camera_effects); + ClassDB::bind_method(D_METHOD("scenario_set_camera_attributes", "scenario", "effects"), &RenderingServer::scenario_set_camera_attributes); /* INSTANCE */ @@ -2842,17 +2845,19 @@ void RenderingServer::init() { GLOBAL_DEF("rendering/limits/time/time_rollover_secs", 3600); ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/time/time_rollover_secs", PropertyInfo(Variant::FLOAT, "rendering/limits/time/time_rollover_secs", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); - GLOBAL_DEF("rendering/shadows/directional_shadow/size", 4096); - GLOBAL_DEF("rendering/shadows/directional_shadow/size.mobile", 2048); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/directional_shadow/size", PropertyInfo(Variant::INT, "rendering/shadows/directional_shadow/size", PROPERTY_HINT_RANGE, "256,16384")); - GLOBAL_DEF("rendering/shadows/directional_shadow/soft_shadow_filter_quality", 2); - GLOBAL_DEF("rendering/shadows/directional_shadow/soft_shadow_filter_quality.mobile", 0); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/directional_shadow/soft_shadow_filter_quality", PropertyInfo(Variant::INT, "rendering/shadows/directional_shadow/soft_shadow_filter_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Very Low (Faster),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)")); - GLOBAL_DEF("rendering/shadows/directional_shadow/16_bits", true); - - GLOBAL_DEF("rendering/shadows/positional_shadow/soft_shadow_filter_quality", 2); - GLOBAL_DEF("rendering/shadows/positional_shadow/soft_shadow_filter_quality.mobile", 0); - ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/positional_shadow/soft_shadow_filter_quality", PropertyInfo(Variant::INT, "rendering/shadows/positional_shadow/soft_shadow_filter_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Very Low (Faster),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)")); + GLOBAL_DEF_RST("rendering/lights_and_shadows/use_physical_light_units", false); + + GLOBAL_DEF("rendering/lights_and_shadows/directional_shadow/size", 4096); + GLOBAL_DEF("rendering/lights_and_shadows/directional_shadow/size.mobile", 2048); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/directional_shadow/size", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/directional_shadow/size", PROPERTY_HINT_RANGE, "256,16384")); + GLOBAL_DEF("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality", 2); + GLOBAL_DEF("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality.mobile", 0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Very Low (Faster),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)")); + GLOBAL_DEF("rendering/lights_and_shadows/directional_shadow/16_bits", true); + + GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality", 2); + GLOBAL_DEF("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality.mobile", 0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality", PropertyInfo(Variant::INT, "rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Very Low (Faster),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)")); GLOBAL_DEF("rendering/2d/shadow_atlas/size", 2048); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index efa8a24325..8cefad7cb5 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -416,6 +416,7 @@ public: enum LightParam { LIGHT_PARAM_ENERGY, LIGHT_PARAM_INDIRECT_ENERGY, + LIGHT_PARAM_VOLUMETRIC_FOG_ENERGY, LIGHT_PARAM_SPECULAR, LIGHT_PARAM_RANGE, LIGHT_PARAM_SIZE, @@ -432,8 +433,8 @@ public: LIGHT_PARAM_SHADOW_PANCAKE_SIZE, LIGHT_PARAM_SHADOW_OPACITY, LIGHT_PARAM_SHADOW_BLUR, - LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE, LIGHT_PARAM_TRANSMITTANCE_BIAS, + LIGHT_PARAM_INTENSITY, LIGHT_PARAM_MAX }; @@ -590,6 +591,7 @@ public: virtual void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) = 0; virtual void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) = 0; virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_energy) = 0; + virtual void voxel_gi_set_baked_exposure_normalization(RID p_voxel_gi, float p_baked_exposure) = 0; virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_bias) = 0; virtual void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) = 0; virtual void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) = 0; @@ -610,6 +612,7 @@ public: virtual void lightmap_set_probe_bounds(RID p_lightmap, const AABB &p_bounds) = 0; virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) = 0; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) = 0; + virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) = 0; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const = 0; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const = 0; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const = 0; @@ -762,7 +765,7 @@ public: virtual void camera_set_transform(RID p_camera, const Transform3D &p_transform) = 0; virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers) = 0; virtual void camera_set_environment(RID p_camera, RID p_env) = 0; - virtual void camera_set_camera_effects(RID p_camera, RID p_camera_effects) = 0; + virtual void camera_set_camera_attributes(RID p_camera, RID p_camera_attributes) = 0; virtual void camera_set_use_vertical_aspect(RID p_camera, bool p_enable) = 0; /* VIEWPORT API */ @@ -1011,7 +1014,7 @@ public: virtual void environment_set_sky_custom_fov(RID p_env, float p_scale) = 0; virtual void environment_set_sky_orientation(RID p_env, const Basis &p_orientation) = 0; virtual void environment_set_bg_color(RID p_env, const Color &p_color) = 0; - virtual void environment_set_bg_energy(RID p_env, float p_energy) = 0; + virtual void environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value) = 0; virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer) = 0; virtual void environment_set_ambient_light(RID p_env, const Color &p_color, EnvironmentAmbientSource p_ambient = ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, EnvironmentReflectionSource p_reflection_source = ENV_REFLECTION_SOURCE_BG) = 0; @@ -1035,7 +1038,7 @@ public: ENV_TONE_MAPPER_ACES }; - virtual void environment_set_tonemap(RID p_env, EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_grey) = 0; + virtual void environment_set_tonemap(RID p_env, EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) = 0; virtual void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, bool p_use_1d_color_correction, RID p_color_correction) = 0; virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance) = 0; @@ -1139,7 +1142,7 @@ public: /* CAMERA EFFECTS */ - virtual RID camera_effects_create() = 0; + virtual RID camera_attributes_create() = 0; enum DOFBlurQuality { DOF_BLUR_QUALITY_VERY_LOW, @@ -1148,7 +1151,7 @@ public: DOF_BLUR_QUALITY_HIGH, }; - virtual void camera_effects_set_dof_blur_quality(DOFBlurQuality p_quality, bool p_use_jitter) = 0; + virtual void camera_attributes_set_dof_blur_quality(DOFBlurQuality p_quality, bool p_use_jitter) = 0; enum DOFBokehShape { DOF_BOKEH_BOX, @@ -1156,10 +1159,11 @@ public: DOF_BOKEH_CIRCLE }; - virtual void camera_effects_set_dof_blur_bokeh_shape(DOFBokehShape p_shape) = 0; + virtual void camera_attributes_set_dof_blur_bokeh_shape(DOFBokehShape p_shape) = 0; - virtual void camera_effects_set_dof_blur(RID p_camera_effects, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) = 0; - virtual void camera_effects_set_custom_exposure(RID p_camera_effects, bool p_enable, float p_exposure) = 0; + virtual void camera_attributes_set_dof_blur(RID p_camera_attributes, bool p_far_enable, float p_far_distance, float p_far_transition, bool p_near_enable, float p_near_distance, float p_near_transition, float p_amount) = 0; + virtual void camera_attributes_set_exposure(RID p_camera_attributes, float p_multiplier, float p_exposure_normalization) = 0; + virtual void camera_attributes_set_auto_exposure(RID p_camera_attributes, bool p_enable, float p_min_sensitivity, float p_max_sensitivity, float p_speed, float p_scale) = 0; /* SCENARIO API */ @@ -1167,7 +1171,7 @@ public: virtual void scenario_set_environment(RID p_scenario, RID p_environment) = 0; virtual void scenario_set_fallback_environment(RID p_scenario, RID p_environment) = 0; - virtual void scenario_set_camera_effects(RID p_scenario, RID p_camera_effects) = 0; + virtual void scenario_set_camera_attributes(RID p_scenario, RID p_camera_attributes) = 0; /* INSTANCING API */ diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 393160fe9e..9df74b1b20 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -498,6 +498,7 @@ void TextServer::_bind_methods() { BIND_BITFIELD_FLAG(BREAK_WORD_BOUND); BIND_BITFIELD_FLAG(BREAK_GRAPHEME_BOUND); BIND_BITFIELD_FLAG(BREAK_ADAPTIVE); + BIND_BITFIELD_FLAG(BREAK_TRIM_EDGE_SPACES); /* VisibleCharactersBehavior */ BIND_ENUM_CONSTANT(VC_CHARS_BEFORE_SHAPING); @@ -680,6 +681,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped real_t width = 0.f; int line_start = MAX(p_start, range.x); + int prev_safe_break = 0; int last_safe_break = -1; int chunk = 0; @@ -688,13 +690,29 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped for (int i = 0; i < l_size; i++) { if (l_gl[i].start < p_start) { + prev_safe_break = i + 1; continue; } if (l_gl[i].count > 0) { if ((p_width[chunk] > 0) && (width + l_gl[i].advance > p_width[chunk]) && (last_safe_break >= 0)) { - lines.push_back(line_start); - lines.push_back(l_gl[last_safe_break].end); + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = prev_safe_break; + int end_pos = last_safe_break; + + while ((start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + } else { + lines.push_back(line_start); + lines.push_back(l_gl[last_safe_break].end); + } line_start = l_gl[last_safe_break].end; + prev_safe_break = last_safe_break + 1; i = last_safe_break; last_safe_break = -1; width = 0; @@ -709,9 +727,24 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped } if (p_break_flags.has_flag(BREAK_MANDATORY)) { if ((l_gl[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) { - lines.push_back(line_start); - lines.push_back(l_gl[i].end); + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = prev_safe_break; + int end_pos = i; + + while ((start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + } else { + lines.push_back(line_start); + lines.push_back(l_gl[i].end); + } line_start = l_gl[i].end; + prev_safe_break = i + 1; last_safe_break = -1; width = 0; chunk = 0; @@ -734,9 +767,23 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped } if (l_size > 0) { - if (lines.size() == 0 || lines[lines.size() - 1] < range.y) { - lines.push_back(line_start); - lines.push_back(range.y); + if (lines.size() == 0 || (lines[lines.size() - 1] < range.y && prev_safe_break < l_size)) { + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = (prev_safe_break < l_size) ? prev_safe_break : l_size - 1; + int end_pos = l_size - 1; + + while ((start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + } else { + lines.push_back(line_start); + lines.push_back(range.y); + } } } else { lines.push_back(0); @@ -754,6 +801,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do double width = 0.f; int line_start = MAX(p_start, range.x); + int prev_safe_break = 0; int last_safe_break = -1; int word_count = 0; @@ -762,13 +810,30 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do for (int i = 0; i < l_size; i++) { if (l_gl[i].start < p_start) { + prev_safe_break = i + 1; continue; } if (l_gl[i].count > 0) { if ((p_width > 0) && (width + l_gl[i].advance * l_gl[i].repeat > p_width) && (last_safe_break >= 0)) { - lines.push_back(line_start); - lines.push_back(l_gl[last_safe_break].end); + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = prev_safe_break; + int end_pos = last_safe_break; + + while ((start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + //printf("%s", vformat("BRK TRIM(W): %d..%d -> %d..%d\n", line_start, l_gl[last_safe_break].end, l_gl[start_pos].start, l_gl[end_pos].end).utf8().get_data()); + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + } else { + lines.push_back(line_start); + lines.push_back(l_gl[last_safe_break].end); + } line_start = l_gl[last_safe_break].end; + prev_safe_break = last_safe_break + 1; i = last_safe_break; last_safe_break = -1; width = 0; @@ -777,9 +842,25 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do } if (p_break_flags.has_flag(BREAK_MANDATORY)) { if ((l_gl[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) { - lines.push_back(line_start); - lines.push_back(l_gl[i].end); + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = prev_safe_break; + int end_pos = i; + + while ((start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + //printf("%s", vformat("BRK TRIM(M): %d..%d -> %d..%d\n", line_start, l_gl[i].end, l_gl[start_pos].start, l_gl[end_pos].end).utf8().get_data()); + } else { + lines.push_back(line_start); + lines.push_back(l_gl[i].end); + } line_start = l_gl[i].end; + prev_safe_break = i + 1; last_safe_break = -1; width = 0; continue; @@ -802,9 +883,23 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do } if (l_size > 0) { - if (lines.size() == 0 || lines[lines.size() - 1] < range.y) { - lines.push_back(line_start); - lines.push_back(range.y); + if (lines.size() == 0 || (lines[lines.size() - 1] < range.y && prev_safe_break < l_size)) { + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = (prev_safe_break < l_size) ? prev_safe_break : l_size - 1; + int end_pos = l_size - 1; + + while ((start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + } else { + lines.push_back(line_start); + lines.push_back(range.y); + } } } else { lines.push_back(0); diff --git a/servers/text_server.h b/servers/text_server.h index 9304771d1b..e8945dd807 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -103,6 +103,7 @@ public: BREAK_WORD_BOUND = 1 << 1, BREAK_GRAPHEME_BOUND = 1 << 2, BREAK_ADAPTIVE = 1 << 3, + BREAK_TRIM_EDGE_SPACES = 1 << 4, }; enum OverrunBehavior { diff --git a/tests/scene/test_curve.h b/tests/scene/test_curve.h index 0370ab15fd..ad7625ddc5 100644 --- a/tests/scene/test_curve.h +++ b/tests/scene/test_curve.h @@ -44,13 +44,13 @@ TEST_CASE("[Curve] Default curve") { curve->get_point_count() == 0, "Default curve should contain the expected number of points."); CHECK_MESSAGE( - Math::is_zero_approx(curve->interpolate(0)), + Math::is_zero_approx(curve->sample(0)), "Default curve should return the expected value at offset 0.0."); CHECK_MESSAGE( - Math::is_zero_approx(curve->interpolate(0.5)), + Math::is_zero_approx(curve->sample(0.5)), "Default curve should return the expected value at offset 0.5."); CHECK_MESSAGE( - Math::is_zero_approx(curve->interpolate(1)), + Math::is_zero_approx(curve->sample(1)), "Default curve should return the expected value at offset 1.0."); } @@ -80,57 +80,57 @@ TEST_CASE("[Curve] Custom curve with free tangents") { "Custom free curve should contain the expected number of points."); CHECK_MESSAGE( - Math::is_zero_approx(curve->interpolate(-0.1)), + Math::is_zero_approx(curve->sample(-0.1)), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(0.1), (real_t)0.352), + Math::is_equal_approx(curve->sample(0.1), (real_t)0.352), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(0.4), (real_t)0.352), + Math::is_equal_approx(curve->sample(0.4), (real_t)0.352), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(0.7), (real_t)0.896), + Math::is_equal_approx(curve->sample(0.7), (real_t)0.896), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(1), 1), + Math::is_equal_approx(curve->sample(1), 1), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(2), 1), + Math::is_equal_approx(curve->sample(2), 1), "Custom free curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_zero_approx(curve->interpolate_baked(-0.1)), + Math::is_zero_approx(curve->sample_baked(-0.1)), "Custom free curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(0.1), (real_t)0.352), + Math::is_equal_approx(curve->sample_baked(0.1), (real_t)0.352), "Custom free curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(0.4), (real_t)0.352), + Math::is_equal_approx(curve->sample_baked(0.4), (real_t)0.352), "Custom free curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(0.7), (real_t)0.896), + Math::is_equal_approx(curve->sample_baked(0.7), (real_t)0.896), "Custom free curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(1), 1), + Math::is_equal_approx(curve->sample_baked(1), 1), "Custom free curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(2), 1), + Math::is_equal_approx(curve->sample_baked(2), 1), "Custom free curve should return the expected baked value at offset 0.1."); curve->remove_point(1); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(0.1), 0), + Math::is_equal_approx(curve->sample(0.1), 0), "Custom free curve should return the expected value at offset 0.1 after removing point at index 1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(0.1), 0), + Math::is_equal_approx(curve->sample_baked(0.1), 0), "Custom free curve should return the expected baked value at offset 0.1 after removing point at index 1."); curve->clear_points(); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(0.6), 0), + Math::is_equal_approx(curve->sample(0.6), 0), "Custom free curve should return the expected value at offset 0.6 after clearing all points."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(0.6), 0), + Math::is_equal_approx(curve->sample_baked(0.6), 0), "Custom free curve should return the expected baked value at offset 0.6 after clearing all points."); } @@ -169,51 +169,51 @@ TEST_CASE("[Curve] Custom curve with linear tangents") { "Custom linear curve should contain the expected number of points."); CHECK_MESSAGE( - Math::is_zero_approx(curve->interpolate(-0.1)), + Math::is_zero_approx(curve->sample(-0.1)), "Custom linear curve should return the expected value at offset -0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(0.1), (real_t)0.4), + Math::is_equal_approx(curve->sample(0.1), (real_t)0.4), "Custom linear curve should return the expected value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(0.4), (real_t)0.4), + Math::is_equal_approx(curve->sample(0.4), (real_t)0.4), "Custom linear curve should return the expected value at offset 0.4."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(0.7), (real_t)0.8), + Math::is_equal_approx(curve->sample(0.7), (real_t)0.8), "Custom linear curve should return the expected value at offset 0.7."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(1), 1), + Math::is_equal_approx(curve->sample(1), 1), "Custom linear curve should return the expected value at offset 1.0."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(2), 1), + Math::is_equal_approx(curve->sample(2), 1), "Custom linear curve should return the expected value at offset 2.0."); CHECK_MESSAGE( - Math::is_zero_approx(curve->interpolate_baked(-0.1)), + Math::is_zero_approx(curve->sample_baked(-0.1)), "Custom linear curve should return the expected baked value at offset -0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(0.1), (real_t)0.4), + Math::is_equal_approx(curve->sample_baked(0.1), (real_t)0.4), "Custom linear curve should return the expected baked value at offset 0.1."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(0.4), (real_t)0.4), + Math::is_equal_approx(curve->sample_baked(0.4), (real_t)0.4), "Custom linear curve should return the expected baked value at offset 0.4."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(0.7), (real_t)0.8), + Math::is_equal_approx(curve->sample_baked(0.7), (real_t)0.8), "Custom linear curve should return the expected baked value at offset 0.7."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(1), 1), + Math::is_equal_approx(curve->sample_baked(1), 1), "Custom linear curve should return the expected baked value at offset 1.0."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(2), 1), + Math::is_equal_approx(curve->sample_baked(2), 1), "Custom linear curve should return the expected baked value at offset 2.0."); ERR_PRINT_OFF; curve->remove_point(10); ERR_PRINT_ON; CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate(0.7), (real_t)0.8), + Math::is_equal_approx(curve->sample(0.7), (real_t)0.8), "Custom free curve should return the expected value at offset 0.7 after removing point at invalid index 10."); CHECK_MESSAGE( - Math::is_equal_approx(curve->interpolate_baked(0.7), (real_t)0.8), + Math::is_equal_approx(curve->sample_baked(0.7), (real_t)0.8), "Custom free curve should return the expected baked value at offset 0.7 after removing point at invalid index 10."); } @@ -228,8 +228,8 @@ TEST_CASE("[Curve2D] Linear sampling should return exact value") { CHECK(len == baked_length); for (int i = 0; i < len; i++) { - Vector2 pos = curve->interpolate_baked(i); - CHECK_MESSAGE(pos.x == i, "interpolate_baked should return exact value"); + Vector2 pos = curve->sample_baked(i); + CHECK_MESSAGE(pos.x == i, "sample_baked should return exact value"); } } @@ -244,8 +244,8 @@ TEST_CASE("[Curve3D] Linear sampling should return exact value") { CHECK(len == baked_length); for (int i = 0; i < len; i++) { - Vector3 pos = curve->interpolate_baked(i); - CHECK_MESSAGE(pos.x == i, "interpolate_baked should return exact value"); + Vector3 pos = curve->sample_baked(i); + CHECK_MESSAGE(pos.x == i, "sample_baked should return exact value"); } } |