mirror of https://github.com/godotengine/godot
Merge branch 'master' of https://github.com/godotengine/godot into feat-cam-mjpeg
This commit is contained in:
commit
3010c78b18
|
|
@ -222,6 +222,23 @@ uint64_t OS::get_embedded_pck_offset() const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default boot screen rect scale mode is "Keep Aspect Centered"
|
||||||
|
Rect2 OS::calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const {
|
||||||
|
Rect2 screenrect;
|
||||||
|
if (p_window_size.width > p_window_size.height) {
|
||||||
|
// Scale horizontally.
|
||||||
|
screenrect.size.y = p_window_size.height;
|
||||||
|
screenrect.size.x = p_imgrect_size.x * p_window_size.height / p_imgrect_size.y;
|
||||||
|
screenrect.position.x = (p_window_size.width - screenrect.size.x) / 2;
|
||||||
|
} else {
|
||||||
|
// Scale vertically.
|
||||||
|
screenrect.size.x = p_window_size.width;
|
||||||
|
screenrect.size.y = p_imgrect_size.y * p_window_size.width / p_imgrect_size.x;
|
||||||
|
screenrect.position.y = (p_window_size.height - screenrect.size.y) / 2;
|
||||||
|
}
|
||||||
|
return screenrect;
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to ensure that a dir name/path will be valid on the OS
|
// Helper function to ensure that a dir name/path will be valid on the OS
|
||||||
String OS::get_safe_dir_name(const String &p_dir_name, bool p_allow_paths) const {
|
String OS::get_safe_dir_name(const String &p_dir_name, bool p_allow_paths) const {
|
||||||
String safe_dir_name = p_dir_name;
|
String safe_dir_name = p_dir_name;
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,8 @@ public:
|
||||||
virtual void open_midi_inputs();
|
virtual void open_midi_inputs();
|
||||||
virtual void close_midi_inputs();
|
virtual void close_midi_inputs();
|
||||||
|
|
||||||
|
virtual Rect2 calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const;
|
||||||
|
|
||||||
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
|
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
|
||||||
|
|
||||||
struct GDExtensionData {
|
struct GDExtensionData {
|
||||||
|
|
|
||||||
|
|
@ -299,12 +299,12 @@ String TranslationServer::get_locale_name(const String &p_locale) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = language_map[lang_name];
|
String name = get_language_name(lang_name);
|
||||||
if (!script_name.is_empty()) {
|
if (!script_name.is_empty()) {
|
||||||
name = name + " (" + script_map[script_name] + ")";
|
name = name + " (" + get_script_name(script_name) + ")";
|
||||||
}
|
}
|
||||||
if (!country_name.is_empty()) {
|
if (!country_name.is_empty()) {
|
||||||
name = name + ", " + country_name_map[country_name];
|
name = name + ", " + get_country_name(country_name);
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
@ -320,7 +320,11 @@ Vector<String> TranslationServer::get_all_languages() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
String TranslationServer::get_language_name(const String &p_language) const {
|
String TranslationServer::get_language_name(const String &p_language) const {
|
||||||
return language_map[p_language];
|
if (language_map.has(p_language)) {
|
||||||
|
return language_map[p_language];
|
||||||
|
} else {
|
||||||
|
return p_language;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<String> TranslationServer::get_all_scripts() const {
|
Vector<String> TranslationServer::get_all_scripts() const {
|
||||||
|
|
@ -334,7 +338,11 @@ Vector<String> TranslationServer::get_all_scripts() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
String TranslationServer::get_script_name(const String &p_script) const {
|
String TranslationServer::get_script_name(const String &p_script) const {
|
||||||
return script_map[p_script];
|
if (script_map.has(p_script)) {
|
||||||
|
return script_map[p_script];
|
||||||
|
} else {
|
||||||
|
return p_script;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<String> TranslationServer::get_all_countries() const {
|
Vector<String> TranslationServer::get_all_countries() const {
|
||||||
|
|
@ -348,7 +356,11 @@ Vector<String> TranslationServer::get_all_countries() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
String TranslationServer::get_country_name(const String &p_country) const {
|
String TranslationServer::get_country_name(const String &p_country) const {
|
||||||
return country_name_map[p_country];
|
if (country_name_map.has(p_country)) {
|
||||||
|
return country_name_map[p_country];
|
||||||
|
} else {
|
||||||
|
return p_country;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TranslationServer::set_locale(const String &p_locale) {
|
void TranslationServer::set_locale(const String &p_locale) {
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@
|
||||||
</member>
|
</member>
|
||||||
<member name="grow" type="bool" setter="set_grow_enabled" getter="is_grow_enabled" default="false">
|
<member name="grow" type="bool" setter="set_grow_enabled" getter="is_grow_enabled" default="false">
|
||||||
If [code]true[/code], enables the vertex grow setting. This can be used to create mesh-based outlines using a second material pass and its [member cull_mode] set to [constant CULL_FRONT]. See also [member grow_amount].
|
If [code]true[/code], enables the vertex grow setting. This can be used to create mesh-based outlines using a second material pass and its [member cull_mode] set to [constant CULL_FRONT]. See also [member grow_amount].
|
||||||
[b]Note:[/b] Vertex growth cannot create new vertices, which means that visible gaps may occur in sharp corners. This can be alleviated by designing the mesh to use smooth normals exclusively using [url=https://wiki.polycount.com/wiki/Face_weighted_normals]face weighted normals[/url] in the 3D authoring software. In this case, grow will be able to join every outline together, just like in the original mesh.
|
[b]Note:[/b] Vertex growth cannot create new vertices, which means that visible gaps may occur in sharp corners. This can be alleviated by designing the mesh to use smooth normals exclusively using [url=http://wiki.polycount.com/wiki/Face_weighted_normals]face weighted normals[/url] in the 3D authoring software. In this case, grow will be able to join every outline together, just like in the original mesh.
|
||||||
</member>
|
</member>
|
||||||
<member name="grow_amount" type="float" setter="set_grow" getter="get_grow" default="0.0">
|
<member name="grow_amount" type="float" setter="set_grow" getter="get_grow" default="0.0">
|
||||||
Grows object vertices in the direction of their normals. Only effective if [member grow] is [code]true[/code].
|
Grows object vertices in the direction of their normals. Only effective if [member grow] is [code]true[/code].
|
||||||
|
|
|
||||||
|
|
@ -1372,7 +1372,7 @@
|
||||||
Automatic layout direction, determined from the parent control layout direction.
|
Automatic layout direction, determined from the parent control layout direction.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="LAYOUT_DIRECTION_APPLICATION_LOCALE" value="1" enum="LayoutDirection">
|
<constant name="LAYOUT_DIRECTION_APPLICATION_LOCALE" value="1" enum="LayoutDirection">
|
||||||
Automatic layout direction, determined from the current locale. Right-to-left layout direction is automatically used for languages that require it such as Arabic and Hebrew., but only if a valid translation file is loaded for the given language. For all other languages (or if no valid translation file is found by Godot), left-to-right layout direction is used. If using [TextServerFallback] ([member ProjectSettings.internationalization/rendering/text_driver]), left-to-right layout direction is always used regardless of the language.
|
Automatic layout direction, determined from the current locale. Right-to-left layout direction is automatically used for languages that require it such as Arabic and Hebrew, but only if a valid translation file is loaded for the given language (unless said language is configured as a fallback in [member ProjectSettings.internationalization/locale/fallback]). For all other languages (or if no valid translation file is found by Godot), left-to-right layout direction is used. If using [TextServerFallback] ([member ProjectSettings.internationalization/rendering/text_driver]), left-to-right layout direction is always used regardless of the language. Right-to-left layout direction can also be forced using [member ProjectSettings.internationalization/rendering/force_right_to_left_layout_direction].
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="LAYOUT_DIRECTION_LTR" value="2" enum="LayoutDirection">
|
<constant name="LAYOUT_DIRECTION_LTR" value="2" enum="LayoutDirection">
|
||||||
Left-to-right layout direction.
|
Left-to-right layout direction.
|
||||||
|
|
|
||||||
|
|
@ -314,13 +314,14 @@
|
||||||
The maximum number of steps for screen-space reflections. Higher values are slower.
|
The maximum number of steps for screen-space reflections. Higher values are slower.
|
||||||
</member>
|
</member>
|
||||||
<member name="tonemap_exposure" type="float" setter="set_tonemap_exposure" getter="get_tonemap_exposure" default="1.0">
|
<member name="tonemap_exposure" type="float" setter="set_tonemap_exposure" getter="get_tonemap_exposure" default="1.0">
|
||||||
The default exposure used for tonemapping. Higher values result in a brighter image. See also [member tonemap_white].
|
Adjusts the brightness of values before they are provided to the tonemapper. Higher [member tonemap_exposure] values result in a brighter image. See also [member tonemap_white].
|
||||||
|
[b]Note:[/b] Values provided to the tonemapper will also be multiplied by [code]2.0[/code] and [code]1.8[/code] for [constant TONE_MAPPER_FILMIC] and [constant TONE_MAPPER_ACES] respectively to produce a similar apparent brightness as [constant TONE_MAPPER_LINEAR].
|
||||||
</member>
|
</member>
|
||||||
<member name="tonemap_mode" type="int" setter="set_tonemapper" getter="get_tonemapper" enum="Environment.ToneMapper" default="0">
|
<member name="tonemap_mode" type="int" setter="set_tonemapper" getter="get_tonemapper" enum="Environment.ToneMapper" default="0">
|
||||||
The tonemapping mode to use. Tonemapping is the process that "converts" HDR values to be suitable for rendering on an LDR display. (Godot doesn't support rendering on HDR displays yet.)
|
The tonemapping mode to use. Tonemapping is the process that "converts" HDR values to be suitable for rendering on an LDR display. (Godot doesn't support rendering on HDR displays yet.)
|
||||||
</member>
|
</member>
|
||||||
<member name="tonemap_white" type="float" setter="set_tonemap_white" getter="get_tonemap_white" default="1.0">
|
<member name="tonemap_white" type="float" setter="set_tonemap_white" getter="get_tonemap_white" default="1.0">
|
||||||
The white reference value for tonemapping (also called "whitepoint"). Higher values can make highlights look less blown out, and will also slightly darken the whole scene as a result. See also [member tonemap_exposure].
|
The white reference value for tonemapping, which indicates where bright white is located in the scale of values provided to the tonemapper. For photorealistic lighting, recommended values are between [code]6.0[/code] and [code]8.0[/code]. Higher values result in less blown out highlights, but may make the scene appear lower contrast. See also [member tonemap_exposure].
|
||||||
[b]Note:[/b] [member tonemap_white] is ignored when using [constant TONE_MAPPER_LINEAR] or [constant TONE_MAPPER_AGX].
|
[b]Note:[/b] [member tonemap_white] is ignored when using [constant TONE_MAPPER_LINEAR] or [constant TONE_MAPPER_AGX].
|
||||||
</member>
|
</member>
|
||||||
<member name="volumetric_fog_albedo" type="Color" setter="set_volumetric_fog_albedo" getter="get_volumetric_fog_albedo" default="Color(1, 1, 1, 1)">
|
<member name="volumetric_fog_albedo" type="Color" setter="set_volumetric_fog_albedo" getter="get_volumetric_fog_albedo" default="Color(1, 1, 1, 1)">
|
||||||
|
|
@ -414,20 +415,22 @@
|
||||||
Use the [Sky] for reflections regardless of what the background is.
|
Use the [Sky] for reflections regardless of what the background is.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="TONE_MAPPER_LINEAR" value="0" enum="ToneMapper">
|
<constant name="TONE_MAPPER_LINEAR" value="0" enum="ToneMapper">
|
||||||
Linear tonemapper operator. Reads the linear data and passes it on unmodified. This can cause bright lighting to look blown out, with noticeable clipping in the output colors.
|
Does not modify color data, resulting in a linear tonemapping curve which unnaturally clips bright values, causing bright lighting to look blown out. The simplest and fastest tonemapper.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="TONE_MAPPER_REINHARDT" value="1" enum="ToneMapper">
|
<constant name="TONE_MAPPER_REINHARDT" value="1" enum="ToneMapper">
|
||||||
Reinhard tonemapper operator. Performs a variation on rendered pixels' colors by this formula: [code]color = color * (1 + color / (white * white)) / (1 + color)[/code]. This avoids clipping bright highlights, but the resulting image can look a bit dull. When [member tonemap_white] is left at the default value of [code]1.0[/code] this is identical to [constant TONE_MAPPER_LINEAR] while also being slightly less performant.
|
A simple tonemapping curve that rolls off bright values to prevent clipping. This results in an image that can appear dull and low contrast. Slower than [constant TONE_MAPPER_LINEAR].
|
||||||
|
[b]Note:[/b] When [member tonemap_white] is left at the default value of [code]1.0[/code], [constant TONE_MAPPER_REINHARDT] produces an identical image to [constant TONE_MAPPER_LINEAR].
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="TONE_MAPPER_FILMIC" value="2" enum="ToneMapper">
|
<constant name="TONE_MAPPER_FILMIC" value="2" enum="ToneMapper">
|
||||||
Filmic tonemapper operator. This avoids clipping bright highlights, with a resulting image that usually looks more vivid than [constant TONE_MAPPER_REINHARDT].
|
Uses a film-like tonemapping curve to prevent clipping of bright values and provide better contrast than [constant TONE_MAPPER_REINHARDT]. Slightly slower than [constant TONE_MAPPER_REINHARDT].
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="TONE_MAPPER_ACES" value="3" enum="ToneMapper">
|
<constant name="TONE_MAPPER_ACES" value="3" enum="ToneMapper">
|
||||||
Use the Academy Color Encoding System tonemapper. ACES is slightly more expensive than other options, but it handles bright lighting in a more realistic fashion by desaturating it as it becomes brighter. ACES typically has a more contrasted output compared to [constant TONE_MAPPER_REINHARDT] and [constant TONE_MAPPER_FILMIC].
|
Uses a high-contrast film-like tonemapping curve and desaturates bright values for a more realistic appearance. Slightly slower than [constant TONE_MAPPER_FILMIC].
|
||||||
[b]Note:[/b] This tonemapping operator is called "ACES Fitted" in Godot 3.x.
|
[b]Note:[/b] This tonemapping operator is called "ACES Fitted" in Godot 3.x.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="TONE_MAPPER_AGX" value="4" enum="ToneMapper">
|
<constant name="TONE_MAPPER_AGX" value="4" enum="ToneMapper">
|
||||||
Use the AgX tonemapper. AgX is slightly more expensive than other options, but it handles bright lighting in a more realistic fashion by desaturating it as it becomes brighter. AgX is less likely to darken parts of the scene compared to [constant TONE_MAPPER_ACES] and can match the overall scene brightness of [constant TONE_MAPPER_FILMIC] more closely.
|
Uses a film-like tonemapping curve and desaturates bright values for a more realistic appearance. Better than other tonemappers at maintaining the hue of colors as they become brighter. The slowest tonemapping option.
|
||||||
|
[b]Note:[/b] [member tonemap_white] is fixed at a value of [code]16.29[/code], which makes [constant TONE_MAPPER_AGX] unsuitable for use with the Mobile rendering method.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="GLOW_BLEND_MODE_ADDITIVE" value="0" enum="GlowBlendMode">
|
<constant name="GLOW_BLEND_MODE_ADDITIVE" value="0" enum="GlowBlendMode">
|
||||||
Additive glow blending mode. Mostly used for particles, glows (bloom), lens flare, bright sources.
|
Additive glow blending mode. Mostly used for particles, glows (bloom), lens flare, bright sources.
|
||||||
|
|
|
||||||
|
|
@ -469,7 +469,7 @@
|
||||||
</signal>
|
</signal>
|
||||||
<signal name="frame_rect_changed">
|
<signal name="frame_rect_changed">
|
||||||
<param index="0" name="frame" type="GraphFrame" />
|
<param index="0" name="frame" type="GraphFrame" />
|
||||||
<param index="1" name="new_rect" type="Vector2" />
|
<param index="1" name="new_rect" type="Rect2" />
|
||||||
<description>
|
<description>
|
||||||
Emitted when the [GraphFrame] [param frame] is resized to [param new_rect].
|
Emitted when the [GraphFrame] [param frame] is resized to [param new_rect].
|
||||||
</description>
|
</description>
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@
|
||||||
<param index="3" name="body" type="String" default="""" />
|
<param index="3" name="body" type="String" default="""" />
|
||||||
<description>
|
<description>
|
||||||
Sends a request to the connected host.
|
Sends a request to the connected host.
|
||||||
The URL parameter is usually just the part after the host, so for [code]https://somehost.com/index.php[/code], it is [code]/index.php[/code]. When sending requests to an HTTP proxy server, it should be an absolute URL. For [constant HTTPClient.METHOD_OPTIONS] requests, [code]*[/code] is also allowed. For [constant HTTPClient.METHOD_CONNECT] requests, it should be the authority component ([code]host:port[/code]).
|
The URL parameter is usually just the part after the host, so for [code]https://example.com/index.php[/code], it is [code]/index.php[/code]. When sending requests to an HTTP proxy server, it should be an absolute URL. For [constant HTTPClient.METHOD_OPTIONS] requests, [code]*[/code] is also allowed. For [constant HTTPClient.METHOD_CONNECT] requests, it should be the authority component ([code]host:port[/code]).
|
||||||
Headers are HTTP request headers. For available HTTP methods, see [enum Method].
|
Headers are HTTP request headers. For available HTTP methods, see [enum Method].
|
||||||
To create a POST request with query strings to push to the server, do:
|
To create a POST request with query strings to push to the server, do:
|
||||||
[codeblocks]
|
[codeblocks]
|
||||||
|
|
@ -171,7 +171,7 @@
|
||||||
<param index="3" name="body" type="PackedByteArray" />
|
<param index="3" name="body" type="PackedByteArray" />
|
||||||
<description>
|
<description>
|
||||||
Sends a raw request to the connected host.
|
Sends a raw request to the connected host.
|
||||||
The URL parameter is usually just the part after the host, so for [code]https://somehost.com/index.php[/code], it is [code]/index.php[/code]. When sending requests to an HTTP proxy server, it should be an absolute URL. For [constant HTTPClient.METHOD_OPTIONS] requests, [code]*[/code] is also allowed. For [constant HTTPClient.METHOD_CONNECT] requests, it should be the authority component ([code]host:port[/code]).
|
The URL parameter is usually just the part after the host, so for [code]https://example.com/index.php[/code], it is [code]/index.php[/code]. When sending requests to an HTTP proxy server, it should be an absolute URL. For [constant HTTPClient.METHOD_OPTIONS] requests, [code]*[/code] is also allowed. For [constant HTTPClient.METHOD_CONNECT] requests, it should be the authority component ([code]host:port[/code]).
|
||||||
Headers are HTTP request headers. For available HTTP methods, see [enum Method].
|
Headers are HTTP request headers. For available HTTP methods, see [enum Method].
|
||||||
Sends the body data raw, as a byte array and does not encode it in any way.
|
Sends the body data raw, as a byte array and does not encode it in any way.
|
||||||
</description>
|
</description>
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@
|
||||||
http_request.request_completed.connect(self._http_request_completed)
|
http_request.request_completed.connect(self._http_request_completed)
|
||||||
|
|
||||||
# Perform the HTTP request. The URL below returns a PNG image as of writing.
|
# Perform the HTTP request. The URL below returns a PNG image as of writing.
|
||||||
var error = http_request.request("https://via.placeholder.com/512")
|
var error = http_request.request("https://placehold.co/512")
|
||||||
if error != OK:
|
if error != OK:
|
||||||
push_error("An error occurred in the HTTP request.")
|
push_error("An error occurred in the HTTP request.")
|
||||||
|
|
||||||
|
|
@ -120,7 +120,7 @@
|
||||||
httpRequest.RequestCompleted += HttpRequestCompleted;
|
httpRequest.RequestCompleted += HttpRequestCompleted;
|
||||||
|
|
||||||
// Perform the HTTP request. The URL below returns a PNG image as of writing.
|
// Perform the HTTP request. The URL below returns a PNG image as of writing.
|
||||||
Error error = httpRequest.Request("https://via.placeholder.com/512");
|
Error error = httpRequest.Request("https://placehold.co/512");
|
||||||
if (error != Error.Ok)
|
if (error != Error.Ok)
|
||||||
{
|
{
|
||||||
GD.PushError("An error occurred in the HTTP request.");
|
GD.PushError("An error occurred in the HTTP request.");
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,18 @@
|
||||||
|
|
||||||
print(datetime.format(formatter))
|
print(datetime.format(formatter))
|
||||||
[/codeblock]
|
[/codeblock]
|
||||||
|
[b]Warning:[/b] When calling Java methods, be sure to check [method JavaClassWrapper.get_exception] to check if the method threw an exception.
|
||||||
</description>
|
</description>
|
||||||
<tutorials>
|
<tutorials>
|
||||||
</tutorials>
|
</tutorials>
|
||||||
<methods>
|
<methods>
|
||||||
|
<method name="get_exception">
|
||||||
|
<return type="JavaObject" />
|
||||||
|
<description>
|
||||||
|
Returns the Java exception from the last call into a Java class. If there was no exception, it will return [code]null[/code].
|
||||||
|
[b]Note:[/b] This method only works on Android. On every other platform, this method will always return [code]null[/code].
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="wrap">
|
<method name="wrap">
|
||||||
<return type="JavaClass" />
|
<return type="JavaClass" />
|
||||||
<param index="0" name="name" type="String" />
|
<param index="0" name="name" type="String" />
|
||||||
|
|
|
||||||
|
|
@ -1429,9 +1429,14 @@
|
||||||
<member name="input_devices/pen_tablet/driver" type="String" setter="" getter="">
|
<member name="input_devices/pen_tablet/driver" type="String" setter="" getter="">
|
||||||
Specifies the tablet driver to use. If left empty, the default driver will be used.
|
Specifies the tablet driver to use. If left empty, the default driver will be used.
|
||||||
[b]Note:[/b] The driver in use can be overridden at runtime via the [code]--tablet-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
|
[b]Note:[/b] The driver in use can be overridden at runtime via the [code]--tablet-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
|
||||||
|
[b]Note:[/b] Use [method DisplayServer.tablet_set_current_driver] to switch tablet driver in runtime.
|
||||||
</member>
|
</member>
|
||||||
<member name="input_devices/pen_tablet/driver.windows" type="String" setter="" getter="">
|
<member name="input_devices/pen_tablet/driver.windows" type="String" setter="" getter="">
|
||||||
Override for [member input_devices/pen_tablet/driver] on Windows.
|
Override for [member input_devices/pen_tablet/driver] on Windows. Supported values are:
|
||||||
|
- [code]auto[/code] (default), uses [code]wintab[/code] if Windows Ink is disabled in the Wacom Tablet Properties or system settings, [code]winink[/code] otherwise.
|
||||||
|
- [code]winink[/code], uses Windows native "Windows Ink" driver.
|
||||||
|
- [code]wintab[/code], uses Wacom "WinTab" driver.
|
||||||
|
- [code]dummy[/code], tablet input is disabled.
|
||||||
</member>
|
</member>
|
||||||
<member name="input_devices/pointing/android/enable_long_press_as_right_click" type="bool" setter="" getter="" default="false">
|
<member name="input_devices/pointing/android/enable_long_press_as_right_click" type="bool" setter="" getter="" default="false">
|
||||||
If [code]true[/code], long press events on an Android touchscreen are transformed into right click events.
|
If [code]true[/code], long press events on an Android touchscreen are transformed into right click events.
|
||||||
|
|
@ -1480,7 +1485,7 @@
|
||||||
The expansion ratio to use during pseudolocalization. A value of [code]0.3[/code] is sufficient for most practical purposes, and will increase the length of each string by 30%.
|
The expansion ratio to use during pseudolocalization. A value of [code]0.3[/code] is sufficient for most practical purposes, and will increase the length of each string by 30%.
|
||||||
</member>
|
</member>
|
||||||
<member name="internationalization/pseudolocalization/fake_bidi" type="bool" setter="" getter="" default="false">
|
<member name="internationalization/pseudolocalization/fake_bidi" type="bool" setter="" getter="" default="false">
|
||||||
If [code]true[/code], emulate bidirectional (right-to-left) text when pseudolocalization is enabled. This can be used to spot issues with RTL layout and UI mirroring that will crop up if the project is localized to RTL languages such as Arabic or Hebrew.
|
If [code]true[/code], emulate bidirectional (right-to-left) text when pseudolocalization is enabled. This can be used to spot issues with RTL layout and UI mirroring that will crop up if the project is localized to RTL languages such as Arabic or Hebrew. See also [member internationalization/rendering/force_right_to_left_layout_direction].
|
||||||
</member>
|
</member>
|
||||||
<member name="internationalization/pseudolocalization/override" type="bool" setter="" getter="" default="false">
|
<member name="internationalization/pseudolocalization/override" type="bool" setter="" getter="" default="false">
|
||||||
Replace all characters in the string with [code]*[/code]. Useful for finding non-localizable strings.
|
Replace all characters in the string with [code]*[/code]. Useful for finding non-localizable strings.
|
||||||
|
|
@ -1502,7 +1507,7 @@
|
||||||
[b]Note:[/b] This property is only read when the project starts. To toggle pseudolocalization at run-time, use [member TranslationServer.pseudolocalization_enabled] instead.
|
[b]Note:[/b] This property is only read when the project starts. To toggle pseudolocalization at run-time, use [member TranslationServer.pseudolocalization_enabled] instead.
|
||||||
</member>
|
</member>
|
||||||
<member name="internationalization/rendering/force_right_to_left_layout_direction" type="bool" setter="" getter="" default="false">
|
<member name="internationalization/rendering/force_right_to_left_layout_direction" type="bool" setter="" getter="" default="false">
|
||||||
Force layout direction and text writing direction to RTL for all controls.
|
Force layout direction and text writing direction to RTL for all controls, even if the current locale is intended to use a left-to-right layout and text writing direction. This should be enabled for testing purposes only. See also [member internationalization/pseudolocalization/fake_bidi].
|
||||||
</member>
|
</member>
|
||||||
<member name="internationalization/rendering/root_node_auto_translate" type="bool" setter="" getter="" default="true">
|
<member name="internationalization/rendering/root_node_auto_translate" type="bool" setter="" getter="" default="true">
|
||||||
If [code]true[/code], root node will use [constant Node.AUTO_TRANSLATE_MODE_ALWAYS], otherwise [constant Node.AUTO_TRANSLATE_MODE_DISABLED] will be used.
|
If [code]true[/code], root node will use [constant Node.AUTO_TRANSLATE_MODE_ALWAYS], otherwise [constant Node.AUTO_TRANSLATE_MODE_DISABLED] will be used.
|
||||||
|
|
@ -2199,6 +2204,10 @@
|
||||||
The CA certificates bundle to use for TLS connections. If this is set to a non-empty value, this will [i]override[/i] Godot's default [url=https://github.com/godotengine/godot/blob/master/thirdparty/certs/ca-certificates.crt]Mozilla certificate bundle[/url]. If left empty, the default certificate bundle will be used.
|
The CA certificates bundle to use for TLS connections. If this is set to a non-empty value, this will [i]override[/i] Godot's default [url=https://github.com/godotengine/godot/blob/master/thirdparty/certs/ca-certificates.crt]Mozilla certificate bundle[/url]. If left empty, the default certificate bundle will be used.
|
||||||
If in doubt, leave this setting empty.
|
If in doubt, leave this setting empty.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="network/tls/enable_tls_v1.3" type="bool" setter="" getter="" default="false">
|
||||||
|
If [code]true[/code], enable TLSv1.3 negotiation.
|
||||||
|
[b]Note:[/b] This is experimental, and may cause connections to fail in some cases (notably, if the remote server uses TLS handshake fragmentation).
|
||||||
|
</member>
|
||||||
<member name="physics/2d/default_angular_damp" type="float" setter="" getter="" default="1.0">
|
<member name="physics/2d/default_angular_damp" type="float" setter="" getter="" default="1.0">
|
||||||
The default rotational motion damping in 2D. Damping is used to gradually slow down physical objects over time. RigidBodies will fall back to this value when combining their own damping values and no area damping value is present.
|
The default rotational motion damping in 2D. Damping is used to gradually slow down physical objects over time. RigidBodies will fall back to this value when combining their own damping values and no area damping value is present.
|
||||||
Suggested values are in the range [code]0[/code] to [code]30[/code]. At value [code]0[/code] objects will keep moving with the same velocity. Greater values will stop the object faster. A value equal to or greater than the physics tick rate ([member physics/common/physics_ticks_per_second]) will bring the object to a stop in one iteration.
|
Suggested values are in the range [code]0[/code] to [code]30[/code]. At value [code]0[/code] objects will keep moving with the same velocity. Greater values will stop the object faster. A value equal to or greater than the physics tick rate ([member physics/common/physics_ticks_per_second]) will bring the object to a stop in one iteration.
|
||||||
|
|
|
||||||
|
|
@ -703,11 +703,10 @@
|
||||||
<param index="1" name="format" type="int" enum="RenderingDevice.IndexBufferFormat" />
|
<param index="1" name="format" type="int" enum="RenderingDevice.IndexBufferFormat" />
|
||||||
<param index="2" name="data" type="PackedByteArray" default="PackedByteArray()" />
|
<param index="2" name="data" type="PackedByteArray" default="PackedByteArray()" />
|
||||||
<param index="3" name="use_restart_indices" type="bool" default="false" />
|
<param index="3" name="use_restart_indices" type="bool" default="false" />
|
||||||
<param index="4" name="enable_device_address" type="bool" default="false" />
|
<param index="4" name="creation_bits" type="int" enum="RenderingDevice.BufferCreationBits" is_bitfield="true" default="0" />
|
||||||
<description>
|
<description>
|
||||||
Creates a new index buffer. It can be accessed with the RID that is returned.
|
Creates a new index buffer. It can be accessed with the RID that is returned.
|
||||||
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method.
|
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method.
|
||||||
Optionally, set [param enable_device_address] if you wish to use [method buffer_get_device_address] functionality and the GPU supports it.
|
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="limit_get" qualifiers="const">
|
<method name="limit_get" qualifiers="const">
|
||||||
|
|
@ -847,6 +846,7 @@
|
||||||
<param index="0" name="size_bytes" type="int" />
|
<param index="0" name="size_bytes" type="int" />
|
||||||
<param index="1" name="data" type="PackedByteArray" default="PackedByteArray()" />
|
<param index="1" name="data" type="PackedByteArray" default="PackedByteArray()" />
|
||||||
<param index="2" name="usage" type="int" enum="RenderingDevice.StorageBufferUsage" is_bitfield="true" default="0" />
|
<param index="2" name="usage" type="int" enum="RenderingDevice.StorageBufferUsage" is_bitfield="true" default="0" />
|
||||||
|
<param index="3" name="creation_bits" type="int" enum="RenderingDevice.BufferCreationBits" is_bitfield="true" default="0" />
|
||||||
<description>
|
<description>
|
||||||
Creates a [url=https://vkguide.dev/docs/chapter-4/storage_buffers/]storage buffer[/url] with the specified [param data] and [param usage]. It can be accessed with the RID that is returned.
|
Creates a [url=https://vkguide.dev/docs/chapter-4/storage_buffers/]storage buffer[/url] with the specified [param data] and [param usage]. It can be accessed with the RID that is returned.
|
||||||
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method.
|
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method.
|
||||||
|
|
@ -1073,11 +1073,10 @@
|
||||||
<return type="RID" />
|
<return type="RID" />
|
||||||
<param index="0" name="size_bytes" type="int" />
|
<param index="0" name="size_bytes" type="int" />
|
||||||
<param index="1" name="data" type="PackedByteArray" default="PackedByteArray()" />
|
<param index="1" name="data" type="PackedByteArray" default="PackedByteArray()" />
|
||||||
<param index="2" name="enable_device_address" type="bool" default="false" />
|
<param index="2" name="creation_bits" type="int" enum="RenderingDevice.BufferCreationBits" is_bitfield="true" default="0" />
|
||||||
<description>
|
<description>
|
||||||
Creates a new uniform buffer. It can be accessed with the RID that is returned.
|
Creates a new uniform buffer. It can be accessed with the RID that is returned.
|
||||||
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method.
|
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method.
|
||||||
Optionally, set [param enable_device_address] if you wish to use [method buffer_get_device_address] functionality and the GPU supports it.
|
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="uniform_set_create">
|
<method name="uniform_set_create">
|
||||||
|
|
@ -1111,12 +1110,10 @@
|
||||||
<return type="RID" />
|
<return type="RID" />
|
||||||
<param index="0" name="size_bytes" type="int" />
|
<param index="0" name="size_bytes" type="int" />
|
||||||
<param index="1" name="data" type="PackedByteArray" default="PackedByteArray()" />
|
<param index="1" name="data" type="PackedByteArray" default="PackedByteArray()" />
|
||||||
<param index="2" name="use_as_storage" type="bool" default="false" />
|
<param index="2" name="creation_bits" type="int" enum="RenderingDevice.BufferCreationBits" is_bitfield="true" default="0" />
|
||||||
<param index="3" name="enable_device_address" type="bool" default="false" />
|
|
||||||
<description>
|
<description>
|
||||||
It can be accessed with the RID that is returned.
|
It can be accessed with the RID that is returned.
|
||||||
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method.
|
Once finished with your RID, you will want to free the RID using the RenderingDevice's [method free_rid] method.
|
||||||
Optionally, set [param enable_device_address] if you wish to use [method buffer_get_device_address] functionality and the GPU supports it.
|
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="vertex_format_create">
|
<method name="vertex_format_create">
|
||||||
|
|
@ -2068,8 +2065,20 @@
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT" value="1" enum="StorageBufferUsage" is_bitfield="true">
|
<constant name="STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT" value="1" enum="StorageBufferUsage" is_bitfield="true">
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="STORAGE_BUFFER_USAGE_DEVICE_ADDRESS" value="2" enum="StorageBufferUsage" is_bitfield="true">
|
<constant name="BUFFER_CREATION_DEVICE_ADDRESS_BIT" value="1" enum="BufferCreationBits" is_bitfield="true">
|
||||||
Allows usage of [method buffer_get_device_address] on supported GPUs.
|
Optionally, set this flag if you wish to use [method buffer_get_device_address] functionality. You must first check the GPU supports it:
|
||||||
|
[codeblocks]
|
||||||
|
[gdscript]
|
||||||
|
rd = RenderingServer.get_rendering_device()
|
||||||
|
|
||||||
|
if rd.has_feature(RenderingDevice.SUPPORTS_BUFFER_DEVICE_ADDRESS):
|
||||||
|
storage_buffer = rd.storage_buffer_create(bytes.size(), bytes, RenderingDevice.STORAGE_BUFFER_USAGE_SHADER_DEVICE_ADDRESS):
|
||||||
|
storage_buffer_address = rd.buffer_get_device_address(storage_buffer)
|
||||||
|
[/gdscript]
|
||||||
|
[/codeblocks]
|
||||||
|
</constant>
|
||||||
|
<constant name="BUFFER_CREATION_AS_STORAGE_BIT" value="2" enum="BufferCreationBits" is_bitfield="true">
|
||||||
|
Set this flag so that it is created as storage. This is useful if Compute Shaders need access (for reading or writing) to the buffer, e.g. skeletal animations are processed in Compute Shaders which need access to vertex buffers, to be later consumed by vertex shaders as part of the regular rasterization pipeline.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="UNIFORM_TYPE_SAMPLER" value="0" enum="UniformType">
|
<constant name="UNIFORM_TYPE_SAMPLER" value="0" enum="UniformType">
|
||||||
Sampler uniform.
|
Sampler uniform.
|
||||||
|
|
|
||||||
|
|
@ -5393,20 +5393,22 @@
|
||||||
Use a simple fog model defined by start and end positions and a custom curve. While not physically accurate, this model can be useful when you need more artistic control.
|
Use a simple fog model defined by start and end positions and a custom curve. While not physically accurate, this model can be useful when you need more artistic control.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="ENV_TONE_MAPPER_LINEAR" value="0" enum="EnvironmentToneMapper">
|
<constant name="ENV_TONE_MAPPER_LINEAR" value="0" enum="EnvironmentToneMapper">
|
||||||
Output color as they came in. This can cause bright lighting to look blown out, with noticeable clipping in the output colors.
|
Does not modify color data, resulting in a linear tonemapping curve which unnaturally clips bright values, causing bright lighting to look blown out. The simplest and fastest tonemapper.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="ENV_TONE_MAPPER_REINHARD" value="1" enum="EnvironmentToneMapper">
|
<constant name="ENV_TONE_MAPPER_REINHARD" value="1" enum="EnvironmentToneMapper">
|
||||||
Use the Reinhard tonemapper. Performs a variation on rendered pixels' colors by this formula: [code]color = color * (1 + color / (white * white)) / (1 + color)[/code]. This avoids clipping bright highlights, but the resulting image can look a bit dull. When [member Environment.tonemap_white] is left at the default value of [code]1.0[/code] this is identical to [constant ENV_TONE_MAPPER_LINEAR] while also being slightly less performant.
|
A simple tonemapping curve that rolls off bright values to prevent clipping. This results in an image that can appear dull and low contrast. Slower than [constant ENV_TONE_MAPPER_LINEAR].
|
||||||
|
[b]Note:[/b] When [member Environment.tonemap_white] is left at the default value of [code]1.0[/code], [constant ENV_TONE_MAPPER_REINHARD] produces an identical image to [constant ENV_TONE_MAPPER_LINEAR].
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="ENV_TONE_MAPPER_FILMIC" value="2" enum="EnvironmentToneMapper">
|
<constant name="ENV_TONE_MAPPER_FILMIC" value="2" enum="EnvironmentToneMapper">
|
||||||
Use the filmic tonemapper. This avoids clipping bright highlights, with a resulting image that usually looks more vivid than [constant ENV_TONE_MAPPER_REINHARD].
|
Uses a film-like tonemapping curve to prevent clipping of bright values and provide better contrast than [constant ENV_TONE_MAPPER_REINHARD]. Slightly slower than [constant ENV_TONE_MAPPER_REINHARD].
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="ENV_TONE_MAPPER_ACES" value="3" enum="EnvironmentToneMapper">
|
<constant name="ENV_TONE_MAPPER_ACES" value="3" enum="EnvironmentToneMapper">
|
||||||
Use the Academy Color Encoding System tonemapper. ACES is slightly more expensive than other options, but it handles bright lighting in a more realistic fashion by desaturating it as it becomes brighter. ACES typically has a more contrasted output compared to [constant ENV_TONE_MAPPER_REINHARD] and [constant ENV_TONE_MAPPER_FILMIC].
|
Uses a high-contrast film-like tonemapping curve and desaturates bright values for a more realistic appearance. Slightly slower than [constant ENV_TONE_MAPPER_FILMIC].
|
||||||
[b]Note:[/b] This tonemapping operator is called "ACES Fitted" in Godot 3.x.
|
[b]Note:[/b] This tonemapping operator is called "ACES Fitted" in Godot 3.x.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="ENV_TONE_MAPPER_AGX" value="4" enum="EnvironmentToneMapper">
|
<constant name="ENV_TONE_MAPPER_AGX" value="4" enum="EnvironmentToneMapper">
|
||||||
Use the AgX tonemapper. AgX is slightly more expensive than other options, but it handles bright lighting in a more realistic fashion by desaturating it as it becomes brighter. AgX is less likely to darken parts of the scene compared to [constant ENV_TONE_MAPPER_ACES], and can match [constant ENV_TONE_MAPPER_FILMIC] more closely.
|
Uses a film-like tonemapping curve and desaturates bright values for a more realistic appearance. Better than other tonemappers at maintaining the hue of colors as they become brighter. The slowest tonemapping option.
|
||||||
|
[b]Note:[/b] [member Environment.tonemap_white] is fixed at a value of [code]16.29[/code], which makes [constant ENV_TONE_MAPPER_AGX] unsuitable for use with the Mobile rendering method.
|
||||||
</constant>
|
</constant>
|
||||||
<constant name="ENV_SSR_ROUGHNESS_QUALITY_DISABLED" value="0" enum="EnvironmentSSRRoughnessQuality">
|
<constant name="ENV_SSR_ROUGHNESS_QUALITY_DISABLED" value="0" enum="EnvironmentSSRRoughnessQuality">
|
||||||
Lowest quality of roughness filter for screen-space reflections. Rough materials will not have blurrier screen-space reflections compared to smooth (non-rough) materials. This is the fastest option.
|
Lowest quality of roughness filter for screen-space reflections. Rough materials will not have blurrier screen-space reflections compared to smooth (non-rough) materials. This is the fastest option.
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
Node for 2D tile-based maps. Tilemaps use a [TileSet] which contain a list of tiles which are used to create grid-based maps. A TileMap may have several layers, layouting tiles on top of each other.
|
Node for 2D tile-based maps. Tilemaps use a [TileSet] which contain a list of tiles which are used to create grid-based maps. A TileMap may have several layers, layouting tiles on top of each other.
|
||||||
For performance reasons, all TileMap updates are batched at the end of a frame. Notably, this means that scene tiles from a [TileSetScenesCollectionSource] may be initialized after their parent. This is only queued when inside the scene tree.
|
For performance reasons, all TileMap updates are batched at the end of a frame. Notably, this means that scene tiles from a [TileSetScenesCollectionSource] may be initialized after their parent. This is only queued when inside the scene tree.
|
||||||
To force an update earlier on, call [method update_internals].
|
To force an update earlier on, call [method update_internals].
|
||||||
|
[b]Note:[/b] For performance and compatibility reasons, the coordinates serialized by [TileMap] are limited to 16-bit signed integers, i.e. the range for X and Y coordinates is from [code]-32768[/code] to [code]32767[/code]. When saving tile data, tiles outside this range are wrapped.
|
||||||
</description>
|
</description>
|
||||||
<tutorials>
|
<tutorials>
|
||||||
<link title="Using Tilemaps">$DOCS_URL/tutorials/2d/using_tilemaps.html</link>
|
<link title="Using Tilemaps">$DOCS_URL/tutorials/2d/using_tilemaps.html</link>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
Node for 2D tile-based maps. A [TileMapLayer] uses a [TileSet] which contain a list of tiles which are used to create grid-based maps. Unlike the [TileMap] node, which is deprecated, [TileMapLayer] has only one layer of tiles. You can use several [TileMapLayer] to achieve the same result as a [TileMap] node.
|
Node for 2D tile-based maps. A [TileMapLayer] uses a [TileSet] which contain a list of tiles which are used to create grid-based maps. Unlike the [TileMap] node, which is deprecated, [TileMapLayer] has only one layer of tiles. You can use several [TileMapLayer] to achieve the same result as a [TileMap] node.
|
||||||
For performance reasons, all TileMap updates are batched at the end of a frame. Notably, this means that scene tiles from a [TileSetScenesCollectionSource] may be initialized after their parent. This is only queued when inside the scene tree.
|
For performance reasons, all TileMap updates are batched at the end of a frame. Notably, this means that scene tiles from a [TileSetScenesCollectionSource] may be initialized after their parent. This is only queued when inside the scene tree.
|
||||||
To force an update earlier on, call [method update_internals].
|
To force an update earlier on, call [method update_internals].
|
||||||
|
[b]Note:[/b] For performance and compatibility reasons, the coordinates serialized by [TileMapLayer] are limited to 16-bit signed integers, i.e. the range for X and Y coordinates is from [code]-32768[/code] to [code]32767[/code]. When saving tile data, tiles outside this range are wrapped.
|
||||||
</description>
|
</description>
|
||||||
<tutorials>
|
<tutorials>
|
||||||
<link title="Using Tilemaps">$DOCS_URL/tutorials/2d/using_tilemaps.html</link>
|
<link title="Using Tilemaps">$DOCS_URL/tutorials/2d/using_tilemaps.html</link>
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@
|
||||||
<return type="float" />
|
<return type="float" />
|
||||||
<param index="0" name="to" type="Vector2" />
|
<param index="0" name="to" type="Vector2" />
|
||||||
<description>
|
<description>
|
||||||
Returns the angle to the given vector, in radians.
|
Returns the signed angle to the given vector, in radians.
|
||||||
[url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/vector2_angle_to.png]Illustration of the returned angle.[/url]
|
[url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/vector2_angle_to.png]Illustration of the returned angle.[/url]
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
|
|
||||||
|
|
@ -2238,6 +2238,8 @@ def format_text_block(
|
||||||
repl_text = target_name
|
repl_text = target_name
|
||||||
if target_class_name != state.current_class:
|
if target_class_name != state.current_class:
|
||||||
repl_text = f"{target_class_name}.{target_name}"
|
repl_text = f"{target_class_name}.{target_name}"
|
||||||
|
if tag_state.name == "method":
|
||||||
|
repl_text = f"{repl_text}()"
|
||||||
tag_text = f":ref:`{repl_text}<class_{sanitize_class_name(target_class_name)}{ref_type}_{target_name}>`"
|
tag_text = f":ref:`{repl_text}<class_{sanitize_class_name(target_class_name)}{ref_type}_{target_name}>`"
|
||||||
escape_pre = True
|
escape_pre = True
|
||||||
escape_post = True
|
escape_post = True
|
||||||
|
|
|
||||||
|
|
@ -6234,6 +6234,8 @@ uint64_t RenderingDeviceDriverD3D12::limit_get(Limit p_limit) {
|
||||||
case LIMIT_VRS_MAX_FRAGMENT_WIDTH:
|
case LIMIT_VRS_MAX_FRAGMENT_WIDTH:
|
||||||
case LIMIT_VRS_MAX_FRAGMENT_HEIGHT:
|
case LIMIT_VRS_MAX_FRAGMENT_HEIGHT:
|
||||||
return vrs_capabilities.ss_max_fragment_size;
|
return vrs_capabilities.ss_max_fragment_size;
|
||||||
|
case LIMIT_MAX_SHADER_VARYINGS:
|
||||||
|
return MIN(D3D12_VS_OUTPUT_REGISTER_COUNT, D3D12_PS_INPUT_REGISTER_COUNT);
|
||||||
default: {
|
default: {
|
||||||
#ifdef DEV_ENABLED
|
#ifdef DEV_ENABLED
|
||||||
WARN_PRINT("Returning maximum value for unknown limit " + itos(p_limit) + ".");
|
WARN_PRINT("Returning maximum value for unknown limit " + itos(p_limit) + ".");
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,11 @@ Config::Config() {
|
||||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
|
||||||
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size);
|
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size);
|
||||||
glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size);
|
glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size);
|
||||||
|
GLint max_vertex_output;
|
||||||
|
glGetIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, &max_vertex_output);
|
||||||
|
GLint max_fragment_input;
|
||||||
|
glGetIntegerv(GL_MAX_FRAGMENT_INPUT_COMPONENTS, &max_fragment_input);
|
||||||
|
max_shader_varyings = (uint32_t)MIN(max_vertex_output, max_fragment_input) / 4;
|
||||||
|
|
||||||
// sanity clamp buffer size to 16K..1MB
|
// sanity clamp buffer size to 16K..1MB
|
||||||
max_uniform_buffer_size = CLAMP(max_uniform_buffer_size, 16384, 1048576);
|
max_uniform_buffer_size = CLAMP(max_uniform_buffer_size, 16384, 1048576);
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ public:
|
||||||
GLint max_texture_size = 0;
|
GLint max_texture_size = 0;
|
||||||
GLint max_viewport_size[2] = { 0, 0 };
|
GLint max_viewport_size[2] = { 0, 0 };
|
||||||
GLint64 max_uniform_buffer_size = 0;
|
GLint64 max_uniform_buffer_size = 0;
|
||||||
|
uint32_t max_shader_varyings = 0;
|
||||||
|
|
||||||
int64_t max_renderable_elements = 0;
|
int64_t max_renderable_elements = 0;
|
||||||
int64_t max_renderable_lights = 0;
|
int64_t max_renderable_lights = 0;
|
||||||
|
|
|
||||||
|
|
@ -465,4 +465,16 @@ Size2i Utilities::get_maximum_viewport_size() const {
|
||||||
return Size2i(config->max_viewport_size[0], config->max_viewport_size[1]);
|
return Size2i(config->max_viewport_size[0], config->max_viewport_size[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t Utilities::get_maximum_shader_varyings() const {
|
||||||
|
Config *config = Config::get_singleton();
|
||||||
|
ERR_FAIL_NULL_V(config, 31);
|
||||||
|
return config->max_shader_varyings;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Utilities::get_maximum_uniform_buffer_size() const {
|
||||||
|
Config *config = Config::get_singleton();
|
||||||
|
ERR_FAIL_NULL_V(config, 65536);
|
||||||
|
return uint64_t(config->max_uniform_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // GLES3_ENABLED
|
#endif // GLES3_ENABLED
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,8 @@ public:
|
||||||
virtual String get_video_adapter_api_version() const override;
|
virtual String get_video_adapter_api_version() const override;
|
||||||
|
|
||||||
virtual Size2i get_maximum_viewport_size() const override;
|
virtual Size2i get_maximum_viewport_size() const override;
|
||||||
|
virtual uint32_t get_maximum_shader_varyings() const override;
|
||||||
|
virtual uint64_t get_maximum_uniform_buffer_size() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace GLES3
|
} // namespace GLES3
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,7 @@ struct MetalLimits {
|
||||||
uint32_t maxVertexInputBindings;
|
uint32_t maxVertexInputBindings;
|
||||||
uint32_t maxVertexInputBindingStride;
|
uint32_t maxVertexInputBindingStride;
|
||||||
uint32_t maxDrawIndexedIndexValue;
|
uint32_t maxDrawIndexedIndexValue;
|
||||||
|
uint32_t maxShaderVaryings;
|
||||||
|
|
||||||
double temporalScalerInputContentMinScale;
|
double temporalScalerInputContentMinScale;
|
||||||
double temporalScalerInputContentMaxScale;
|
double temporalScalerInputContentMaxScale;
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,7 @@ void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
|
||||||
limits.maxVertexInputAttributes = 31;
|
limits.maxVertexInputAttributes = 31;
|
||||||
limits.maxVertexInputBindings = 31;
|
limits.maxVertexInputBindings = 31;
|
||||||
limits.maxVertexInputBindingStride = (2 * KIBI);
|
limits.maxVertexInputBindingStride = (2 * KIBI);
|
||||||
|
limits.maxShaderVaryings = 31; // Accurate on Apple4 and above. See: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
|
||||||
|
|
||||||
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
|
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
|
||||||
limits.minUniformBufferOffsetAlignment = 64;
|
limits.minUniformBufferOffsetAlignment = 64;
|
||||||
|
|
|
||||||
|
|
@ -3999,6 +3999,8 @@ uint64_t RenderingDeviceDriverMetal::limit_get(Limit p_limit) {
|
||||||
return (uint64_t)((1.0 / limits.temporalScalerInputContentMaxScale) * 1000'000);
|
return (uint64_t)((1.0 / limits.temporalScalerInputContentMaxScale) * 1000'000);
|
||||||
case LIMIT_METALFX_TEMPORAL_SCALER_MAX_SCALE:
|
case LIMIT_METALFX_TEMPORAL_SCALER_MAX_SCALE:
|
||||||
return (uint64_t)((1.0 / limits.temporalScalerInputContentMinScale) * 1000'000);
|
return (uint64_t)((1.0 / limits.temporalScalerInputContentMinScale) * 1000'000);
|
||||||
|
case LIMIT_MAX_SHADER_VARYINGS:
|
||||||
|
return limits.maxShaderVaryings;
|
||||||
UNKNOWN(LIMIT_VRS_TEXEL_WIDTH);
|
UNKNOWN(LIMIT_VRS_TEXEL_WIDTH);
|
||||||
UNKNOWN(LIMIT_VRS_TEXEL_HEIGHT);
|
UNKNOWN(LIMIT_VRS_TEXEL_HEIGHT);
|
||||||
UNKNOWN(LIMIT_VRS_MAX_FRAGMENT_WIDTH);
|
UNKNOWN(LIMIT_VRS_MAX_FRAGMENT_WIDTH);
|
||||||
|
|
|
||||||
|
|
@ -1522,6 +1522,9 @@ RDD::BufferID RenderingDeviceDriverVulkan::buffer_create(uint64_t p_size, BitFie
|
||||||
create_info.usage = p_usage;
|
create_info.usage = p_usage;
|
||||||
create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
|
||||||
|
VmaMemoryUsage vma_usage = VMA_MEMORY_USAGE_UNKNOWN;
|
||||||
|
uint32_t vma_flags_to_remove = 0;
|
||||||
|
|
||||||
VmaAllocationCreateInfo alloc_create_info = {};
|
VmaAllocationCreateInfo alloc_create_info = {};
|
||||||
switch (p_allocation_type) {
|
switch (p_allocation_type) {
|
||||||
case MEMORY_ALLOCATION_TYPE_CPU: {
|
case MEMORY_ALLOCATION_TYPE_CPU: {
|
||||||
|
|
@ -1531,15 +1534,19 @@ RDD::BufferID RenderingDeviceDriverVulkan::buffer_create(uint64_t p_size, BitFie
|
||||||
// Looks like a staging buffer: CPU maps, writes sequentially, then GPU copies to VRAM.
|
// Looks like a staging buffer: CPU maps, writes sequentially, then GPU copies to VRAM.
|
||||||
alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
|
alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
|
||||||
alloc_create_info.preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
alloc_create_info.preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
|
vma_flags_to_remove |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
}
|
}
|
||||||
if (is_dst && !is_src) {
|
if (is_dst && !is_src) {
|
||||||
// Looks like a readback buffer: GPU copies from VRAM, then CPU maps and reads.
|
// Looks like a readback buffer: GPU copies from VRAM, then CPU maps and reads.
|
||||||
alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
|
alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
|
||||||
alloc_create_info.preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
|
alloc_create_info.preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
|
||||||
|
vma_flags_to_remove |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
|
||||||
}
|
}
|
||||||
|
vma_usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
|
||||||
alloc_create_info.requiredFlags = (VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
|
alloc_create_info.requiredFlags = (VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
|
||||||
} break;
|
} break;
|
||||||
case MEMORY_ALLOCATION_TYPE_GPU: {
|
case MEMORY_ALLOCATION_TYPE_GPU: {
|
||||||
|
vma_usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||||
alloc_create_info.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
alloc_create_info.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
if (p_size <= SMALL_ALLOCATION_MAX_SIZE) {
|
if (p_size <= SMALL_ALLOCATION_MAX_SIZE) {
|
||||||
uint32_t mem_type_index = 0;
|
uint32_t mem_type_index = 0;
|
||||||
|
|
@ -1553,12 +1560,19 @@ RDD::BufferID RenderingDeviceDriverVulkan::buffer_create(uint64_t p_size, BitFie
|
||||||
VmaAllocation allocation = nullptr;
|
VmaAllocation allocation = nullptr;
|
||||||
VmaAllocationInfo alloc_info = {};
|
VmaAllocationInfo alloc_info = {};
|
||||||
|
|
||||||
VkResult err = vkCreateBuffer(vk_device, &create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_BUFFER), &vk_buffer);
|
if (!Engine::get_singleton()->is_extra_gpu_memory_tracking_enabled()) {
|
||||||
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't create buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
|
alloc_create_info.preferredFlags &= ~vma_flags_to_remove;
|
||||||
err = vmaAllocateMemoryForBuffer(allocator, vk_buffer, &alloc_create_info, &allocation, &alloc_info);
|
alloc_create_info.usage = vma_usage;
|
||||||
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't allocate memory for buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
|
VkResult err = vmaCreateBuffer(allocator, &create_info, &alloc_create_info, &vk_buffer, &allocation, &alloc_info);
|
||||||
err = vmaBindBufferMemory2(allocator, allocation, 0, vk_buffer, nullptr);
|
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't create buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
|
||||||
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't bind memory to buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
|
} else {
|
||||||
|
VkResult err = vkCreateBuffer(vk_device, &create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_BUFFER), &vk_buffer);
|
||||||
|
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't create buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
|
||||||
|
err = vmaAllocateMemoryForBuffer(allocator, vk_buffer, &alloc_create_info, &allocation, &alloc_info);
|
||||||
|
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't allocate memory for buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
|
||||||
|
err = vmaBindBufferMemory2(allocator, allocation, 0, vk_buffer, nullptr);
|
||||||
|
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't bind memory to buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
|
||||||
|
}
|
||||||
|
|
||||||
// Bookkeep.
|
// Bookkeep.
|
||||||
BufferInfo *buf_info = VersatileResource::allocate<BufferInfo>(resources_allocator);
|
BufferInfo *buf_info = VersatileResource::allocate<BufferInfo>(resources_allocator);
|
||||||
|
|
@ -1593,8 +1607,12 @@ void RenderingDeviceDriverVulkan::buffer_free(BufferID p_buffer) {
|
||||||
vkDestroyBufferView(vk_device, buf_info->vk_view, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_BUFFER_VIEW));
|
vkDestroyBufferView(vk_device, buf_info->vk_view, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_BUFFER_VIEW));
|
||||||
}
|
}
|
||||||
|
|
||||||
vkDestroyBuffer(vk_device, buf_info->vk_buffer, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_BUFFER));
|
if (!Engine::get_singleton()->is_extra_gpu_memory_tracking_enabled()) {
|
||||||
vmaFreeMemory(allocator, buf_info->allocation.handle);
|
vmaDestroyBuffer(allocator, buf_info->vk_buffer, buf_info->allocation.handle);
|
||||||
|
} else {
|
||||||
|
vkDestroyBuffer(vk_device, buf_info->vk_buffer, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_BUFFER));
|
||||||
|
vmaFreeMemory(allocator, buf_info->allocation.handle);
|
||||||
|
}
|
||||||
|
|
||||||
VersatileResource::free(resources_allocator, buf_info);
|
VersatileResource::free(resources_allocator, buf_info);
|
||||||
}
|
}
|
||||||
|
|
@ -1808,12 +1826,18 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create(const TextureFormat &
|
||||||
VmaAllocation allocation = nullptr;
|
VmaAllocation allocation = nullptr;
|
||||||
VmaAllocationInfo alloc_info = {};
|
VmaAllocationInfo alloc_info = {};
|
||||||
|
|
||||||
VkResult err = vkCreateImage(vk_device, &create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_IMAGE), &vk_image);
|
if (!Engine::get_singleton()->is_extra_gpu_memory_tracking_enabled()) {
|
||||||
ERR_FAIL_COND_V_MSG(err, TextureID(), "vkCreateImage failed with error " + itos(err) + ".");
|
alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||||
err = vmaAllocateMemoryForImage(allocator, vk_image, &alloc_create_info, &allocation, &alloc_info);
|
VkResult err = vmaCreateImage(allocator, &create_info, &alloc_create_info, &vk_image, &allocation, &alloc_info);
|
||||||
ERR_FAIL_COND_V_MSG(err, TextureID(), "Can't allocate memory for image, error: " + itos(err) + ".");
|
ERR_FAIL_COND_V_MSG(err, TextureID(), "vmaCreateImage failed with error " + itos(err) + ".");
|
||||||
err = vmaBindImageMemory2(allocator, allocation, 0, vk_image, nullptr);
|
} else {
|
||||||
ERR_FAIL_COND_V_MSG(err, TextureID(), "Can't bind memory to image, error: " + itos(err) + ".");
|
VkResult err = vkCreateImage(vk_device, &create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_IMAGE), &vk_image);
|
||||||
|
ERR_FAIL_COND_V_MSG(err, TextureID(), "vkCreateImage failed with error " + itos(err) + ".");
|
||||||
|
err = vmaAllocateMemoryForImage(allocator, vk_image, &alloc_create_info, &allocation, &alloc_info);
|
||||||
|
ERR_FAIL_COND_V_MSG(err, TextureID(), "Can't allocate memory for image, error: " + itos(err) + ".");
|
||||||
|
err = vmaBindImageMemory2(allocator, allocation, 0, vk_image, nullptr);
|
||||||
|
ERR_FAIL_COND_V_MSG(err, TextureID(), "Can't bind memory to image, error: " + itos(err) + ".");
|
||||||
|
}
|
||||||
|
|
||||||
// Create view.
|
// Create view.
|
||||||
|
|
||||||
|
|
@ -1845,10 +1869,15 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create(const TextureFormat &
|
||||||
}
|
}
|
||||||
|
|
||||||
VkImageView vk_image_view = VK_NULL_HANDLE;
|
VkImageView vk_image_view = VK_NULL_HANDLE;
|
||||||
err = vkCreateImageView(vk_device, &image_view_create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_IMAGE_VIEW), &vk_image_view);
|
VkResult err = vkCreateImageView(vk_device, &image_view_create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_IMAGE_VIEW), &vk_image_view);
|
||||||
if (err) {
|
if (err) {
|
||||||
vkDestroyImage(vk_device, vk_image, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_IMAGE));
|
if (!Engine::get_singleton()->is_extra_gpu_memory_tracking_enabled()) {
|
||||||
vmaFreeMemory(allocator, allocation);
|
vmaDestroyImage(allocator, vk_image, allocation);
|
||||||
|
} else {
|
||||||
|
vkDestroyImage(vk_device, vk_image, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_IMAGE));
|
||||||
|
vmaFreeMemory(allocator, allocation);
|
||||||
|
}
|
||||||
|
|
||||||
ERR_FAIL_COND_V_MSG(err, TextureID(), "vkCreateImageView failed with error " + itos(err) + ".");
|
ERR_FAIL_COND_V_MSG(err, TextureID(), "vkCreateImageView failed with error " + itos(err) + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2023,8 +2052,12 @@ void RenderingDeviceDriverVulkan::texture_free(TextureID p_texture) {
|
||||||
TextureInfo *tex_info = (TextureInfo *)p_texture.id;
|
TextureInfo *tex_info = (TextureInfo *)p_texture.id;
|
||||||
vkDestroyImageView(vk_device, tex_info->vk_view, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_IMAGE_VIEW));
|
vkDestroyImageView(vk_device, tex_info->vk_view, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_IMAGE_VIEW));
|
||||||
if (tex_info->allocation.handle) {
|
if (tex_info->allocation.handle) {
|
||||||
vkDestroyImage(vk_device, tex_info->vk_image, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_BUFFER));
|
if (!Engine::get_singleton()->is_extra_gpu_memory_tracking_enabled()) {
|
||||||
vmaFreeMemory(allocator, tex_info->allocation.handle);
|
vmaDestroyImage(allocator, tex_info->vk_view_create_info.image, tex_info->allocation.handle);
|
||||||
|
} else {
|
||||||
|
vkDestroyImage(vk_device, tex_info->vk_image, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_BUFFER));
|
||||||
|
vmaFreeMemory(allocator, tex_info->allocation.handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
VersatileResource::free(resources_allocator, tex_info);
|
VersatileResource::free(resources_allocator, tex_info);
|
||||||
}
|
}
|
||||||
|
|
@ -5886,6 +5919,10 @@ uint64_t RenderingDeviceDriverVulkan::limit_get(Limit p_limit) {
|
||||||
return vrs_capabilities.max_fragment_size.x;
|
return vrs_capabilities.max_fragment_size.x;
|
||||||
case LIMIT_VRS_MAX_FRAGMENT_HEIGHT:
|
case LIMIT_VRS_MAX_FRAGMENT_HEIGHT:
|
||||||
return vrs_capabilities.max_fragment_size.y;
|
return vrs_capabilities.max_fragment_size.y;
|
||||||
|
case LIMIT_MAX_SHADER_VARYINGS:
|
||||||
|
// The Vulkan spec states that built in varyings like gl_FragCoord should count against this, but in
|
||||||
|
// practice, that doesn't seem to be the case. The validation layers don't even complain.
|
||||||
|
return MIN(limits.maxVertexOutputComponents / 4, limits.maxFragmentInputComponents / 4);
|
||||||
default:
|
default:
|
||||||
ERR_FAIL_V(0);
|
ERR_FAIL_V(0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -308,6 +308,8 @@ void FileAccessWindows::seek_end(int64_t p_position) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t FileAccessWindows::get_position() const {
|
uint64_t FileAccessWindows::get_position() const {
|
||||||
|
ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use.");
|
||||||
|
|
||||||
int64_t aux_position = _ftelli64(f);
|
int64_t aux_position = _ftelli64(f);
|
||||||
if (aux_position < 0) {
|
if (aux_position < 0) {
|
||||||
check_errors();
|
check_errors();
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@
|
||||||
#include "editor/multi_node_edit.h"
|
#include "editor/multi_node_edit.h"
|
||||||
#include "editor/plugins/editor_context_menu_plugin.h"
|
#include "editor/plugins/editor_context_menu_plugin.h"
|
||||||
#include "editor/plugins/editor_plugin.h"
|
#include "editor/plugins/editor_plugin.h"
|
||||||
|
#include "scene/property_utils.h"
|
||||||
#include "scene/resources/packed_scene.h"
|
#include "scene/resources/packed_scene.h"
|
||||||
|
|
||||||
void EditorSelectionHistory::cleanup_history() {
|
void EditorSelectionHistory::cleanup_history() {
|
||||||
|
|
@ -536,15 +537,18 @@ Variant EditorData::instantiate_custom_type(const String &p_type, const String &
|
||||||
if (get_custom_types()[p_inherits][i].name == p_type) {
|
if (get_custom_types()[p_inherits][i].name == p_type) {
|
||||||
Ref<Script> script = get_custom_types()[p_inherits][i].script;
|
Ref<Script> script = get_custom_types()[p_inherits][i].script;
|
||||||
|
|
||||||
Variant ob = ClassDB::instantiate(p_inherits);
|
// Store in a variant to initialize the refcount if needed.
|
||||||
ERR_FAIL_COND_V(!ob, Variant());
|
Variant v = ClassDB::instantiate(p_inherits);
|
||||||
|
ERR_FAIL_COND_V(!v, Variant());
|
||||||
|
Object *ob = v;
|
||||||
|
|
||||||
Node *n = Object::cast_to<Node>(ob);
|
Node *n = Object::cast_to<Node>(ob);
|
||||||
if (n) {
|
if (n) {
|
||||||
n->set_name(p_type);
|
n->set_name(p_type);
|
||||||
}
|
}
|
||||||
n->set_meta(SceneStringName(_custom_type_script), script);
|
PropertyUtils::assign_custom_type_script(ob, script);
|
||||||
((Object *)ob)->set_script(script);
|
ob->set_script(script);
|
||||||
return ob;
|
return v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -988,12 +992,13 @@ Variant EditorData::script_class_instance(const String &p_class) {
|
||||||
Ref<Script> script = script_class_load_script(p_class);
|
Ref<Script> script = script_class_load_script(p_class);
|
||||||
if (script.is_valid()) {
|
if (script.is_valid()) {
|
||||||
// Store in a variant to initialize the refcount if needed.
|
// Store in a variant to initialize the refcount if needed.
|
||||||
Variant obj = ClassDB::instantiate(script->get_instance_base_type());
|
Variant v = ClassDB::instantiate(script->get_instance_base_type());
|
||||||
if (obj) {
|
if (v) {
|
||||||
Object::cast_to<Object>(obj)->set_meta(SceneStringName(_custom_type_script), script);
|
Object *obj = v;
|
||||||
obj.operator Object *()->set_script(script);
|
PropertyUtils::assign_custom_type_script(obj, script);
|
||||||
|
obj->set_script(script);
|
||||||
}
|
}
|
||||||
return obj;
|
return v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Variant();
|
return Variant();
|
||||||
|
|
@ -1041,24 +1046,23 @@ void EditorData::script_class_set_name(const String &p_path, const StringName &p
|
||||||
_script_class_file_to_path[p_path] = p_class;
|
_script_class_file_to_path[p_path] = p_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorData::script_class_save_icon_paths() {
|
void EditorData::script_class_save_global_classes() {
|
||||||
Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
|
List<StringName> global_classes;
|
||||||
|
ScriptServer::get_global_class_list(&global_classes);
|
||||||
Dictionary d;
|
Array array_classes;
|
||||||
for (const KeyValue<StringName, String> &E : _script_class_icon_paths) {
|
for (const StringName &class_name : global_classes) {
|
||||||
if (ScriptServer::is_global_class(E.key)) {
|
Dictionary d;
|
||||||
d[E.key] = E.value;
|
String *icon = _script_class_icon_paths.getptr(class_name);
|
||||||
}
|
d["class"] = class_name;
|
||||||
|
d["language"] = ScriptServer::get_global_class_language(class_name);
|
||||||
|
d["path"] = ScriptServer::get_global_class_path(class_name);
|
||||||
|
d["base"] = ScriptServer::get_global_class_base(class_name);
|
||||||
|
d["icon"] = icon ? *icon : String();
|
||||||
|
d["is_abstract"] = ScriptServer::is_global_class_abstract(class_name);
|
||||||
|
d["is_tool"] = ScriptServer::is_global_class_tool(class_name);
|
||||||
|
array_classes.push_back(d);
|
||||||
}
|
}
|
||||||
|
ProjectSettings::get_singleton()->store_global_class_list(array_classes);
|
||||||
for (int i = 0; i < script_classes.size(); i++) {
|
|
||||||
Dictionary d2 = script_classes[i];
|
|
||||||
if (!d2.has("class")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
d2["icon"] = d.get(d2["class"], "");
|
|
||||||
}
|
|
||||||
ProjectSettings::get_singleton()->store_global_class_list(script_classes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorData::script_class_load_icon_paths() {
|
void EditorData::script_class_load_icon_paths() {
|
||||||
|
|
|
||||||
|
|
@ -251,7 +251,7 @@ public:
|
||||||
String script_class_get_icon_path(const String &p_class, bool *r_valid = nullptr) const;
|
String script_class_get_icon_path(const String &p_class, bool *r_valid = nullptr) const;
|
||||||
void script_class_set_icon_path(const String &p_class, const String &p_icon_path);
|
void script_class_set_icon_path(const String &p_class, const String &p_icon_path);
|
||||||
void script_class_clear_icon_paths() { _script_class_icon_paths.clear(); }
|
void script_class_clear_icon_paths() { _script_class_icon_paths.clear(); }
|
||||||
void script_class_save_icon_paths();
|
void script_class_save_global_classes();
|
||||||
void script_class_load_icon_paths();
|
void script_class_load_icon_paths();
|
||||||
|
|
||||||
Ref<Texture2D> extension_class_get_icon(const String &p_class) const;
|
Ref<Texture2D> extension_class_get_icon(const String &p_class) const;
|
||||||
|
|
|
||||||
|
|
@ -319,7 +319,12 @@ void EditorFileSystem::_first_scan_filesystem() {
|
||||||
_first_scan_process_scripts(first_scan_root_dir, gdextension_extensions, existing_class_names, extensions);
|
_first_scan_process_scripts(first_scan_root_dir, gdextension_extensions, existing_class_names, extensions);
|
||||||
|
|
||||||
// Removing invalid global class to prevent having invalid paths in ScriptServer.
|
// Removing invalid global class to prevent having invalid paths in ScriptServer.
|
||||||
_remove_invalid_global_class_names(existing_class_names);
|
bool save_scripts = _remove_invalid_global_class_names(existing_class_names);
|
||||||
|
|
||||||
|
// If a global class is found or removed, we sync global_script_class_cache.cfg with the ScriptServer
|
||||||
|
if (!existing_class_names.is_empty() || save_scripts) {
|
||||||
|
EditorNode::get_editor_data().script_class_save_global_classes();
|
||||||
|
}
|
||||||
|
|
||||||
// Processing extensions to add new extensions or remove invalid ones.
|
// Processing extensions to add new extensions or remove invalid ones.
|
||||||
// Important to do it in the first scan so custom types, new class names, custom importers, etc...
|
// Important to do it in the first scan so custom types, new class names, custom importers, etc...
|
||||||
|
|
@ -1618,7 +1623,7 @@ void EditorFileSystem::_thread_func_sources(void *_userdata) {
|
||||||
efs->scanning_changes_done.set();
|
efs->scanning_changes_done.set();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorFileSystem::_remove_invalid_global_class_names(const HashSet<String> &p_existing_class_names) {
|
bool EditorFileSystem::_remove_invalid_global_class_names(const HashSet<String> &p_existing_class_names) {
|
||||||
List<StringName> global_classes;
|
List<StringName> global_classes;
|
||||||
bool must_save = false;
|
bool must_save = false;
|
||||||
ScriptServer::get_global_class_list(&global_classes);
|
ScriptServer::get_global_class_list(&global_classes);
|
||||||
|
|
@ -1628,9 +1633,7 @@ void EditorFileSystem::_remove_invalid_global_class_names(const HashSet<String>
|
||||||
must_save = true;
|
must_save = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (must_save) {
|
return must_save;
|
||||||
ScriptServer::save_global_classes();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String EditorFileSystem::_get_file_by_class_name(EditorFileSystemDirectory *p_dir, const String &p_class_name, EditorFileSystemDirectory::FileInfo *&r_file_info) {
|
String EditorFileSystem::_get_file_by_class_name(EditorFileSystemDirectory *p_dir, const String &p_class_name, EditorFileSystemDirectory::FileInfo *&r_file_info) {
|
||||||
|
|
@ -2043,6 +2046,7 @@ EditorFileSystem::ScriptClassInfo EditorFileSystem::_get_global_script_class(con
|
||||||
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
|
||||||
if (ScriptServer::get_language(i)->handles_global_class_type(p_type)) {
|
if (ScriptServer::get_language(i)->handles_global_class_type(p_type)) {
|
||||||
info.name = ScriptServer::get_language(i)->get_global_class_name(p_path, &info.extends, &info.icon_path, &info.is_abstract, &info.is_tool);
|
info.name = ScriptServer::get_language(i)->get_global_class_name(p_path, &info.extends, &info.icon_path, &info.is_abstract, &info.is_tool);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return info;
|
return info;
|
||||||
|
|
@ -2130,8 +2134,7 @@ void EditorFileSystem::_update_script_classes() {
|
||||||
update_script_paths.clear();
|
update_script_paths.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptServer::save_global_classes();
|
EditorNode::get_editor_data().script_class_save_global_classes();
|
||||||
EditorNode::get_editor_data().script_class_save_icon_paths();
|
|
||||||
|
|
||||||
emit_signal("script_classes_updated");
|
emit_signal("script_classes_updated");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -374,7 +374,7 @@ class EditorFileSystem : public Node {
|
||||||
|
|
||||||
void _update_file_icon_path(EditorFileSystemDirectory::FileInfo *file_info);
|
void _update_file_icon_path(EditorFileSystemDirectory::FileInfo *file_info);
|
||||||
void _update_files_icon_path(EditorFileSystemDirectory *edp = nullptr);
|
void _update_files_icon_path(EditorFileSystemDirectory *edp = nullptr);
|
||||||
void _remove_invalid_global_class_names(const HashSet<String> &p_existing_class_names);
|
bool _remove_invalid_global_class_names(const HashSet<String> &p_existing_class_names);
|
||||||
String _get_file_by_class_name(EditorFileSystemDirectory *p_dir, const String &p_class_name, EditorFileSystemDirectory::FileInfo *&r_file_info);
|
String _get_file_by_class_name(EditorFileSystemDirectory *p_dir, const String &p_class_name, EditorFileSystemDirectory::FileInfo *&r_file_info);
|
||||||
|
|
||||||
void _register_global_class_script(const String &p_search_path, const String &p_target_path, const ScriptClassInfoUpdate &p_script_update);
|
void _register_global_class_script(const String &p_search_path, const String &p_target_path, const ScriptClassInfoUpdate &p_script_update);
|
||||||
|
|
|
||||||
|
|
@ -1017,7 +1017,8 @@ void EditorHelp::_update_doc() {
|
||||||
class_desc->add_text(nbsp); // Otherwise icon borrows hyperlink from `_add_type()`.
|
class_desc->add_text(nbsp); // Otherwise icon borrows hyperlink from `_add_type()`.
|
||||||
_add_type(inherits);
|
_add_type(inherits);
|
||||||
|
|
||||||
inherits = doc->class_list[inherits].inherits;
|
const DocData::ClassDoc *base_class_doc = doc->class_list.getptr(inherits);
|
||||||
|
inherits = base_class_doc ? base_class_doc->inherits : String();
|
||||||
|
|
||||||
if (!inherits.is_empty()) {
|
if (!inherits.is_empty()) {
|
||||||
class_desc->add_text(" < ");
|
class_desc->add_text(" < ");
|
||||||
|
|
@ -3713,7 +3714,8 @@ void EditorHelpBit::_update_labels() {
|
||||||
|
|
||||||
_add_type_to_title({ inherits, String(), false });
|
_add_type_to_title({ inherits, String(), false });
|
||||||
|
|
||||||
inherits = class_list[inherits].inherits;
|
const DocData::ClassDoc *base_class_doc = class_list.getptr(inherits);
|
||||||
|
inherits = base_class_doc ? base_class_doc->inherits : String();
|
||||||
}
|
}
|
||||||
|
|
||||||
title->pop(); // font_size
|
title->pop(); // font_size
|
||||||
|
|
|
||||||
|
|
@ -414,6 +414,9 @@ void EditorProperty::_notification(int p_what) {
|
||||||
|
|
||||||
int ofs = get_theme_constant(SNAME("font_offset"));
|
int ofs = get_theme_constant(SNAME("font_offset"));
|
||||||
int text_limit = text_size - ofs;
|
int text_limit = text_size - ofs;
|
||||||
|
int base_spacing = EDITOR_GET("interface/theme/base_spacing");
|
||||||
|
int padding = base_spacing * EDSCALE;
|
||||||
|
int half_padding = padding / 2;
|
||||||
|
|
||||||
if (checkable) {
|
if (checkable) {
|
||||||
Ref<Texture2D> checkbox;
|
Ref<Texture2D> checkbox;
|
||||||
|
|
@ -444,19 +447,31 @@ void EditorProperty::_notification(int p_what) {
|
||||||
|
|
||||||
if (can_revert && !is_read_only()) {
|
if (can_revert && !is_read_only()) {
|
||||||
Ref<Texture2D> reload_icon = get_editor_theme_icon(SNAME("ReloadSmall"));
|
Ref<Texture2D> reload_icon = get_editor_theme_icon(SNAME("ReloadSmall"));
|
||||||
text_limit -= reload_icon->get_width() + get_theme_constant(SNAME("h_separation"), SNAME("Tree"));
|
text_limit -= reload_icon->get_width() + half_padding + get_theme_constant(SNAME("h_separation"), SNAME("Tree"));
|
||||||
revert_rect = Rect2(ofs + text_limit, (size.height - reload_icon->get_height()) / 2, reload_icon->get_width(), reload_icon->get_height());
|
revert_rect = Rect2(ofs + text_limit, 0, reload_icon->get_width() + padding + (1 * EDSCALE), size.height);
|
||||||
|
|
||||||
|
Point2 rtl_pos = Point2();
|
||||||
|
if (rtl) {
|
||||||
|
rtl_pos = Point2(size.width - revert_rect.position.x - (reload_icon->get_width() + padding + (1 * EDSCALE)), revert_rect.position.y);
|
||||||
|
}
|
||||||
|
|
||||||
Color color2(1, 1, 1);
|
Color color2(1, 1, 1);
|
||||||
if (revert_hover) {
|
if (revert_hover) {
|
||||||
color2.r *= 1.2;
|
color2.r *= 1.2;
|
||||||
color2.g *= 1.2;
|
color2.g *= 1.2;
|
||||||
color2.b *= 1.2;
|
color2.b *= 1.2;
|
||||||
|
|
||||||
|
Ref<StyleBox> sb_hover = get_theme_stylebox(SceneStringName(hover), "Button");
|
||||||
|
if (rtl) {
|
||||||
|
draw_style_box(sb_hover, Rect2(rtl_pos, revert_rect.size));
|
||||||
|
} else {
|
||||||
|
draw_style_box(sb_hover, revert_rect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (rtl) {
|
if (rtl) {
|
||||||
draw_texture(reload_icon, Vector2(size.width - revert_rect.position.x - reload_icon->get_width(), revert_rect.position.y), color2);
|
draw_texture(reload_icon, rtl_pos + Point2(padding, size.height - reload_icon->get_height()) / 2, color2);
|
||||||
} else {
|
} else {
|
||||||
draw_texture(reload_icon, revert_rect.position, color2);
|
draw_texture(reload_icon, revert_rect.position + Point2(padding, size.height - reload_icon->get_height()) / 2, color2);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
revert_rect = Rect2();
|
revert_rect = Rect2();
|
||||||
|
|
@ -494,19 +509,32 @@ void EditorProperty::_notification(int p_what) {
|
||||||
key = get_editor_theme_icon(SNAME("Key"));
|
key = get_editor_theme_icon(SNAME("Key"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ofs -= key->get_width() + get_theme_constant(SNAME("h_separation"), SNAME("Tree"));
|
ofs -= key->get_width() + half_padding + get_theme_constant(SNAME("h_separation"), SNAME("Tree"));
|
||||||
|
keying_rect = Rect2(ofs, 0, key->get_width() + padding, size.height);
|
||||||
|
|
||||||
|
Point2 rtl_pos = Point2();
|
||||||
|
if (rtl) {
|
||||||
|
rtl_pos = Point2(size.width - keying_rect.position.x - (key->get_width() + padding), keying_rect.position.y);
|
||||||
|
}
|
||||||
|
|
||||||
Color color2(1, 1, 1);
|
Color color2(1, 1, 1);
|
||||||
if (keying_hover) {
|
if (keying_hover) {
|
||||||
color2.r *= 1.2;
|
color2.r *= 1.2;
|
||||||
color2.g *= 1.2;
|
color2.g *= 1.2;
|
||||||
color2.b *= 1.2;
|
color2.b *= 1.2;
|
||||||
|
|
||||||
|
Ref<StyleBox> sb_hover = get_theme_stylebox(SceneStringName(hover), "Button");
|
||||||
|
if (rtl) {
|
||||||
|
draw_style_box(sb_hover, Rect2(rtl_pos, keying_rect.size));
|
||||||
|
} else {
|
||||||
|
draw_style_box(sb_hover, keying_rect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
keying_rect = Rect2(ofs, ((size.height - key->get_height()) / 2), key->get_width(), key->get_height());
|
|
||||||
if (rtl) {
|
if (rtl) {
|
||||||
draw_texture(key, Vector2(size.width - keying_rect.position.x - key->get_width(), keying_rect.position.y), color2);
|
draw_texture(key, rtl_pos + Point2(padding, size.height - key->get_height()) / 2, color2);
|
||||||
} else {
|
} else {
|
||||||
draw_texture(key, keying_rect.position, color2);
|
draw_texture(key, keying_rect.position + Point2(padding, size.height - key->get_height()) / 2, color2);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -518,19 +546,32 @@ void EditorProperty::_notification(int p_what) {
|
||||||
|
|
||||||
close = get_editor_theme_icon(SNAME("Close"));
|
close = get_editor_theme_icon(SNAME("Close"));
|
||||||
|
|
||||||
ofs -= close->get_width() + get_theme_constant(SNAME("h_separation"), SNAME("Tree"));
|
ofs -= close->get_width() + half_padding + get_theme_constant(SNAME("h_separation"), SNAME("Tree"));
|
||||||
|
delete_rect = Rect2(ofs, 0, close->get_width() + padding, size.height);
|
||||||
|
|
||||||
|
Point2 rtl_pos = Point2();
|
||||||
|
if (rtl) {
|
||||||
|
rtl_pos = Point2(size.width - delete_rect.position.x - (close->get_width() + padding), delete_rect.position.y);
|
||||||
|
}
|
||||||
|
|
||||||
Color color2(1, 1, 1);
|
Color color2(1, 1, 1);
|
||||||
if (delete_hover) {
|
if (delete_hover) {
|
||||||
color2.r *= 1.2;
|
color2.r *= 1.2;
|
||||||
color2.g *= 1.2;
|
color2.g *= 1.2;
|
||||||
color2.b *= 1.2;
|
color2.b *= 1.2;
|
||||||
|
|
||||||
|
Ref<StyleBox> sb_hover = get_theme_stylebox(SceneStringName(hover), "Button");
|
||||||
|
if (rtl) {
|
||||||
|
draw_style_box(sb_hover, Rect2(rtl_pos, delete_rect.size));
|
||||||
|
} else {
|
||||||
|
draw_style_box(sb_hover, delete_rect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
delete_rect = Rect2(ofs, ((size.height - close->get_height()) / 2), close->get_width(), close->get_height());
|
|
||||||
if (rtl) {
|
if (rtl) {
|
||||||
draw_texture(close, Vector2(size.width - delete_rect.position.x - close->get_width(), delete_rect.position.y), color2);
|
draw_texture(close, rtl_pos + Point2(padding, size.height - close->get_height()) / 2, color2);
|
||||||
} else {
|
} else {
|
||||||
draw_texture(close, delete_rect.position, color2);
|
draw_texture(close, delete_rect.position + Point2(padding, size.height - close->get_height()) / 2, color2);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
delete_rect = Rect2();
|
delete_rect = Rect2();
|
||||||
|
|
@ -547,6 +588,15 @@ void EditorProperty::_notification(int p_what) {
|
||||||
get_parent()->disconnect(SceneStringName(theme_changed), callable_mp(this, &EditorProperty::_update_property_bg));
|
get_parent()->disconnect(SceneStringName(theme_changed), callable_mp(this, &EditorProperty::_update_property_bg));
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
case NOTIFICATION_MOUSE_EXIT: {
|
||||||
|
if (keying_hover || revert_hover || check_hover || delete_hover) {
|
||||||
|
keying_hover = false;
|
||||||
|
revert_hover = false;
|
||||||
|
check_hover = false;
|
||||||
|
delete_hover = false;
|
||||||
|
queue_redraw();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3782,13 +3832,16 @@ void EditorInspector::update_tree() {
|
||||||
ep->set_doc_path(doc_path);
|
ep->set_doc_path(doc_path);
|
||||||
ep->set_internal(p.usage & PROPERTY_USAGE_INTERNAL);
|
ep->set_internal(p.usage & PROPERTY_USAGE_INTERNAL);
|
||||||
|
|
||||||
ep->update_property();
|
// If this property is favorited, it won't be in the tree yet. So don't do this setup right now.
|
||||||
ep->_update_flags();
|
if (ep->is_inside_tree()) {
|
||||||
ep->update_editor_property_status();
|
ep->update_property();
|
||||||
ep->update_cache();
|
ep->_update_flags();
|
||||||
|
ep->update_editor_property_status();
|
||||||
|
ep->update_cache();
|
||||||
|
|
||||||
if (current_selected && ep->property == current_selected) {
|
if (current_selected && ep->property == current_selected) {
|
||||||
ep->select(current_focusable);
|
ep->select(current_focusable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3841,6 +3894,16 @@ void EditorInspector::update_tree() {
|
||||||
|
|
||||||
for (EditorProperty *ep : KV2.value) {
|
for (EditorProperty *ep : KV2.value) {
|
||||||
vbox->add_child(ep);
|
vbox->add_child(ep);
|
||||||
|
|
||||||
|
// Now that it's inside the tree, do the setup.
|
||||||
|
ep->update_property();
|
||||||
|
ep->_update_flags();
|
||||||
|
ep->update_editor_property_status();
|
||||||
|
ep->update_cache();
|
||||||
|
|
||||||
|
if (current_selected && ep->property == current_selected) {
|
||||||
|
ep->select(current_focusable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4465,6 +4528,14 @@ void EditorInspector::_node_removed(Node *p_node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorInspector::_gui_focus_changed(Control *p_control) {
|
||||||
|
if (!is_visible_in_tree() && !is_following_focus()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Don't follow focus when the inspector nor any of its children is focused. Prevents potential jumping when gaining focus.
|
||||||
|
set_follow_focus(has_focus() || child_has_focus());
|
||||||
|
}
|
||||||
|
|
||||||
void EditorInspector::_update_current_favorites() {
|
void EditorInspector::_update_current_favorites() {
|
||||||
current_favorites.clear();
|
current_favorites.clear();
|
||||||
if (!can_favorite) {
|
if (!can_favorite) {
|
||||||
|
|
@ -4662,6 +4733,10 @@ void EditorInspector::_notification(int p_what) {
|
||||||
if (!is_sub_inspector()) {
|
if (!is_sub_inspector()) {
|
||||||
get_tree()->connect("node_removed", callable_mp(this, &EditorInspector::_node_removed));
|
get_tree()->connect("node_removed", callable_mp(this, &EditorInspector::_node_removed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Viewport *viewport = get_viewport();
|
||||||
|
ERR_FAIL_NULL(viewport);
|
||||||
|
viewport->connect("gui_focus_changed", callable_mp(this, &EditorInspector::_gui_focus_changed));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case NOTIFICATION_PREDELETE: {
|
case NOTIFICATION_PREDELETE: {
|
||||||
|
|
@ -4744,15 +4819,6 @@ void EditorInspector::_notification(int p_what) {
|
||||||
update_tree();
|
update_tree();
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case NOTIFICATION_FOCUS_ENTER: {
|
|
||||||
set_follow_focus(true);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NOTIFICATION_FOCUS_EXIT: {
|
|
||||||
// Don't follow focus when the inspector is not focused. Prevents potential jumping when gaining focus.
|
|
||||||
set_follow_focus(false);
|
|
||||||
} break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -606,6 +606,7 @@ class EditorInspector : public ScrollContainer {
|
||||||
void _clear_current_favorites();
|
void _clear_current_favorites();
|
||||||
|
|
||||||
void _node_removed(Node *p_node);
|
void _node_removed(Node *p_node);
|
||||||
|
void _gui_focus_changed(Control *p_control);
|
||||||
|
|
||||||
HashMap<StringName, int> per_array_page;
|
HashMap<StringName, int> per_array_page;
|
||||||
void _page_change_request(int p_new_page, const StringName &p_array_prefix);
|
void _page_change_request(int p_new_page, const StringName &p_array_prefix);
|
||||||
|
|
|
||||||
|
|
@ -649,17 +649,11 @@ void EditorNode::_notification(int p_what) {
|
||||||
|
|
||||||
OS::get_singleton()->benchmark_begin_measure("Editor", "First Scan");
|
OS::get_singleton()->benchmark_begin_measure("Editor", "First Scan");
|
||||||
|
|
||||||
if (run_surface_upgrade_tool) {
|
if (run_surface_upgrade_tool || run_uid_upgrade_tool) {
|
||||||
run_surface_upgrade_tool = false;
|
EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &EditorNode::_execute_upgrades), CONNECT_ONE_SHOT);
|
||||||
SurfaceUpgradeTool::get_singleton()->connect("upgrade_finished", callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan), CONNECT_ONE_SHOT);
|
|
||||||
SurfaceUpgradeTool::get_singleton()->finish_upgrade();
|
|
||||||
} else if (run_uid_upgrade_tool) {
|
|
||||||
run_uid_upgrade_tool = false;
|
|
||||||
UIDUpgradeTool::get_singleton()->connect("upgrade_finished", callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan), CONNECT_ONE_SHOT);
|
|
||||||
UIDUpgradeTool::get_singleton()->finish_upgrade();
|
|
||||||
} else {
|
|
||||||
EditorFileSystem::get_singleton()->scan();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EditorFileSystem::get_singleton()->scan();
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
|
@ -901,6 +895,20 @@ void EditorNode::_update_update_spinner() {
|
||||||
OS::get_singleton()->set_low_processor_usage_mode(!update_continuously);
|
OS::get_singleton()->set_low_processor_usage_mode(!update_continuously);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorNode::_execute_upgrades() {
|
||||||
|
if (run_surface_upgrade_tool) {
|
||||||
|
run_surface_upgrade_tool = false;
|
||||||
|
// Execute another scan to reimport the modified files.
|
||||||
|
SurfaceUpgradeTool::get_singleton()->connect("upgrade_finished", callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan), CONNECT_ONE_SHOT);
|
||||||
|
SurfaceUpgradeTool::get_singleton()->finish_upgrade();
|
||||||
|
} else if (run_uid_upgrade_tool) {
|
||||||
|
run_uid_upgrade_tool = false;
|
||||||
|
// Execute another scan to reimport the modified files.
|
||||||
|
UIDUpgradeTool::get_singleton()->connect("upgrade_finished", callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan), CONNECT_ONE_SHOT);
|
||||||
|
UIDUpgradeTool::get_singleton()->finish_upgrade();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EditorNode::init_plugins() {
|
void EditorNode::init_plugins() {
|
||||||
_initializing_plugins = true;
|
_initializing_plugins = true;
|
||||||
Vector<String> addons;
|
Vector<String> addons;
|
||||||
|
|
@ -2049,45 +2057,7 @@ void EditorNode::try_autosave() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorNode::restart_editor(bool p_goto_project_manager) {
|
void EditorNode::restart_editor(bool p_goto_project_manager) {
|
||||||
exiting = true;
|
_menu_option_confirm(p_goto_project_manager ? PROJECT_QUIT_TO_PROJECT_MANAGER : PROJECT_RELOAD_CURRENT_PROJECT, false);
|
||||||
|
|
||||||
if (project_run_bar->is_playing()) {
|
|
||||||
project_run_bar->stop_playing();
|
|
||||||
}
|
|
||||||
|
|
||||||
String to_reopen;
|
|
||||||
if (!p_goto_project_manager && get_tree()->get_edited_scene_root()) {
|
|
||||||
to_reopen = get_tree()->get_edited_scene_root()->get_scene_file_path();
|
|
||||||
}
|
|
||||||
|
|
||||||
_exit_editor(EXIT_SUCCESS);
|
|
||||||
|
|
||||||
List<String> args;
|
|
||||||
for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_TOOL)) {
|
|
||||||
args.push_back(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_goto_project_manager) {
|
|
||||||
args.push_back("--project-manager");
|
|
||||||
|
|
||||||
// Setup working directory.
|
|
||||||
const String exec_dir = OS::get_singleton()->get_executable_path().get_base_dir();
|
|
||||||
if (!exec_dir.is_empty()) {
|
|
||||||
args.push_back("--path");
|
|
||||||
args.push_back(exec_dir);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
args.push_back("--path");
|
|
||||||
args.push_back(ProjectSettings::get_singleton()->get_resource_path());
|
|
||||||
|
|
||||||
args.push_back("-e");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!to_reopen.is_empty()) {
|
|
||||||
args.push_back(to_reopen);
|
|
||||||
}
|
|
||||||
|
|
||||||
OS::get_singleton()->set_restart_on_exit(true, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorNode::_save_all_scenes() {
|
void EditorNode::_save_all_scenes() {
|
||||||
|
|
@ -3472,10 +3442,10 @@ void EditorNode::_discard_changes(const String &p_str) {
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case PROJECT_QUIT_TO_PROJECT_MANAGER: {
|
case PROJECT_QUIT_TO_PROJECT_MANAGER: {
|
||||||
restart_editor(true);
|
_restart_editor(true);
|
||||||
} break;
|
} break;
|
||||||
case PROJECT_RELOAD_CURRENT_PROJECT: {
|
case PROJECT_RELOAD_CURRENT_PROJECT: {
|
||||||
restart_editor();
|
_restart_editor();
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4751,7 +4721,7 @@ Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) cons
|
||||||
|
|
||||||
const Node *node = Object::cast_to<const Node>(p_object);
|
const Node *node = Object::cast_to<const Node>(p_object);
|
||||||
if (node && node->has_meta(SceneStringName(_custom_type_script))) {
|
if (node && node->has_meta(SceneStringName(_custom_type_script))) {
|
||||||
return node->get_meta(SceneStringName(_custom_type_script));
|
return PropertyUtils::get_custom_type_script(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Script> scr = p_object->get_script();
|
Ref<Script> scr = p_object->get_script();
|
||||||
|
|
@ -5500,11 +5470,11 @@ bool EditorNode::has_scenes_in_session() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorNode::undo() {
|
void EditorNode::undo() {
|
||||||
trigger_menu_option(FILE_UNDO, true);
|
_menu_option_confirm(FILE_UNDO, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorNode::redo() {
|
void EditorNode::redo() {
|
||||||
trigger_menu_option(FILE_REDO, true);
|
_menu_option_confirm(FILE_REDO, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditorNode::ensure_main_scene(bool p_from_native) {
|
bool EditorNode::ensure_main_scene(bool p_from_native) {
|
||||||
|
|
@ -5672,6 +5642,48 @@ bool EditorNode::_is_closing_editor() const {
|
||||||
return tab_closing_menu_option == FILE_QUIT || tab_closing_menu_option == PROJECT_QUIT_TO_PROJECT_MANAGER || tab_closing_menu_option == PROJECT_RELOAD_CURRENT_PROJECT;
|
return tab_closing_menu_option == FILE_QUIT || tab_closing_menu_option == PROJECT_QUIT_TO_PROJECT_MANAGER || tab_closing_menu_option == PROJECT_RELOAD_CURRENT_PROJECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditorNode::_restart_editor(bool p_goto_project_manager) {
|
||||||
|
exiting = true;
|
||||||
|
|
||||||
|
if (project_run_bar->is_playing()) {
|
||||||
|
project_run_bar->stop_playing();
|
||||||
|
}
|
||||||
|
|
||||||
|
String to_reopen;
|
||||||
|
if (!p_goto_project_manager && get_tree()->get_edited_scene_root()) {
|
||||||
|
to_reopen = get_tree()->get_edited_scene_root()->get_scene_file_path();
|
||||||
|
}
|
||||||
|
|
||||||
|
_exit_editor(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
List<String> args;
|
||||||
|
for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_TOOL)) {
|
||||||
|
args.push_back(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_goto_project_manager) {
|
||||||
|
args.push_back("--project-manager");
|
||||||
|
|
||||||
|
// Setup working directory.
|
||||||
|
const String exec_dir = OS::get_singleton()->get_executable_path().get_base_dir();
|
||||||
|
if (!exec_dir.is_empty()) {
|
||||||
|
args.push_back("--path");
|
||||||
|
args.push_back(exec_dir);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args.push_back("--path");
|
||||||
|
args.push_back(ProjectSettings::get_singleton()->get_resource_path());
|
||||||
|
|
||||||
|
args.push_back("-e");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!to_reopen.is_empty()) {
|
||||||
|
args.push_back(to_reopen);
|
||||||
|
}
|
||||||
|
|
||||||
|
OS::get_singleton()->set_restart_on_exit(true, args);
|
||||||
|
}
|
||||||
|
|
||||||
void EditorNode::_scene_tab_closed(int p_tab) {
|
void EditorNode::_scene_tab_closed(int p_tab) {
|
||||||
current_menu_option = SCENE_TAB_CLOSE;
|
current_menu_option = SCENE_TAB_CLOSE;
|
||||||
tab_closing_idx = p_tab;
|
tab_closing_idx = p_tab;
|
||||||
|
|
|
||||||
|
|
@ -624,6 +624,7 @@ private:
|
||||||
|
|
||||||
void _proceed_closing_scene_tabs();
|
void _proceed_closing_scene_tabs();
|
||||||
bool _is_closing_editor() const;
|
bool _is_closing_editor() const;
|
||||||
|
void _restart_editor(bool p_goto_project_manager = false);
|
||||||
|
|
||||||
Dictionary _get_main_scene_state();
|
Dictionary _get_main_scene_state();
|
||||||
void _set_main_scene_state(Dictionary p_state, Node *p_for_scene);
|
void _set_main_scene_state(Dictionary p_state, Node *p_for_scene);
|
||||||
|
|
@ -675,6 +676,8 @@ private:
|
||||||
void _progress_dialog_visibility_changed();
|
void _progress_dialog_visibility_changed();
|
||||||
void _load_error_dialog_visibility_changed();
|
void _load_error_dialog_visibility_changed();
|
||||||
|
|
||||||
|
void _execute_upgrades();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class FileSystemDock;
|
friend class FileSystemDock;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -321,6 +321,9 @@ void EditorPropertyTextEnum::update_property() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
option_button->select(default_option);
|
option_button->select(default_option);
|
||||||
|
if (default_option < 0) {
|
||||||
|
option_button->set_text(current_value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -699,6 +702,7 @@ void EditorPropertyEnum::update_property() {
|
||||||
Variant current = get_edited_property_value();
|
Variant current = get_edited_property_value();
|
||||||
if (current.get_type() == Variant::NIL) {
|
if (current.get_type() == Variant::NIL) {
|
||||||
options->select(-1);
|
options->select(-1);
|
||||||
|
options->set_text("<null>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -709,6 +713,8 @@ void EditorPropertyEnum::update_property() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
options->select(-1);
|
||||||
|
options->set_text(itos(which));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorPropertyEnum::setup(const Vector<String> &p_options) {
|
void EditorPropertyEnum::setup(const Vector<String> &p_options) {
|
||||||
|
|
|
||||||
|
|
@ -1525,7 +1525,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
||||||
custom_list.append_array(export_plugins[i]->_get_export_features(Ref<EditorExportPlatform>(this), p_debug));
|
custom_list.append_array(export_plugins[i]->_get_export_features(Ref<EditorExportPlatform>(this), p_debug));
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectSettings::CustomMap custom_map;
|
ProjectSettings::CustomMap custom_map = get_custom_project_settings(p_preset);
|
||||||
if (path_remaps.size()) {
|
if (path_remaps.size()) {
|
||||||
if (true) { //new remap mode, use always as it's friendlier with multiple .pck exports
|
if (true) { //new remap mode, use always as it's friendlier with multiple .pck exports
|
||||||
for (int i = 0; i < path_remaps.size(); i += 2) {
|
for (int i = 0; i < path_remaps.size(); i += 2) {
|
||||||
|
|
|
||||||
|
|
@ -336,6 +336,7 @@ public:
|
||||||
virtual void get_platform_features(List<String> *r_features) const = 0;
|
virtual void get_platform_features(List<String> *r_features) const = 0;
|
||||||
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) {}
|
virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) {}
|
||||||
virtual String get_debug_protocol() const { return "tcp://"; }
|
virtual String get_debug_protocol() const { return "tcp://"; }
|
||||||
|
virtual HashMap<String, Variant> get_custom_project_settings(const Ref<EditorExportPreset> &p_preset) const { return HashMap<String, Variant>(); }
|
||||||
|
|
||||||
EditorExportPlatform();
|
EditorExportPlatform();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1573,8 +1573,7 @@ void FileSystemDock::_update_resource_paths_after_move(const HashMap<String, Str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptServer::save_global_classes();
|
EditorNode::get_editor_data().script_class_save_global_classes();
|
||||||
EditorNode::get_editor_data().script_class_save_icon_paths();
|
|
||||||
EditorFileSystem::get_singleton()->emit_signal(SNAME("script_classes_updated"));
|
EditorFileSystem::get_singleton()->emit_signal(SNAME("script_classes_updated"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1708,8 +1707,7 @@ void FileSystemDock::_resource_removed(const Ref<Resource> &p_resource) {
|
||||||
const Ref<Script> &scr = p_resource;
|
const Ref<Script> &scr = p_resource;
|
||||||
if (scr.is_valid()) {
|
if (scr.is_valid()) {
|
||||||
ScriptServer::remove_global_class_by_path(scr->get_path());
|
ScriptServer::remove_global_class_by_path(scr->get_path());
|
||||||
ScriptServer::save_global_classes();
|
EditorNode::get_editor_data().script_class_save_global_classes();
|
||||||
EditorNode::get_editor_data().script_class_save_icon_paths();
|
|
||||||
EditorFileSystem::get_singleton()->emit_signal(SNAME("script_classes_updated"));
|
EditorFileSystem::get_singleton()->emit_signal(SNAME("script_classes_updated"));
|
||||||
}
|
}
|
||||||
emit_signal(SNAME("resource_removed"), p_resource);
|
emit_signal(SNAME("resource_removed"), p_resource);
|
||||||
|
|
|
||||||
|
|
@ -127,8 +127,26 @@ void EditorFileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_file
|
||||||
emit_signal(SNAME("files_selected"), files);
|
emit_signal(SNAME("files_selected"), files);
|
||||||
} else {
|
} else {
|
||||||
if (mode == FILE_MODE_SAVE_FILE) {
|
if (mode == FILE_MODE_SAVE_FILE) {
|
||||||
if (p_filter != 0 && p_filter != filter->get_item_count() - 1) {
|
bool valid = false;
|
||||||
bool valid = false;
|
|
||||||
|
if (p_filter == filter->get_item_count() - 1) {
|
||||||
|
valid = true; // Match none.
|
||||||
|
} else if (filters.size() > 1 && p_filter == 0) {
|
||||||
|
// Match all filters.
|
||||||
|
for (int i = 0; i < filters.size(); i++) {
|
||||||
|
String flt = filters[i].get_slice(";", 0);
|
||||||
|
for (int j = 0; j < flt.get_slice_count(","); j++) {
|
||||||
|
String str = flt.get_slice(",", j).strip_edges();
|
||||||
|
if (f.matchn(str)) {
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
int idx = p_filter;
|
int idx = p_filter;
|
||||||
if (filters.size() > 1) {
|
if (filters.size() > 1) {
|
||||||
idx--;
|
idx--;
|
||||||
|
|
@ -138,7 +156,7 @@ void EditorFileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_file
|
||||||
int filter_slice_count = flt.get_slice_count(",");
|
int filter_slice_count = flt.get_slice_count(",");
|
||||||
for (int j = 0; j < filter_slice_count; j++) {
|
for (int j = 0; j < filter_slice_count; j++) {
|
||||||
String str = (flt.get_slice(",", j).strip_edges());
|
String str = (flt.get_slice(",", j).strip_edges());
|
||||||
if (f.match(str)) {
|
if (f.matchn(str)) {
|
||||||
valid = true;
|
valid = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -147,9 +165,21 @@ void EditorFileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_file
|
||||||
if (!valid && filter_slice_count > 0) {
|
if (!valid && filter_slice_count > 0) {
|
||||||
String str = (flt.get_slice(",", 0).strip_edges());
|
String str = (flt.get_slice(",", 0).strip_edges());
|
||||||
f += str.substr(1, str.length() - 1);
|
f += str.substr(1, str.length() - 1);
|
||||||
|
file->set_text(f.get_file());
|
||||||
|
valid = true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
valid = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add first extension of filter if no valid extension is found.
|
||||||
|
if (!valid) {
|
||||||
|
int idx = p_filter;
|
||||||
|
String flt = filters[idx].get_slice(";", 0);
|
||||||
|
String ext = flt.get_slice(",", 0).strip_edges().get_extension();
|
||||||
|
f += "." + ext;
|
||||||
|
}
|
||||||
emit_signal(SNAME("file_selected"), f);
|
emit_signal(SNAME("file_selected"), f);
|
||||||
} else if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) {
|
} else if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) {
|
||||||
emit_signal(SNAME("file_selected"), f);
|
emit_signal(SNAME("file_selected"), f);
|
||||||
|
|
@ -578,9 +608,9 @@ void EditorFileDialog::_action_pressed() {
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
|
|
||||||
if (filter->get_selected() == filter->get_item_count() - 1) {
|
if (filter->get_selected() == filter->get_item_count() - 1) {
|
||||||
valid = true; // match none
|
valid = true; // Match none.
|
||||||
} else if (filters.size() > 1 && filter->get_selected() == 0) {
|
} else if (filters.size() > 1 && filter->get_selected() == 0) {
|
||||||
// match all filters
|
// Match all filters.
|
||||||
for (int i = 0; i < filters.size(); i++) {
|
for (int i = 0; i < filters.size(); i++) {
|
||||||
String flt = filters[i].get_slice(";", 0);
|
String flt = filters[i].get_slice(";", 0);
|
||||||
for (int j = 0; j < flt.get_slice_count(","); j++) {
|
for (int j = 0; j < flt.get_slice_count(","); j++) {
|
||||||
|
|
|
||||||
|
|
@ -924,12 +924,19 @@ void SceneTreeEditor::_update_tree(bool p_scroll_to_selected) {
|
||||||
// If pinned state changed, update the currently pinned node.
|
// If pinned state changed, update the currently pinned node.
|
||||||
if (AnimationPlayerEditor::get_singleton()->is_pinned() != node_cache.current_has_pin) {
|
if (AnimationPlayerEditor::get_singleton()->is_pinned() != node_cache.current_has_pin) {
|
||||||
node_cache.current_has_pin = AnimationPlayerEditor::get_singleton()->is_pinned();
|
node_cache.current_has_pin = AnimationPlayerEditor::get_singleton()->is_pinned();
|
||||||
node_cache.mark_dirty(pinned_node);
|
if (node_cache.has(pinned_node)) {
|
||||||
|
node_cache.mark_dirty(pinned_node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// If the current pinned node changed update both the old and new node.
|
// If the current pinned node changed update both the old and new node.
|
||||||
if (node_cache.current_pinned_node != pinned_node) {
|
if (node_cache.current_pinned_node != pinned_node) {
|
||||||
node_cache.mark_dirty(pinned_node);
|
// get_editing_node() will return deleted nodes. If the nodes are not in cache don't try to mark them.
|
||||||
node_cache.mark_dirty(node_cache.current_pinned_node);
|
if (node_cache.has(pinned_node)) {
|
||||||
|
node_cache.mark_dirty(pinned_node);
|
||||||
|
}
|
||||||
|
if (node_cache.has(node_cache.current_pinned_node)) {
|
||||||
|
node_cache.mark_dirty(node_cache.current_pinned_node);
|
||||||
|
}
|
||||||
node_cache.current_pinned_node = pinned_node;
|
node_cache.current_pinned_node = pinned_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2373,6 +2380,10 @@ HashMap<Node *, SceneTreeEditor::CachedNode>::Iterator SceneTreeEditor::NodeCach
|
||||||
return I;
|
return I;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SceneTreeEditor::NodeCache::has(Node *p_node) {
|
||||||
|
return get(p_node, false).operator bool();
|
||||||
|
}
|
||||||
|
|
||||||
void SceneTreeEditor::NodeCache::remove(Node *p_node, bool p_recursive) {
|
void SceneTreeEditor::NodeCache::remove(Node *p_node, bool p_recursive) {
|
||||||
if (!p_node) {
|
if (!p_node) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -2382,6 +2393,11 @@ void SceneTreeEditor::NodeCache::remove(Node *p_node, bool p_recursive) {
|
||||||
editor->selected = nullptr;
|
editor->selected = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (p_node == current_pinned_node) {
|
||||||
|
current_pinned_node = nullptr;
|
||||||
|
current_has_pin = false;
|
||||||
|
}
|
||||||
|
|
||||||
editor->marked.erase(p_node);
|
editor->marked.erase(p_node);
|
||||||
|
|
||||||
HashMap<Node *, CachedNode>::Iterator I = cache.find(p_node);
|
HashMap<Node *, CachedNode>::Iterator I = cache.find(p_node);
|
||||||
|
|
@ -2419,6 +2435,7 @@ void SceneTreeEditor::NodeCache::mark_dirty(Node *p_node, bool p_parents) {
|
||||||
if (!p_parents) {
|
if (!p_parents) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node->get_parent();
|
node = node->get_parent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2483,4 +2500,6 @@ void SceneTreeEditor::NodeCache::clear() {
|
||||||
}
|
}
|
||||||
cache.clear();
|
cache.clear();
|
||||||
to_delete.clear();
|
to_delete.clear();
|
||||||
|
current_pinned_node = nullptr;
|
||||||
|
current_has_pin = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ class SceneTreeEditor : public Control {
|
||||||
|
|
||||||
HashMap<Node *, CachedNode>::Iterator add(Node *p_node, TreeItem *p_item);
|
HashMap<Node *, CachedNode>::Iterator add(Node *p_node, TreeItem *p_item);
|
||||||
HashMap<Node *, CachedNode>::Iterator get(Node *p_node, bool p_deleted_ok = true);
|
HashMap<Node *, CachedNode>::Iterator get(Node *p_node, bool p_deleted_ok = true);
|
||||||
|
bool has(Node *p_node);
|
||||||
void remove(Node *p_node, bool p_recursive = false);
|
void remove(Node *p_node, bool p_recursive = false);
|
||||||
void mark_dirty(Node *p_node, bool p_parents = true);
|
void mark_dirty(Node *p_node, bool p_parents = true);
|
||||||
void mark_children_dirty(Node *p_node, bool p_recursive = false);
|
void mark_children_dirty(Node *p_node, bool p_recursive = false);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#eee" d="M6 2a4 4 0 0 0-2 7.453V13a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2V9.453A4 4 0 0 0 10 2zM5 5a1 1 0 0 1 0 2 1 1 0 0 1 0-2zm6 0a1 1 0 0 1 0 2 1 1 0 0 1 0-2zM7 7h2v1H7zM5 9h1v1h1V9h2v1h1V9h1v4h-1v-1H9v1H7v-1H6v1H5V8z"/></svg>
|
||||||
|
After Width: | Height: | Size: 295 B |
|
|
@ -1344,7 +1344,7 @@ void SceneImportSettingsDialog::_notification(int p_what) {
|
||||||
light_2_switch->set_button_icon(theme_cache.light_2_icon);
|
light_2_switch->set_button_icon(theme_cache.light_2_icon);
|
||||||
light_rotate_switch->set_button_icon(theme_cache.rotate_icon);
|
light_rotate_switch->set_button_icon(theme_cache.rotate_icon);
|
||||||
|
|
||||||
animation_toggle_skeleton_visibility->set_button_icon(get_editor_theme_icon(SNAME("Skeleton3D")));
|
animation_toggle_skeleton_visibility->set_button_icon(get_editor_theme_icon(SNAME("SkeletonPreview")));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case NOTIFICATION_PROCESS: {
|
case NOTIFICATION_PROCESS: {
|
||||||
|
|
@ -1740,7 +1740,7 @@ SceneImportSettingsDialog::SceneImportSettingsDialog() {
|
||||||
animation_toggle_skeleton_visibility = memnew(Button);
|
animation_toggle_skeleton_visibility = memnew(Button);
|
||||||
animation_hbox->add_child(animation_toggle_skeleton_visibility);
|
animation_hbox->add_child(animation_toggle_skeleton_visibility);
|
||||||
animation_toggle_skeleton_visibility->set_toggle_mode(true);
|
animation_toggle_skeleton_visibility->set_toggle_mode(true);
|
||||||
animation_toggle_skeleton_visibility->set_flat(true);
|
animation_toggle_skeleton_visibility->set_theme_type_variation("FlatButton");
|
||||||
animation_toggle_skeleton_visibility->set_focus_mode(Control::FOCUS_NONE);
|
animation_toggle_skeleton_visibility->set_focus_mode(Control::FOCUS_NONE);
|
||||||
animation_toggle_skeleton_visibility->set_tooltip_text(TTR("Toggle Animation Skeleton Visibility"));
|
animation_toggle_skeleton_visibility->set_tooltip_text(TTR("Toggle Animation Skeleton Visibility"));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,13 @@ Ref<Texture2D> EditorTexturePreviewPlugin::generate(const Ref<Resource> &p_from,
|
||||||
return Ref<Texture2D>();
|
return Ref<Texture2D>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (atlas->is_compressed()) {
|
||||||
|
atlas = atlas->duplicate();
|
||||||
|
if (atlas->decompress() != OK) {
|
||||||
|
return Ref<Texture2D>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!tex_atlas->get_region().has_area()) {
|
if (!tex_atlas->get_region().has_area()) {
|
||||||
return Ref<Texture2D>();
|
return Ref<Texture2D>();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,20 +73,10 @@ void EmbeddedProcess::_notification(int p_what) {
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_APPLICATION_FOCUS_IN: {
|
case NOTIFICATION_APPLICATION_FOCUS_IN: {
|
||||||
application_has_focus = true;
|
application_has_focus = true;
|
||||||
if (embedded_process_was_focused) {
|
last_application_focus_time = OS::get_singleton()->get_ticks_msec();
|
||||||
embedded_process_was_focused = false;
|
|
||||||
// Refocus the embedded process if it was focused when the application lost focus,
|
|
||||||
// but do not refocus if the embedded process is currently focused (indicating it just lost focus)
|
|
||||||
// or if the current window is a different popup or secondary window.
|
|
||||||
if (embedding_completed && current_process_id != focused_process_id && window && window->has_focus()) {
|
|
||||||
grab_focus();
|
|
||||||
queue_update_embedded_process();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
|
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
|
||||||
application_has_focus = false;
|
application_has_focus = false;
|
||||||
embedded_process_was_focused = embedding_completed && current_process_id == focused_process_id;
|
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -295,14 +285,27 @@ void EmbeddedProcess::_check_mouse_over() {
|
||||||
// This method checks if the mouse is over the embedded process while the current application is focused.
|
// This method checks if the mouse is over the embedded process while the current application is focused.
|
||||||
// The goal is to give focus to the embedded process as soon as the mouse hovers over it,
|
// The goal is to give focus to the embedded process as soon as the mouse hovers over it,
|
||||||
// allowing the user to interact with it immediately without needing to click first.
|
// allowing the user to interact with it immediately without needing to click first.
|
||||||
if (!is_visible_in_tree() || !embedding_completed || !application_has_focus || !window || !window->has_focus() || Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT) || Input::get_singleton()->is_mouse_button_pressed(MouseButton::RIGHT)) {
|
if (!embedding_completed || !application_has_focus || !window || has_focus() || !is_visible_in_tree() || !window->has_focus() || Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT) || Input::get_singleton()->is_mouse_button_pressed(MouseButton::RIGHT)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool focused = has_focus();
|
// Before checking whether the mouse is truly inside the embedded process, ensure
|
||||||
|
// the editor has enough time to re-render. When a breakpoint is hit in the script editor,
|
||||||
|
// `_check_mouse_over` may be triggered before the editor hides the game workspace.
|
||||||
|
// This prevents the embedded process from regaining focus immediately after the editor has taken it.
|
||||||
|
if (OS::get_singleton()->get_ticks_msec() - last_application_focus_time < 500) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input::is_mouse_button_pressed is not sufficient to detect the mouse button state
|
||||||
|
// while the floating game window is being resized.
|
||||||
|
BitField<MouseButtonMask> mouse_button_mask = DisplayServer::get_singleton()->mouse_get_button_state();
|
||||||
|
if (!mouse_button_mask.is_empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Not stealing focus from a textfield.
|
// Not stealing focus from a textfield.
|
||||||
if (!focused && get_viewport()->gui_get_focus_owner() && get_viewport()->gui_get_focus_owner()->is_text_field()) {
|
if (get_viewport()->gui_get_focus_owner() && get_viewport()->gui_get_focus_owner()->is_text_field()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -318,14 +321,17 @@ void EmbeddedProcess::_check_mouse_over() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When we already have the focus and the user moves the mouse over the embedded process,
|
// When there's a modal window, we don't want to grab the focus to prevent
|
||||||
// we just need to refocus the process.
|
// the game window to go in front of the modal window.
|
||||||
if (focused) {
|
if (_get_current_modal_window()) {
|
||||||
queue_update_embedded_process();
|
return;
|
||||||
} else {
|
|
||||||
grab_focus();
|
|
||||||
queue_redraw();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force "regrabbing" the game window focus.
|
||||||
|
last_updated_embedded_process_focused = false;
|
||||||
|
|
||||||
|
grab_focus();
|
||||||
|
queue_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmbeddedProcess::_check_focused_process_id() {
|
void EmbeddedProcess::_check_focused_process_id() {
|
||||||
|
|
@ -334,17 +340,48 @@ void EmbeddedProcess::_check_focused_process_id() {
|
||||||
focused_process_id = process_id;
|
focused_process_id = process_id;
|
||||||
if (focused_process_id == current_process_id) {
|
if (focused_process_id == current_process_id) {
|
||||||
// The embedded process got the focus.
|
// The embedded process got the focus.
|
||||||
emit_signal(SNAME("embedded_process_focused"));
|
|
||||||
if (has_focus()) {
|
// Refocus the current model when focusing the embedded process.
|
||||||
// Redraw to updated the focus style.
|
Window *modal_window = _get_current_modal_window();
|
||||||
queue_redraw();
|
if (!modal_window) {
|
||||||
} else {
|
emit_signal(SNAME("embedded_process_focused"));
|
||||||
grab_focus();
|
if (has_focus()) {
|
||||||
|
// Redraw to updated the focus style.
|
||||||
|
queue_redraw();
|
||||||
|
} else {
|
||||||
|
grab_focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (has_focus()) {
|
} else if (has_focus()) {
|
||||||
release_focus();
|
release_focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure that the opened modal dialog is refocused when the focused process is the embedded process.
|
||||||
|
if (!application_has_focus && focused_process_id == current_process_id) {
|
||||||
|
Window *modal_window = _get_current_modal_window();
|
||||||
|
if (modal_window) {
|
||||||
|
if (modal_window->get_mode() == Window::MODE_MINIMIZED) {
|
||||||
|
modal_window->set_mode(Window::MODE_WINDOWED);
|
||||||
|
}
|
||||||
|
callable_mp(modal_window, &Window::grab_focus).call_deferred();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Window *EmbeddedProcess::_get_current_modal_window() {
|
||||||
|
Vector<DisplayServer::WindowID> wl = DisplayServer::get_singleton()->get_window_list();
|
||||||
|
for (const DisplayServer::WindowID &window_id : wl) {
|
||||||
|
Window *w = Window::get_from_id(window_id);
|
||||||
|
if (!w) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w->is_exclusive()) {
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmbeddedProcess::_bind_methods() {
|
void EmbeddedProcess::_bind_methods() {
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ class EmbeddedProcess : public Control {
|
||||||
GDCLASS(EmbeddedProcess, Control);
|
GDCLASS(EmbeddedProcess, Control);
|
||||||
|
|
||||||
bool application_has_focus = true;
|
bool application_has_focus = true;
|
||||||
bool embedded_process_was_focused = false;
|
uint64_t last_application_focus_time = 0;
|
||||||
OS::ProcessID focused_process_id = 0;
|
OS::ProcessID focused_process_id = 0;
|
||||||
OS::ProcessID current_process_id = 0;
|
OS::ProcessID current_process_id = 0;
|
||||||
bool embedding_grab_focus = false;
|
bool embedding_grab_focus = false;
|
||||||
|
|
@ -68,6 +68,7 @@ class EmbeddedProcess : public Control {
|
||||||
void _check_focused_process_id();
|
void _check_focused_process_id();
|
||||||
bool _is_embedded_process_updatable();
|
bool _is_embedded_process_updatable();
|
||||||
Rect2i _get_global_embedded_window_rect();
|
Rect2i _get_global_embedded_window_rect();
|
||||||
|
Window *_get_current_modal_window();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ void GameViewDebugger::_session_started(Ref<EditorDebuggerSession> p_session) {
|
||||||
settings["editors/panning/warped_mouse_panning"] = EDITOR_GET("editors/panning/warped_mouse_panning");
|
settings["editors/panning/warped_mouse_panning"] = EDITOR_GET("editors/panning/warped_mouse_panning");
|
||||||
settings["editors/panning/2d_editor_pan_speed"] = EDITOR_GET("editors/panning/2d_editor_pan_speed");
|
settings["editors/panning/2d_editor_pan_speed"] = EDITOR_GET("editors/panning/2d_editor_pan_speed");
|
||||||
settings["canvas_item_editor/pan_view"] = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("canvas_item_editor/pan_view"));
|
settings["canvas_item_editor/pan_view"] = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("canvas_item_editor/pan_view"));
|
||||||
|
settings["editors/3d/freelook/freelook_base_speed"] = EDITOR_GET("editors/3d/freelook/freelook_base_speed");
|
||||||
setup_data.append(settings);
|
setup_data.append(settings);
|
||||||
p_session->send_message("scene:runtime_node_select_setup", setup_data);
|
p_session->send_message("scene:runtime_node_select_setup", setup_data);
|
||||||
|
|
||||||
|
|
@ -243,20 +244,29 @@ void GameView::_show_update_window_wrapper() {
|
||||||
Size2i size = floating_window_rect.size;
|
Size2i size = floating_window_rect.size;
|
||||||
int screen = floating_window_screen;
|
int screen = floating_window_screen;
|
||||||
|
|
||||||
Size2 wrapped_margins_size = window_wrapper->get_margins_size();
|
|
||||||
Point2 offset_embedded_process = embedded_process->get_global_position() - get_global_position();
|
|
||||||
offset_embedded_process.x += embedded_process->get_margin_size(SIDE_LEFT);
|
|
||||||
offset_embedded_process.y += embedded_process->get_margin_size(SIDE_TOP);
|
|
||||||
|
|
||||||
// Obtain the size around the embedded process control. Usually, the difference between the game view's get_size
|
// Obtain the size around the embedded process control. Usually, the difference between the game view's get_size
|
||||||
// and the embedded control should work. However, when the control is hidden and has never been displayed,
|
// and the embedded control should work. However, when the control is hidden and has never been displayed,
|
||||||
// the size of the embedded control is not calculated.
|
// the size of the embedded control is not calculated.
|
||||||
Size2 old_min_size = embedded_process->get_custom_minimum_size();
|
Size2 old_min_size = embedded_process->get_custom_minimum_size();
|
||||||
embedded_process->set_custom_minimum_size(Size2i());
|
embedded_process->set_custom_minimum_size(Size2i());
|
||||||
Size2 min_size = get_minimum_size();
|
|
||||||
|
Size2 embedded_process_min_size = get_minimum_size();
|
||||||
|
Size2 wrapped_margins_size = window_wrapper->get_margins_size();
|
||||||
|
Size2 wrapped_min_size = window_wrapper->get_minimum_size();
|
||||||
|
Point2 offset_embedded_process = embedded_process->get_global_position() - get_global_position();
|
||||||
|
|
||||||
|
// On the first startup, the global position of the embedded process control is invalid because it was
|
||||||
|
// never displayed. We will calculated it manually using the minimum size of the window.
|
||||||
|
if (offset_embedded_process == Point2()) {
|
||||||
|
offset_embedded_process.y = wrapped_min_size.y;
|
||||||
|
}
|
||||||
|
offset_embedded_process.x += embedded_process->get_margin_size(SIDE_LEFT);
|
||||||
|
offset_embedded_process.y += embedded_process->get_margin_size(SIDE_TOP);
|
||||||
|
offset_embedded_process += window_wrapper->get_margins_top_left();
|
||||||
|
|
||||||
embedded_process->set_custom_minimum_size(old_min_size);
|
embedded_process->set_custom_minimum_size(old_min_size);
|
||||||
|
|
||||||
Point2 size_diff_embedded_process = Point2(0, min_size.y) + embedded_process->get_margins_size();
|
Point2 size_diff_embedded_process = Point2(0, embedded_process_min_size.y) + embedded_process->get_margins_size();
|
||||||
|
|
||||||
if (placement.position != Point2i(INT_MAX, INT_MAX)) {
|
if (placement.position != Point2i(INT_MAX, INT_MAX)) {
|
||||||
position = placement.position - offset_embedded_process;
|
position = placement.position - offset_embedded_process;
|
||||||
|
|
@ -436,6 +446,10 @@ GameView::EmbedAvailability GameView::_get_embed_available() {
|
||||||
if (get_tree()->get_root()->is_embedding_subwindows()) {
|
if (get_tree()->get_root()->is_embedding_subwindows()) {
|
||||||
return EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE;
|
return EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE;
|
||||||
}
|
}
|
||||||
|
String display_driver = GLOBAL_GET("display/display_server/driver");
|
||||||
|
if (display_driver == "headless" || display_driver == "wayland") {
|
||||||
|
return EMBED_NOT_AVAILABLE_PROJECT_DISPLAY_DRIVER;
|
||||||
|
}
|
||||||
|
|
||||||
EditorRun::WindowPlacement placement = EditorRun::get_window_placement();
|
EditorRun::WindowPlacement placement = EditorRun::get_window_placement();
|
||||||
if (placement.force_fullscreen) {
|
if (placement.force_fullscreen) {
|
||||||
|
|
@ -479,7 +493,14 @@ void GameView::_update_ui() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EMBED_NOT_AVAILABLE_FEATURE_NOT_SUPPORTED:
|
case EMBED_NOT_AVAILABLE_FEATURE_NOT_SUPPORTED:
|
||||||
state_label->set_text(TTR("Game embedding not available on your OS."));
|
if (DisplayServer::get_singleton()->get_name() == "Wayland") {
|
||||||
|
state_label->set_text(TTR("Game embedding not available on Wayland.\nWayland can be disabled in the Editor Settings (Run > Platforms > Linux/*BSD > Prefer Wayland)."));
|
||||||
|
} else {
|
||||||
|
state_label->set_text(TTR("Game embedding not available on your OS."));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EMBED_NOT_AVAILABLE_PROJECT_DISPLAY_DRIVER:
|
||||||
|
state_label->set_text(vformat(TTR("Game embedding not available for the Display Server: '%s'.\nDisplay Server can be modified in the Project Settings (Display > Display Server > Driver)."), GLOBAL_GET("display/display_server/driver")));
|
||||||
break;
|
break;
|
||||||
case EMBED_NOT_AVAILABLE_MINIMIZED:
|
case EMBED_NOT_AVAILABLE_MINIMIZED:
|
||||||
state_label->set_text(TTR("Game embedding not available when the game starts minimized.\nConsider overriding the window mode project setting with the editor feature tag to Windowed to use game embedding while leaving the exported project intact."));
|
state_label->set_text(TTR("Game embedding not available when the game starts minimized.\nConsider overriding the window mode project setting with the editor feature tag to Windowed to use game embedding while leaving the exported project intact."));
|
||||||
|
|
@ -786,8 +807,21 @@ void GameView::_update_arguments_for_instance(int p_idx, List<String> &r_argumen
|
||||||
|
|
||||||
// Be sure to have the correct window size in the embedded_process control.
|
// Be sure to have the correct window size in the embedded_process control.
|
||||||
_update_embed_window_size();
|
_update_embed_window_size();
|
||||||
|
|
||||||
Rect2i rect = embedded_process->get_screen_embedded_window_rect();
|
Rect2i rect = embedded_process->get_screen_embedded_window_rect();
|
||||||
|
|
||||||
|
// When using the floating window, we need to force the position and size from the
|
||||||
|
// editor/project settings, because the get_screen_embedded_window_rect of the
|
||||||
|
// embedded_process will be updated only on the next frame.
|
||||||
|
if (p_idx == 0 && window_wrapper->get_window_enabled()) {
|
||||||
|
EditorRun::WindowPlacement placement = EditorRun::get_window_placement();
|
||||||
|
if (placement.position != Point2i(INT_MAX, INT_MAX)) {
|
||||||
|
rect.position = placement.position;
|
||||||
|
}
|
||||||
|
if (placement.size != Size2i()) {
|
||||||
|
rect.size = placement.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
N = r_arguments.insert_after(N, "--position");
|
N = r_arguments.insert_after(N, "--position");
|
||||||
N = r_arguments.insert_after(N, itos(rect.position.x) + "," + itos(rect.position.y));
|
N = r_arguments.insert_after(N, itos(rect.position.x) + "," + itos(rect.position.y));
|
||||||
N = r_arguments.insert_after(N, "--resolution");
|
N = r_arguments.insert_after(N, "--resolution");
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ class GameView : public VBoxContainer {
|
||||||
EMBED_NOT_AVAILABLE_MAXIMIZED,
|
EMBED_NOT_AVAILABLE_MAXIMIZED,
|
||||||
EMBED_NOT_AVAILABLE_FULLSCREEN,
|
EMBED_NOT_AVAILABLE_FULLSCREEN,
|
||||||
EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE,
|
EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE,
|
||||||
|
EMBED_NOT_AVAILABLE_PROJECT_DISPLAY_DRIVER,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline static GameView *singleton = nullptr;
|
inline static GameView *singleton = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -4382,8 +4382,11 @@ void Node3DEditorViewport::assign_pending_data_pointers(Node3D *p_preview_node,
|
||||||
|
|
||||||
void _insert_rid_recursive(Node *node, HashSet<RID> &rids) {
|
void _insert_rid_recursive(Node *node, HashSet<RID> &rids) {
|
||||||
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(node);
|
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(node);
|
||||||
|
|
||||||
if (co) {
|
if (co) {
|
||||||
rids.insert(co->get_rid());
|
rids.insert(co->get_rid());
|
||||||
|
} else if (node->is_class("CSGShape3D")) { // HACK: We should avoid referencing module logic.
|
||||||
|
rids.insert(node->call("_get_root_collision_instance"));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < node->get_child_count(); i++) {
|
for (int i = 0; i < node->get_child_count(); i++) {
|
||||||
|
|
|
||||||
|
|
@ -535,12 +535,14 @@ Polygon3DEditor::Polygon3DEditor() {
|
||||||
|
|
||||||
button_create = memnew(Button);
|
button_create = memnew(Button);
|
||||||
button_create->set_theme_type_variation(SceneStringName(FlatButton));
|
button_create->set_theme_type_variation(SceneStringName(FlatButton));
|
||||||
|
button_create->set_tooltip_text(TTRC("Create Polygon"));
|
||||||
add_child(button_create);
|
add_child(button_create);
|
||||||
button_create->connect(SceneStringName(pressed), callable_mp(this, &Polygon3DEditor::_menu_option).bind(MODE_CREATE));
|
button_create->connect(SceneStringName(pressed), callable_mp(this, &Polygon3DEditor::_menu_option).bind(MODE_CREATE));
|
||||||
button_create->set_toggle_mode(true);
|
button_create->set_toggle_mode(true);
|
||||||
|
|
||||||
button_edit = memnew(Button);
|
button_edit = memnew(Button);
|
||||||
button_edit->set_theme_type_variation(SceneStringName(FlatButton));
|
button_edit->set_theme_type_variation(SceneStringName(FlatButton));
|
||||||
|
button_edit->set_tooltip_text(TTRC("Edit Polygon"));
|
||||||
add_child(button_edit);
|
add_child(button_edit);
|
||||||
button_edit->connect(SceneStringName(pressed), callable_mp(this, &Polygon3DEditor::_menu_option).bind(MODE_EDIT));
|
button_edit->connect(SceneStringName(pressed), callable_mp(this, &Polygon3DEditor::_menu_option).bind(MODE_EDIT));
|
||||||
button_edit->set_toggle_mode(true);
|
button_edit->set_toggle_mode(true);
|
||||||
|
|
|
||||||
|
|
@ -2843,10 +2843,13 @@ void ScriptEditor::_reload_scripts(bool p_refresh_only) {
|
||||||
Ref<Resource> edited_res = se->get_edited_resource();
|
Ref<Resource> edited_res = se->get_edited_resource();
|
||||||
|
|
||||||
if (edited_res->is_built_in()) {
|
if (edited_res->is_built_in()) {
|
||||||
continue; //internal script, who cares
|
continue; // Internal script, who cares.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!p_refresh_only) {
|
if (p_refresh_only) {
|
||||||
|
// Make sure the modified time is correct.
|
||||||
|
se->edited_file_data.last_modified_time = FileAccess::get_modified_time(edited_res->get_path());
|
||||||
|
} else {
|
||||||
uint64_t last_date = se->edited_file_data.last_modified_time;
|
uint64_t last_date = se->edited_file_data.last_modified_time;
|
||||||
uint64_t date = FileAccess::get_modified_time(edited_res->get_path());
|
uint64_t date = FileAccess::get_modified_time(edited_res->get_path());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -833,6 +833,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
|
||||||
left_panel->set_custom_minimum_size(Size2(100, 300) * EDSCALE);
|
left_panel->set_custom_minimum_size(Size2(100, 300) * EDSCALE);
|
||||||
|
|
||||||
shader_tabs = memnew(TabContainer);
|
shader_tabs = memnew(TabContainer);
|
||||||
|
shader_tabs->set_custom_minimum_size(Size2(460, 300) * EDSCALE);
|
||||||
shader_tabs->set_tabs_visible(false);
|
shader_tabs->set_tabs_visible(false);
|
||||||
shader_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
shader_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||||
main_split->add_child(shader_tabs);
|
main_split->add_child(shader_tabs);
|
||||||
|
|
|
||||||
|
|
@ -132,8 +132,21 @@ static Image::Format get_texture_2d_format(const Ref<Texture2D> &p_texture) {
|
||||||
|
|
||||||
static int get_texture_mipmaps_count(const Ref<Texture2D> &p_texture) {
|
static int get_texture_mipmaps_count(const Ref<Texture2D> &p_texture) {
|
||||||
ERR_FAIL_COND_V(p_texture.is_null(), -1);
|
ERR_FAIL_COND_V(p_texture.is_null(), -1);
|
||||||
|
|
||||||
// We are having to download the image only to get its mipmaps count. It would be nice if we didn't have to.
|
// We are having to download the image only to get its mipmaps count. It would be nice if we didn't have to.
|
||||||
Ref<Image> image = p_texture->get_image();
|
Ref<Image> image;
|
||||||
|
Ref<AtlasTexture> at = p_texture;
|
||||||
|
if (at.is_valid()) {
|
||||||
|
// The AtlasTexture tries to obtain the region from the atlas as an image,
|
||||||
|
// which will fail if it is a compressed format.
|
||||||
|
Ref<Texture2D> atlas = at->get_atlas();
|
||||||
|
if (atlas.is_valid()) {
|
||||||
|
image = atlas->get_image();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
image = p_texture->get_image();
|
||||||
|
}
|
||||||
|
|
||||||
if (image.is_valid()) {
|
if (image.is_valid()) {
|
||||||
return image->get_mipmap_count();
|
return image->get_mipmap_count();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -971,40 +971,44 @@ void TileSetAtlasSourceEditor::_update_atlas_view() {
|
||||||
tile_create_help->set_visible(tools_button_group->get_pressed_button() == tool_setup_atlas_source_button);
|
tile_create_help->set_visible(tools_button_group->get_pressed_button() == tool_setup_atlas_source_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2i pos;
|
if (tools_button_group->get_pressed_button() != tool_paint_button) {
|
||||||
Vector2 texture_region_base_size = tile_set_atlas_source->get_texture_region_size();
|
Vector2i pos;
|
||||||
int texture_region_base_size_min = MIN(texture_region_base_size.x, texture_region_base_size.y);
|
Vector2 texture_region_base_size = tile_set_atlas_source->get_texture_region_size();
|
||||||
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
|
int texture_region_base_size_min = MIN(texture_region_base_size.x, texture_region_base_size.y);
|
||||||
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
|
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
|
||||||
int alternative_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
|
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
|
||||||
if (alternative_count > 1) {
|
int alternative_count = tile_set_atlas_source->get_alternative_tiles_count(tile_id);
|
||||||
// Compute the right extremity of alternative.
|
if (alternative_count > 1) {
|
||||||
int y_increment = 0;
|
// Compute the right extremity of alternative.
|
||||||
pos.x = 0;
|
int y_increment = 0;
|
||||||
for (int j = 1; j < alternative_count; j++) {
|
pos.x = 0;
|
||||||
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
|
for (int j = 1; j < alternative_count; j++) {
|
||||||
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(tile_id, alternative_id);
|
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(tile_id, j);
|
||||||
pos.x = MAX(pos.x, rect.get_end().x);
|
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(tile_id, alternative_id);
|
||||||
y_increment = MAX(y_increment, rect.size.y);
|
pos.x = MAX(pos.x, rect.get_end().x);
|
||||||
|
y_increment = MAX(y_increment, rect.size.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and position the button.
|
||||||
|
Button *button = memnew(Button);
|
||||||
|
button->set_flat(true);
|
||||||
|
button->set_button_icon(get_editor_theme_icon(SNAME("Add")));
|
||||||
|
button->add_theme_style_override(CoreStringName(normal), memnew(StyleBoxEmpty));
|
||||||
|
button->add_theme_style_override(SceneStringName(hover), memnew(StyleBoxEmpty));
|
||||||
|
button->add_theme_style_override("focus", memnew(StyleBoxEmpty));
|
||||||
|
button->add_theme_style_override(SceneStringName(pressed), memnew(StyleBoxEmpty));
|
||||||
|
button->add_theme_constant_override("align_to_largest_stylebox", false);
|
||||||
|
button->set_mouse_filter(Control::MOUSE_FILTER_PASS);
|
||||||
|
button->connect(SceneStringName(pressed), callable_mp(this, &TileSetAtlasSourceEditor::_tile_alternatives_create_button_pressed).bind(tile_id));
|
||||||
|
button->set_rect(Rect2(Vector2(pos.x, pos.y + (y_increment - texture_region_base_size.y) / 2.0), Vector2(texture_region_base_size_min, texture_region_base_size_min)));
|
||||||
|
button->set_expand_icon(true);
|
||||||
|
alternative_tiles_control->add_child(button);
|
||||||
|
|
||||||
|
pos.y += y_increment;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and position the button.
|
|
||||||
Button *button = memnew(Button);
|
|
||||||
button->set_flat(true);
|
|
||||||
button->set_button_icon(get_editor_theme_icon(SNAME("Add")));
|
|
||||||
button->add_theme_style_override(CoreStringName(normal), memnew(StyleBoxEmpty));
|
|
||||||
button->add_theme_style_override(SceneStringName(hover), memnew(StyleBoxEmpty));
|
|
||||||
button->add_theme_style_override("focus", memnew(StyleBoxEmpty));
|
|
||||||
button->add_theme_style_override(SceneStringName(pressed), memnew(StyleBoxEmpty));
|
|
||||||
button->connect(SceneStringName(pressed), callable_mp(tile_set_atlas_source, &TileSetAtlasSource::create_alternative_tile).bind(tile_id, TileSetSource::INVALID_TILE_ALTERNATIVE));
|
|
||||||
button->set_rect(Rect2(Vector2(pos.x, pos.y + (y_increment - texture_region_base_size.y) / 2.0), Vector2(texture_region_base_size_min, texture_region_base_size_min)));
|
|
||||||
button->set_expand_icon(true);
|
|
||||||
alternative_tiles_control->add_child(button);
|
|
||||||
|
|
||||||
pos.y += y_increment;
|
|
||||||
}
|
}
|
||||||
|
tile_atlas_view->set_padding(Side::SIDE_RIGHT, texture_region_base_size_min);
|
||||||
}
|
}
|
||||||
tile_atlas_view->set_padding(Side::SIDE_RIGHT, texture_region_base_size_min);
|
|
||||||
|
|
||||||
// Redraw everything.
|
// Redraw everything.
|
||||||
tile_atlas_control->queue_redraw();
|
tile_atlas_control->queue_redraw();
|
||||||
|
|
@ -2009,6 +2013,17 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_mouse_exited() {
|
||||||
alternative_tiles_control_unscaled->queue_redraw();
|
alternative_tiles_control_unscaled->queue_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TileSetAtlasSourceEditor::_tile_alternatives_create_button_pressed(const Vector2i &p_atlas_coords) {
|
||||||
|
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||||
|
|
||||||
|
// FIXME: Doesn't undo changes to `next_alternative_id` counter.
|
||||||
|
undo_redo->create_action(TTR("Create tile alternatives"));
|
||||||
|
int next_id = tile_set_atlas_source->get_next_alternative_tile_id(p_atlas_coords);
|
||||||
|
undo_redo->add_do_method(tile_set_atlas_source, "create_alternative_tile", p_atlas_coords, next_id);
|
||||||
|
undo_redo->add_undo_method(tile_set_atlas_source, "remove_alternative_tile", p_atlas_coords, next_id);
|
||||||
|
undo_redo->commit_action();
|
||||||
|
}
|
||||||
|
|
||||||
void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() {
|
void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() {
|
||||||
// Update the hovered alternative tile.
|
// Update the hovered alternative tile.
|
||||||
if (tools_button_group->get_pressed_button() == tool_select_button) {
|
if (tools_button_group->get_pressed_button() == tool_select_button) {
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,7 @@ private:
|
||||||
PopupMenu *alternative_tile_popup_menu = nullptr;
|
PopupMenu *alternative_tile_popup_menu = nullptr;
|
||||||
Control *alternative_tiles_control = nullptr;
|
Control *alternative_tiles_control = nullptr;
|
||||||
Control *alternative_tiles_control_unscaled = nullptr;
|
Control *alternative_tiles_control_unscaled = nullptr;
|
||||||
|
void _tile_alternatives_create_button_pressed(const Vector2i &p_atlas_coords);
|
||||||
void _tile_alternatives_control_draw();
|
void _tile_alternatives_control_draw();
|
||||||
void _tile_alternatives_control_unscaled_draw();
|
void _tile_alternatives_control_unscaled_draw();
|
||||||
void _tile_alternatives_control_mouse_exited();
|
void _tile_alternatives_control_mouse_exited();
|
||||||
|
|
|
||||||
|
|
@ -2834,7 +2834,7 @@ void SceneTreeDock::_update_script_button() {
|
||||||
Ref<Script> cts;
|
Ref<Script> cts;
|
||||||
|
|
||||||
if (n->has_meta(SceneStringName(_custom_type_script))) {
|
if (n->has_meta(SceneStringName(_custom_type_script))) {
|
||||||
cts = n->get_meta(SceneStringName(_custom_type_script));
|
cts = PropertyUtils::get_custom_type_script(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selection.size() == 1) {
|
if (selection.size() == 1) {
|
||||||
|
|
@ -3114,7 +3114,7 @@ void SceneTreeDock::_replace_node(Node *p_node, Node *p_by_node, bool p_keep_pro
|
||||||
|
|
||||||
// If we're dealing with a custom node type, we need to create a default instance of the custom type instead of the native type for property comparison.
|
// If we're dealing with a custom node type, we need to create a default instance of the custom type instead of the native type for property comparison.
|
||||||
if (oldnode->has_meta(SceneStringName(_custom_type_script))) {
|
if (oldnode->has_meta(SceneStringName(_custom_type_script))) {
|
||||||
Ref<Script> cts = oldnode->get_meta(SceneStringName(_custom_type_script));
|
Ref<Script> cts = PropertyUtils::get_custom_type_script(oldnode);
|
||||||
default_oldnode = Object::cast_to<Node>(get_editor_data()->script_class_instance(cts->get_global_name()));
|
default_oldnode = Object::cast_to<Node>(get_editor_data()->script_class_instance(cts->get_global_name()));
|
||||||
if (default_oldnode) {
|
if (default_oldnode) {
|
||||||
default_oldnode->set_name(cts->get_global_name());
|
default_oldnode->set_name(cts->get_global_name());
|
||||||
|
|
@ -3618,7 +3618,7 @@ void SceneTreeDock::_script_dropped(const String &p_file, NodePath p_to) {
|
||||||
} else {
|
} else {
|
||||||
// Check if dropped script is compatible.
|
// Check if dropped script is compatible.
|
||||||
if (n->has_meta(SceneStringName(_custom_type_script))) {
|
if (n->has_meta(SceneStringName(_custom_type_script))) {
|
||||||
Ref<Script> ct_scr = n->get_meta(SceneStringName(_custom_type_script));
|
Ref<Script> ct_scr = PropertyUtils::get_custom_type_script(n);
|
||||||
if (!scr->inherits_script(ct_scr)) {
|
if (!scr->inherits_script(ct_scr)) {
|
||||||
String custom_type_name = ct_scr->get_global_name();
|
String custom_type_name = ct_scr->get_global_name();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -338,6 +338,14 @@ Size2 WindowWrapper::get_margins_size() {
|
||||||
return Size2(margins->get_margin_size(SIDE_LEFT) + margins->get_margin_size(SIDE_RIGHT), margins->get_margin_size(SIDE_TOP) + margins->get_margin_size(SIDE_RIGHT));
|
return Size2(margins->get_margin_size(SIDE_LEFT) + margins->get_margin_size(SIDE_RIGHT), margins->get_margin_size(SIDE_TOP) + margins->get_margin_size(SIDE_RIGHT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Size2 WindowWrapper::get_margins_top_left() {
|
||||||
|
if (!margins) {
|
||||||
|
return Size2();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Size2(margins->get_margin_size(SIDE_LEFT), margins->get_margin_size(SIDE_TOP));
|
||||||
|
}
|
||||||
|
|
||||||
void WindowWrapper::grab_window_focus() {
|
void WindowWrapper::grab_window_focus() {
|
||||||
if (get_window_enabled() && is_visible()) {
|
if (get_window_enabled() && is_visible()) {
|
||||||
window->grab_focus();
|
window->grab_focus();
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@ public:
|
||||||
void set_window_title(const String &p_title);
|
void set_window_title(const String &p_title);
|
||||||
void set_margins_enabled(bool p_enabled);
|
void set_margins_enabled(bool p_enabled);
|
||||||
Size2 get_margins_size();
|
Size2 get_margins_size();
|
||||||
|
Size2 get_margins_top_left();
|
||||||
void grab_window_focus();
|
void grab_window_focus();
|
||||||
|
|
||||||
void set_override_close_request(bool p_enabled);
|
void set_override_close_request(bool p_enabled);
|
||||||
|
|
|
||||||
|
|
@ -864,6 +864,11 @@ void Main::test_cleanup() {
|
||||||
|
|
||||||
int Main::test_entrypoint(int argc, char *argv[], bool &tests_need_run) {
|
int Main::test_entrypoint(int argc, char *argv[], bool &tests_need_run) {
|
||||||
for (int x = 0; x < argc; x++) {
|
for (int x = 0; x < argc; x++) {
|
||||||
|
// Early return to ignore a possible user-provided "--test" argument.
|
||||||
|
if ((strlen(argv[x]) == 2) && ((strncmp(argv[x], "--", 2) == 0) || (strncmp(argv[x], "++", 2) == 0))) {
|
||||||
|
tests_need_run = false;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
if ((strncmp(argv[x], "--test", 6) == 0) && (strlen(argv[x]) == 6)) {
|
if ((strncmp(argv[x], "--test", 6) == 0) && (strlen(argv[x]) == 6)) {
|
||||||
tests_need_run = true;
|
tests_need_run = true;
|
||||||
#ifdef TESTS_ENABLED
|
#ifdef TESTS_ENABLED
|
||||||
|
|
@ -3116,7 +3121,7 @@ Error Main::setup2(bool p_show_boot_logo) {
|
||||||
OS::get_singleton()->benchmark_begin_measure("Servers", "Tablet Driver");
|
OS::get_singleton()->benchmark_begin_measure("Servers", "Tablet Driver");
|
||||||
|
|
||||||
GLOBAL_DEF_RST_NOVAL("input_devices/pen_tablet/driver", "");
|
GLOBAL_DEF_RST_NOVAL("input_devices/pen_tablet/driver", "");
|
||||||
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "input_devices/pen_tablet/driver.windows", PROPERTY_HINT_ENUM, "winink,wintab,dummy"), "");
|
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "input_devices/pen_tablet/driver.windows", PROPERTY_HINT_ENUM, "auto,winink,wintab,dummy"), "");
|
||||||
|
|
||||||
if (tablet_driver.is_empty()) { // specified in project.godot
|
if (tablet_driver.is_empty()) { // specified in project.godot
|
||||||
tablet_driver = GLOBAL_GET("input_devices/pen_tablet/driver");
|
tablet_driver = GLOBAL_GET("input_devices/pen_tablet/driver");
|
||||||
|
|
@ -3136,7 +3141,7 @@ Error Main::setup2(bool p_show_boot_logo) {
|
||||||
DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(0));
|
DisplayServer::get_singleton()->tablet_set_current_driver(DisplayServer::get_singleton()->tablet_get_driver_name(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
print_verbose("Using \"" + tablet_driver + "\" pen tablet driver...");
|
print_verbose("Using \"" + DisplayServer::get_singleton()->tablet_get_current_driver() + "\" pen tablet driver...");
|
||||||
|
|
||||||
OS::get_singleton()->benchmark_end_measure("Servers", "Tablet Driver");
|
OS::get_singleton()->benchmark_end_measure("Servers", "Tablet Driver");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -294,13 +294,16 @@ Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/set_table_c
|
||||||
Added optional "shrink" argument. Compatibility method registered.
|
Added optional "shrink" argument. Compatibility method registered.
|
||||||
|
|
||||||
|
|
||||||
GH-100062
|
GH-101561
|
||||||
--------
|
--------
|
||||||
Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/index_buffer_create/arguments': size changed value in new API, from 4 to 5.
|
Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/index_buffer_create/arguments': size changed value in new API, from 4 to 5.
|
||||||
Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/uniform_buffer_create/arguments': size changed value in new API, from 2 to 3.
|
Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/uniform_buffer_create/arguments': size changed value in new API, from 2 to 3.
|
||||||
|
Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/storage_buffer_create/arguments': size changed value in new API, from 3 to 4.
|
||||||
Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/vertex_buffer_create/arguments': size changed value in new API, from 3 to 4.
|
Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/vertex_buffer_create/arguments': size changed value in new API, from 3 to 4.
|
||||||
|
Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/vertex_buffer_create/arguments/2': default_value changed value in new API, from "false" to "0".
|
||||||
|
Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/vertex_buffer_create/arguments/2': type changed value in new API, from "bool" to "bitfield::RenderingDevice.BufferCreationBits".
|
||||||
|
|
||||||
Optional argument added. Compatibility methods registered.
|
Optional argument (creation flags) added. Compatibility methods registered.
|
||||||
|
|
||||||
|
|
||||||
GH-101531
|
GH-101531
|
||||||
|
|
@ -316,3 +319,10 @@ GH-100913
|
||||||
Validate extension JSON: Error: Field 'classes/TextEdit/methods/get_line_column_at_pos/arguments': size changed value in new API, from 2 to 3.
|
Validate extension JSON: Error: Field 'classes/TextEdit/methods/get_line_column_at_pos/arguments': size changed value in new API, from 2 to 3.
|
||||||
|
|
||||||
Added optional argument to disallow positions that are outside the column range of the line. Compatibility method registered.
|
Added optional argument to disallow positions that are outside the column range of the line. Compatibility method registered.
|
||||||
|
|
||||||
|
|
||||||
|
GH-102796
|
||||||
|
---------
|
||||||
|
Validate extension JSON: Error: Field 'classes/GraphEdit/signals/frame_rect_changed/arguments/1': type changed value in new API, from "Vector2" to "Rect2".
|
||||||
|
|
||||||
|
Previous type was incorrect. No compatibility system for signal arguments.
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,16 @@ bool CSGShape3D::get_collision_mask_value(int p_layer_number) const {
|
||||||
return get_collision_mask() & (1 << (p_layer_number - 1));
|
return get_collision_mask() & (1 << (p_layer_number - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RID CSGShape3D::_get_root_collision_instance() const {
|
||||||
|
if (root_collision_instance.is_valid()) {
|
||||||
|
return root_collision_instance;
|
||||||
|
} else if (parent_shape) {
|
||||||
|
return parent_shape->_get_root_collision_instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
return RID();
|
||||||
|
}
|
||||||
|
|
||||||
void CSGShape3D::set_collision_priority(real_t p_priority) {
|
void CSGShape3D::set_collision_priority(real_t p_priority) {
|
||||||
collision_priority = p_priority;
|
collision_priority = p_priority;
|
||||||
if (root_collision_instance.is_valid()) {
|
if (root_collision_instance.is_valid()) {
|
||||||
|
|
@ -982,6 +992,8 @@ void CSGShape3D::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CSGShape3D::set_collision_mask_value);
|
ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CSGShape3D::set_collision_mask_value);
|
||||||
ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CSGShape3D::get_collision_mask_value);
|
ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CSGShape3D::get_collision_mask_value);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("_get_root_collision_instance"), &CSGShape3D::_get_root_collision_instance);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CSGShape3D::set_collision_layer_value);
|
ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CSGShape3D::set_collision_layer_value);
|
||||||
ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CSGShape3D::get_collision_layer_value);
|
ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CSGShape3D::get_collision_layer_value);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,8 @@ public:
|
||||||
void set_collision_mask_value(int p_layer_number, bool p_value);
|
void set_collision_mask_value(int p_layer_number, bool p_value);
|
||||||
bool get_collision_mask_value(int p_layer_number) const;
|
bool get_collision_mask_value(int p_layer_number) const;
|
||||||
|
|
||||||
|
RID _get_root_collision_instance() const;
|
||||||
|
|
||||||
void set_collision_priority(real_t p_priority);
|
void set_collision_priority(real_t p_priority);
|
||||||
real_t get_collision_priority() const;
|
real_t get_collision_priority() const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -301,6 +301,7 @@ void ENetMultiplayerPeer::close() {
|
||||||
}
|
}
|
||||||
for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
|
for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
|
||||||
E.value->flush();
|
E.value->flush();
|
||||||
|
E.value->destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
active_mode = MODE_NONE;
|
active_mode = MODE_NONE;
|
||||||
|
|
|
||||||
|
|
@ -878,8 +878,7 @@ static void _get_directory_contents(EditorFileSystemDirectory *p_dir, HashMap<St
|
||||||
if (requires_type && !ClassDB::is_parent_class(p_dir->get_file_type(i), p_required_type)) {
|
if (requires_type && !ClassDB::is_parent_class(p_dir->get_file_type(i), p_required_type)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ScriptLanguage::CodeCompletionOption option(p_dir->get_file_path(i), ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH);
|
ScriptLanguage::CodeCompletionOption option(p_dir->get_file_path(i).quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH);
|
||||||
option.insert_text = option.display.quote(quote_style);
|
|
||||||
r_list.insert(option.display, option);
|
r_list.insert(option.display, option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3487,6 +3487,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_
|
||||||
|
|
||||||
if (preload->path == nullptr) {
|
if (preload->path == nullptr) {
|
||||||
push_error(R"(Expected resource path after "(".)");
|
push_error(R"(Expected resource path after "(".)");
|
||||||
|
} else if (preload->path->type == Node::LITERAL) {
|
||||||
|
override_completion_context(preload->path, COMPLETION_RESOURCE_PATH, preload);
|
||||||
}
|
}
|
||||||
|
|
||||||
pop_completion_call();
|
pop_completion_call();
|
||||||
|
|
|
||||||
|
|
@ -1002,7 +1002,8 @@ LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDSh
|
||||||
// We denoise in fixed size regions and synchronize execution to avoid GPU timeouts.
|
// We denoise in fixed size regions and synchronize execution to avoid GPU timeouts.
|
||||||
// We use a region with 1/4 the amount of pixels if we're denoising SH lightmaps, as
|
// We use a region with 1/4 the amount of pixels if we're denoising SH lightmaps, as
|
||||||
// all four of them are denoised in the shader in one dispatch.
|
// all four of them are denoised in the shader in one dispatch.
|
||||||
const int max_region_size = p_bake_sh ? 512 : 1024;
|
const int user_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size")));
|
||||||
|
const int max_region_size = p_bake_sh ? user_region_size / 2 : user_region_size;
|
||||||
int x_regions = Math::division_round_up(p_atlas_size.width, max_region_size);
|
int x_regions = Math::division_round_up(p_atlas_size.width, max_region_size);
|
||||||
int y_regions = Math::division_round_up(p_atlas_size.height, max_region_size);
|
int y_regions = Math::division_round_up(p_atlas_size.height, max_region_size);
|
||||||
for (int s = 0; s < p_atlas_slices; s++) {
|
for (int s = 0; s < p_atlas_slices; s++) {
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@
|
||||||
#include "packet_peer_mbed_dtls.h"
|
#include "packet_peer_mbed_dtls.h"
|
||||||
#include "stream_peer_mbedtls.h"
|
#include "stream_peer_mbedtls.h"
|
||||||
|
|
||||||
|
#include "core/config/project_settings.h"
|
||||||
|
|
||||||
#if MBEDTLS_VERSION_MAJOR >= 3
|
#if MBEDTLS_VERSION_MAJOR >= 3
|
||||||
#include <psa/crypto.h>
|
#include <psa/crypto.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -50,6 +52,8 @@ void initialize_mbedtls_module(ModuleInitializationLevel p_level) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLOBAL_DEF("network/tls/enable_tls_v1.3", false);
|
||||||
|
|
||||||
#if MBEDTLS_VERSION_MAJOR >= 3
|
#if MBEDTLS_VERSION_MAJOR >= 3
|
||||||
int status = psa_crypto_init();
|
int status = psa_crypto_init();
|
||||||
ERR_FAIL_COND_MSG(status != PSA_SUCCESS, "Failed to initialize psa crypto. The mbedTLS modules will not work.");
|
ERR_FAIL_COND_MSG(status != PSA_SUCCESS, "Failed to initialize psa crypto. The mbedTLS modules will not work.");
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@
|
||||||
|
|
||||||
#include "tls_context_mbedtls.h"
|
#include "tls_context_mbedtls.h"
|
||||||
|
|
||||||
|
#include "core/config/project_settings.h"
|
||||||
|
|
||||||
static void my_debug(void *ctx, int level,
|
static void my_debug(void *ctx, int level,
|
||||||
const char *file, int line,
|
const char *file, int line,
|
||||||
const char *str) {
|
const char *str) {
|
||||||
|
|
@ -144,6 +146,11 @@ Error TLSContextMbedTLS::init_server(int p_transport, Ref<TLSOptions> p_options,
|
||||||
cookies = p_cookies;
|
cookies = p_cookies;
|
||||||
mbedtls_ssl_conf_dtls_cookies(&conf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &(cookies->cookie_ctx));
|
mbedtls_ssl_conf_dtls_cookies(&conf, mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, &(cookies->cookie_ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Engine::get_singleton()->is_editor_hint() || !(bool)GLOBAL_GET("network/tls/enable_tls_v1.3")) {
|
||||||
|
mbedtls_ssl_conf_max_tls_version(&conf, MBEDTLS_SSL_VERSION_TLS1_2);
|
||||||
|
}
|
||||||
|
|
||||||
mbedtls_ssl_setup(&tls, &conf);
|
mbedtls_ssl_setup(&tls, &conf);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
@ -187,6 +194,10 @@ Error TLSContextMbedTLS::init_client(int p_transport, const String &p_hostname,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Engine::get_singleton()->is_editor_hint() || !(bool)GLOBAL_GET("network/tls/enable_tls_v1.3")) {
|
||||||
|
mbedtls_ssl_conf_max_tls_version(&conf, MBEDTLS_SSL_VERSION_TLS1_2);
|
||||||
|
}
|
||||||
|
|
||||||
// Set valid CAs
|
// Set valid CAs
|
||||||
mbedtls_ssl_conf_ca_chain(&conf, &(cas->cert), nullptr);
|
mbedtls_ssl_conf_ca_chain(&conf, &(cas->cert), nullptr);
|
||||||
mbedtls_ssl_setup(&tls, &conf);
|
mbedtls_ssl_setup(&tls, &conf);
|
||||||
|
|
|
||||||
|
|
@ -101,4 +101,13 @@ public class ExportDiagnosticsTests
|
||||||
new string[] { "ExportDiagnostics_GD0110_ScriptProperties.generated.cs" }
|
new string[] { "ExportDiagnostics_GD0110_ScriptProperties.generated.cs" }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async void ExportToolButtonStoringCallable()
|
||||||
|
{
|
||||||
|
await CSharpSourceGeneratorVerifier<ScriptPropertiesGenerator>.Verify(
|
||||||
|
new string[] { "ExportDiagnostics_GD0111.cs" },
|
||||||
|
new string[] { "ExportDiagnostics_GD0111_ScriptProperties.generated.cs" }
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,22 +9,12 @@ partial class ExportDiagnostics_GD0108
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new class PropertyName : global::Godot.Node.PropertyName {
|
public new class PropertyName : global::Godot.Node.PropertyName {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cached name for the 'MyButton' field.
|
/// Cached name for the 'MyButton' property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new static readonly global::Godot.StringName @MyButton = "MyButton";
|
public new static readonly global::Godot.StringName @MyButton = "MyButton";
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||||
protected override bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
|
|
||||||
{
|
|
||||||
if (name == PropertyName.@MyButton) {
|
|
||||||
this.@MyButton = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Callable>(value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return base.SetGodotClassPropertyValue(name, value);
|
|
||||||
}
|
|
||||||
/// <inheritdoc/>
|
|
||||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
|
||||||
protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
|
protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
|
||||||
{
|
{
|
||||||
if (name == PropertyName.@MyButton) {
|
if (name == PropertyName.@MyButton) {
|
||||||
|
|
|
||||||
|
|
@ -9,22 +9,12 @@ partial class ExportDiagnostics_GD0109
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new class PropertyName : global::Godot.Node.PropertyName {
|
public new class PropertyName : global::Godot.Node.PropertyName {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cached name for the 'MyButton' field.
|
/// Cached name for the 'MyButton' property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new static readonly global::Godot.StringName @MyButton = "MyButton";
|
public new static readonly global::Godot.StringName @MyButton = "MyButton";
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||||
protected override bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
|
|
||||||
{
|
|
||||||
if (name == PropertyName.@MyButton) {
|
|
||||||
this.@MyButton = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Callable>(value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return base.SetGodotClassPropertyValue(name, value);
|
|
||||||
}
|
|
||||||
/// <inheritdoc/>
|
|
||||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
|
||||||
protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
|
protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
|
||||||
{
|
{
|
||||||
if (name == PropertyName.@MyButton) {
|
if (name == PropertyName.@MyButton) {
|
||||||
|
|
|
||||||
|
|
@ -9,26 +9,16 @@ partial class ExportDiagnostics_GD0110
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new class PropertyName : global::Godot.Node.PropertyName {
|
public new class PropertyName : global::Godot.Node.PropertyName {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cached name for the 'MyButton' field.
|
/// Cached name for the 'MyButton' property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new static readonly global::Godot.StringName @MyButton = "MyButton";
|
public new static readonly global::Godot.StringName @MyButton = "MyButton";
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||||
protected override bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
|
|
||||||
{
|
|
||||||
if (name == PropertyName.@MyButton) {
|
|
||||||
this.@MyButton = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return base.SetGodotClassPropertyValue(name, value);
|
|
||||||
}
|
|
||||||
/// <inheritdoc/>
|
|
||||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
|
||||||
protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
|
protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
|
||||||
{
|
{
|
||||||
if (name == PropertyName.@MyButton) {
|
if (name == PropertyName.@MyButton) {
|
||||||
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.@MyButton);
|
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<int>(this.@MyButton);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return base.GetGodotClassPropertyValue(name, out value);
|
return base.GetGodotClassPropertyValue(name, out value);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
using Godot;
|
||||||
|
using Godot.NativeInterop;
|
||||||
|
|
||||||
|
partial class ExportDiagnostics_GD0111
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword
|
||||||
|
/// <summary>
|
||||||
|
/// Cached StringNames for the properties and fields contained in this class, for fast lookup.
|
||||||
|
/// </summary>
|
||||||
|
public new class PropertyName : global::Godot.Node.PropertyName {
|
||||||
|
/// <summary>
|
||||||
|
/// Cached name for the 'MyButtonGet' property.
|
||||||
|
/// </summary>
|
||||||
|
public new static readonly global::Godot.StringName @MyButtonGet = "MyButtonGet";
|
||||||
|
/// <summary>
|
||||||
|
/// Cached name for the 'MyButtonGetSet' property.
|
||||||
|
/// </summary>
|
||||||
|
public new static readonly global::Godot.StringName @MyButtonGetSet = "MyButtonGetSet";
|
||||||
|
/// <summary>
|
||||||
|
/// Cached name for the 'MyButtonGetWithBackingField' property.
|
||||||
|
/// </summary>
|
||||||
|
public new static readonly global::Godot.StringName @MyButtonGetWithBackingField = "MyButtonGetWithBackingField";
|
||||||
|
/// <summary>
|
||||||
|
/// Cached name for the 'MyButtonGetSetWithBackingField' property.
|
||||||
|
/// </summary>
|
||||||
|
public new static readonly global::Godot.StringName @MyButtonGetSetWithBackingField = "MyButtonGetSetWithBackingField";
|
||||||
|
/// <summary>
|
||||||
|
/// Cached name for the 'MyButtonOkWithCallableCreationExpression' property.
|
||||||
|
/// </summary>
|
||||||
|
public new static readonly global::Godot.StringName @MyButtonOkWithCallableCreationExpression = "MyButtonOkWithCallableCreationExpression";
|
||||||
|
/// <summary>
|
||||||
|
/// Cached name for the 'MyButtonOkWithImplicitCallableCreationExpression' property.
|
||||||
|
/// </summary>
|
||||||
|
public new static readonly global::Godot.StringName @MyButtonOkWithImplicitCallableCreationExpression = "MyButtonOkWithImplicitCallableCreationExpression";
|
||||||
|
/// <summary>
|
||||||
|
/// Cached name for the 'MyButtonOkWithCallableFromExpression' property.
|
||||||
|
/// </summary>
|
||||||
|
public new static readonly global::Godot.StringName @MyButtonOkWithCallableFromExpression = "MyButtonOkWithCallableFromExpression";
|
||||||
|
/// <summary>
|
||||||
|
/// Cached name for the '_backingField' field.
|
||||||
|
/// </summary>
|
||||||
|
public new static readonly global::Godot.StringName @_backingField = "_backingField";
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||||
|
protected override bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
|
||||||
|
{
|
||||||
|
if (name == PropertyName.@MyButtonGetSet) {
|
||||||
|
this.@MyButtonGetSet = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Callable>(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (name == PropertyName.@MyButtonGetSetWithBackingField) {
|
||||||
|
this.@MyButtonGetSetWithBackingField = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Callable>(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (name == PropertyName.@_backingField) {
|
||||||
|
this.@_backingField = global::Godot.NativeInterop.VariantUtils.ConvertTo<global::Godot.Callable>(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return base.SetGodotClassPropertyValue(name, value);
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||||
|
protected override bool GetGodotClassPropertyValue(in godot_string_name name, out godot_variant value)
|
||||||
|
{
|
||||||
|
if (name == PropertyName.@MyButtonGet) {
|
||||||
|
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Callable>(this.@MyButtonGet);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (name == PropertyName.@MyButtonGetSet) {
|
||||||
|
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Callable>(this.@MyButtonGetSet);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (name == PropertyName.@MyButtonGetWithBackingField) {
|
||||||
|
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Callable>(this.@MyButtonGetWithBackingField);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (name == PropertyName.@MyButtonGetSetWithBackingField) {
|
||||||
|
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Callable>(this.@MyButtonGetSetWithBackingField);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (name == PropertyName.@MyButtonOkWithCallableCreationExpression) {
|
||||||
|
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Callable>(this.@MyButtonOkWithCallableCreationExpression);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (name == PropertyName.@MyButtonOkWithImplicitCallableCreationExpression) {
|
||||||
|
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Callable>(this.@MyButtonOkWithImplicitCallableCreationExpression);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (name == PropertyName.@MyButtonOkWithCallableFromExpression) {
|
||||||
|
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Callable>(this.@MyButtonOkWithCallableFromExpression);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (name == PropertyName.@_backingField) {
|
||||||
|
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<global::Godot.Callable>(this.@_backingField);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return base.GetGodotClassPropertyValue(name, out value);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Get the property information for all the properties declared in this class.
|
||||||
|
/// This method is used by Godot to register the available properties in the editor.
|
||||||
|
/// Do not call this method.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||||
|
internal new static global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo> GetGodotPropertyList()
|
||||||
|
{
|
||||||
|
var properties = new global::System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>();
|
||||||
|
properties.Add(new(type: (global::Godot.Variant.Type)25, name: PropertyName.@_backingField, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false));
|
||||||
|
properties.Add(new(type: (global::Godot.Variant.Type)25, name: PropertyName.@MyButtonOkWithCallableCreationExpression, hint: (global::Godot.PropertyHint)39, hintString: "", usage: (global::Godot.PropertyUsageFlags)4, exported: true));
|
||||||
|
properties.Add(new(type: (global::Godot.Variant.Type)25, name: PropertyName.@MyButtonOkWithImplicitCallableCreationExpression, hint: (global::Godot.PropertyHint)39, hintString: "", usage: (global::Godot.PropertyUsageFlags)4, exported: true));
|
||||||
|
properties.Add(new(type: (global::Godot.Variant.Type)25, name: PropertyName.@MyButtonOkWithCallableFromExpression, hint: (global::Godot.PropertyHint)39, hintString: "", usage: (global::Godot.PropertyUsageFlags)4, exported: true));
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS0109
|
||||||
|
}
|
||||||
|
|
@ -4,5 +4,5 @@ using Godot.Collections;
|
||||||
public partial class ExportDiagnostics_GD0108 : Node
|
public partial class ExportDiagnostics_GD0108 : Node
|
||||||
{
|
{
|
||||||
[ExportToolButton("")]
|
[ExportToolButton("")]
|
||||||
public Callable {|GD0108:MyButton|};
|
public Callable {|GD0108:MyButton|} => new Callable();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,5 @@ using Godot.Collections;
|
||||||
public partial class ExportDiagnostics_GD0109 : Node
|
public partial class ExportDiagnostics_GD0109 : Node
|
||||||
{
|
{
|
||||||
[Export, ExportToolButton("")]
|
[Export, ExportToolButton("")]
|
||||||
public Callable {|GD0109:MyButton|};
|
public Callable {|GD0109:MyButton|} => new Callable();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,5 @@ using Godot.Collections;
|
||||||
public partial class ExportDiagnostics_GD0110 : Node
|
public partial class ExportDiagnostics_GD0110 : Node
|
||||||
{
|
{
|
||||||
[ExportToolButton("")]
|
[ExportToolButton("")]
|
||||||
public string {|GD0110:MyButton|};
|
public int {|GD0110:MyButton|} => new();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
using Godot;
|
||||||
|
using Godot.Collections;
|
||||||
|
|
||||||
|
[Tool]
|
||||||
|
public partial class ExportDiagnostics_GD0111 : Node
|
||||||
|
{
|
||||||
|
private Callable _backingField;
|
||||||
|
|
||||||
|
[ExportToolButton("")]
|
||||||
|
public Callable {|GD0111:MyButtonGet|} { get; }
|
||||||
|
|
||||||
|
[ExportToolButton("")]
|
||||||
|
public Callable {|GD0111:MyButtonGetSet|} { get; set; }
|
||||||
|
|
||||||
|
[ExportToolButton("")]
|
||||||
|
public Callable {|GD0111:MyButtonGetWithBackingField|} { get => _backingField; }
|
||||||
|
|
||||||
|
[ExportToolButton("")]
|
||||||
|
public Callable {|GD0111:MyButtonGetSetWithBackingField|} { get => _backingField; set => _backingField = value; }
|
||||||
|
|
||||||
|
[ExportToolButton("")]
|
||||||
|
public Callable MyButtonOkWithCallableCreationExpression => new Callable(this, "");
|
||||||
|
|
||||||
|
[ExportToolButton("")]
|
||||||
|
public Callable MyButtonOkWithImplicitCallableCreationExpression => new(this, "");
|
||||||
|
|
||||||
|
[ExportToolButton("")]
|
||||||
|
public Callable MyButtonOkWithCallableFromExpression => Callable.From(null);
|
||||||
|
}
|
||||||
|
|
@ -6,3 +6,4 @@ GD0003 | Usage | Error | ScriptPathAttributeGenerator, [Documentation](ht
|
||||||
GD0108 | Usage | Error | ScriptPropertiesGenerator, [Documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/diagnostics/GD0108.html)
|
GD0108 | Usage | Error | ScriptPropertiesGenerator, [Documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/diagnostics/GD0108.html)
|
||||||
GD0109 | Usage | Error | ScriptPropertiesGenerator, [Documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/diagnostics/GD0109.html)
|
GD0109 | Usage | Error | ScriptPropertiesGenerator, [Documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/diagnostics/GD0109.html)
|
||||||
GD0110 | Usage | Error | ScriptPropertiesGenerator, [Documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/diagnostics/GD0110.html)
|
GD0110 | Usage | Error | ScriptPropertiesGenerator, [Documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/diagnostics/GD0110.html)
|
||||||
|
GD0111 | Usage | Error | ScriptPropertiesGenerator, [Documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/diagnostics/GD0111.html)
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,16 @@ namespace Godot.SourceGenerators
|
||||||
"The exported tool button is not a Callable. The '[ExportToolButton]' attribute is only supported on members of type Callable.",
|
"The exported tool button is not a Callable. The '[ExportToolButton]' attribute is only supported on members of type Callable.",
|
||||||
helpLinkUri: string.Format(_helpLinkFormat, "GD0110"));
|
helpLinkUri: string.Format(_helpLinkFormat, "GD0110"));
|
||||||
|
|
||||||
|
public static readonly DiagnosticDescriptor ExportToolButtonMustBeExpressionBodiedProperty =
|
||||||
|
new DiagnosticDescriptor(id: "GD0111",
|
||||||
|
title: "The exported tool button must be an expression-bodied property",
|
||||||
|
messageFormat: "The exported tool button '{0}' must be an expression-bodied property",
|
||||||
|
category: "Usage",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
"The exported tool button must be an expression-bodied property. The '[ExportToolButton]' attribute is only supported on expression-bodied properties with a 'new Callable(...)' or 'Callable.From(...)' expression.",
|
||||||
|
helpLinkUri: string.Format(_helpLinkFormat, "GD0111"));
|
||||||
|
|
||||||
public static readonly DiagnosticDescriptor SignalDelegateMissingSuffixRule =
|
public static readonly DiagnosticDescriptor SignalDelegateMissingSuffixRule =
|
||||||
new DiagnosticDescriptor(id: "GD0201",
|
new DiagnosticDescriptor(id: "GD0201",
|
||||||
title: "The name of the delegate must end with 'EventHandler'",
|
title: "The name of the delegate must end with 'EventHandler'",
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ namespace Godot.SourceGenerators
|
||||||
{
|
{
|
||||||
public const string GodotObject = "Godot.GodotObject";
|
public const string GodotObject = "Godot.GodotObject";
|
||||||
public const string Node = "Godot.Node";
|
public const string Node = "Godot.Node";
|
||||||
|
public const string Callable = "Godot.Callable";
|
||||||
public const string AssemblyHasScriptsAttr = "Godot.AssemblyHasScriptsAttribute";
|
public const string AssemblyHasScriptsAttr = "Godot.AssemblyHasScriptsAttribute";
|
||||||
public const string ExportAttr = "Godot.ExportAttribute";
|
public const string ExportAttr = "Godot.ExportAttribute";
|
||||||
public const string ExportCategoryAttr = "Godot.ExportCategoryAttribute";
|
public const string ExportCategoryAttr = "Godot.ExportCategoryAttribute";
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
using Microsoft.CodeAnalysis.Text;
|
using Microsoft.CodeAnalysis.Text;
|
||||||
|
|
||||||
|
|
@ -465,6 +466,94 @@ namespace Godot.SourceGenerators
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (exportToolButtonAttr != null && propertySymbol != null)
|
||||||
|
{
|
||||||
|
if (!PropertyIsExpressionBodiedAndReturnsNewCallable(context.Compilation, propertySymbol))
|
||||||
|
{
|
||||||
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
|
Common.ExportToolButtonMustBeExpressionBodiedProperty,
|
||||||
|
propertySymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
|
||||||
|
propertySymbol.ToDisplayString()
|
||||||
|
));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool PropertyIsExpressionBodiedAndReturnsNewCallable(Compilation compilation, IPropertySymbol? propertySymbol)
|
||||||
|
{
|
||||||
|
if (propertySymbol == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var propertyDeclarationSyntax = propertySymbol.DeclaringSyntaxReferences
|
||||||
|
.Select(r => r.GetSyntax() as PropertyDeclarationSyntax).FirstOrDefault();
|
||||||
|
if (propertyDeclarationSyntax == null || propertyDeclarationSyntax.Initializer != null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyDeclarationSyntax.AccessorList != null)
|
||||||
|
{
|
||||||
|
var accessors = propertyDeclarationSyntax.AccessorList.Accessors;
|
||||||
|
foreach (var accessor in accessors)
|
||||||
|
{
|
||||||
|
if (!accessor.IsKind(SyntaxKind.GetAccessorDeclaration))
|
||||||
|
{
|
||||||
|
// Only getters are allowed.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ExpressionBodyReturnsNewCallable(compilation, accessor.ExpressionBody))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!ExpressionBodyReturnsNewCallable(compilation, propertyDeclarationSyntax.ExpressionBody))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ExpressionBodyReturnsNewCallable(Compilation compilation, ArrowExpressionClauseSyntax? expressionSyntax)
|
||||||
|
{
|
||||||
|
if (expressionSyntax == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var semanticModel = compilation.GetSemanticModel(expressionSyntax.SyntaxTree);
|
||||||
|
|
||||||
|
switch (expressionSyntax.Expression)
|
||||||
|
{
|
||||||
|
case ImplicitObjectCreationExpressionSyntax creationExpression:
|
||||||
|
// We already validate that the property type must be 'Callable'
|
||||||
|
// so we can assume this constructor is valid.
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case ObjectCreationExpressionSyntax creationExpression:
|
||||||
|
var typeSymbol = semanticModel.GetSymbolInfo(creationExpression.Type).Symbol as ITypeSymbol;
|
||||||
|
if (typeSymbol != null)
|
||||||
|
{
|
||||||
|
return typeSymbol.FullQualifiedNameOmitGlobal() == GodotClasses.Callable;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InvocationExpressionSyntax invocationExpression:
|
||||||
|
var methodSymbol = semanticModel.GetSymbolInfo(invocationExpression).Symbol as IMethodSymbol;
|
||||||
|
if (methodSymbol != null && methodSymbol.Name == "From")
|
||||||
|
{
|
||||||
|
return methodSymbol.ContainingType.FullQualifiedNameOmitGlobal() == GodotClasses.Callable;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var memberType = propertySymbol?.Type ?? fieldSymbol!.Type;
|
var memberType = propertySymbol?.Type ?? fieldSymbol!.Type;
|
||||||
|
|
||||||
var memberVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(marshalType)!.Value;
|
var memberVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(marshalType)!.Value;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace Godot
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Exports the annotated <see cref="Callable"/> as a clickable button.
|
/// Exports the annotated <see cref="Callable"/> as a clickable button.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public sealed class ExportToolButtonAttribute : Attribute
|
public sealed class ExportToolButtonAttribute : Attribute
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -52,16 +52,16 @@ public:
|
||||||
virtual void set_use_edge_connections(bool p_enabled) {}
|
virtual void set_use_edge_connections(bool p_enabled) {}
|
||||||
virtual bool get_use_edge_connections() const { return false; }
|
virtual bool get_use_edge_connections() const { return false; }
|
||||||
|
|
||||||
void set_navigation_layers(uint32_t p_navigation_layers) { navigation_layers = p_navigation_layers; }
|
virtual void set_navigation_layers(uint32_t p_navigation_layers) {}
|
||||||
uint32_t get_navigation_layers() const { return navigation_layers; }
|
uint32_t get_navigation_layers() const { return navigation_layers; }
|
||||||
|
|
||||||
void set_enter_cost(real_t p_enter_cost) { enter_cost = MAX(p_enter_cost, 0.0); }
|
virtual void set_enter_cost(real_t p_enter_cost) {}
|
||||||
real_t get_enter_cost() const { return enter_cost; }
|
real_t get_enter_cost() const { return enter_cost; }
|
||||||
|
|
||||||
void set_travel_cost(real_t p_travel_cost) { travel_cost = MAX(p_travel_cost, 0.0); }
|
virtual void set_travel_cost(real_t p_travel_cost) {}
|
||||||
real_t get_travel_cost() const { return travel_cost; }
|
real_t get_travel_cost() const { return travel_cost; }
|
||||||
|
|
||||||
void set_owner_id(ObjectID p_owner_id) { owner_id = p_owner_id; }
|
virtual void set_owner_id(ObjectID p_owner_id) {}
|
||||||
ObjectID get_owner_id() const { return owner_id; }
|
ObjectID get_owner_id() const { return owner_id; }
|
||||||
|
|
||||||
virtual ~NavBase() {}
|
virtual ~NavBase() {}
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,48 @@ void NavLink::set_end_position(const Vector3 p_position) {
|
||||||
request_sync();
|
request_sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavLink::set_navigation_layers(uint32_t p_navigation_layers) {
|
||||||
|
if (navigation_layers == p_navigation_layers) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigation_layers = p_navigation_layers;
|
||||||
|
link_dirty = true;
|
||||||
|
|
||||||
|
request_sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavLink::set_enter_cost(real_t p_enter_cost) {
|
||||||
|
real_t new_enter_cost = MAX(p_enter_cost, 0.0);
|
||||||
|
if (enter_cost == new_enter_cost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
enter_cost = new_enter_cost;
|
||||||
|
link_dirty = true;
|
||||||
|
|
||||||
|
request_sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavLink::set_travel_cost(real_t p_travel_cost) {
|
||||||
|
real_t new_travel_cost = MAX(p_travel_cost, 0.0);
|
||||||
|
if (travel_cost == new_travel_cost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
travel_cost = new_travel_cost;
|
||||||
|
link_dirty = true;
|
||||||
|
|
||||||
|
request_sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavLink::set_owner_id(ObjectID p_owner_id) {
|
||||||
|
if (owner_id == p_owner_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
owner_id = p_owner_id;
|
||||||
|
link_dirty = true;
|
||||||
|
|
||||||
|
request_sync();
|
||||||
|
}
|
||||||
|
|
||||||
bool NavLink::is_dirty() const {
|
bool NavLink::is_dirty() const {
|
||||||
return link_dirty;
|
return link_dirty;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,12 @@ public:
|
||||||
return end_position;
|
return end_position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NavBase properties.
|
||||||
|
virtual void set_navigation_layers(uint32_t p_navigation_layers) override;
|
||||||
|
virtual void set_enter_cost(real_t p_enter_cost) override;
|
||||||
|
virtual void set_travel_cost(real_t p_travel_cost) override;
|
||||||
|
virtual void set_owner_id(ObjectID p_owner_id) override;
|
||||||
|
|
||||||
bool is_dirty() const;
|
bool is_dirty() const;
|
||||||
void sync();
|
void sync();
|
||||||
void request_sync();
|
void request_sync();
|
||||||
|
|
|
||||||
|
|
@ -141,10 +141,54 @@ Vector3 NavRegion::get_random_point(uint32_t p_navigation_layers, bool p_uniform
|
||||||
return NavMeshQueries3D::polygons_get_random_point(get_polygons(), p_navigation_layers, p_uniformly);
|
return NavMeshQueries3D::polygons_get_random_point(get_polygons(), p_navigation_layers, p_uniformly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NavRegion::set_navigation_layers(uint32_t p_navigation_layers) {
|
||||||
|
if (navigation_layers == p_navigation_layers) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigation_layers = p_navigation_layers;
|
||||||
|
region_dirty = true;
|
||||||
|
|
||||||
|
request_sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegion::set_enter_cost(real_t p_enter_cost) {
|
||||||
|
real_t new_enter_cost = MAX(p_enter_cost, 0.0);
|
||||||
|
if (enter_cost == new_enter_cost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
enter_cost = new_enter_cost;
|
||||||
|
region_dirty = true;
|
||||||
|
|
||||||
|
request_sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegion::set_travel_cost(real_t p_travel_cost) {
|
||||||
|
real_t new_travel_cost = MAX(p_travel_cost, 0.0);
|
||||||
|
if (travel_cost == new_travel_cost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
travel_cost = new_travel_cost;
|
||||||
|
region_dirty = true;
|
||||||
|
|
||||||
|
request_sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavRegion::set_owner_id(ObjectID p_owner_id) {
|
||||||
|
if (owner_id == p_owner_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
owner_id = p_owner_id;
|
||||||
|
region_dirty = true;
|
||||||
|
|
||||||
|
request_sync();
|
||||||
|
}
|
||||||
|
|
||||||
bool NavRegion::sync() {
|
bool NavRegion::sync() {
|
||||||
RWLockWrite write_lock(region_rwlock);
|
RWLockWrite write_lock(region_rwlock);
|
||||||
|
|
||||||
bool something_changed = polygons_dirty /* || something_dirty? */;
|
bool something_changed = region_dirty || polygons_dirty;
|
||||||
|
|
||||||
|
region_dirty = false;
|
||||||
|
|
||||||
update_polygons();
|
update_polygons();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ class NavRegion : public NavBase {
|
||||||
|
|
||||||
bool use_edge_connections = true;
|
bool use_edge_connections = true;
|
||||||
|
|
||||||
|
bool region_dirty = true;
|
||||||
bool polygons_dirty = true;
|
bool polygons_dirty = true;
|
||||||
|
|
||||||
LocalVector<gd::Polygon> navmesh_polygons;
|
LocalVector<gd::Polygon> navmesh_polygons;
|
||||||
|
|
@ -77,10 +78,8 @@ public:
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_use_edge_connections(bool p_enabled);
|
virtual void set_use_edge_connections(bool p_enabled) override;
|
||||||
bool get_use_edge_connections() const {
|
virtual bool get_use_edge_connections() const override { return use_edge_connections; }
|
||||||
return use_edge_connections;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_transform(Transform3D transform);
|
void set_transform(Transform3D transform);
|
||||||
const Transform3D &get_transform() const {
|
const Transform3D &get_transform() const {
|
||||||
|
|
@ -100,6 +99,12 @@ public:
|
||||||
real_t get_surface_area() const { return surface_area; }
|
real_t get_surface_area() const { return surface_area; }
|
||||||
AABB get_bounds() const { return bounds; }
|
AABB get_bounds() const { return bounds; }
|
||||||
|
|
||||||
|
// NavBase properties.
|
||||||
|
virtual void set_navigation_layers(uint32_t p_navigation_layers) override;
|
||||||
|
virtual void set_enter_cost(real_t p_enter_cost) override;
|
||||||
|
virtual void set_travel_cost(real_t p_travel_cost) override;
|
||||||
|
virtual void set_owner_id(ObjectID p_owner_id) override;
|
||||||
|
|
||||||
bool sync();
|
bool sync();
|
||||||
void request_sync();
|
void request_sync();
|
||||||
void cancel_sync_request();
|
void cancel_sync_request();
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,8 @@ env_svg.Prepend(CPPPATH=[thirdparty_dir + "inc"])
|
||||||
|
|
||||||
# Enable ThorVG static object linking.
|
# Enable ThorVG static object linking.
|
||||||
env_svg.Append(CPPDEFINES=["TVG_STATIC"])
|
env_svg.Append(CPPDEFINES=["TVG_STATIC"])
|
||||||
|
# Explicit support for embedded images in svg.
|
||||||
|
env_svg.Append(CPPDEFINES=["THORVG_FILE_IO_SUPPORT"])
|
||||||
|
|
||||||
env_thirdparty = env_svg.Clone()
|
env_thirdparty = env_svg.Clone()
|
||||||
env_thirdparty.disable_warnings()
|
env_thirdparty.disable_warnings()
|
||||||
|
|
|
||||||
|
|
@ -39,31 +39,9 @@
|
||||||
int VideoStreamPlaybackTheora::buffer_data() {
|
int VideoStreamPlaybackTheora::buffer_data() {
|
||||||
char *buffer = ogg_sync_buffer(&oy, 4096);
|
char *buffer = ogg_sync_buffer(&oy, 4096);
|
||||||
|
|
||||||
#ifdef THEORA_USE_THREAD_STREAMING
|
|
||||||
|
|
||||||
int read;
|
|
||||||
|
|
||||||
do {
|
|
||||||
thread_sem->post();
|
|
||||||
read = MIN(ring_buffer.data_left(), 4096);
|
|
||||||
if (read) {
|
|
||||||
ring_buffer.read((uint8_t *)buffer, read);
|
|
||||||
ogg_sync_wrote(&oy, read);
|
|
||||||
} else {
|
|
||||||
OS::get_singleton()->delay_usec(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
} while (read == 0);
|
|
||||||
|
|
||||||
return read;
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
uint64_t bytes = file->get_buffer((uint8_t *)buffer, 4096);
|
uint64_t bytes = file->get_buffer((uint8_t *)buffer, 4096);
|
||||||
ogg_sync_wrote(&oy, bytes);
|
ogg_sync_wrote(&oy, bytes);
|
||||||
return (bytes);
|
return (bytes);
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int VideoStreamPlaybackTheora::queue_page(ogg_page *page) {
|
int VideoStreamPlaybackTheora::queue_page(ogg_page *page) {
|
||||||
|
|
@ -82,34 +60,24 @@ int VideoStreamPlaybackTheora::queue_page(ogg_page *page) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoStreamPlaybackTheora::video_write() {
|
void VideoStreamPlaybackTheora::video_write(th_ycbcr_buffer yuv) {
|
||||||
th_ycbcr_buffer yuv;
|
uint8_t *w = frame_data.ptrw();
|
||||||
th_decode_ycbcr_out(td, yuv);
|
char *dst = (char *)w;
|
||||||
|
uint32_t y_offset = region.position.y * yuv[0].stride + region.position.x;
|
||||||
|
uint32_t uv_offset = region.position.y * yuv[1].stride + region.position.x;
|
||||||
|
|
||||||
int pitch = 4;
|
if (px_fmt == TH_PF_444) {
|
||||||
frame_data.resize(size.x * size.y * pitch);
|
yuv444_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data + y_offset, (uint8_t *)yuv[1].data + uv_offset, (uint8_t *)yuv[2].data + uv_offset, region.size.x, region.size.y, yuv[0].stride, yuv[1].stride, region.size.x << 2);
|
||||||
{
|
} else if (px_fmt == TH_PF_422) {
|
||||||
uint8_t *w = frame_data.ptrw();
|
yuv422_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data + y_offset, (uint8_t *)yuv[1].data + uv_offset, (uint8_t *)yuv[2].data + uv_offset, region.size.x, region.size.y, yuv[0].stride, yuv[1].stride, region.size.x << 2);
|
||||||
char *dst = (char *)w;
|
} else if (px_fmt == TH_PF_420) {
|
||||||
|
yuv420_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data + y_offset, (uint8_t *)yuv[1].data + uv_offset, (uint8_t *)yuv[2].data + uv_offset, region.size.x, region.size.y, yuv[0].stride, yuv[1].stride, region.size.x << 2);
|
||||||
if (px_fmt == TH_PF_444) {
|
|
||||||
yuv444_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2);
|
|
||||||
|
|
||||||
} else if (px_fmt == TH_PF_422) {
|
|
||||||
yuv422_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2);
|
|
||||||
|
|
||||||
} else if (px_fmt == TH_PF_420) {
|
|
||||||
yuv420_2_rgb8888((uint8_t *)dst, (uint8_t *)yuv[0].data, (uint8_t *)yuv[1].data, (uint8_t *)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x << 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
format = Image::FORMAT_RGBA8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<Image> img = memnew(Image(size.x, size.y, false, Image::FORMAT_RGBA8, frame_data)); //zero copy image creation
|
Ref<Image> img;
|
||||||
|
img.instantiate(region.size.x, region.size.y, false, Image::FORMAT_RGBA8, frame_data); //zero copy image creation
|
||||||
|
|
||||||
texture->update(img); //zero copy send to rendering server
|
texture->update(img); //zero copy send to rendering server
|
||||||
|
|
||||||
frames_pending = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoStreamPlaybackTheora::clear() {
|
void VideoStreamPlaybackTheora::clear() {
|
||||||
|
|
@ -136,20 +104,15 @@ void VideoStreamPlaybackTheora::clear() {
|
||||||
}
|
}
|
||||||
ogg_sync_clear(&oy);
|
ogg_sync_clear(&oy);
|
||||||
|
|
||||||
#ifdef THEORA_USE_THREAD_STREAMING
|
|
||||||
thread_exit = true;
|
|
||||||
thread_sem->post(); //just in case
|
|
||||||
thread.wait_to_finish();
|
|
||||||
ring_buffer.clear();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
theora_p = 0;
|
theora_p = 0;
|
||||||
vorbis_p = 0;
|
vorbis_p = 0;
|
||||||
videobuf_ready = 0;
|
next_frame_time = 0;
|
||||||
frames_pending = 0;
|
current_frame_time = 0;
|
||||||
videobuf_time = 0;
|
|
||||||
theora_eos = false;
|
theora_eos = false;
|
||||||
vorbis_eos = false;
|
vorbis_eos = false;
|
||||||
|
video_ready = false;
|
||||||
|
video_done = false;
|
||||||
|
audio_done = false;
|
||||||
|
|
||||||
file.unref();
|
file.unref();
|
||||||
playing = false;
|
playing = false;
|
||||||
|
|
@ -164,17 +127,6 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
|
||||||
file = FileAccess::open(p_file, FileAccess::READ);
|
file = FileAccess::open(p_file, FileAccess::READ);
|
||||||
ERR_FAIL_COND_MSG(file.is_null(), "Cannot open file '" + p_file + "'.");
|
ERR_FAIL_COND_MSG(file.is_null(), "Cannot open file '" + p_file + "'.");
|
||||||
|
|
||||||
#ifdef THEORA_USE_THREAD_STREAMING
|
|
||||||
thread_exit = false;
|
|
||||||
thread_eof = false;
|
|
||||||
//pre-fill buffer
|
|
||||||
int to_read = ring_buffer.space_left();
|
|
||||||
uint64_t read = file->get_buffer(read_buffer.ptr(), to_read);
|
|
||||||
ring_buffer.write(read_buffer.ptr(), read);
|
|
||||||
|
|
||||||
thread.start(_streaming_thread, this);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ogg_sync_init(&oy);
|
ogg_sync_init(&oy);
|
||||||
|
|
||||||
/* init supporting Vorbis structures needed in header parsing */
|
/* init supporting Vorbis structures needed in header parsing */
|
||||||
|
|
@ -327,16 +279,18 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
|
||||||
th_decode_ctl(td, TH_DECCTL_SET_PPLEVEL, &pp_level, sizeof(pp_level));
|
th_decode_ctl(td, TH_DECCTL_SET_PPLEVEL, &pp_level, sizeof(pp_level));
|
||||||
pp_inc = 0;
|
pp_inc = 0;
|
||||||
|
|
||||||
int w;
|
size.x = ti.frame_width;
|
||||||
int h;
|
size.y = ti.frame_height;
|
||||||
w = ((ti.pic_x + ti.frame_width + 1) & ~1) - (ti.pic_x & ~1);
|
region.position.x = ti.pic_x;
|
||||||
h = ((ti.pic_y + ti.frame_height + 1) & ~1) - (ti.pic_y & ~1);
|
region.position.y = ti.pic_y;
|
||||||
size.x = w;
|
region.size.x = ti.pic_width;
|
||||||
size.y = h;
|
region.size.y = ti.pic_height;
|
||||||
|
|
||||||
Ref<Image> img = Image::create_empty(w, h, false, Image::FORMAT_RGBA8);
|
Ref<Image> img = Image::create_empty(region.size.x, region.size.y, false, Image::FORMAT_RGBA8);
|
||||||
texture->set_image(img);
|
texture->set_image(img);
|
||||||
|
frame_data.resize(region.size.x * region.size.y * 4);
|
||||||
|
|
||||||
|
frame_duration = (double)ti.fps_denominator / ti.fps_numerator;
|
||||||
} else {
|
} else {
|
||||||
/* tear down the partial theora setup */
|
/* tear down the partial theora setup */
|
||||||
th_info_clear(&ti);
|
th_info_clear(&ti);
|
||||||
|
|
@ -358,7 +312,8 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
|
||||||
playing = false;
|
playing = false;
|
||||||
buffering = true;
|
buffering = true;
|
||||||
time = 0;
|
time = 0;
|
||||||
audio_frames_wrote = 0;
|
video_done = !theora_p;
|
||||||
|
audio_done = !vorbis_p;
|
||||||
}
|
}
|
||||||
|
|
||||||
double VideoStreamPlaybackTheora::get_time() const {
|
double VideoStreamPlaybackTheora::get_time() const {
|
||||||
|
|
@ -378,68 +333,41 @@ void VideoStreamPlaybackTheora::update(double p_delta) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!playing || paused) {
|
if (!playing || paused) {
|
||||||
//printf("not playing\n");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef THEORA_USE_THREAD_STREAMING
|
|
||||||
thread_sem->post();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
time += p_delta;
|
time += p_delta;
|
||||||
|
|
||||||
if (videobuf_time > get_time()) {
|
double comp_time = get_time();
|
||||||
return; //no new frames need to be produced
|
bool audio_ready = false;
|
||||||
}
|
|
||||||
|
|
||||||
bool frame_done = false;
|
|
||||||
bool audio_done = !vorbis_p;
|
|
||||||
|
|
||||||
while (!frame_done || (!audio_done && !vorbis_eos)) {
|
|
||||||
//a frame needs to be produced
|
|
||||||
|
|
||||||
|
// Read data until we fill the audio buffer and get a new video frame.
|
||||||
|
while ((!audio_ready && !audio_done) || (!video_ready && !video_done)) {
|
||||||
ogg_packet op;
|
ogg_packet op;
|
||||||
bool no_theora = false;
|
|
||||||
bool buffer_full = false;
|
|
||||||
|
|
||||||
while (vorbis_p && !audio_done && !buffer_full) {
|
while (!audio_ready && !audio_done) {
|
||||||
int ret;
|
|
||||||
float **pcm;
|
float **pcm;
|
||||||
|
int ret = vorbis_synthesis_pcmout(&vd, &pcm);
|
||||||
/* if there's pending, decoded audio, grab it */
|
|
||||||
ret = vorbis_synthesis_pcmout(&vd, &pcm);
|
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
const int AUXBUF_LEN = 4096;
|
const int AUXBUF_LEN = 4096;
|
||||||
int to_read = ret;
|
int to_read = ret;
|
||||||
float aux_buffer[AUXBUF_LEN];
|
float aux_buffer[AUXBUF_LEN];
|
||||||
|
|
||||||
while (to_read) {
|
while (to_read) {
|
||||||
int m = MIN(AUXBUF_LEN / vi.channels, to_read);
|
int m = MIN(AUXBUF_LEN / vi.channels, to_read);
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
for (int j = 0; j < m; j++) {
|
for (int j = 0; j < m; j++) {
|
||||||
for (int i = 0; i < vi.channels; i++) {
|
for (int i = 0; i < vi.channels; i++) {
|
||||||
aux_buffer[count++] = pcm[i][j];
|
aux_buffer[count++] = pcm[i][j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int mixed = mix_callback(mix_udata, aux_buffer, m);
|
||||||
if (mix_callback) {
|
to_read -= mixed;
|
||||||
int mixed = mix_callback(mix_udata, aux_buffer, m);
|
if (mixed != m) { //could mix no more
|
||||||
to_read -= mixed;
|
audio_ready = true;
|
||||||
if (mixed != m) { //could mix no more
|
break;
|
||||||
buffer_full = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
to_read -= m; //just pretend we sent the audio
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vorbis_synthesis_read(&vd, ret - to_read);
|
vorbis_synthesis_read(&vd, ret - to_read);
|
||||||
|
|
||||||
audio_frames_wrote += ret - to_read;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* no pending audio; is there a pending packet to decode? */
|
/* no pending audio; is there a pending packet to decode? */
|
||||||
if (ogg_stream_packetout(&vo, &op) > 0) {
|
if (ogg_stream_packetout(&vo, &op) > 0) {
|
||||||
|
|
@ -447,19 +375,13 @@ void VideoStreamPlaybackTheora::update(double p_delta) {
|
||||||
vorbis_synthesis_blockin(&vd, &vb);
|
vorbis_synthesis_blockin(&vd, &vb);
|
||||||
}
|
}
|
||||||
} else { /* we need more data; break out to suck in another page */
|
} else { /* we need more data; break out to suck in another page */
|
||||||
|
audio_done = vorbis_eos;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_done = videobuf_time < (audio_frames_wrote / float(vi.rate));
|
|
||||||
|
|
||||||
if (buffer_full) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (theora_p && !frame_done) {
|
while (!video_ready && !video_done) {
|
||||||
/* theora is one in, one out... */
|
|
||||||
if (ogg_stream_packetout(&to, &op) > 0) {
|
if (ogg_stream_packetout(&to, &op) > 0) {
|
||||||
/*HACK: This should be set after a seek or a gap, but we might not have
|
/*HACK: This should be set after a seek or a gap, but we might not have
|
||||||
a granulepos for the first packet (we only have them for the last
|
a granulepos for the first packet (we only have them for the last
|
||||||
|
|
@ -472,62 +394,37 @@ void VideoStreamPlaybackTheora::update(double p_delta) {
|
||||||
sizeof(op.granulepos));
|
sizeof(op.granulepos));
|
||||||
}
|
}
|
||||||
ogg_int64_t videobuf_granulepos;
|
ogg_int64_t videobuf_granulepos;
|
||||||
if (th_decode_packetin(td, &op, &videobuf_granulepos) == 0) {
|
int ret = th_decode_packetin(td, &op, &videobuf_granulepos);
|
||||||
videobuf_time = th_granule_time(td, videobuf_granulepos);
|
if (ret == 0 || ret == TH_DUPFRAME) {
|
||||||
|
next_frame_time = th_granule_time(td, videobuf_granulepos);
|
||||||
//printf("frame time %f, play time %f, ready %i\n", (float)videobuf_time, get_time(), videobuf_ready);
|
if (next_frame_time > comp_time) {
|
||||||
|
dup_frame = (ret == TH_DUPFRAME);
|
||||||
/* is it already too old to be useful? This is only actually
|
video_ready = true;
|
||||||
useful cosmetically after a SIGSTOP. Note that we have to
|
|
||||||
decode the frame even if we don't show it (for now) due to
|
|
||||||
keyframing. Soon enough libtheora will be able to deal
|
|
||||||
with non-keyframe seeks. */
|
|
||||||
|
|
||||||
if (videobuf_time >= get_time()) {
|
|
||||||
frame_done = true;
|
|
||||||
} else {
|
} else {
|
||||||
/*If we are too slow, reduce the pp level.*/
|
/*If we are too slow, reduce the pp level.*/
|
||||||
pp_inc = pp_level > 0 ? -1 : 0;
|
pp_inc = pp_level > 0 ? -1 : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else { /* we need more data; break out to suck in another page */
|
||||||
} else {
|
video_done = theora_eos;
|
||||||
no_theora = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef THEORA_USE_THREAD_STREAMING
|
if (!video_ready || !audio_ready) {
|
||||||
if (file.is_valid() && thread_eof && no_theora && theora_eos && ring_buffer.data_left() == 0) {
|
int ret = buffer_data();
|
||||||
#else
|
if (ret > 0) {
|
||||||
if (file.is_valid() && /*!videobuf_ready && */ no_theora && theora_eos) {
|
while (ogg_sync_pageout(&oy, &og) > 0) {
|
||||||
#endif
|
queue_page(&og);
|
||||||
//printf("video done, stopping\n");
|
}
|
||||||
stop();
|
} else {
|
||||||
return;
|
vorbis_eos = true;
|
||||||
}
|
theora_eos = true;
|
||||||
|
break;
|
||||||
if (!frame_done || !audio_done) {
|
|
||||||
//what's the point of waiting for audio to grab a page?
|
|
||||||
|
|
||||||
buffer_data();
|
|
||||||
while (ogg_sync_pageout(&oy, &og) > 0) {
|
|
||||||
queue_page(&og);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If playback has begun, top audio buffer off immediately. */
|
double tdiff = next_frame_time - comp_time;
|
||||||
//if(stateflag) audio_write_nonblocking();
|
|
||||||
|
|
||||||
/* are we at or past time for this video frame? */
|
|
||||||
if (videobuf_ready && videobuf_time <= get_time()) {
|
|
||||||
//video_write();
|
|
||||||
//videobuf_ready=0;
|
|
||||||
} else {
|
|
||||||
//printf("frame at %f not ready (time %f), ready %i\n", (float)videobuf_time, get_time(), videobuf_ready);
|
|
||||||
}
|
|
||||||
|
|
||||||
double tdiff = videobuf_time - get_time();
|
|
||||||
/*If we have lots of extra time, increase the post-processing level.*/
|
/*If we have lots of extra time, increase the post-processing level.*/
|
||||||
if (tdiff > ti.fps_denominator * 0.25 / ti.fps_numerator) {
|
if (tdiff > ti.fps_denominator * 0.25 / ti.fps_numerator) {
|
||||||
pp_inc = pp_level < pp_level_max ? 1 : 0;
|
pp_inc = pp_level < pp_level_max ? 1 : 0;
|
||||||
|
|
@ -536,7 +433,22 @@ void VideoStreamPlaybackTheora::update(double p_delta) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
video_write();
|
if (!video_ready && video_done && audio_done) {
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the last frame to end before rendering the next one.
|
||||||
|
if (video_ready && comp_time >= current_frame_time) {
|
||||||
|
if (!dup_frame) {
|
||||||
|
th_ycbcr_buffer yuv;
|
||||||
|
th_decode_ycbcr_out(td, yuv);
|
||||||
|
video_write(yuv);
|
||||||
|
}
|
||||||
|
dup_frame = false;
|
||||||
|
video_ready = false;
|
||||||
|
current_frame_time = next_frame_time;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoStreamPlaybackTheora::play() {
|
void VideoStreamPlaybackTheora::play() {
|
||||||
|
|
@ -596,44 +508,11 @@ int VideoStreamPlaybackTheora::get_mix_rate() const {
|
||||||
return vi.rate;
|
return vi.rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef THEORA_USE_THREAD_STREAMING
|
|
||||||
|
|
||||||
void VideoStreamPlaybackTheora::_streaming_thread(void *ud) {
|
|
||||||
VideoStreamPlaybackTheora *vs = static_cast<VideoStreamPlaybackTheora *>(ud);
|
|
||||||
|
|
||||||
while (!vs->thread_exit) {
|
|
||||||
//just fill back the buffer
|
|
||||||
if (!vs->thread_eof) {
|
|
||||||
int to_read = vs->ring_buffer.space_left();
|
|
||||||
if (to_read > 0) {
|
|
||||||
uint64_t read = vs->file->get_buffer(vs->read_buffer.ptr(), to_read);
|
|
||||||
vs->ring_buffer.write(vs->read_buffer.ptr(), read);
|
|
||||||
vs->thread_eof = vs->file->eof_reached();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vs->thread_sem->wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
VideoStreamPlaybackTheora::VideoStreamPlaybackTheora() {
|
VideoStreamPlaybackTheora::VideoStreamPlaybackTheora() {
|
||||||
texture.instantiate();
|
texture.instantiate();
|
||||||
|
|
||||||
#ifdef THEORA_USE_THREAD_STREAMING
|
|
||||||
int rb_power = nearest_shift(RB_SIZE_KB * 1024);
|
|
||||||
ring_buffer.resize(rb_power);
|
|
||||||
read_buffer.resize(RB_SIZE_KB * 1024);
|
|
||||||
thread_sem = Semaphore::create();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoStreamPlaybackTheora::~VideoStreamPlaybackTheora() {
|
VideoStreamPlaybackTheora::~VideoStreamPlaybackTheora() {
|
||||||
#ifdef THEORA_USE_THREAD_STREAMING
|
|
||||||
memdelete(thread_sem);
|
|
||||||
#endif
|
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,27 +41,20 @@
|
||||||
|
|
||||||
class ImageTexture;
|
class ImageTexture;
|
||||||
|
|
||||||
//#define THEORA_USE_THREAD_STREAMING
|
|
||||||
|
|
||||||
class VideoStreamPlaybackTheora : public VideoStreamPlayback {
|
class VideoStreamPlaybackTheora : public VideoStreamPlayback {
|
||||||
GDCLASS(VideoStreamPlaybackTheora, VideoStreamPlayback);
|
GDCLASS(VideoStreamPlaybackTheora, VideoStreamPlayback);
|
||||||
|
|
||||||
enum {
|
|
||||||
MAX_FRAMES = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
//Image frames[MAX_FRAMES];
|
|
||||||
Image::Format format = Image::Format::FORMAT_L8;
|
Image::Format format = Image::Format::FORMAT_L8;
|
||||||
Vector<uint8_t> frame_data;
|
Vector<uint8_t> frame_data;
|
||||||
int frames_pending = 0;
|
int frames_pending = 0;
|
||||||
Ref<FileAccess> file;
|
Ref<FileAccess> file;
|
||||||
String file_name;
|
String file_name;
|
||||||
int audio_frames_wrote = 0;
|
|
||||||
Point2i size;
|
Point2i size;
|
||||||
|
Rect2i region;
|
||||||
|
|
||||||
int buffer_data();
|
int buffer_data();
|
||||||
int queue_page(ogg_page *page);
|
int queue_page(ogg_page *page);
|
||||||
void video_write();
|
void video_write(th_ycbcr_buffer yuv);
|
||||||
double get_time() const;
|
double get_time() const;
|
||||||
|
|
||||||
bool theora_eos = false;
|
bool theora_eos = false;
|
||||||
|
|
@ -79,43 +72,30 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback {
|
||||||
vorbis_block vb;
|
vorbis_block vb;
|
||||||
vorbis_comment vc;
|
vorbis_comment vc;
|
||||||
th_pixel_fmt px_fmt;
|
th_pixel_fmt px_fmt;
|
||||||
double videobuf_time = 0;
|
double frame_duration;
|
||||||
int pp_inc = 0;
|
|
||||||
|
|
||||||
int theora_p = 0;
|
int theora_p = 0;
|
||||||
int vorbis_p = 0;
|
int vorbis_p = 0;
|
||||||
int pp_level_max = 0;
|
int pp_level_max = 0;
|
||||||
int pp_level = 0;
|
int pp_level = 0;
|
||||||
int videobuf_ready = 0;
|
int pp_inc = 0;
|
||||||
|
|
||||||
bool playing = false;
|
bool playing = false;
|
||||||
bool buffering = false;
|
bool buffering = false;
|
||||||
|
bool paused = false;
|
||||||
|
|
||||||
|
bool dup_frame = false;
|
||||||
|
bool video_ready = false;
|
||||||
|
bool video_done = false;
|
||||||
|
bool audio_done = false;
|
||||||
|
|
||||||
double last_update_time = 0;
|
|
||||||
double time = 0;
|
double time = 0;
|
||||||
|
double next_frame_time = 0;
|
||||||
|
double current_frame_time = 0;
|
||||||
double delay_compensation = 0;
|
double delay_compensation = 0;
|
||||||
|
|
||||||
Ref<ImageTexture> texture;
|
Ref<ImageTexture> texture;
|
||||||
|
|
||||||
bool paused = false;
|
|
||||||
|
|
||||||
#ifdef THEORA_USE_THREAD_STREAMING
|
|
||||||
|
|
||||||
enum {
|
|
||||||
RB_SIZE_KB = 1024
|
|
||||||
};
|
|
||||||
|
|
||||||
RingBuffer<uint8_t> ring_buffer;
|
|
||||||
Vector<uint8_t> read_buffer;
|
|
||||||
bool thread_eof = false;
|
|
||||||
Semaphore *thread_sem = nullptr;
|
|
||||||
Thread thread;
|
|
||||||
SafeFlag thread_exit;
|
|
||||||
|
|
||||||
static void _streaming_thread(void *ud);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int audio_track = 0;
|
int audio_track = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ void JavaObject::_bind_methods() {
|
||||||
|
|
||||||
void JavaClassWrapper::_bind_methods() {
|
void JavaClassWrapper::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("wrap", "name"), &JavaClassWrapper::wrap);
|
ClassDB::bind_method(D_METHOD("wrap", "name"), &JavaClassWrapper::wrap);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_exception"), &JavaClassWrapper::get_exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(ANDROID_ENABLED)
|
#if !defined(ANDROID_ENABLED)
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,8 @@ class JavaClassWrapper : public Object {
|
||||||
bool _get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, String &strsig);
|
bool _get_type_sig(JNIEnv *env, jobject obj, uint32_t &sig, String &strsig);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Ref<JavaObject> exception;
|
||||||
|
|
||||||
Ref<JavaClass> _wrap(const String &p_class, bool p_allow_private_methods_access);
|
Ref<JavaClass> _wrap(const String &p_class, bool p_allow_private_methods_access);
|
||||||
|
|
||||||
static JavaClassWrapper *singleton;
|
static JavaClassWrapper *singleton;
|
||||||
|
|
@ -285,6 +287,10 @@ public:
|
||||||
return _wrap(p_class, false);
|
return _wrap(p_class, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ref<JavaObject> get_exception() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef ANDROID_ENABLED
|
#ifdef ANDROID_ENABLED
|
||||||
Ref<JavaClass> wrap_jclass(jclass p_class, bool p_allow_private_methods_access = false);
|
Ref<JavaClass> wrap_jclass(jclass p_class, bool p_allow_private_methods_access = false);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ ext.versions = [
|
||||||
javaVersion : JavaVersion.VERSION_17,
|
javaVersion : JavaVersion.VERSION_17,
|
||||||
// Also update 'platform/android/detect.py#get_ndk_version()' when this is updated.
|
// Also update 'platform/android/detect.py#get_ndk_version()' when this is updated.
|
||||||
ndkVersion : '23.2.8568313',
|
ndkVersion : '23.2.8568313',
|
||||||
splashscreenVersion: '1.0.1'
|
splashscreenVersion: '1.0.1',
|
||||||
|
openxrVendorsVersion: '3.1.2-stable'
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ dependencies {
|
||||||
implementation "org.bouncycastle:bcprov-jdk15to18:1.78"
|
implementation "org.bouncycastle:bcprov-jdk15to18:1.78"
|
||||||
|
|
||||||
// Meta dependencies
|
// Meta dependencies
|
||||||
horizonosImplementation "org.godotengine:godot-openxr-vendors-meta:3.0.0-stable"
|
horizonosImplementation "org.godotengine:godot-openxr-vendors-meta:$versions.openxrVendorsVersion"
|
||||||
// Pico dependencies
|
// Pico dependencies
|
||||||
picoosImplementation "org.godotengine:godot-openxr-vendors-pico:3.0.1-stable"
|
picoosImplementation "org.godotengine:godot-openxr-vendors-pico:$versions.openxrVendorsVersion"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
package org.godotengine.editor.embed
|
package org.godotengine.editor.embed
|
||||||
|
|
||||||
|
import android.content.pm.ActivityInfo
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
|
|
@ -63,6 +64,9 @@ class EmbeddedGodotGame : GodotGame() {
|
||||||
private var layoutWidthInPx = 0
|
private var layoutWidthInPx = 0
|
||||||
private var layoutHeightInPx = 0
|
private var layoutHeightInPx = 0
|
||||||
|
|
||||||
|
private var gameRequestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|
||||||
|
private var isFullscreen = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
|
@ -81,13 +85,26 @@ class EmbeddedGodotGame : GodotGame() {
|
||||||
window.attributes = layoutParams
|
window.attributes = layoutParams
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setRequestedOrientation(requestedOrientation: Int) {
|
||||||
|
// Allow orientation change only if fullscreen mode is active
|
||||||
|
// or if the requestedOrientation is landscape (i.e switching to default).
|
||||||
|
if (isFullscreen || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE) {
|
||||||
|
super.setRequestedOrientation(requestedOrientation)
|
||||||
|
} else {
|
||||||
|
// Cache the requestedOrientation to apply when switching to fullscreen.
|
||||||
|
gameRequestedOrientation = requestedOrientation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
||||||
when (event.actionMasked) {
|
when (event.actionMasked) {
|
||||||
MotionEvent.ACTION_OUTSIDE -> {
|
MotionEvent.ACTION_OUTSIDE -> {
|
||||||
if (gameMenuFragment?.isAlwaysOnTop() == true) {
|
if (!isFullscreen) {
|
||||||
enterPiPMode()
|
if (gameMenuFragment?.isAlwaysOnTop() == true) {
|
||||||
} else {
|
enterPiPMode()
|
||||||
minimizeGameWindow()
|
} else {
|
||||||
|
minimizeGameWindow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,12 +144,18 @@ class EmbeddedGodotGame : GodotGame() {
|
||||||
|
|
||||||
override fun onFullScreenUpdated(enabled: Boolean) {
|
override fun onFullScreenUpdated(enabled: Boolean) {
|
||||||
godot?.enableImmersiveMode(enabled)
|
godot?.enableImmersiveMode(enabled)
|
||||||
|
isFullscreen = enabled
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
layoutWidthInPx = FULL_SCREEN_WIDTH
|
layoutWidthInPx = FULL_SCREEN_WIDTH
|
||||||
layoutHeightInPx = FULL_SCREEN_HEIGHT
|
layoutHeightInPx = FULL_SCREEN_HEIGHT
|
||||||
|
requestedOrientation = gameRequestedOrientation
|
||||||
} else {
|
} else {
|
||||||
layoutWidthInPx = defaultWidthInPx
|
layoutWidthInPx = defaultWidthInPx
|
||||||
layoutHeightInPx = defaultHeightInPx
|
layoutHeightInPx = defaultHeightInPx
|
||||||
|
|
||||||
|
// Cache the last used orientation in fullscreen to reapply when re-entering fullscreen.
|
||||||
|
gameRequestedOrientation = requestedOrientation
|
||||||
|
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|
||||||
}
|
}
|
||||||
updateWindowDimensions(layoutWidthInPx, layoutHeightInPx)
|
updateWindowDimensions(layoutWidthInPx, layoutHeightInPx)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -517,6 +517,20 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
|
||||||
env->DeleteLocalRef(E);
|
env->DeleteLocalRef(E);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jobject exception = env->ExceptionOccurred();
|
||||||
|
if (exception) {
|
||||||
|
env->ExceptionClear();
|
||||||
|
|
||||||
|
jclass java_class = env->GetObjectClass(exception);
|
||||||
|
Ref<JavaClass> java_class_wrapped = JavaClassWrapper::singleton->wrap_jclass(java_class);
|
||||||
|
env->DeleteLocalRef(java_class);
|
||||||
|
|
||||||
|
JavaClassWrapper::singleton->exception.instantiate(java_class_wrapped, exception);
|
||||||
|
env->DeleteLocalRef(exception);
|
||||||
|
} else {
|
||||||
|
JavaClassWrapper::singleton->exception.unref();
|
||||||
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,19 @@ static const DataCollectionInfo data_collect_purpose_info[] = {
|
||||||
{ "Other", "NSPrivacyCollectedDataTypePurposeOther" },
|
{ "Other", "NSPrivacyCollectedDataTypePurposeOther" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const String export_method_string[] = {
|
||||||
|
"app-store",
|
||||||
|
"development",
|
||||||
|
"ad-hoc",
|
||||||
|
"enterprise"
|
||||||
|
};
|
||||||
|
static const String storyboard_image_scale_mode[] = {
|
||||||
|
"center",
|
||||||
|
"scaleAspectFit",
|
||||||
|
"scaleAspectFill",
|
||||||
|
"scaleToFill"
|
||||||
|
};
|
||||||
|
|
||||||
String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const {
|
String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const {
|
||||||
if (p_preset) {
|
if (p_preset) {
|
||||||
if (p_name == "application/app_store_team_id") {
|
if (p_name == "application/app_store_team_id") {
|
||||||
|
|
@ -402,19 +415,28 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color()));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HashMap<String, Variant> EditorExportPlatformIOS::get_custom_project_settings(const Ref<EditorExportPreset> &p_preset) const {
|
||||||
|
HashMap<String, Variant> settings;
|
||||||
|
|
||||||
|
int image_scale_mode = p_preset->get("storyboard/image_scale_mode");
|
||||||
|
String value;
|
||||||
|
|
||||||
|
switch (image_scale_mode) {
|
||||||
|
case 0: {
|
||||||
|
String logo_path = GLOBAL_GET("application/boot_splash/image");
|
||||||
|
bool is_on = GLOBAL_GET("application/boot_splash/fullsize");
|
||||||
|
// If custom logo is not specified, Godot does not scale default one, so we should do the same.
|
||||||
|
value = (is_on && logo_path.length() > 0) ? "scaleAspectFit" : "center";
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
value = storyboard_image_scale_mode[image_scale_mode - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings["ios/launch_screen_image_mode"] = value;
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) {
|
void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) {
|
||||||
static const String export_method_string[] = {
|
|
||||||
"app-store",
|
|
||||||
"development",
|
|
||||||
"ad-hoc",
|
|
||||||
"enterprise"
|
|
||||||
};
|
|
||||||
static const String storyboard_image_scale_mode[] = {
|
|
||||||
"center",
|
|
||||||
"scaleAspectFit",
|
|
||||||
"scaleAspectFill",
|
|
||||||
"scaleToFill"
|
|
||||||
};
|
|
||||||
String dbg_sign_id = p_preset->get("application/code_sign_identity_debug").operator String().is_empty() ? "iPhone Developer" : p_preset->get("application/code_sign_identity_debug");
|
String dbg_sign_id = p_preset->get("application/code_sign_identity_debug").operator String().is_empty() ? "iPhone Developer" : p_preset->get("application/code_sign_identity_debug");
|
||||||
String rel_sign_id = p_preset->get("application/code_sign_identity_release").operator String().is_empty() ? "iPhone Distribution" : p_preset->get("application/code_sign_identity_release");
|
String rel_sign_id = p_preset->get("application/code_sign_identity_release").operator String().is_empty() ? "iPhone Distribution" : p_preset->get("application/code_sign_identity_release");
|
||||||
bool dbg_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG).operator String().is_empty() || (dbg_sign_id != "iPhone Developer" && dbg_sign_id != "iPhone Distribution");
|
bool dbg_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG).operator String().is_empty() || (dbg_sign_id != "iPhone Developer" && dbg_sign_id != "iPhone Distribution");
|
||||||
|
|
|
||||||
|
|
@ -203,6 +203,8 @@ public:
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual HashMap<String, Variant> get_custom_project_settings(const Ref<EditorExportPreset> &p_preset) const override;
|
||||||
|
|
||||||
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
|
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
|
||||||
|
|
||||||
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
|
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue