Merge pull request #75397 from YuriSizov/4.0-cherrypicks
Cherry-picks for the 4.0 branch (future 4.0.2) - 1st batch
This commit is contained in:
commit
19501f8eb1
|
@ -22,7 +22,7 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Emscripten latest
|
||||
uses: mymindstorm/setup-emsdk@v11
|
||||
uses: mymindstorm/setup-emsdk@v12
|
||||
with:
|
||||
version: ${{env.EM_VERSION}}
|
||||
actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
|
||||
|
|
|
@ -1331,6 +1331,7 @@ ProjectSettings::ProjectSettings() {
|
|||
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/occlusion_culling/bvh_build_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), 2);
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1"), 60); // No negative and limit to 500 due to crashes.
|
||||
GLOBAL_DEF_RST("internationalization/rendering/force_right_to_left_layout_direction", false);
|
||||
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_RANGE, "Based on Locale,Left-to-Right,Right-to-Left"), 0);
|
||||
|
||||
GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 2000);
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ struct _IP_ResolverPrivate {
|
|||
Semaphore sem;
|
||||
|
||||
Thread thread;
|
||||
bool thread_abort = false;
|
||||
SafeFlag thread_abort;
|
||||
|
||||
void resolve_queues() {
|
||||
for (int i = 0; i < IP::RESOLVER_MAX_QUERIES; i++) {
|
||||
|
@ -111,7 +111,7 @@ struct _IP_ResolverPrivate {
|
|||
static void _thread_function(void *self) {
|
||||
_IP_ResolverPrivate *ipr = static_cast<_IP_ResolverPrivate *>(self);
|
||||
|
||||
while (!ipr->thread_abort) {
|
||||
while (!ipr->thread_abort.is_set()) {
|
||||
ipr->sem.wait();
|
||||
ipr->resolve_queues();
|
||||
}
|
||||
|
@ -343,12 +343,12 @@ IP::IP() {
|
|||
singleton = this;
|
||||
resolver = memnew(_IP_ResolverPrivate);
|
||||
|
||||
resolver->thread_abort = false;
|
||||
resolver->thread_abort.clear();
|
||||
resolver->thread.start(_IP_ResolverPrivate::_thread_function, resolver);
|
||||
}
|
||||
|
||||
IP::~IP() {
|
||||
resolver->thread_abort = true;
|
||||
resolver->thread_abort.set();
|
||||
resolver->sem.post();
|
||||
resolver->thread.wait_to_finish();
|
||||
|
||||
|
|
|
@ -492,13 +492,21 @@ Ref<Resource> ResourceLoader::load_threaded_get(const String &p_path, Error *r_e
|
|||
print_lt("GET: load count: " + itos(thread_loading_count) + " / wait count: " + itos(thread_waiting_count) + " / suspended count: " + itos(thread_suspended_count) + " / active: " + itos(thread_loading_count - thread_suspended_count));
|
||||
}
|
||||
|
||||
bool still_valid = true;
|
||||
bool was_thread = load_task.thread;
|
||||
do {
|
||||
load_task.cond_var->wait(thread_load_lock);
|
||||
if (!thread_load_tasks.has(local_path)) { //may have been erased during unlock and this was always an invalid call
|
||||
still_valid = false;
|
||||
break;
|
||||
}
|
||||
} while (load_task.cond_var); // In case of spurious wakeup.
|
||||
|
||||
thread_suspended_count--;
|
||||
if (was_thread) {
|
||||
thread_suspended_count--;
|
||||
}
|
||||
|
||||
if (!thread_load_tasks.has(local_path)) { //may have been erased during unlock and this was always an invalid call
|
||||
if (!still_valid) {
|
||||
if (r_error) {
|
||||
*r_error = ERR_INVALID_PARAMETER;
|
||||
}
|
||||
|
|
|
@ -126,10 +126,18 @@ public:
|
|||
}
|
||||
|
||||
_FORCE_INLINE_ double randfn(double p_mean, double p_deviation) {
|
||||
return p_mean + p_deviation * (cos(Math_TAU * randd()) * sqrt(-2.0 * log(randd()))); // Box-Muller transform
|
||||
double temp = randd();
|
||||
if (temp < CMP_EPSILON) {
|
||||
temp += CMP_EPSILON; // To prevent generating of INF value in log function, resulting to return NaN value from this function.
|
||||
}
|
||||
return p_mean + p_deviation * (cos(Math_TAU * randd()) * sqrt(-2.0 * log(temp))); // Box-Muller transform.
|
||||
}
|
||||
_FORCE_INLINE_ float randfn(float p_mean, float p_deviation) {
|
||||
return p_mean + p_deviation * (cos((float)Math_TAU * randf()) * sqrt(-2.0 * log(randf()))); // Box-Muller transform
|
||||
float temp = randf();
|
||||
if (temp < CMP_EPSILON) {
|
||||
temp += CMP_EPSILON; // To prevent generating of INF value in log function, resulting to return NaN value from this function.
|
||||
}
|
||||
return p_mean + p_deviation * (cos((float)Math_TAU * randf()) * sqrt(-2.0 * log(temp))); // Box-Muller transform.
|
||||
}
|
||||
|
||||
double random(double p_from, double p_to);
|
||||
|
|
|
@ -50,8 +50,8 @@ void Thread::_set_platform_functions(const PlatformFunctions &p_functions) {
|
|||
platform_functions = p_functions;
|
||||
}
|
||||
|
||||
void Thread::callback(Thread *p_self, const Settings &p_settings, Callback p_callback, void *p_userdata) {
|
||||
Thread::caller_id = _thread_id_hash(p_self->thread.get_id());
|
||||
void Thread::callback(ID p_caller_id, const Settings &p_settings, Callback p_callback, void *p_userdata) {
|
||||
Thread::caller_id = p_caller_id;
|
||||
if (platform_functions.set_priority) {
|
||||
platform_functions.set_priority(p_settings.priority);
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_
|
|||
std::thread empty_thread;
|
||||
thread.swap(empty_thread);
|
||||
}
|
||||
std::thread new_thread(&Thread::callback, this, p_settings, p_callback, p_user);
|
||||
std::thread new_thread(&Thread::callback, _thread_id_hash(thread.get_id()), p_settings, p_callback, p_user);
|
||||
thread.swap(new_thread);
|
||||
id = _thread_id_hash(thread.get_id());
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ private:
|
|||
static thread_local ID caller_id;
|
||||
std::thread thread;
|
||||
|
||||
static void callback(Thread *p_self, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata);
|
||||
static void callback(ID p_caller_id, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata);
|
||||
|
||||
static PlatformFunctions platform_functions;
|
||||
|
||||
|
|
|
@ -712,7 +712,25 @@ String TranslationServer::get_tool_locale() {
|
|||
#else
|
||||
{
|
||||
#endif
|
||||
return get_locale();
|
||||
// Look for best matching loaded translation.
|
||||
String best_locale = "en";
|
||||
int best_score = 0;
|
||||
|
||||
for (const Ref<Translation> &E : translations) {
|
||||
const Ref<Translation> &t = E;
|
||||
ERR_FAIL_COND_V(t.is_null(), best_locale);
|
||||
String l = t->get_locale();
|
||||
|
||||
int score = compare_locales(locale, l);
|
||||
if (score > 0 && score >= best_score) {
|
||||
best_locale = l;
|
||||
best_score = score;
|
||||
if (score == 10) {
|
||||
break; // Exact match, skip the rest.
|
||||
}
|
||||
}
|
||||
}
|
||||
return best_locale;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -305,9 +305,9 @@ struct BuildIndexSequence<0, Is...> : IndexSequence<Is...> {};
|
|||
#endif
|
||||
|
||||
// Macro GD_IS_DEFINED() allows to check if a macro is defined. It needs to be defined to anything (say 1) to work.
|
||||
#define __GDARG_PLACEHOLDER_1 0,
|
||||
#define __GDARG_PLACEHOLDER_1 false,
|
||||
#define __gd_take_second_arg(__ignored, val, ...) val
|
||||
#define ____gd_is_defined(arg1_or_junk) __gd_take_second_arg(arg1_or_junk 1, 0)
|
||||
#define ____gd_is_defined(arg1_or_junk) __gd_take_second_arg(arg1_or_junk true, false)
|
||||
#define ___gd_is_defined(val) ____gd_is_defined(__GDARG_PLACEHOLDER_##val)
|
||||
#define GD_IS_DEFINED(x) ___gd_is_defined(x)
|
||||
|
||||
|
|
|
@ -466,7 +466,7 @@ Array Array::slice(int p_begin, int p_end, int p_step, bool p_deep) const {
|
|||
ERR_FAIL_COND_V_MSG(p_step > 0 && begin > end, result, "Slice is positive, but bounds is decreasing.");
|
||||
ERR_FAIL_COND_V_MSG(p_step < 0 && begin < end, result, "Slice is negative, but bounds is increasing.");
|
||||
|
||||
int result_size = (end - begin) / p_step;
|
||||
int result_size = (end - begin) / p_step + (((end - begin) % p_step != 0) ? 1 : 0);
|
||||
result.resize(result_size);
|
||||
|
||||
for (int src_idx = begin, dest_idx = 0; dest_idx < result_size; ++dest_idx) {
|
||||
|
|
|
@ -2941,7 +2941,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
|
|||
return hash_one_uint64((uint64_t)_data._int);
|
||||
} break;
|
||||
case FLOAT: {
|
||||
return hash_murmur3_one_float(_data._float);
|
||||
return hash_murmur3_one_double(_data._float);
|
||||
} break;
|
||||
case STRING: {
|
||||
return reinterpret_cast<const String *>(_data._mem)->hash();
|
||||
|
@ -3158,7 +3158,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
|
|||
}
|
||||
return hash_fmix32(h);
|
||||
} else {
|
||||
return hash_murmur3_one_float(0.0);
|
||||
return hash_murmur3_one_double(0.0);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
|
|
@ -542,7 +542,8 @@ struct VariantUtilityFunctions {
|
|||
}
|
||||
Variant base = *p_args[0];
|
||||
Variant ret;
|
||||
for (int i = 1; i < p_argcount; i++) {
|
||||
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
Variant::Type arg_type = p_args[i]->get_type();
|
||||
if (arg_type != Variant::INT && arg_type != Variant::FLOAT) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
|
@ -550,6 +551,9 @@ struct VariantUtilityFunctions {
|
|||
r_error.argument = i;
|
||||
return Variant();
|
||||
}
|
||||
if (i == 0) {
|
||||
continue;
|
||||
}
|
||||
bool valid;
|
||||
Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid);
|
||||
if (!valid) {
|
||||
|
@ -582,7 +586,8 @@ struct VariantUtilityFunctions {
|
|||
}
|
||||
Variant base = *p_args[0];
|
||||
Variant ret;
|
||||
for (int i = 1; i < p_argcount; i++) {
|
||||
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
Variant::Type arg_type = p_args[i]->get_type();
|
||||
if (arg_type != Variant::INT && arg_type != Variant::FLOAT) {
|
||||
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
|
||||
|
@ -590,6 +595,9 @@ struct VariantUtilityFunctions {
|
|||
r_error.argument = i;
|
||||
return Variant();
|
||||
}
|
||||
if (i == 0) {
|
||||
continue;
|
||||
}
|
||||
bool valid;
|
||||
Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid);
|
||||
if (!valid) {
|
||||
|
|
|
@ -622,7 +622,7 @@
|
|||
<description>
|
||||
Linearly interpolates between two values by the factor defined in [param weight]. To perform interpolation, [param weight] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). However, values outside this range are allowed and can be used to perform [i]extrapolation[/i]. If this is not desired, use [method clampf] on the result of this function.
|
||||
[codeblock]
|
||||
lerp(0, 4, 0.75) # Returns 3.0
|
||||
lerpf(0, 4, 0.75) # Returns 3.0
|
||||
[/codeblock]
|
||||
See also [method inverse_lerp] which performs the reverse of this operation. To perform eased interpolation with [method lerp], combine it with [method ease] or [method smoothstep].
|
||||
</description>
|
||||
|
@ -645,8 +645,8 @@
|
|||
<return type="float" />
|
||||
<param index="0" name="x" type="float" />
|
||||
<description>
|
||||
Returns the natural logarithm of [param x]. This is the amount of time needed to reach a certain level of continuous growth.
|
||||
[b]Note:[/b] This is not the same as the "log" function on most calculators, which uses a base 10 logarithm.
|
||||
Returns the [url=https://en.wikipedia.org/wiki/Natural_logarithm]natural logarithm[/url] of [param x] (base [url=https://en.wikipedia.org/wiki/E_(mathematical_constant)][i]e[/i][/url], with [i]e[/i] being approximately 2.71828). This is the amount of time needed to reach a certain level of continuous growth.
|
||||
[b]Note:[/b] This is not the same as the "log" function on most calculators, which uses a base 10 logarithm. To use base 10 logarithm, use [code]log(x) / log(10)[/code].
|
||||
[codeblock]
|
||||
log(10) # Returns 2.302585
|
||||
[/codeblock]
|
||||
|
@ -1142,9 +1142,9 @@
|
|||
<description>
|
||||
Returns [code]-1.0[/code] if [param x] is negative, [code]1.0[/code] if [param x] is positive, and [code]0.0[/code] if [param x] is zero.
|
||||
[codeblock]
|
||||
sign(-6.5) # Returns -1.0
|
||||
sign(0.0) # Returns 0.0
|
||||
sign(6.5) # Returns 1.0
|
||||
signf(-6.5) # Returns -1.0
|
||||
signf(0.0) # Returns 0.0
|
||||
signf(6.5) # Returns 1.0
|
||||
[/codeblock]
|
||||
</description>
|
||||
</method>
|
||||
|
@ -1154,9 +1154,9 @@
|
|||
<description>
|
||||
Returns [code]-1[/code] if [param x] is negative, [code]1[/code] if [param x] is positive, and [code]0[/code] if if [param x] is zero.
|
||||
[codeblock]
|
||||
sign(-6) # Returns -1
|
||||
sign(0) # Returns 0
|
||||
sign(6) # Returns 1
|
||||
signi(-6) # Returns -1
|
||||
signi(0) # Returns 0
|
||||
signi(6) # Returns 1
|
||||
[/codeblock]
|
||||
</description>
|
||||
</method>
|
||||
|
@ -1226,8 +1226,8 @@
|
|||
Returns the multiple of [param step] that is the closest to [param x]. This can also be used to round a floating point number to an arbitrary number of decimals.
|
||||
A type-safe version of [method snapped], returning a [float].
|
||||
[codeblock]
|
||||
snapped(32.0, 2.5) # Returns 32.5
|
||||
snapped(3.14159, 0.01) # Returns 3.14
|
||||
snappedf(32.0, 2.5) # Returns 32.5
|
||||
snappedf(3.14159, 0.01) # Returns 3.14
|
||||
[/codeblock]
|
||||
</description>
|
||||
</method>
|
||||
|
@ -1239,8 +1239,8 @@
|
|||
Returns the multiple of [param step] that is the closest to [param x].
|
||||
A type-safe version of [method snapped], returning an [int].
|
||||
[codeblock]
|
||||
snapped(53, 16) # Returns 48
|
||||
snapped(4096, 100) # Returns 4100
|
||||
snappedi(53, 16) # Returns 48
|
||||
snappedi(4096, 100) # Returns 4100
|
||||
[/codeblock]
|
||||
</description>
|
||||
</method>
|
||||
|
|
|
@ -390,7 +390,7 @@
|
|||
Repeat flags for the texture. See [enum TextureFilter] for options.
|
||||
</member>
|
||||
<member name="transparency" type="int" setter="set_transparency" getter="get_transparency" enum="BaseMaterial3D.Transparency" default="0">
|
||||
If [code]true[/code], transparency is enabled on the body. Some transparency modes will disable shadow casting. Any transparency mode other than Disabled has a greater performance impact compared to opaque rendering. See also [member blend_mode].
|
||||
The material's transparency mode. Some transparency modes will disable shadow casting. Any transparency mode other than [constant TRANSPARENCY_DISABLED] has a greater performance impact compared to opaque rendering. See also [member blend_mode].
|
||||
</member>
|
||||
<member name="use_particle_trails" type="bool" setter="set_flag" getter="get_flag" default="false">
|
||||
If [code]true[/code], enables parts of the shader required for [GPUParticles3D] trails to function. This also requires using a mesh with appropriate skinning, such as [RibbonTrailMesh] or [TubeTrailMesh]. Enabling this feature outside of materials used in [GPUParticles3D] meshes will break material rendering.
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="CollisionPolygon2D" inherits="Node2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
Defines a 2D collision polygon.
|
||||
Node that represents a 2D collision polygon.
|
||||
</brief_description>
|
||||
<description>
|
||||
Provides a concave or convex 2D collision polygon to a [CollisionObject2D] parent. Polygons can be drawn in the editor or specified by a list of vertices. See also [ConvexPolygonShape2D].
|
||||
Provides a 2D collision polygon to a [CollisionObject2D] parent. Polygons can be drawn in the editor or specified by a list of vertices.
|
||||
Depending on the build mode, this node effectively provides several convex shapes (by convex decomposition of the polygon) or a single concave shape made of the polygon's segments.
|
||||
In the editor, a [CollisionPolygon2D] can be generated from a [Sprite2D]'s outline by selecting a [Sprite2D] node, going to the [b]Sprite2D[/b] menu at the top of the 2D editor viewport then choosing [b]Create CollisionPolygon2D Sibling[/b].
|
||||
</description>
|
||||
<tutorials>
|
||||
|
@ -24,15 +25,16 @@
|
|||
The margin used for one-way collision (in pixels). Higher values will make the shape thicker, and work better for colliders that enter the polygon at a high velocity.
|
||||
</member>
|
||||
<member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array()">
|
||||
The polygon's list of vertices. The final point will be connected to the first. The returned value is a clone of the [PackedVector2Array], not a reference.
|
||||
The polygon's list of vertices. Each point will be connected to the next, and the final point will be connected to the first.
|
||||
[b]Warning:[/b] The returned value is a clone of the [PackedVector2Array], not a reference.
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
<constant name="BUILD_SOLIDS" value="0" enum="BuildMode">
|
||||
Collisions will include the polygon and its contained area.
|
||||
Collisions will include the polygon and its contained area. In this mode the node has the same effect as several [ConvexPolygonShape2D] nodes, one for each convex shape in the convex decomposition of the polygon (but without the overhead of multiple nodes).
|
||||
</constant>
|
||||
<constant name="BUILD_SEGMENTS" value="1" enum="BuildMode">
|
||||
Collisions will only include the polygon edges.
|
||||
Collisions will only include the polygon edges. In this mode the node has the same effect as a single [ConcavePolygonShape2D] made of segments, with the restriction that each segment (after the first one) starts where the previous one ends, and the last one ends where the first one starts (forming a closed but hollow polygon).
|
||||
</constant>
|
||||
</constants>
|
||||
</class>
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="CollisionPolygon3D" inherits="Node3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
Editor-only node for defining a collision polygon in 3D space.
|
||||
Node that represents a 3D collision polygon, given by the thickening of a 2D polygon in the local XY plane along the local Z axis.
|
||||
</brief_description>
|
||||
<description>
|
||||
Allows editing a concave or convex collision polygon's vertices on a selected plane. Can also set a depth perpendicular to that plane. This class is only available in the editor. It will not appear in the scene tree at run-time. Creates several [ConvexPolygonShape3D]s at run-time to represent the original polygon using convex decomposition.
|
||||
[b]Note:[/b] Since this is an editor-only helper, properties modified during gameplay will have no effect.
|
||||
Provides a 3D collision polygon to a [CollisionObject3D] parent, by thickening a 2D (convex or concave) polygon in the local XY plane along the local Z axis. The 2D polygon in the local XY plane can be drawn in the editor or specified by a list of vertices. That 2D polygon is thickened evenly in the local Z and -Z directions.
|
||||
This node has the same effect as several [ConvexPolygonShape3D] nodes, created by thickening the 2D convex polygons in the convex decomposition of the given 2D polygon (but without the overhead of multiple nodes).
|
||||
[b]Warning:[/b] A non-uniformly scaled CollisionPolygon3D node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change its [member polygon]'s vertices instead.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<members>
|
||||
<member name="depth" type="float" setter="set_depth" getter="get_depth" default="1.0">
|
||||
Length that the resulting collision extends in either direction perpendicular to its polygon.
|
||||
Length that the resulting collision extends in either direction perpendicular to its 2D polygon.
|
||||
</member>
|
||||
<member name="disabled" type="bool" setter="set_disabled" getter="is_disabled" default="false">
|
||||
If [code]true[/code], no collision will be produced.
|
||||
|
@ -21,7 +21,7 @@
|
|||
The collision margin for the generated [Shape3D]. See [member Shape3D.margin] for more details.
|
||||
</member>
|
||||
<member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array()">
|
||||
Array of vertices which define the polygon.
|
||||
Array of vertices which define the 2D polygon in the local XY plane.
|
||||
[b]Note:[/b] The returned value is a copy of the original. Methods which mutate the size or properties of the return value will not impact the original polygon. To change properties of the polygon, assign it to a temporary variable and make changes before reassigning the [code]polygon[/code] member.
|
||||
</member>
|
||||
</members>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
Node that represents collision shape data in 2D space.
|
||||
</brief_description>
|
||||
<description>
|
||||
Editor facility for creating and editing collision shapes in 2D space. Set the [member shape] property to configure the shape. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method CollisionObject2D.shape_owner_get_shape] to get the actual shape.
|
||||
Editor facility for creating and editing collision shapes in 2D space. Set the [member shape] property to configure the shape.
|
||||
You can use this node to represent all sorts of collision shapes, for example, add this to an [Area2D] to give it a detection shape, or add it to a [PhysicsBody2D] to create a solid object.
|
||||
</description>
|
||||
<tutorials>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
Node that represents collision shape data in 3D space.
|
||||
</brief_description>
|
||||
<description>
|
||||
Editor facility for creating and editing collision shapes in 3D space. Set the [member shape] property to configure the shape. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method CollisionObject3D.shape_owner_get_shape] to get the actual shape.
|
||||
Editor facility for creating and editing collision shapes in 3D space. Set the [member shape] property to configure the shape.
|
||||
You can use this node to represent all sorts of collision shapes, for example, add this to an [Area3D] to give it a detection shape, or add it to a [PhysicsBody3D] to create a solid object.
|
||||
[b]Warning:[/b] A non-uniformly scaled CollisionShape3D node will probably not function as expected. Please make sure to keep its scale uniform (i.e. the same on all axes), and change the size of its [member shape] resource instead.
|
||||
</description>
|
||||
|
|
|
@ -4,16 +4,19 @@
|
|||
Concave polygon shape resource for 2D physics.
|
||||
</brief_description>
|
||||
<description>
|
||||
2D concave polygon shape to be added as a [i]direct[/i] child of a [PhysicsBody2D] or [Area2D] using a [CollisionShape2D] node. It is made out of segments and is optimal for complex polygonal concave collisions. However, it is not advised to use for [RigidBody2D] nodes. A CollisionPolygon2D in convex decomposition mode (solids) or several convex objects are advised for that instead. Otherwise, a concave polygon 2D shape is better for static collisions.
|
||||
The main difference between a [ConvexPolygonShape2D] and a [ConcavePolygonShape2D] is that a concave polygon assumes it is concave and uses a more complex method of collision detection, and a convex one forces itself to be convex to speed up collision detection.
|
||||
[b]Performance:[/b] Due to its complexity, [ConcavePolygonShape2D] is the slowest collision shape to check collisions against. Its use should generally be limited to level geometry. For convex geometry, using [ConvexPolygonShape2D] will perform better. For dynamic physics bodies that need concave collision, several [ConvexPolygonShape2D]s can be used to represent its collision by using convex decomposition; see [ConvexPolygonShape2D]'s documentation for instructions. However, consider using primitive collision shapes such as [CircleShape2D] or [RectangleShape2D] first.
|
||||
2D concave polygon shape to be added as a [i]direct[/i] child of a [PhysicsBody2D] or [Area2D] using a [CollisionShape2D] node.
|
||||
The shape consists of a collection of line segments, and as such it does not include any "inside" that the segments might be enclosing. If the segments do enclose anything, then the shape is [i]hollow[/i], as opposed to a [ConvexPolygonShape2D] which is solid. See also [CollisionPolygon2D].
|
||||
Being made out of line segments, this shape is the most freely configurable single 2D shape. It can be used to form (hollow) polygons of any nature, convex or concave.
|
||||
[b]Note:[/b] When used for collision, [b]ConcavePolygonShape2D[/b] is intended to work with static [PhysicsBody2D] nodes like [StaticBody2D] and is not recommended to use with [RigidBody2D] nodes in a mode other than Static. A [CollisionPolygon2D] in convex decomposition mode (solids) or several convex objects are advised for that instead. Otherwise, a concave polygon 2D shape is better suited for static bodies.
|
||||
[b]Warning:[/b] The nature of this shape makes it extra prone to being tunneled through by (small) fast physics bodies. For example, consider a (small) rigid body [i]Ball[/i] traveling toward a static body [i]Box[/i] at high speed. If the box uses a [b]ConcavePolygonShape2D[/b] consisting of four segments, then the ball might end up inside the box or tunnel all the way through the box, if it goes fast enough. This is (partly) because the ball can only collide against the individual segments of the hollow box. In interactions with rigid bodies tunneling can be avoided by enabling continuous collision detection on the rigid body.
|
||||
[b]Warning:[/b] Using this shape for an [Area2D] (via a [CollisionShape2D] node) may give unexpected results: the area will only detect collisions with the segments in the [ConcavePolygonShape2D] (and not with any "inside" of the shape, for example).
|
||||
[b]Performance:[/b] Due to its complexity, [ConcavePolygonShape2D] is the slowest collision shape to check collisions against. Its use should generally be limited to level geometry. For convex geometry, using [ConvexPolygonShape2D] will perform better. For dynamic physics bodies that need concave collision, several [ConvexPolygonShape2D]s can be used to represent its collision by using convex decomposition; see [ConvexPolygonShape2D]'s documentation for instructions. However, consider using primitive collision shapes such as [CircleShape2D] or [RectangleShape2D] first.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<members>
|
||||
<member name="segments" type="PackedVector2Array" setter="set_segments" getter="get_segments" default="PackedVector2Array()">
|
||||
The array of points that make up the [ConcavePolygonShape2D]'s line segments.
|
||||
The array of points that make up the [ConcavePolygonShape2D]'s line segments. The array (of length divisible by two) is naturally divided into pairs (one pair for each segment); each pair consists of the starting point of a segment and the endpoint of a segment.
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
||||
|
|
|
@ -4,10 +4,13 @@
|
|||
Concave polygon shape resource (also called "trimesh") for 3D physics.
|
||||
</brief_description>
|
||||
<description>
|
||||
3D concave polygon shape resource (also called "trimesh") to be added as a [i]direct[/i] child of a [PhysicsBody3D] or [Area3D] using a [CollisionShape3D] node. This shape is created by feeding a list of triangles. Despite its name, [ConcavePolygonShape3D] can also store convex polygon shapes. However, unlike [ConvexPolygonShape3D], [ConcavePolygonShape3D] is [i]not[/i] limited to storing convex shapes exclusively.
|
||||
[b]Note:[/b] When used for collision, [ConcavePolygonShape3D] is intended to work with static [PhysicsBody3D] nodes like [StaticBody3D] and will not work with [CharacterBody3D] or [RigidBody3D] with a mode other than Static.
|
||||
[b]Performance:[/b] Due to its complexity, [ConcavePolygonShape3D] is the slowest collision shape to check collisions against. Its use should generally be limited to level geometry. For convex geometry, using [ConvexPolygonShape3D] will perform better. For dynamic physics bodies that need concave collision, several [ConvexPolygonShape3D]s can be used to represent its collision by using convex decomposition; see [ConvexPolygonShape3D]'s documentation for instructions. However, consider using primitive collision shapes such as [SphereShape3D] or [BoxShape3D] first.
|
||||
3D concave polygon shape resource (also called "trimesh") to be added as a [i]direct[/i] child of a [PhysicsBody3D] or [Area3D] using a [CollisionShape3D] node.
|
||||
The shape consists of a collection of triangle faces, and as such it does not include any "inside" that the faces might be enclosing. If the faces enclose anything, then the shape is [i]hollow[/i], as opposed to a [ConvexPolygonShape3D] which is solid. See also [CollisionPolygon3D].
|
||||
Being made out of triangle faces, this shape is the most freely configurable single 3D shape. Despite its name, it can be used to form (hollow) polyhedra of any nature, convex or concave.
|
||||
[b]Note:[/b] When used for collision, [b]ConcavePolygonShape3D[/b] is intended to work with static [PhysicsBody3D] nodes like [StaticBody3D] and will not work with [CharacterBody3D] or [RigidBody3D] in a mode other than Static.
|
||||
[b]Warning:[/b] The nature of this shape makes it extra prone to being tunneled through by (small) fast physics bodies. For example, consider a (small) rigid body [i]Ball[/i] traveling toward a static body [i]Box[/i] at high speed. If the box uses a [b]ConcavePolygonShape3D[/b] consisting of twelve triangle faces (two triangle faces for each of the six sides of the box), then the ball might end up inside the box or tunnel all the way through the box, if it goes fast enough. This is (partly) because the ball can only collide against the individual faces of the hollow box. In interactions with rigid bodies tunneling can be avoided by enabling continuous collision detection on the rigid body.
|
||||
[b]Warning:[/b] Using this shape for an [Area3D] (via a [CollisionShape3D] node, created e.g. by using the [i]Create Trimesh Collision Sibling[/i] option in the [i]Mesh[/i] menu that appears when selecting a [MeshInstance3D] node) may give unexpected results: the area will only detect collisions with the triangle faces in the [ConcavePolygonShape3D] (and not with any "inside" of the shape, for example); moreover it will only detect all such collisions if [member backface_collision] is [code]true[/code].
|
||||
[b]Performance:[/b] Due to its complexity, [ConcavePolygonShape3D] is the slowest collision shape to check collisions against. Its use should generally be limited to level geometry. For convex geometry, using [ConvexPolygonShape3D] will perform better. For dynamic physics bodies that need concave collision, several [ConvexPolygonShape3D]s can be used to represent its collision by using convex decomposition; see [ConvexPolygonShape3D]'s documentation for instructions. However, consider using primitive collision shapes such as [SphereShape3D] or [BoxShape3D] first.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link>
|
||||
|
@ -16,14 +19,14 @@
|
|||
<method name="get_faces" qualifiers="const">
|
||||
<return type="PackedVector3Array" />
|
||||
<description>
|
||||
Returns the faces (an array of triangles).
|
||||
Returns the faces of the trimesh shape as an array of vertices. The array (of length divisible by three) is naturally divided into triples; each triple of vertices defines a triangle.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_faces">
|
||||
<return type="void" />
|
||||
<param index="0" name="faces" type="PackedVector3Array" />
|
||||
<description>
|
||||
Sets the faces (an array of triangles).
|
||||
Sets the faces of the trimesh shape from an array of vertices. The [param faces] array should be composed of triples such that each triple of vertices defines a triangle.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
|
|
|
@ -399,7 +399,7 @@
|
|||
<return type="float" />
|
||||
<param index="0" name="offset" type="int" enum="Side" />
|
||||
<description>
|
||||
Returns the anchor for the specified [enum Side]. A getter method for [member offset_bottom], [member offset_left], [member offset_right] and [member offset_top].
|
||||
Returns the offset for the specified [enum Side]. A getter method for [member offset_bottom], [member offset_left], [member offset_right] and [member offset_top].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_parent_area_size" qualifiers="const">
|
||||
|
@ -840,7 +840,7 @@
|
|||
<param index="0" name="side" type="int" enum="Side" />
|
||||
<param index="1" name="neighbor" type="NodePath" />
|
||||
<description>
|
||||
Sets the anchor for the specified [enum Side] to the [Control] at [param neighbor] node path. A setter method for [member focus_neighbor_bottom], [member focus_neighbor_left], [member focus_neighbor_right] and [member focus_neighbor_top].
|
||||
Sets the focus neighbor for the specified [enum Side] to the [Control] at [param neighbor] node path. A setter method for [member focus_neighbor_bottom], [member focus_neighbor_left], [member focus_neighbor_right] and [member focus_neighbor_top].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_global_position">
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
Convex polygon shape resource for 2D physics.
|
||||
</brief_description>
|
||||
<description>
|
||||
2D convex polygon shape to be added as a [i]direct[/i] child of a [PhysicsBody2D] or [Area2D] using a [CollisionShape2D] node. A convex polygon, whatever its shape, is internally decomposed into as many convex polygons as needed to ensure all collision checks against it are always done on convex polygons (which are faster to check). See also [CollisionPolygon2D].
|
||||
The main difference between a [ConvexPolygonShape2D] and a [ConcavePolygonShape2D] is that a concave polygon assumes it is concave and uses a more complex method of collision detection, and a convex one forces itself to be convex to speed up collision detection.
|
||||
[b]Performance:[/b] [ConvexPolygonShape2D] is faster to check collisions against compared to [ConcavePolygonShape2D], but it is slower than primitive collision shapes such as [CircleShape2D] or [RectangleShape2D]. Its use should generally be limited to medium-sized objects that cannot have their collision accurately represented by a primitive shape.
|
||||
2D convex polygon shape to be added as a [i]direct[/i] child of a [PhysicsBody2D] or [Area2D] using a [CollisionShape2D] node.
|
||||
The shape is a [i]solid[/i] that includes all the points that it encloses, as opposed to [ConcavePolygonShape2D] which is hollow if it encloses anything. See also [CollisionPolygon2D].
|
||||
The solid nature of the shape makes it well-suited for both detection and physics; in physics body interactions this allows depenetrating even those shapes which end up (e.g. due to high speed) fully inside the convex shape (similarly to primitive shapes, but unlike [ConcavePolygonShape2D]). The convexity limits the possible geometric shape of a single [ConvexPolygonShape2D]: it cannot be concave.
|
||||
[b]Convex decomposition:[/b] Concave objects' collisions can be represented accurately using [i]several[/i] convex shapes. This allows dynamic physics bodies to have complex concave collisions (at a performance cost). It can be achieved using several [ConvexPolygonShape2D] nodes or by using the [CollisionPolygon2D] node in Solids build mode. To generate a collision polygon from a sprite, select the [Sprite2D] node, go to the [b]Sprite2D[/b] menu that appears above the viewport, and choose [b]Create Polygon2D Sibling[/b].
|
||||
[b]Performance:[/b] [ConvexPolygonShape2D] is faster to check collisions against compared to [ConcavePolygonShape2D], but it is slower than primitive collision shapes such as [CircleShape2D] or [RectangleShape2D]. Its use should generally be limited to medium-sized objects that cannot have their collision accurately represented by primitive shapes.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -15,13 +17,14 @@
|
|||
<return type="void" />
|
||||
<param index="0" name="point_cloud" type="PackedVector2Array" />
|
||||
<description>
|
||||
Based on the set of points provided, this creates and assigns the [member points] property using the convex hull algorithm. Removing all unneeded points. See [method Geometry2D.convex_hull] for details.
|
||||
Based on the set of points provided, this assigns the [member points] property using the convex hull algorithm, removing all unneeded points. See [method Geometry2D.convex_hull] for details.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="points" type="PackedVector2Array" setter="set_points" getter="get_points" default="PackedVector2Array()">
|
||||
The polygon's list of vertices. Can be in either clockwise or counterclockwise order. Only set this property with convex hull points, use [method set_point_cloud] to generate a convex hull shape from concave shape points.
|
||||
The polygon's list of vertices that form a convex hull. Can be in either clockwise or counterclockwise order.
|
||||
[b]Warning:[/b] Only set this property to a list of points that actually form a convex hull. Use [method set_point_cloud] to generate the convex hull of an arbitrary set of points.
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
Convex polygon shape resource for 3D physics.
|
||||
</brief_description>
|
||||
<description>
|
||||
3D convex polygon shape resource to be added as a [i]direct[/i] child of a [PhysicsBody3D] or [Area3D] using a [CollisionShape3D] node. Unlike [ConcavePolygonShape3D], [ConvexPolygonShape3D] cannot store concave polygon shapes. [ConvexPolygonShape3D]s can be manually drawn in the editor using the [CollisionPolygon3D] node.
|
||||
[b]Convex decomposition:[/b] Concave objects' collisions can be represented accurately using [i]several[/i] [ConvexPolygonShape3D]s. This allows dynamic physics bodies to have complex concave collisions (at a performance cost). This is available in the editor by selecting the [MeshInstance3D], going to the [b]Mesh[/b] menu and choosing [b]Create Multiple Convex Collision Siblings[/b]. Alternatively, [method MeshInstance3D.create_multiple_convex_collisions] can be called in a script to perform this decomposition at run-time.
|
||||
[b]Performance:[/b] [ConvexPolygonShape3D] is faster to check collisions against compared to [ConcavePolygonShape3D], but it is slower than primitive collision shapes such as [SphereShape3D] or [BoxShape3D]. Its use should generally be limited to medium-sized objects that cannot have their collision accurately represented by a primitive shape.
|
||||
3D convex polygon shape resource to be added as a [i]direct[/i] child of a [PhysicsBody3D] or [Area3D] using a [CollisionShape3D] node.
|
||||
The shape is a [i]solid[/i] that includes all the points that it encloses, as opposed to [ConcavePolygonShape3D] which is hollow if it encloses anything. See also [CollisionPolygon3D].
|
||||
The solid nature of the shape makes it well-suited for both detection and physics; in physics body interactions this allows depenetrating even those shapes which end up (e.g. due to high speed) fully inside the convex shape (similarly to primitive shapes, but unlike [ConcavePolygonShape3D] and [HeightMapShape3D]). The convexity restricts the possible geometric shape of a single [ConvexPolygonShape3D]: it cannot be concave.
|
||||
[b]Convex decomposition:[/b] Concave objects' collisions can be represented accurately using [i]several[/i] convex shapes. This allows dynamic physics bodies to have complex concave collisions (at a performance cost). It can be achieved by using several [ConvexPolygonShape3D] nodes or by using the [CollisionPolygon3D] node. To generate a collision polygon from a mesh, select the [MeshInstance3D] node, go to the [b]Mesh[/b] menu that appears above the viewport and choose [b]Create Multiple Convex Collision Siblings[/b]. Alternatively, [method MeshInstance3D.create_multiple_convex_collisions] can be called in a script to perform this decomposition at run-time.
|
||||
[b]Performance:[/b] [ConvexPolygonShape3D] is faster to check collisions against compared to [ConcavePolygonShape3D], but it is slower than primitive collision shapes such as [SphereShape3D] or [BoxShape3D]. Its use should generally be limited to medium-sized objects that cannot have their collision accurately represented by primitive shapes.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="3D Physics Tests Demo">https://godotengine.org/asset-library/asset/675</link>
|
||||
|
|
|
@ -64,7 +64,9 @@
|
|||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the editor is marked as self-contained, [code]false[/code] otherwise. When self-contained mode is enabled, user configuration, data and cache files are saved in an [code]editor_data/[/code] folder next to the editor binary. This makes portable usage easier and ensures the Godot editor minimizes file writes outside its own folder. Self-contained mode is not available for exported projects.
|
||||
Self-contained mode can be enabled by creating a file named [code]._sc_[/code] or [code]_sc_[/code] in the same folder as the editor binary while the editor is not running. See also [method get_self_contained_file].
|
||||
Self-contained mode can be enabled by creating a file named [code]._sc_[/code] or [code]_sc_[/code] in the same folder as the editor binary or macOS .app bundle while the editor is not running. See also [method get_self_contained_file].
|
||||
[b]Note:[/b] On macOS, quarantine flag should be manually removed before using self-contained mode, see [url=https://docs.godotengine.org/en/stable/tutorials/export/running_on_macos.html]Running on macOS[/url].
|
||||
[b]Note:[/b] On macOS, placing [code]_sc_[/code] or any other file inside .app bundle will break digital signature and make it non-portable, consider placing it in the same folder as the .app bundle instead.
|
||||
[b]Note:[/b] The Steam release of Godot uses self-contained mode by default.
|
||||
</description>
|
||||
</method>
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
</methods>
|
||||
<members>
|
||||
<member name="avoidance_enabled" type="bool" setter="set_avoidance_enabled" getter="get_avoidance_enabled" default="false">
|
||||
If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer2D]. When [method NavigationAgent2D.set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector2 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
|
||||
If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer2D]. When [method set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector2 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
|
||||
</member>
|
||||
<member name="debug_enabled" type="bool" setter="set_debug_enabled" getter="get_debug_enabled" default="false">
|
||||
If [code]true[/code] shows debug visuals for this agent.
|
||||
|
@ -193,7 +193,7 @@
|
|||
<signal name="velocity_computed">
|
||||
<param index="0" name="safe_velocity" type="Vector2" />
|
||||
<description>
|
||||
Notifies when the collision avoidance velocity is calculated. Emitted by [method set_velocity]. Only emitted when [member avoidance_enabled] is true.
|
||||
Notifies when the collision avoidance velocity is calculated. Emitted at the end of the physics frame in which [method set_velocity] is called. Only emitted when [member avoidance_enabled] is true.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="waypoint_reached">
|
||||
|
|
|
@ -112,7 +112,7 @@
|
|||
The NavigationAgent height offset is subtracted from the y-axis value of any vector path position for this NavigationAgent. The NavigationAgent height offset does not change or influence the navigation mesh or pathfinding query result. Additional navigation maps that use regions with navigation meshes that the developer baked with appropriate agent radius or height values are required to support different-sized agents.
|
||||
</member>
|
||||
<member name="avoidance_enabled" type="bool" setter="set_avoidance_enabled" getter="get_avoidance_enabled" default="false">
|
||||
If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer3D]. When [method NavigationAgent3D.set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector3 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
|
||||
If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer3D]. When [method set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector3 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
|
||||
</member>
|
||||
<member name="debug_enabled" type="bool" setter="set_debug_enabled" getter="get_debug_enabled" default="false">
|
||||
If [code]true[/code] shows debug visuals for this agent.
|
||||
|
@ -136,7 +136,7 @@
|
|||
The maximum speed that an agent can move.
|
||||
</member>
|
||||
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
|
||||
A bitfield determining what navigation layers of navigation regions this NavigationAgent will use to calculate path. Changing it runtime will clear current navigation path and generate new one, according to new navigation layers.
|
||||
A bitfield determining what navigation layers of navigation regions this agent will use to calculate path. Changing it runtime will clear current navigation path and generate new one, according to new navigation layers.
|
||||
</member>
|
||||
<member name="neighbor_distance" type="float" setter="set_neighbor_distance" getter="get_neighbor_distance" default="50.0">
|
||||
The distance to search for other agents.
|
||||
|
@ -196,7 +196,7 @@
|
|||
<signal name="velocity_computed">
|
||||
<param index="0" name="safe_velocity" type="Vector3" />
|
||||
<description>
|
||||
Notifies when the collision avoidance velocity is calculated. Emitted by [method set_velocity]. Only emitted when [member avoidance_enabled] is true.
|
||||
Notifies when the collision avoidance velocity is calculated. Emitted at the end of the physics frame in which [method set_velocity] is called. Only emitted when [member avoidance_enabled] is true.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="waypoint_reached">
|
||||
|
|
|
@ -141,7 +141,7 @@
|
|||
<return type="StringName" />
|
||||
<param index="0" name="idx" type="int" />
|
||||
<description>
|
||||
Gets the resource or property name indicated by [param idx] (0 to [method get_subname_count]).
|
||||
Gets the resource or property name indicated by [param idx] (0 to [method get_subname_count] - 1).
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
var node_path = NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path")
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</description>
|
||||
</method>
|
||||
<method name="collide_shape">
|
||||
<return type="PackedVector2Array[]" />
|
||||
<return type="Vector2[]" />
|
||||
<param index="0" name="parameters" type="PhysicsShapeQueryParameters2D" />
|
||||
<param index="1" name="max_results" type="int" default="32" />
|
||||
<description>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
</description>
|
||||
</method>
|
||||
<method name="collide_shape">
|
||||
<return type="PackedVector3Array[]" />
|
||||
<return type="Vector3[]" />
|
||||
<param index="0" name="parameters" type="PhysicsShapeQueryParameters3D" />
|
||||
<param index="1" name="max_results" type="int" default="32" />
|
||||
<description>
|
||||
|
|
|
@ -119,6 +119,13 @@
|
|||
<param index="0" name="area" type="RID" />
|
||||
<param index="1" name="callback" type="Callable" />
|
||||
<description>
|
||||
Sets the area's area monitor callback. This callback will be called when any other (shape of an) area enters or exits (a shape of) the given area, and must take the following five parameters:
|
||||
1. an integer [code]status[/code]: either [constant AREA_BODY_ADDED] or [constant AREA_BODY_REMOVED] depending on whether the other area's shape entered or exited the area,
|
||||
2. an [RID] [code]area_rid[/code]: the [RID] of the other area that entered or exited the area,
|
||||
3. an integer [code]instance_id[/code]: the [code]ObjectID[/code] attached to the other area,
|
||||
4. an integer [code]area_shape_idx[/code]: the index of the shape of the other area that entered or exited the area,
|
||||
5. an integer [code]self_shape_idx[/code]: the index of the shape of the area where the other area entered or exited.
|
||||
By counting (or keeping track of) the shapes that enter and exit, it can be determined if an area (with all its shapes) is entering for the first time or exiting for the last time.
|
||||
</description>
|
||||
</method>
|
||||
<method name="area_set_collision_layer">
|
||||
|
@ -142,12 +149,13 @@
|
|||
<param index="0" name="area" type="RID" />
|
||||
<param index="1" name="callback" type="Callable" />
|
||||
<description>
|
||||
Sets the function to call when any body/area enters or exits the area. This callback will be called for any object interacting with the area, and takes five parameters:
|
||||
1: [constant AREA_BODY_ADDED] or [constant AREA_BODY_REMOVED], depending on whether the object entered or exited the area.
|
||||
2: [RID] of the object that entered/exited the area.
|
||||
3: Instance ID of the object that entered/exited the area.
|
||||
4: The shape index of the object that entered/exited the area.
|
||||
5: The shape index of the area where the object entered/exited.
|
||||
Sets the area's body monitor callback. This callback will be called when any other (shape of a) body enters or exits (a shape of) the given area, and must take the following five parameters:
|
||||
1. an integer [code]status[/code]: either [constant AREA_BODY_ADDED] or [constant AREA_BODY_REMOVED] depending on whether the other body shape entered or exited the area,
|
||||
2. an [RID] [code]body_rid[/code]: the [RID] of the body that entered or exited the area,
|
||||
3. an integer [code]instance_id[/code]: the [code]ObjectID[/code] attached to the body,
|
||||
4. an integer [code]body_shape_idx[/code]: the index of the shape of the body that entered or exited the area,
|
||||
5. an integer [code]self_shape_idx[/code]: the index of the shape of the area where the body entered or exited.
|
||||
By counting (or keeping track of) the shapes that enter and exit, it can be determined if a body (with all its shapes) is entering for the first time or exiting for the last time.
|
||||
</description>
|
||||
</method>
|
||||
<method name="area_set_monitorable">
|
||||
|
|
|
@ -1199,7 +1199,10 @@
|
|||
[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 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 locales.
|
||||
Force layout direction and text writing direction to RTL for all controls.
|
||||
</member>
|
||||
<member name="internationalization/rendering/root_node_layout_direction" type="int" setter="" getter="" default="0">
|
||||
Root node default layout direction.
|
||||
</member>
|
||||
<member name="internationalization/rendering/text_driver" type="String" setter="" getter="" default="""">
|
||||
Specifies the [TextServer] to use. If left empty, the default will be used.
|
||||
|
|
|
@ -587,7 +587,7 @@
|
|||
<method name="canvas_light_occluder_create">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Creates a light occluder and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]canvas_light_ocluder_*[/code] RenderingServer functions.
|
||||
Creates a light occluder and adds it to the RenderingServer. It can be accessed with the RID that is returned. This RID will be used in all [code]canvas_light_occluder_*[/code] RenderingServer functions.
|
||||
Once finished with your RID, you will want to free the RID using the RenderingServer's [method free_rid] static method.
|
||||
</description>
|
||||
</method>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
When set to [code]0[/code], the default value from [member ProjectSettings.physics/3d/solver/default_contact_bias] is used.
|
||||
</member>
|
||||
<member name="margin" type="float" setter="set_margin" getter="get_margin" default="0.04">
|
||||
The collision margin for the shape. Used in Bullet Physics only.
|
||||
The collision margin for the shape. This is not used in Godot Physics.
|
||||
Collision margins allow collision detection to be more efficient by adding an extra shell around shapes. Collision algorithms are more expensive when objects overlap by more than their margin, so a higher value for margins is better for performance, at the cost of accuracy around edges as it makes them less sharp.
|
||||
</member>
|
||||
</members>
|
||||
|
|
|
@ -1154,7 +1154,7 @@
|
|||
<param index="1" name="width" type="float" />
|
||||
<param index="2" name="jst_flags" type="int" enum="TextServer.JustificationFlag" default="3" />
|
||||
<description>
|
||||
Adjusts text with to fit to specified width, returns new text width.
|
||||
Adjusts text width to fit to specified width, returns new text width.
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_text_get_ascent" qualifiers="const">
|
||||
|
|
|
@ -226,7 +226,7 @@
|
|||
<method name="is_normalized" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the vector is normalized, [code]false[/code] otherwise.
|
||||
Returns [code]true[/code] if the vector is normalized, i.e. its length is approximately equal to 1.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_zero_approx" qualifiers="const">
|
||||
|
|
|
@ -194,7 +194,7 @@
|
|||
<method name="is_normalized" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the vector is [method normalized], [code]false[/code] otherwise.
|
||||
Returns [code]true[/code] if the vector is normalized, i.e. its length is approximately equal to 1.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_zero_approx" qualifiers="const">
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
<method name="is_normalized" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the vector is normalized, i.e. its length is equal to 1.
|
||||
Returns [code]true[/code] if the vector is normalized, i.e. its length is approximately equal to 1.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_zero_approx" qualifiers="const">
|
||||
|
|
|
@ -108,6 +108,10 @@
|
|||
<member name="primary_interface" type="XRInterface" setter="set_primary_interface" getter="get_primary_interface">
|
||||
The primary [XRInterface] currently bound to the [XRServer].
|
||||
</member>
|
||||
<member name="world_origin" type="Transform3D" setter="set_world_origin" getter="get_world_origin" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)">
|
||||
The current origin of our tracking space in the virtual world. This is used by the renderer to properly position the camera with new tracking data.
|
||||
[b]Note:[/b] This property is managed by the current [XROrigin3D] node. It is exposed for access from GDExtensions.
|
||||
</member>
|
||||
<member name="world_scale" type="float" setter="set_world_scale" getter="get_world_scale" default="1.0">
|
||||
Allows you to adjust the scale to your game's units. Most AR/VR platforms assume a scale of 1 game world unit = 1 real world meter.
|
||||
</member>
|
||||
|
|
|
@ -1,40 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="int" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
Integer built-in type.
|
||||
Built-in integer Variant type.
|
||||
</brief_description>
|
||||
<description>
|
||||
Signed 64-bit integer type.
|
||||
It can take values in the interval [code][-2^63, 2^63 - 1][/code], i.e. [code][-9223372036854775808, 9223372036854775807][/code]. Exceeding those bounds will wrap around.
|
||||
[int] is a [Variant] type, and will thus be used when assigning an integer value to a [Variant]. It can also be enforced with the [code]: int[/code] type hint.
|
||||
Signed 64-bit integer type. This means that it can take values from [code]-2^63[/code] to [code]2^63 - 1[/code], i.e. from [code]-9223372036854775808[/code] to [code]9223372036854775807[/code]. When it exceeds these bounds, it will wrap around.
|
||||
[int]s can be automatically converted to [float]s when necessary, for example when passing them as arguments in functions. The [float] will be as close to the original integer as possible.
|
||||
Likewise, [float]s can be automatically converted into [int]s. This will truncate the [float], discarding anything after the floating point.
|
||||
[b]Note:[/b] In a boolean context, an [int] will evaluate to [code]false[/code] if it equals [code]0[/code], and to [code]true[/code] otherwise.
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
var my_variant = 0 # int, value 0.
|
||||
my_variant += 4.2 # float, value 4.2.
|
||||
var my_int: int = 1 # int, value 1.
|
||||
my_int = 4.2 # int, value 4, the right value is implicitly cast to int.
|
||||
my_int = int("6.7") # int, value 6, the String is explicitly cast with int.
|
||||
var max_int = 9223372036854775807
|
||||
print(max_int) # 9223372036854775807, OK.
|
||||
max_int += 1
|
||||
print(max_int) # -9223372036854775808, we overflowed and wrapped around.
|
||||
var x: int = 1 # x is 1
|
||||
x = 4.2 # x is 4, because 4.2 gets truncated
|
||||
var max_int = 9223372036854775807 # Biggest value an int can store
|
||||
max_int += 1 # max_int is -9223372036854775808, because it wrapped around
|
||||
[/gdscript]
|
||||
[csharp]
|
||||
int myInt = (int)"6.7".ToFloat(); // int, value 6, the String is explicitly cast with int.
|
||||
// We have to use `long` here, because GDSript's `int`
|
||||
// is 64 bits long while C#'s `int` is only 32 bits.
|
||||
long maxInt = 9223372036854775807;
|
||||
GD.Print(maxInt); // 9223372036854775807, OK.
|
||||
maxInt++;
|
||||
GD.Print(maxInt); // -9223372036854775808, we overflowed and wrapped around.
|
||||
int x = 1; // x is 1
|
||||
x = 4.2; // x is 4, because 4.2 gets truncated
|
||||
// We use long below, because GDScript's int is 64-bit while C#'s int is 32-bit.
|
||||
long maxLong = 9223372036854775807; // Biggest value a long can store
|
||||
maxLong++; // maxLong is now -9223372036854775808, because it wrapped around.
|
||||
|
||||
// Alternatively, if we used C#'s 32-bit `int` type, the maximum value is much smaller:
|
||||
int halfInt = 2147483647;
|
||||
GD.Print(halfInt); // 2147483647, OK.
|
||||
halfInt++;
|
||||
GD.Print(halfInt); // -2147483648, we overflowed and wrapped around.
|
||||
// Alternatively with C#'s 32-bit int type, which has a smaller maximum value.
|
||||
int maxInt = 2147483647; // Biggest value an int can store
|
||||
maxInt++; // maxInt is now -2147483648, because it wrapped around
|
||||
[/csharp]
|
||||
[/codeblocks]
|
||||
In GDScript, you can use the [code]0b[/code] literal for binary representation, the [code]0x[/code] literal for hexadecimal representation, and the [code]_[/code] symbol to separate long numbers and improve readability.
|
||||
[codeblock]
|
||||
var x = 0b1001 # x is 9
|
||||
var y = 0xF5 # y is 245
|
||||
var z = 10_000_000 # z is 10000000
|
||||
[/codeblock]
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
|
@ -42,7 +40,7 @@
|
|||
<constructor name="int">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Constructs a default-initialized [int] set to [code]0[/code].
|
||||
Constructs an [int] set to [code]0[/code].
|
||||
</description>
|
||||
</constructor>
|
||||
<constructor name="int">
|
||||
|
@ -56,21 +54,21 @@
|
|||
<return type="int" />
|
||||
<param index="0" name="from" type="String" />
|
||||
<description>
|
||||
Converts a [String] to an [int], following the same rules as [method String.to_int].
|
||||
Constructs a new [int] from a [String], following the same rules as [method String.to_int].
|
||||
</description>
|
||||
</constructor>
|
||||
<constructor name="int">
|
||||
<return type="int" />
|
||||
<param index="0" name="from" type="bool" />
|
||||
<description>
|
||||
Cast a [bool] value to an integer value, [code]int(true)[/code] will be equals to 1 and [code]int(false)[/code] will be equals to 0.
|
||||
Constructs a new [int] from a [bool]. [code]true[/code] is converted to [code]1[/code] and [code]false[/code] is converted to [code]0[/code].
|
||||
</description>
|
||||
</constructor>
|
||||
<constructor name="int">
|
||||
<return type="int" />
|
||||
<param index="0" name="from" type="float" />
|
||||
<description>
|
||||
Cast a float value to an integer value, this method simply removes the number fractions (i.e. rounds [param from] towards zero), so for example [code]int(2.7)[/code] will be equals to 2, [code]int(0.1)[/code] will be equals to 0 and [code]int(-2.7)[/code] will be equals to -2. This operation is also called truncation.
|
||||
Constructs a new [int] from a [float]. This will truncate the [float], discarding anything after the floating point.
|
||||
</description>
|
||||
</constructor>
|
||||
</constructors>
|
||||
|
@ -79,25 +77,25 @@
|
|||
<return type="bool" />
|
||||
<param index="0" name="right" type="float" />
|
||||
<description>
|
||||
Returns [code]true[/code] if this [int] is not equivalent to the given [float].
|
||||
Returns [code]true[/code] if the [int] is not equivalent to the [float].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator !=">
|
||||
<return type="bool" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the integers are not equal.
|
||||
Returns [code]true[/code] if the [int]s are not equal.
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator %">
|
||||
<return type="int" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Returns the remainder after dividing two integers. This operation uses truncated division, which is often not desired as it does not work well with negative numbers. Consider using [method @GlobalScope.posmod] instead if you want to handle negative numbers.
|
||||
Returns the remainder after dividing two [int]s. Uses truncated division, which returns a negative number if the dividend is negative. If this is not desired, consider using [method @GlobalScope.posmod].
|
||||
[codeblock]
|
||||
print(5 % 2) # 1
|
||||
print(12 % 4) # 0
|
||||
print(-5 % 3) # -2
|
||||
print(6 % 2) # Prints 0
|
||||
print(11 % 4) # Prints 3
|
||||
print(-5 % 3) # Prints -2
|
||||
[/codeblock]
|
||||
</description>
|
||||
</operator>
|
||||
|
@ -105,17 +103,16 @@
|
|||
<return type="int" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Returns the result of bitwise [code]AND[/code] operation for two integers.
|
||||
Performs the bitwise [code]AND[/code] operation.
|
||||
[codeblock]
|
||||
print(3 & 1) # 1
|
||||
print(11 & 3) # 3
|
||||
print(0b1100 & 0b1010) # Prints 8 (binary 1000)
|
||||
[/codeblock]
|
||||
It's useful to retrieve binary flags from a variable.
|
||||
This is useful for retrieving binary flags from a variable.
|
||||
[codeblock]
|
||||
var flags = 5
|
||||
# Do something if the first bit is enabled.
|
||||
if flags & 1:
|
||||
do_stuff()
|
||||
var flags = 0b101
|
||||
# Check if the first or second bit are enabled.
|
||||
if flags & 0b011:
|
||||
do_stuff() # This line will run.
|
||||
[/codeblock]
|
||||
</description>
|
||||
</operator>
|
||||
|
@ -123,23 +120,23 @@
|
|||
<return type="Color" />
|
||||
<param index="0" name="right" type="Color" />
|
||||
<description>
|
||||
Multiplies each component of the [Color] by the given [int].
|
||||
Multiplies each component of the [Color] by the [int].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator *">
|
||||
<return type="Quaternion" />
|
||||
<param index="0" name="right" type="Quaternion" />
|
||||
<description>
|
||||
Multiplies each component of the [Quaternion] by the given [int]. This operation is not meaningful on its own, but it can be used as a part of a larger expression.
|
||||
Multiplies each component of the [Quaternion] by the [int]. This operation is not meaningful on its own, but it can be used as a part of a larger expression.
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator *">
|
||||
<return type="Vector2" />
|
||||
<param index="0" name="right" type="Vector2" />
|
||||
<description>
|
||||
Multiplies each component of the [Vector2] by the given [int].
|
||||
Multiplies each component of the [Vector2] by the [int].
|
||||
[codeblock]
|
||||
print(2 * Vector2(1, 1)) # Vector2(2, 2)
|
||||
print(2 * Vector2(1, 4)) # Prints (2, 8)
|
||||
[/codeblock]
|
||||
</description>
|
||||
</operator>
|
||||
|
@ -147,49 +144,49 @@
|
|||
<return type="Vector2i" />
|
||||
<param index="0" name="right" type="Vector2i" />
|
||||
<description>
|
||||
Multiplies each component of the [Vector2i] by the given [int].
|
||||
Multiplies each component of the [Vector2i] by the [int].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator *">
|
||||
<return type="Vector3" />
|
||||
<param index="0" name="right" type="Vector3" />
|
||||
<description>
|
||||
Multiplies each component of the [Vector3] by the given [int].
|
||||
Multiplies each component of the [Vector3] by the [int].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator *">
|
||||
<return type="Vector3i" />
|
||||
<param index="0" name="right" type="Vector3i" />
|
||||
<description>
|
||||
Multiplies each component of the [Vector3i] by the given [int].
|
||||
Multiplies each component of the [Vector3i] by the [int].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator *">
|
||||
<return type="Vector4" />
|
||||
<param index="0" name="right" type="Vector4" />
|
||||
<description>
|
||||
Multiplies each component of the [Vector4] by the given [int].
|
||||
Multiplies each component of the [Vector4] by the [int].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator *">
|
||||
<return type="Vector4i" />
|
||||
<param index="0" name="right" type="Vector4i" />
|
||||
<description>
|
||||
Multiplies each component of the [Vector4i] by the given [int].
|
||||
Multiplies each component of the [Vector4i] by the [int].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator *">
|
||||
<return type="float" />
|
||||
<param index="0" name="right" type="float" />
|
||||
<description>
|
||||
Multiplies an [int] and a [float]. The result is a [float].
|
||||
Multiplies the [float] by the [int]. The result is a [float].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator *">
|
||||
<return type="int" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Multiplies two [int]s.
|
||||
Multiplies the two [int]s.
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator **">
|
||||
|
@ -198,7 +195,7 @@
|
|||
<description>
|
||||
Raises an [int] to a power of a [float]. The result is a [float].
|
||||
[codeblock]
|
||||
print(8**0.25) # 1.68179283050743
|
||||
print(2 ** 0.5) # Prints 1.4142135623731
|
||||
[/codeblock]
|
||||
</description>
|
||||
</operator>
|
||||
|
@ -206,9 +203,9 @@
|
|||
<return type="int" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Raises an [int] to a power of a [int].
|
||||
Raises the left [int] to a power of the right [int].
|
||||
[codeblock]
|
||||
print(5**5) # 3125
|
||||
print(3 ** 4) # Prints 81
|
||||
[/codeblock]
|
||||
</description>
|
||||
</operator>
|
||||
|
@ -216,37 +213,37 @@
|
|||
<return type="float" />
|
||||
<param index="0" name="right" type="float" />
|
||||
<description>
|
||||
Adds an [int] and a [float]. The result is a [float].
|
||||
Adds the [int] and the [float]. The result is a [float].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator +">
|
||||
<return type="int" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Adds two integers.
|
||||
Adds the two [int]s.
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator -">
|
||||
<return type="float" />
|
||||
<param index="0" name="right" type="float" />
|
||||
<description>
|
||||
Subtracts a [float] from an [int]. The result is a [float].
|
||||
Subtracts the [float] from the [int]. The result is a [float].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator -">
|
||||
<return type="int" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Subtracts two integers.
|
||||
Subtracts the two [int]s.
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator /">
|
||||
<return type="float" />
|
||||
<param index="0" name="right" type="float" />
|
||||
<description>
|
||||
Divides an [int] by a [float]. The result is a [float].
|
||||
Divides the [int] by the [float]. The result is a [float].
|
||||
[codeblock]
|
||||
print(10 / 3.0) # 3.333...
|
||||
print(10 / 3.0) # Prints 3.33333333333333
|
||||
[/codeblock]
|
||||
</description>
|
||||
</operator>
|
||||
|
@ -254,10 +251,10 @@
|
|||
<return type="int" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Divides two integers. The decimal part of the result is discarded (truncated).
|
||||
Divides the two [int]s. The result is an [int]. This will truncate the [float], discarding anything after the floating point.
|
||||
[codeblock]
|
||||
print(10 / 2) # 5
|
||||
print(10 / 3) # 3
|
||||
print(6 / 2) # Prints 3
|
||||
print(5 / 3) # Prints 1
|
||||
[/codeblock]
|
||||
</description>
|
||||
</operator>
|
||||
|
@ -265,24 +262,24 @@
|
|||
<return type="bool" />
|
||||
<param index="0" name="right" type="float" />
|
||||
<description>
|
||||
Returns [code]true[/code] if this [int] is less than the given [float].
|
||||
Returns [code]true[/code] if the [int] is less than the [float].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator <">
|
||||
<return type="bool" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the left integer is less than the right one.
|
||||
Returns [code]true[/code] if the left [int] is less than the right [int].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator <<">
|
||||
<return type="int" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Performs bitwise shift left operation on the integer. Effectively the same as multiplying by a power of 2.
|
||||
Performs the bitwise shift left operation. Effectively the same as multiplying by a power of 2.
|
||||
[codeblock]
|
||||
print(10 << 1) # 20
|
||||
print(10 << 4) # 160
|
||||
print(0b1010 << 1) # Prints 20 (binary 10100)
|
||||
print(0b1010 << 3) # Prints 80 (binary 1010000)
|
||||
[/codeblock]
|
||||
</description>
|
||||
</operator>
|
||||
|
@ -290,66 +287,66 @@
|
|||
<return type="bool" />
|
||||
<param index="0" name="right" type="float" />
|
||||
<description>
|
||||
Returns [code]true[/code] if this [int] is less than or equal to the given [float].
|
||||
Returns [code]true[/code] if the [int] is less than or equal to the [float].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator <=">
|
||||
<return type="bool" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the left integer is less than or equal to the right one.
|
||||
Returns [code]true[/code] if the left [int] is less than or equal to the right [int].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator ==">
|
||||
<return type="bool" />
|
||||
<param index="0" name="right" type="float" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the integer is equal to the given [float].
|
||||
Returns [code]true[/code] if the [int] is equal to the [float].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator ==">
|
||||
<return type="bool" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Returns [code]true[/code] if both integers are equal.
|
||||
Returns [code]true[/code] if the two [int]s are equal.
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator >">
|
||||
<return type="bool" />
|
||||
<param index="0" name="right" type="float" />
|
||||
<description>
|
||||
Returns [code]true[/code] if this [int] is greater than the given [float].
|
||||
Returns [code]true[/code] if the [int] is greater than the [float].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator >">
|
||||
<return type="bool" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the left integer is greater than the right one.
|
||||
Returns [code]true[/code] if the left [int] is greater than the right [int].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator >=">
|
||||
<return type="bool" />
|
||||
<param index="0" name="right" type="float" />
|
||||
<description>
|
||||
Returns [code]true[/code] if this [int] is greater than or equal to the given [float].
|
||||
Returns [code]true[/code] if the [int] is greater than or equal to the [float].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator >=">
|
||||
<return type="bool" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Returns [code]true[/code] if the left integer is greater than or equal to the right one.
|
||||
Returns [code]true[/code] if the left [int] is greater than or equal to the right [int].
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator >>">
|
||||
<return type="int" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Performs bitwise shift right operation on the integer. Effectively the same as dividing by a power of 2.
|
||||
Performs the bitwise shift right operation. Effectively the same as dividing by a power of 2.
|
||||
[codeblock]
|
||||
print(10 >> 1) # 5
|
||||
print(10 >> 2) # 2
|
||||
print(0b1010 >> 1) # Prints 5 (binary 101)
|
||||
print(0b1010 >> 2) # Prints 2 (binary 10)
|
||||
[/codeblock]
|
||||
</description>
|
||||
</operator>
|
||||
|
@ -357,10 +354,9 @@
|
|||
<return type="int" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Returns the result of bitwise [code]XOR[/code] operation for two integers.
|
||||
Performs the bitwise [code]XOR[/code] operation.
|
||||
[codeblock]
|
||||
print(5 ^ 1) # 4
|
||||
print(4 ^ 7) # 3
|
||||
print(0b1100 ^ 0b1010) # Prints 6 (binary 110)
|
||||
[/codeblock]
|
||||
</description>
|
||||
</operator>
|
||||
|
@ -380,27 +376,24 @@
|
|||
<return type="int" />
|
||||
<param index="0" name="right" type="int" />
|
||||
<description>
|
||||
Returns the result of bitwise [code]OR[/code] operation for two integers.
|
||||
Performs the bitwise [code]OR[/code] operation.
|
||||
[codeblock]
|
||||
print(2 | 4) # 6
|
||||
print(1 | 3) # 3
|
||||
print(0b1100 | 0b1010) # Prints 14 (binary 1110)
|
||||
[/codeblock]
|
||||
It's useful to store binary flags in a variable.
|
||||
This is useful for storing binary flags in a variable.
|
||||
[codeblock]
|
||||
var flags = 0
|
||||
# Turn first and third bit on.
|
||||
flags |= 1
|
||||
flags |= 4
|
||||
flags |= 0b101 # Turn the first and third bits on.
|
||||
[/codeblock]
|
||||
</description>
|
||||
</operator>
|
||||
<operator name="operator ~">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns the result of bitwise [code]NOT[/code] operation for the integer. It's effectively equal to [code]-int + 1[/code].
|
||||
Performs the bitwise [code]NOT[/code] operation on the [int]. Due to [url=https://en.wikipedia.org/wiki/Two%27s_complement/]2's complement[/url], it's effectively equal to [code]-(int + 1)[/code].
|
||||
[codeblock]
|
||||
print(~4) # -3
|
||||
print(~7) # -6
|
||||
print(~4) # Prints -5
|
||||
print(~(-7)) # Prints 6
|
||||
[/codeblock]
|
||||
</description>
|
||||
</operator>
|
||||
|
|
|
@ -1994,6 +1994,28 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
|
|||
_draw_sky(render_data.environment, render_data.cam_projection, render_data.cam_transform, sky_energy_multiplier, p_camera_data->view_count > 1, flip_y);
|
||||
}
|
||||
|
||||
if (scene_state.used_screen_texture || scene_state.used_depth_texture) {
|
||||
texture_storage->copy_scene_to_backbuffer(rt, scene_state.used_screen_texture, scene_state.used_depth_texture);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo);
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rt->backbuffer_fbo);
|
||||
if (scene_state.used_screen_texture) {
|
||||
glBlitFramebuffer(0, 0, rt->size.x, rt->size.y,
|
||||
0, 0, rt->size.x, rt->size.y,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5);
|
||||
glBindTexture(GL_TEXTURE_2D, rt->backbuffer);
|
||||
}
|
||||
if (scene_state.used_depth_texture) {
|
||||
glBlitFramebuffer(0, 0, rt->size.x, rt->size.y,
|
||||
0, 0, rt->size.x, rt->size.y,
|
||||
GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
||||
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6);
|
||||
glBindTexture(GL_TEXTURE_2D, rt->backbuffer_depth);
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo);
|
||||
}
|
||||
|
||||
RENDER_TIMESTAMP("Render 3D Transparent Pass");
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
|
@ -2206,6 +2228,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
|
|||
}
|
||||
|
||||
RS::PrimitiveType primitive = surf->primitive;
|
||||
if (shader->uses_point_size) {
|
||||
primitive = RS::PRIMITIVE_POINTS;
|
||||
}
|
||||
static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP };
|
||||
GLenum primitive_gl = prim[int(primitive)];
|
||||
|
||||
|
|
|
@ -1510,7 +1510,7 @@ MaterialStorage::MaterialStorage() {
|
|||
actions.renames["LIGHT_VERTEX"] = "light_vertex";
|
||||
actions.renames["SHADOW_VERTEX"] = "shadow_vertex";
|
||||
actions.renames["UV"] = "uv";
|
||||
actions.renames["POINT_SIZE"] = "gl_PointSize";
|
||||
actions.renames["POINT_SIZE"] = "point_size";
|
||||
|
||||
actions.renames["MODEL_MATRIX"] = "model_matrix";
|
||||
actions.renames["CANVAS_MATRIX"] = "canvas_transform";
|
||||
|
@ -1591,7 +1591,7 @@ MaterialStorage::MaterialStorage() {
|
|||
actions.renames["UV"] = "uv_interp";
|
||||
actions.renames["UV2"] = "uv2_interp";
|
||||
actions.renames["COLOR"] = "color_interp";
|
||||
actions.renames["POINT_SIZE"] = "gl_PointSize";
|
||||
actions.renames["POINT_SIZE"] = "point_size";
|
||||
actions.renames["INSTANCE_ID"] = "gl_InstanceID";
|
||||
actions.renames["VERTEX_ID"] = "gl_VertexID";
|
||||
|
||||
|
|
|
@ -1782,7 +1782,64 @@ void TextureStorage::_create_render_target_backbuffer(RenderTarget *rt) {
|
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
}
|
||||
void GLES3::TextureStorage::copy_scene_to_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture) {
|
||||
if (rt->backbuffer != 0 && rt->backbuffer_depth != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Config *config = Config::get_singleton();
|
||||
bool use_multiview = rt->view_count > 1 && config->multiview_supported;
|
||||
GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
|
||||
if (rt->backbuffer_fbo == 0) {
|
||||
glGenFramebuffers(1, &rt->backbuffer_fbo);
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo);
|
||||
if (rt->backbuffer == 0 && uses_screen_texture) {
|
||||
glGenTextures(1, &rt->backbuffer);
|
||||
glBindTexture(texture_target, rt->backbuffer);
|
||||
if (use_multiview) {
|
||||
glTexImage3D(texture_target, 0, rt->color_internal_format, rt->size.x, rt->size.y, rt->view_count, 0, rt->color_format, rt->color_type, nullptr);
|
||||
} else {
|
||||
glTexImage2D(texture_target, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr);
|
||||
}
|
||||
|
||||
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
#ifndef IOS_ENABLED
|
||||
if (use_multiview) {
|
||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->backbuffer, 0, 0, rt->view_count);
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->backbuffer, 0);
|
||||
}
|
||||
}
|
||||
if (rt->backbuffer_depth == 0 && uses_depth_texture) {
|
||||
glGenTextures(1, &rt->backbuffer_depth);
|
||||
glBindTexture(texture_target, rt->backbuffer_depth);
|
||||
if (use_multiview) {
|
||||
glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
|
||||
} else {
|
||||
glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
|
||||
}
|
||||
glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
#ifndef IOS_ENABLED
|
||||
if (use_multiview) {
|
||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->backbuffer_depth, 0, 0, rt->view_count);
|
||||
} else {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->backbuffer_depth, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
void TextureStorage::_clear_render_target(RenderTarget *rt) {
|
||||
// there is nothing to clear when DIRECT_TO_SCREEN is used
|
||||
if (rt->direct_to_screen) {
|
||||
|
@ -1848,6 +1905,10 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
|
|||
rt->backbuffer = 0;
|
||||
rt->backbuffer_fbo = 0;
|
||||
}
|
||||
if (rt->backbuffer_depth != 0) {
|
||||
glDeleteTextures(1, &rt->backbuffer_depth);
|
||||
rt->backbuffer_depth = 0;
|
||||
}
|
||||
_render_target_clear_sdf(rt);
|
||||
}
|
||||
|
||||
|
|
|
@ -345,6 +345,7 @@ struct RenderTarget {
|
|||
GLuint depth = 0;
|
||||
GLuint backbuffer_fbo = 0;
|
||||
GLuint backbuffer = 0;
|
||||
GLuint backbuffer_depth = 0;
|
||||
|
||||
GLuint color_internal_format = GL_RGBA8;
|
||||
GLuint color_format = GL_RGBA;
|
||||
|
@ -604,6 +605,8 @@ public:
|
|||
RenderTarget *get_render_target(RID p_rid) { return render_target_owner.get_or_null(p_rid); };
|
||||
bool owns_render_target(RID p_rid) { return render_target_owner.owns(p_rid); };
|
||||
|
||||
void copy_scene_to_backbuffer(RenderTarget *rt, const bool uses_screen_texture, const bool uses_depth_texture);
|
||||
|
||||
virtual RID render_target_create() override;
|
||||
virtual void render_target_free(RID p_rid) override;
|
||||
|
||||
|
|
|
@ -343,6 +343,9 @@ void ConnectDialog::_update_method_tree() {
|
|||
si_item->set_icon(0, get_theme_icon(SNAME("Script"), SNAME("EditorIcons")));
|
||||
si_item->set_selectable(0, false);
|
||||
|
||||
if (si->get_script()->is_built_in()) {
|
||||
si->get_script()->reload();
|
||||
}
|
||||
List<MethodInfo> methods;
|
||||
si->get_method_list(&methods);
|
||||
methods = _filter_method_list(methods, signal_info, search_string);
|
||||
|
|
|
@ -3370,7 +3370,17 @@ void EditorInspector::set_keying(bool p_active) {
|
|||
return;
|
||||
}
|
||||
keying = p_active;
|
||||
update_tree();
|
||||
_keying_changed();
|
||||
}
|
||||
|
||||
void EditorInspector::_keying_changed() {
|
||||
for (const KeyValue<StringName, List<EditorProperty *>> &F : editor_property_map) {
|
||||
for (EditorProperty *E : F.value) {
|
||||
if (E) {
|
||||
E->set_keying(keying);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorInspector::set_read_only(bool p_read_only) {
|
||||
|
|
|
@ -521,6 +521,8 @@ class EditorInspector : public ScrollContainer {
|
|||
void _changed_callback();
|
||||
void _edit_request_change(Object *p_object, const String &p_prop);
|
||||
|
||||
void _keying_changed();
|
||||
|
||||
void _filter_changed(const String &p_text);
|
||||
void _parse_added_editors(VBoxContainer *current_vbox, EditorInspectorSection *p_section, Ref<EditorInspectorPlugin> ped);
|
||||
|
||||
|
|
|
@ -140,7 +140,7 @@ EditorLayoutsDialog::EditorLayoutsDialog() {
|
|||
|
||||
name = memnew(LineEdit);
|
||||
makevb->add_child(name);
|
||||
name->set_placeholder("Or enter new layout name");
|
||||
name->set_placeholder(TTR("Or enter new layout name"));
|
||||
name->set_offset(SIDE_TOP, 5);
|
||||
name->set_anchor_and_offset(SIDE_LEFT, Control::ANCHOR_BEGIN, 5);
|
||||
name->set_anchor_and_offset(SIDE_RIGHT, Control::ANCHOR_END, -5);
|
||||
|
|
|
@ -356,12 +356,13 @@ void EditorNode::_update_scene_tabs() {
|
|||
scene_tabs->set_current_tab(editor_data.get_edited_scene());
|
||||
}
|
||||
|
||||
const Size2 add_button_size = Size2(0, scene_tabs->get_size().y);
|
||||
if (scene_tabs->get_offset_buttons_visible()) {
|
||||
// Move the add button to a fixed position.
|
||||
if (scene_tab_add->get_parent() == scene_tabs) {
|
||||
scene_tabs->remove_child(scene_tab_add);
|
||||
scene_tab_add_ph->add_child(scene_tab_add);
|
||||
scene_tab_add->set_position(Point2());
|
||||
scene_tab_add->set_rect(Rect2(Point2(), add_button_size));
|
||||
}
|
||||
} else {
|
||||
// Move the add button to be after the last tab.
|
||||
|
@ -371,16 +372,16 @@ void EditorNode::_update_scene_tabs() {
|
|||
}
|
||||
|
||||
if (scene_tabs->get_tab_count() == 0) {
|
||||
scene_tab_add->set_position(Point2());
|
||||
scene_tab_add->set_rect(Rect2(Point2(), add_button_size));
|
||||
return;
|
||||
}
|
||||
|
||||
Rect2 last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1);
|
||||
int hsep = scene_tabs->get_theme_constant(SNAME("h_separation"));
|
||||
if (scene_tabs->is_layout_rtl()) {
|
||||
scene_tab_add->set_position(Point2(last_tab.position.x - scene_tab_add->get_size().x - hsep, last_tab.position.y));
|
||||
scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x - scene_tab_add->get_size().x - hsep, last_tab.position.y), add_button_size));
|
||||
} else {
|
||||
scene_tab_add->set_position(Point2(last_tab.position.x + last_tab.size.width + hsep, last_tab.position.y));
|
||||
scene_tab_add->set_rect(Rect2(Point2(last_tab.position.x + last_tab.size.width + hsep, last_tab.position.y), add_button_size));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3533,6 +3534,10 @@ void EditorNode::remove_editor_plugin(EditorPlugin *p_editor, bool p_config_chan
|
|||
singleton->editor_plugins_force_input_forwarding->remove_plugin(p_editor);
|
||||
singleton->remove_child(p_editor);
|
||||
singleton->editor_data.remove_editor_plugin(p_editor);
|
||||
|
||||
for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : singleton->active_plugins) {
|
||||
kv.value.erase(p_editor);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorNode::_update_addon_config() {
|
||||
|
|
|
@ -35,6 +35,12 @@
|
|||
#include "editor/editor_scale.h"
|
||||
#include "editor/multi_node_edit.h"
|
||||
|
||||
Size2 EditorPath::get_minimum_size() const {
|
||||
Ref<Font> font = get_theme_font(SNAME("font"));
|
||||
int font_size = get_theme_font_size(SNAME("font_size"));
|
||||
return Button::get_minimum_size() + Size2(0, font->get_height(font_size));
|
||||
}
|
||||
|
||||
void EditorPath::_add_children_to_popup(Object *p_obj, int p_depth) {
|
||||
if (p_depth > 8) {
|
||||
return;
|
||||
|
|
|
@ -61,6 +61,8 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual Size2 get_minimum_size() const override;
|
||||
|
||||
void update_path();
|
||||
void clear_path();
|
||||
void enable_path();
|
||||
|
|
|
@ -117,14 +117,20 @@ EditorPaths::EditorPaths() {
|
|||
|
||||
// Self-contained mode if a `._sc_` or `_sc_` file is present in executable dir.
|
||||
String exe_path = OS::get_singleton()->get_executable_path().get_base_dir();
|
||||
Ref<DirAccess> d = DirAccess::create_for_path(exe_path);
|
||||
if (d->file_exists(exe_path + "/._sc_")) {
|
||||
self_contained = true;
|
||||
self_contained_file = exe_path + "/._sc_";
|
||||
} else if (d->file_exists(exe_path + "/_sc_")) {
|
||||
self_contained = true;
|
||||
self_contained_file = exe_path + "/_sc_";
|
||||
}
|
||||
|
||||
// On macOS, look outside .app bundle, since .app bundle is read-only.
|
||||
// Note: This will not work if Gatekeeper path randomization is active.
|
||||
if (OS::get_singleton()->has_feature("macos") && exe_path.ends_with("MacOS") && exe_path.path_join("..").simplify_path().ends_with("Contents")) {
|
||||
exe_path = exe_path.path_join("../../..").simplify_path();
|
||||
}
|
||||
{
|
||||
Ref<DirAccess> d = DirAccess::create_for_path(exe_path);
|
||||
|
||||
d = DirAccess::create_for_path(exe_path);
|
||||
if (d->file_exists(exe_path + "/._sc_")) {
|
||||
self_contained = true;
|
||||
self_contained_file = exe_path + "/._sc_";
|
||||
|
|
|
@ -248,12 +248,32 @@ void EditorUndoRedoManager::commit_action(bool p_execute) {
|
|||
}
|
||||
|
||||
if (!history.undo_stack.is_empty()) {
|
||||
const Action &prev_action = history.undo_stack.back()->get();
|
||||
if (pending_action.merge_mode != UndoRedo::MERGE_DISABLE && pending_action.merge_mode == prev_action.merge_mode && pending_action.action_name == prev_action.action_name) {
|
||||
// Discard action if it should be merged (UndoRedo handles merging internally).
|
||||
pending_action = Action();
|
||||
is_committing = false;
|
||||
return;
|
||||
// Discard action if it should be merged (UndoRedo handles merging internally).
|
||||
switch (pending_action.merge_mode) {
|
||||
case UndoRedo::MERGE_DISABLE:
|
||||
break; // Nothing to do here.
|
||||
case UndoRedo::MERGE_ENDS: {
|
||||
if (history.undo_stack.size() < 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
const Action &prev_action = history.undo_stack.back()->get();
|
||||
const Action &pre_prev_action = history.undo_stack.back()->prev()->get();
|
||||
if (pending_action.merge_mode == prev_action.merge_mode && pending_action.merge_mode == pre_prev_action.merge_mode &&
|
||||
pending_action.action_name == prev_action.action_name && pending_action.action_name == pre_prev_action.action_name) {
|
||||
pending_action = Action();
|
||||
is_committing = false;
|
||||
return;
|
||||
}
|
||||
} break;
|
||||
case UndoRedo::MERGE_ALL: {
|
||||
const Action &prev_action = history.undo_stack.back()->get();
|
||||
if (pending_action.merge_mode == prev_action.merge_mode && pending_action.action_name == prev_action.action_name) {
|
||||
pending_action = Action();
|
||||
is_committing = false;
|
||||
return;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1026,13 +1026,13 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||
String type = ResourceLoader::get_resource_type(path);
|
||||
|
||||
if (FileAccess::exists(path + ".import")) {
|
||||
// Before doing this, try to see if it can be customized
|
||||
// Before doing this, try to see if it can be customized.
|
||||
|
||||
String export_path = _export_customize(path, customize_resources_plugins, customize_scenes_plugins, export_cache, export_base_path, false);
|
||||
|
||||
if (export_path != path) {
|
||||
// It was actually customized..
|
||||
// Since the original file is likely not recognized, just use the import system
|
||||
// It was actually customized.
|
||||
// Since the original file is likely not recognized, just use the import system.
|
||||
|
||||
Ref<ConfigFile> config;
|
||||
config.instantiate();
|
||||
|
@ -1043,18 +1043,18 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||
}
|
||||
config->set_value("remap", "type", ResourceLoader::get_resource_type(export_path));
|
||||
|
||||
// Erase all PAths
|
||||
// Erase all Paths.
|
||||
List<String> keys;
|
||||
config->get_section_keys("remap", &keys);
|
||||
for (const String &K : keys) {
|
||||
if (E.begins_with("path")) {
|
||||
if (K.begins_with("path")) {
|
||||
config->erase_section_key("remap", K);
|
||||
}
|
||||
}
|
||||
// Set actual converted path.
|
||||
config->set_value("remap", "path", export_path);
|
||||
|
||||
// erase useless sections
|
||||
// Erase useless sections.
|
||||
config->erase_section("deps");
|
||||
config->erase_section("params");
|
||||
|
||||
|
@ -1075,7 +1075,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||
return err;
|
||||
}
|
||||
} else {
|
||||
// file is imported and not customized, replace by what it imports
|
||||
// File is imported and not customized, replace by what it imports.
|
||||
Ref<ConfigFile> config;
|
||||
config.instantiate();
|
||||
err = config->load(path + ".import");
|
||||
|
@ -1087,7 +1087,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||
String importer_type = config->get_value("remap", "importer");
|
||||
|
||||
if (importer_type == "keep") {
|
||||
//just keep file as-is
|
||||
// Just keep file as-is.
|
||||
Vector<uint8_t> array = FileAccess::get_file_as_bytes(path);
|
||||
err = p_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key);
|
||||
|
||||
|
@ -1130,6 +1130,9 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||
String remapped_path = config->get_value("remap", remap);
|
||||
Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path);
|
||||
err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key);
|
||||
} else {
|
||||
// Remove paths if feature not enabled.
|
||||
config->erase_section_key("remap", remap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1138,9 +1141,17 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||
return err;
|
||||
}
|
||||
|
||||
//also save the .import file
|
||||
Vector<uint8_t> array = FileAccess::get_file_as_bytes(path + ".import");
|
||||
err = p_func(p_udata, path + ".import", array, idx, total, enc_in_filters, enc_ex_filters, key);
|
||||
// Erase useless sections.
|
||||
config->erase_section("deps");
|
||||
config->erase_section("params");
|
||||
|
||||
String import_text = config->encode_to_text();
|
||||
CharString cs = import_text.utf8();
|
||||
Vector<uint8_t> sarr;
|
||||
sarr.resize(cs.size());
|
||||
memcpy(sarr.ptrw(), cs.ptr(), sarr.size());
|
||||
|
||||
err = p_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key);
|
||||
|
||||
if (err != OK) {
|
||||
return err;
|
||||
|
@ -1148,7 +1159,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
|
|||
}
|
||||
|
||||
} else {
|
||||
// Customize
|
||||
// Customize.
|
||||
|
||||
bool do_export = true;
|
||||
for (int i = 0; i < export_plugins.size(); i++) {
|
||||
|
|
|
@ -286,7 +286,7 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons
|
|||
Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
int compress_mode = p_options["compress/mode"];
|
||||
float lossy = p_options["compress/lossy_quality"];
|
||||
float high_quality = p_options["compress/high_quality"];
|
||||
bool high_quality = p_options["compress/high_quality"];
|
||||
int hdr_compression = p_options["compress/hdr_compression"];
|
||||
bool mipmaps = p_options["mipmaps/generate"];
|
||||
|
||||
|
|
|
@ -329,7 +329,7 @@ void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) {
|
|||
|
||||
if (p_node == scene) {
|
||||
icon = get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
|
||||
item->set_text(0, "Scene");
|
||||
item->set_text(0, TTR("Scene"));
|
||||
}
|
||||
|
||||
item->set_icon(0, icon);
|
||||
|
@ -1032,12 +1032,12 @@ void SceneImportSettings::_save_path_changed(const String &p_path) {
|
|||
save_path_item->set_text(1, p_path);
|
||||
|
||||
if (FileAccess::exists(p_path)) {
|
||||
save_path_item->set_text(2, "Warning: File exists");
|
||||
save_path_item->set_text(2, TTR("Warning: File exists"));
|
||||
save_path_item->set_tooltip_text(2, TTR("Existing file with the same name will be replaced."));
|
||||
save_path_item->set_icon(2, get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
|
||||
|
||||
} else {
|
||||
save_path_item->set_text(2, "Will create new File");
|
||||
save_path_item->set_text(2, TTR("Will create new file"));
|
||||
save_path_item->set_icon(2, get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")));
|
||||
}
|
||||
}
|
||||
|
@ -1077,7 +1077,7 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
|
|||
|
||||
if (md.has_import_id) {
|
||||
if (md.settings.has("use_external/enabled") && bool(md.settings["use_external/enabled"])) {
|
||||
item->set_text(2, "Already External");
|
||||
item->set_text(2, TTR("Already External"));
|
||||
item->set_tooltip_text(2, TTR("This material already references an external file, no action will be taken.\nDisable the external property for it to be extracted again."));
|
||||
} else {
|
||||
item->set_metadata(0, E.key);
|
||||
|
@ -1092,12 +1092,12 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
|
|||
|
||||
item->set_text(1, path);
|
||||
if (FileAccess::exists(path)) {
|
||||
item->set_text(2, "Warning: File exists");
|
||||
item->set_text(2, TTR("Warning: File exists"));
|
||||
item->set_tooltip_text(2, TTR("Existing file with the same name will be replaced."));
|
||||
item->set_icon(2, get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
|
||||
|
||||
} else {
|
||||
item->set_text(2, "Will create new File");
|
||||
item->set_text(2, TTR("Will create new file"));
|
||||
item->set_icon(2, get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")));
|
||||
}
|
||||
|
||||
|
@ -1105,7 +1105,7 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
|
|||
}
|
||||
|
||||
} else {
|
||||
item->set_text(2, "No import ID");
|
||||
item->set_text(2, TTR("No import ID"));
|
||||
item->set_tooltip_text(2, TTR("Material has no name nor any other way to identify on re-import.\nPlease name it or ensure it is exported with an unique ID."));
|
||||
item->set_icon(2, get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons")));
|
||||
}
|
||||
|
@ -1130,7 +1130,7 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
|
|||
|
||||
if (md.has_import_id) {
|
||||
if (md.settings.has("save_to_file/enabled") && bool(md.settings["save_to_file/enabled"])) {
|
||||
item->set_text(2, "Already Saving");
|
||||
item->set_text(2, TTR("Already Saving"));
|
||||
item->set_tooltip_text(2, TTR("This mesh already saves to an external resource, no action will be taken."));
|
||||
} else {
|
||||
item->set_metadata(0, E.key);
|
||||
|
@ -1145,12 +1145,12 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
|
|||
|
||||
item->set_text(1, path);
|
||||
if (FileAccess::exists(path)) {
|
||||
item->set_text(2, "Warning: File exists");
|
||||
item->set_text(2, TTR("Warning: File exists"));
|
||||
item->set_tooltip_text(2, TTR("Existing file with the same name will be replaced on import."));
|
||||
item->set_icon(2, get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
|
||||
|
||||
} else {
|
||||
item->set_text(2, "Will save to new File");
|
||||
item->set_text(2, TTR("Will save to new file"));
|
||||
item->set_icon(2, get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")));
|
||||
}
|
||||
|
||||
|
@ -1158,7 +1158,7 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
|
|||
}
|
||||
|
||||
} else {
|
||||
item->set_text(2, "No import ID");
|
||||
item->set_text(2, TTR("No import ID"));
|
||||
item->set_tooltip_text(2, TTR("Mesh has no name nor any other way to identify on re-import.\nPlease name it or ensure it is exported with an unique ID."));
|
||||
item->set_icon(2, get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons")));
|
||||
}
|
||||
|
@ -1182,7 +1182,7 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
|
|||
item->set_text(0, name);
|
||||
|
||||
if (ad.settings.has("save_to_file/enabled") && bool(ad.settings["save_to_file/enabled"])) {
|
||||
item->set_text(2, "Already Saving");
|
||||
item->set_text(2, TTR("Already Saving"));
|
||||
item->set_tooltip_text(2, TTR("This animation already saves to an external resource, no action will be taken."));
|
||||
} else {
|
||||
item->set_metadata(0, E.key);
|
||||
|
@ -1197,12 +1197,12 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
|
|||
|
||||
item->set_text(1, path);
|
||||
if (FileAccess::exists(path)) {
|
||||
item->set_text(2, "Warning: File exists");
|
||||
item->set_text(2, TTR("Warning: File exists"));
|
||||
item->set_tooltip_text(2, TTR("Existing file with the same name will be replaced on import."));
|
||||
item->set_icon(2, get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
|
||||
|
||||
} else {
|
||||
item->set_text(2, "Will save to new File");
|
||||
item->set_text(2, TTR("Will save to new file"));
|
||||
item->set_icon(2, get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")));
|
||||
}
|
||||
|
||||
|
|
|
@ -212,7 +212,8 @@ private:
|
|||
bool selected_from_canvas = false;
|
||||
|
||||
Point2 grid_offset;
|
||||
Point2 grid_step = Point2(8, 8); // A power-of-two value works better as a default.
|
||||
// A power-of-two value works better as a default grid size.
|
||||
Point2 grid_step = Point2(8, 8);
|
||||
int primary_grid_steps = 8;
|
||||
int grid_step_multiplier = 0;
|
||||
|
||||
|
|
|
@ -356,6 +356,7 @@ bool CollisionShape2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e
|
|||
return false;
|
||||
}
|
||||
|
||||
original_point = handles[edit_handle];
|
||||
original = get_handle_value(edit_handle);
|
||||
original_transform = node->get_global_transform();
|
||||
last_point = original;
|
||||
|
@ -572,6 +573,11 @@ void CollisionShape2DEditor::edit(Node *p_node) {
|
|||
_get_current_shape_type();
|
||||
|
||||
} else {
|
||||
if (pressed) {
|
||||
set_handle(edit_handle, original_point);
|
||||
pressed = false;
|
||||
}
|
||||
|
||||
edit_handle = -1;
|
||||
shape_type = -1;
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ class CollisionShape2DEditor : public Control {
|
|||
bool pressed;
|
||||
Variant original;
|
||||
Transform2D original_transform;
|
||||
Vector2 original_point;
|
||||
Point2 last_point;
|
||||
|
||||
Variant get_handle_value(int idx) const;
|
||||
|
|
|
@ -54,68 +54,95 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p
|
|||
|
||||
String entry_symbol = config->get_value("configuration", "entry_symbol");
|
||||
|
||||
PackedStringArray tags;
|
||||
String library_path = GDExtension::find_extension_library(
|
||||
p_path, config, [p_features](String p_feature) { return p_features.has(p_feature); }, &tags);
|
||||
if (!library_path.is_empty()) {
|
||||
add_shared_object(library_path, tags);
|
||||
HashSet<String> all_archs;
|
||||
all_archs.insert("x86_32");
|
||||
all_archs.insert("x86_64");
|
||||
all_archs.insert("arm32");
|
||||
all_archs.insert("arm64");
|
||||
all_archs.insert("rv64");
|
||||
all_archs.insert("ppc32");
|
||||
all_archs.insert("ppc64");
|
||||
all_archs.insert("wasm32");
|
||||
all_archs.insert("universal");
|
||||
|
||||
if (p_features.has("iOS") && (library_path.ends_with(".a") || library_path.ends_with(".xcframework"))) {
|
||||
String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n"
|
||||
"extern void add_ios_init_callback(void (*cb)());\n"
|
||||
"\n"
|
||||
"extern \"C\" void $ENTRY();\n"
|
||||
"void $ENTRY_init() {\n"
|
||||
" if (&$ENTRY) register_dynamic_symbol((char *)\"$ENTRY\", (void *)$ENTRY);\n"
|
||||
"}\n"
|
||||
"struct $ENTRY_struct {\n"
|
||||
" $ENTRY_struct() {\n"
|
||||
" add_ios_init_callback($ENTRY_init);\n"
|
||||
" }\n"
|
||||
"};\n"
|
||||
"$ENTRY_struct $ENTRY_struct_instance;\n\n";
|
||||
additional_code = additional_code.replace("$ENTRY", entry_symbol);
|
||||
add_ios_cpp_code(additional_code);
|
||||
|
||||
String linker_flags = "-Wl,-U,_" + entry_symbol;
|
||||
add_ios_linker_flags(linker_flags);
|
||||
HashSet<String> archs;
|
||||
HashSet<String> features_wo_arch;
|
||||
for (const String &tag : p_features) {
|
||||
if (all_archs.has(tag)) {
|
||||
archs.insert(tag);
|
||||
} else {
|
||||
features_wo_arch.insert(tag);
|
||||
}
|
||||
} else {
|
||||
Vector<String> features_vector;
|
||||
for (const String &E : p_features) {
|
||||
features_vector.append(E);
|
||||
}
|
||||
ERR_FAIL_MSG(vformat("No suitable library found for GDExtension: %s. Possible feature flags for your platform: %s", p_path, String(", ").join(features_vector)));
|
||||
}
|
||||
|
||||
List<String> dependencies;
|
||||
if (config->has_section("dependencies")) {
|
||||
config->get_section_keys("dependencies", &dependencies);
|
||||
if (archs.is_empty()) {
|
||||
archs.insert("unknown_arch"); // Not archs specified, still try to match.
|
||||
}
|
||||
|
||||
for (const String &E : dependencies) {
|
||||
Vector<String> dependency_tags = E.split(".");
|
||||
bool all_tags_met = true;
|
||||
for (int i = 0; i < dependency_tags.size(); i++) {
|
||||
String tag = dependency_tags[i].strip_edges();
|
||||
if (!p_features.has(tag)) {
|
||||
all_tags_met = false;
|
||||
for (const String &arch_tag : archs) {
|
||||
PackedStringArray tags;
|
||||
String library_path = GDExtension::find_extension_library(
|
||||
p_path, config, [features_wo_arch, arch_tag](String p_feature) { return features_wo_arch.has(p_feature) || (p_feature == arch_tag); }, &tags);
|
||||
if (!library_path.is_empty()) {
|
||||
add_shared_object(library_path, tags);
|
||||
|
||||
if (p_features.has("iOS") && (library_path.ends_with(".a") || library_path.ends_with(".xcframework"))) {
|
||||
String additional_code = "extern void register_dynamic_symbol(char *name, void *address);\n"
|
||||
"extern void add_ios_init_callback(void (*cb)());\n"
|
||||
"\n"
|
||||
"extern \"C\" void $ENTRY();\n"
|
||||
"void $ENTRY_init() {\n"
|
||||
" if (&$ENTRY) register_dynamic_symbol((char *)\"$ENTRY\", (void *)$ENTRY);\n"
|
||||
"}\n"
|
||||
"struct $ENTRY_struct {\n"
|
||||
" $ENTRY_struct() {\n"
|
||||
" add_ios_init_callback($ENTRY_init);\n"
|
||||
" }\n"
|
||||
"};\n"
|
||||
"$ENTRY_struct $ENTRY_struct_instance;\n\n";
|
||||
additional_code = additional_code.replace("$ENTRY", entry_symbol);
|
||||
add_ios_cpp_code(additional_code);
|
||||
|
||||
String linker_flags = "-Wl,-U,_" + entry_symbol;
|
||||
add_ios_linker_flags(linker_flags);
|
||||
}
|
||||
} else {
|
||||
Vector<String> features_vector;
|
||||
for (const String &E : p_features) {
|
||||
features_vector.append(E);
|
||||
}
|
||||
ERR_FAIL_MSG(vformat("No suitable library found for GDExtension: %s. Possible feature flags for your platform: %s", p_path, String(", ").join(features_vector)));
|
||||
}
|
||||
|
||||
List<String> dependencies;
|
||||
if (config->has_section("dependencies")) {
|
||||
config->get_section_keys("dependencies", &dependencies);
|
||||
}
|
||||
|
||||
for (const String &E : dependencies) {
|
||||
Vector<String> dependency_tags = E.split(".");
|
||||
bool all_tags_met = true;
|
||||
for (int i = 0; i < dependency_tags.size(); i++) {
|
||||
String tag = dependency_tags[i].strip_edges();
|
||||
if (!p_features.has(tag)) {
|
||||
all_tags_met = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_tags_met) {
|
||||
Dictionary dependency = config->get_value("dependencies", E);
|
||||
for (const Variant *key = dependency.next(nullptr); key; key = dependency.next(key)) {
|
||||
String dependency_path = *key;
|
||||
String target_path = dependency[*key];
|
||||
if (dependency_path.is_relative_path()) {
|
||||
dependency_path = p_path.get_base_dir().path_join(dependency_path);
|
||||
}
|
||||
add_shared_object(dependency_path, dependency_tags, target_path);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_tags_met) {
|
||||
Dictionary dependency = config->get_value("dependencies", E);
|
||||
for (const Variant *key = dependency.next(nullptr); key; key = dependency.next(key)) {
|
||||
String dependency_path = *key;
|
||||
String target_path = dependency[*key];
|
||||
if (dependency_path.is_relative_path()) {
|
||||
dependency_path = p_path.get_base_dir().path_join(dependency_path);
|
||||
}
|
||||
add_shared_object(dependency_path, dependency_tags, target_path);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2102,6 +2102,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
|
||||
if (_edit.mode == TRANSFORM_NONE) {
|
||||
if (_edit.gizmo.is_valid()) {
|
||||
// Restore.
|
||||
_edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_handle_secondary, _edit.gizmo_initial_value, true);
|
||||
_edit.gizmo = Ref<EditorNode3DGizmo>();
|
||||
}
|
||||
if (k->get_keycode() == Key::ESCAPE && !cursor.region_select) {
|
||||
_clear_selected();
|
||||
return;
|
||||
|
|
|
@ -1232,7 +1232,8 @@ Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const {
|
|||
|
||||
Polygon2DEditor::Polygon2DEditor() {
|
||||
snap_offset = EditorSettings::get_singleton()->get_project_metadata("polygon_2d_uv_editor", "snap_offset", Vector2());
|
||||
snap_step = EditorSettings::get_singleton()->get_project_metadata("polygon_2d_uv_editor", "snap_step", Vector2(10, 10));
|
||||
// A power-of-two value works better as a default grid size.
|
||||
snap_step = EditorSettings::get_singleton()->get_project_metadata("polygon_2d_uv_editor", "snap_step", Vector2(8, 8));
|
||||
use_snap = EditorSettings::get_singleton()->get_project_metadata("polygon_2d_uv_editor", "snap_enabled", false);
|
||||
snap_show_grid = EditorSettings::get_singleton()->get_project_metadata("polygon_2d_uv_editor", "show_grid", false);
|
||||
|
||||
|
|
|
@ -3586,7 +3586,72 @@ void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_numb
|
|||
shader_editor->get_shader_editor(res)->goto_line_selection(line_number - 1, begin, end);
|
||||
return;
|
||||
} else if (fpath.get_extension() == "tscn") {
|
||||
Ref<FileAccess> f = FileAccess::open(fpath, FileAccess::READ);
|
||||
bool is_script_found = false;
|
||||
|
||||
// Starting from top of the tscn file.
|
||||
int scr_start_line = 1;
|
||||
|
||||
String scr_header = "[sub_resource type=\"GDScript\" id=\"";
|
||||
String scr_id = "";
|
||||
String line = "";
|
||||
|
||||
int l = 0;
|
||||
|
||||
while (!f->eof_reached()) {
|
||||
line = f->get_line();
|
||||
l++;
|
||||
|
||||
if (!line.begins_with(scr_header)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found the end of the script.
|
||||
scr_id = line.get_slice(scr_header, 1);
|
||||
scr_id = scr_id.get_slice("\"", 0);
|
||||
|
||||
scr_start_line = l + 1;
|
||||
int scr_line_count = 0;
|
||||
|
||||
do {
|
||||
line = f->get_line();
|
||||
l++;
|
||||
String strline = line.strip_edges();
|
||||
|
||||
if (strline.ends_with("\"") && !strline.ends_with("\\\"")) {
|
||||
// Found the end of script.
|
||||
break;
|
||||
}
|
||||
scr_line_count++;
|
||||
|
||||
} while (!f->eof_reached());
|
||||
|
||||
if (line_number > scr_start_line + scr_line_count) {
|
||||
// Find in another built-in GDScript.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Real line number of the built-in script.
|
||||
line_number = line_number - scr_start_line;
|
||||
|
||||
is_script_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
EditorNode::get_singleton()->load_scene(fpath);
|
||||
|
||||
if (is_script_found && !scr_id.is_empty()) {
|
||||
Ref<Script> scr = ResourceLoader::load(fpath + "::" + scr_id, "Script");
|
||||
if (scr.is_valid()) {
|
||||
edit(scr);
|
||||
ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(_get_current_editor());
|
||||
|
||||
if (ste) {
|
||||
ste->goto_line_selection(line_number, begin, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
Ref<Script> scr = res;
|
||||
|
|
|
@ -376,6 +376,12 @@ bool ShaderEditorPlugin::can_drop_data_fw(const Point2 &p_point, const Variant &
|
|||
return true;
|
||||
}
|
||||
}
|
||||
if (ResourceLoader::exists(file, "ShaderInclude")) {
|
||||
Ref<ShaderInclude> sinclude = ResourceLoader::load(file);
|
||||
if (sinclude.is_valid()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -405,11 +411,10 @@ void ShaderEditorPlugin::drop_data_fw(const Point2 &p_point, const Variant &p_da
|
|||
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
String file = files[i];
|
||||
if (!ResourceLoader::exists(file, "Shader")) {
|
||||
continue;
|
||||
Ref<Resource> res;
|
||||
if (ResourceLoader::exists(file, "Shader") || ResourceLoader::exists(file, "ShaderInclude")) {
|
||||
res = ResourceLoader::load(file);
|
||||
}
|
||||
|
||||
Ref<Resource> res = ResourceLoader::load(file);
|
||||
if (res.is_valid()) {
|
||||
edit(res.ptr());
|
||||
}
|
||||
|
|
|
@ -1075,7 +1075,8 @@ TextureRegionEditor::TextureRegionEditor() {
|
|||
|
||||
preview_tex = Ref<CanvasTexture>(memnew(CanvasTexture));
|
||||
|
||||
snap_step = Vector2(10, 10);
|
||||
// A power-of-two value works better as a default grid size.
|
||||
snap_step = Vector2(8, 8);
|
||||
snap_separation = Vector2(0, 0);
|
||||
snap_mode = SNAP_NONE;
|
||||
edited_margin = -1;
|
||||
|
|
|
@ -564,7 +564,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
|
|||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_valid()) {
|
||||
has_mouse = true;
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
|
||||
Vector2 mpos = xform.affine_inverse().xform(mm->get_position());
|
||||
|
||||
switch (drag_type) {
|
||||
|
@ -613,7 +613,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
|
|||
Ref<InputEventMouseButton> mb = p_event;
|
||||
if (mb.is_valid()) {
|
||||
has_mouse = true;
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
|
||||
Vector2 mpos = xform.affine_inverse().xform(mb->get_position());
|
||||
|
||||
if (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT) {
|
||||
|
@ -736,14 +736,14 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
|
|||
return;
|
||||
}
|
||||
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
|
||||
Vector2i tile_shape_size = tile_set->get_tile_size();
|
||||
|
||||
// Draw the selection.
|
||||
if ((tiles_bottom_panel->is_visible_in_tree() || patterns_bottom_panel->is_visible_in_tree()) && tool_buttons_group->get_pressed_button() == select_tool_button) {
|
||||
// In select mode, we only draw the current selection if we are modifying it (pressing control or shift).
|
||||
if (drag_type == DRAG_TYPE_MOVE || (drag_type == DRAG_TYPE_SELECT && !Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
|
||||
// Do nothing
|
||||
// Do nothing.
|
||||
} else {
|
||||
Color grid_color = EDITOR_GET("editors/tiles_editor/grid_color");
|
||||
Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
|
||||
|
@ -839,10 +839,11 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
|
|||
|
||||
// Expand the grid if needed
|
||||
if (expand_grid && !preview.is_empty()) {
|
||||
drawn_grid_rect = Rect2i(preview.begin()->key, Vector2i(1, 1));
|
||||
drawn_grid_rect = Rect2i(preview.begin()->key, Vector2i(0, 0));
|
||||
for (const KeyValue<Vector2i, TileMapCell> &E : preview) {
|
||||
drawn_grid_rect.expand_to(E.key);
|
||||
}
|
||||
drawn_grid_rect.size += Vector2i(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -910,8 +911,6 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
|
|||
dest_rect.position = (tile_map->map_to_local(E.key) - dest_rect.size / 2 - tile_offset);
|
||||
}
|
||||
|
||||
dest_rect = xform.xform(dest_rect);
|
||||
|
||||
if (tile_data->get_flip_h()) {
|
||||
dest_rect.size.x = -dest_rect.size.x;
|
||||
}
|
||||
|
@ -927,7 +926,9 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
|
|||
modulate *= tile_map->get_layer_modulate(tile_map_layer);
|
||||
|
||||
// Draw the tile.
|
||||
p_overlay->draw_set_transform_matrix(xform);
|
||||
p_overlay->draw_texture_rect_region(atlas_source->get_texture(), dest_rect, source_rect, modulate * Color(1.0, 1.0, 1.0, 0.5), transpose, tile_set->is_uv_clipping());
|
||||
p_overlay->draw_set_transform_matrix(Transform2D());
|
||||
} else {
|
||||
tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
|
||||
}
|
||||
|
@ -1237,7 +1238,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
|
|||
return;
|
||||
}
|
||||
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
|
||||
Vector2 mpos = xform.affine_inverse().xform(CanvasItemEditor::get_singleton()->get_viewport_control()->get_local_mouse_position());
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
|
@ -2649,7 +2650,7 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
|
|||
return;
|
||||
}
|
||||
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
|
||||
Vector2 mpos = xform.affine_inverse().xform(CanvasItemEditor::get_singleton()->get_viewport_control()->get_local_mouse_position());
|
||||
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
|
@ -2845,7 +2846,7 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
|
|||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_valid()) {
|
||||
has_mouse = true;
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
|
||||
Vector2 mpos = xform.affine_inverse().xform(mm->get_position());
|
||||
|
||||
switch (drag_type) {
|
||||
|
@ -2872,7 +2873,7 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
|
|||
Ref<InputEventMouseButton> mb = p_event;
|
||||
if (mb.is_valid()) {
|
||||
has_mouse = true;
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
|
||||
Vector2 mpos = xform.affine_inverse().xform(mb->get_position());
|
||||
|
||||
if (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT) {
|
||||
|
@ -2976,7 +2977,7 @@ void TileMapEditorTerrainsPlugin::forward_canvas_draw_over_viewport(Control *p_o
|
|||
return;
|
||||
}
|
||||
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
|
||||
Vector2i tile_shape_size = tile_set->get_tile_size();
|
||||
|
||||
// Handle the preview of the tiles to be placed.
|
||||
|
@ -3817,7 +3818,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
|
|||
return;
|
||||
}
|
||||
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
|
||||
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform_with_canvas();
|
||||
Transform2D xform_inv = xform.affine_inverse();
|
||||
Vector2i tile_shape_size = tile_set->get_tile_size();
|
||||
|
||||
|
|
|
@ -1134,6 +1134,7 @@ const char *RenamesMap3To4::gdscript_properties_renames[][2] = {
|
|||
{ "tab_align", "tab_alignment" }, // TabContainer
|
||||
{ "table_hseparation", "table_h_separation" }, // Theme
|
||||
{ "table_vseparation", "table_v_separation" }, // Theme
|
||||
{ "tangent", "orthogonal" }, // Vector2
|
||||
{ "toplevel", "top_level" }, // Node
|
||||
{ "translation", "position" }, // Node3D
|
||||
{ "unit_db", "volume_db" }, // AudioStreamPlayer3D
|
||||
|
@ -1198,6 +1199,7 @@ const char *RenamesMap3To4::csharp_properties_renames[][2] = {
|
|||
{ "Oneshot", "OneShot" }, // AnimatedTexture
|
||||
{ "OutOfRangeMode", "MaxPolyphony" }, // AudioStreamPlayer3D
|
||||
{ "PauseMode", "ProcessMode" }, // Node
|
||||
{ "Perpendicular", "Orthogonal" }, // Vector2 - Only exists in C#
|
||||
{ "PhysicalScancode", "PhysicalKeycode" }, // InputEventKey
|
||||
{ "PopupExclusive", "Exclusive" }, // Window
|
||||
{ "ProximityFadeEnable", "ProximityFadeEnabled" }, // Material
|
||||
|
@ -1228,6 +1230,7 @@ const char *RenamesMap3To4::csharp_properties_renames[][2] = {
|
|||
{ "TabAlign", "TabAlignment" }, // TabContainer
|
||||
{ "TableHseparation", "TableHSeparation" }, // Theme
|
||||
{ "TableVseparation", "TableVSeparation" }, // Theme
|
||||
{ "Tangent", "Orthogonal" }, // Vector2
|
||||
{ "Toplevel", "TopLevel" }, // Node
|
||||
{ "Translation", "Position" }, // Node3D
|
||||
{ "UnitDb", "VolumeDb" }, // AudioStreamPlayer3D
|
||||
|
|
|
@ -18,12 +18,13 @@
|
|||
<param index="2" name="b8" type="int" />
|
||||
<param index="3" name="a8" type="int" default="255" />
|
||||
<description>
|
||||
Returns a [Color] constructed from red ([param r8]), green ([param g8]), blue ([param b8]), and optionally alpha ([param a8]) integer channels, each divided by [code]255.0[/code] for their final value.
|
||||
Returns a [Color] constructed from red ([param r8]), green ([param g8]), blue ([param b8]), and optionally alpha ([param a8]) integer channels, each divided by [code]255.0[/code] for their final value. Using [method Color8] instead of the standard [Color] constructor is useful when you need to match exact color values in an [Image].
|
||||
[codeblock]
|
||||
var red = Color8(255, 0, 0) # Same as Color(1, 0, 0).
|
||||
var dark_blue = Color8(0, 0, 51) # Same as Color(0, 0, 0.2).
|
||||
var my_color = Color8(306, 255, 0, 102) # Same as Color(1.2, 1, 0, 0.4).
|
||||
[/codeblock]
|
||||
[b]Note:[/b] Due to the lower precision of [method Color8] compared to the standard [Color] constructor, a color created with [method Color8] will generally not be equal to the same color created with the standard [Color] constructor. Use [method Color.is_equal_approx] for comparisons to avoid issues with floating-point precision error.
|
||||
</description>
|
||||
</method>
|
||||
<method name="assert">
|
||||
|
|
|
@ -294,17 +294,27 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
|
|||
!(str[j] == '_' && (prev_is_digit || str[j - 1] == 'b' || str[j - 1] == 'x' || str[j - 1] == '.')) &&
|
||||
!(str[j] == 'e' && (prev_is_digit || str[j - 1] == '_')) &&
|
||||
!(str[j] == '.' && (prev_is_digit || (!prev_is_binary_op && (j > 0 && (str[j - 1] == '-' || str[j - 1] == '+' || str[j - 1] == '~'))))) &&
|
||||
!((str[j] == '-' || str[j] == '+' || str[j] == '~') && !prev_is_binary_op && str[j - 1] != 'e')) {
|
||||
/* This condition continues Number highlighting in special cases.
|
||||
1st row: '+' or '-' after scientific notation;
|
||||
!((str[j] == '-' || str[j] == '+' || str[j] == '~') && !is_binary_op && !prev_is_binary_op && str[j - 1] != 'e')) {
|
||||
/* This condition continues number highlighting in special cases.
|
||||
1st row: '+' or '-' after scientific notation (like 3e-4);
|
||||
2nd row: '_' as a numeric separator;
|
||||
3rd row: Scientific notation 'e' and floating points;
|
||||
4th row: Floating points inside the number, or leading if after a unary mathematical operator;
|
||||
5th row: Multiple unary mathematical operators */
|
||||
5th row: Multiple unary mathematical operators (like ~-7)*/
|
||||
in_number = false;
|
||||
}
|
||||
} else if (!is_binary_op && (str[j] == '-' || str[j] == '+' || str[j] == '~' || (str[j] == '.' && str[j + 1] != '.' && (j == 0 || (j > 0 && str[j - 1] != '.'))))) {
|
||||
} else if (str[j] == '.' && !is_binary_op && is_digit(str[j + 1]) && (j == 0 || (j > 0 && str[j - 1] != '.'))) {
|
||||
// Start number highlighting from leading decimal points (like .42)
|
||||
in_number = true;
|
||||
} else if ((str[j] == '-' || str[j] == '+' || str[j] == '~') && !is_binary_op) {
|
||||
// Only start number highlighting on unary operators if a digit follows them.
|
||||
int non_op = j + 1;
|
||||
while (str[non_op] == '-' || str[non_op] == '+' || str[non_op] == '~') {
|
||||
non_op++;
|
||||
}
|
||||
if (is_digit(str[non_op]) || (str[non_op] == '.' && non_op < line_length && is_digit(str[non_op + 1]))) {
|
||||
in_number = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_word && is_unicode_identifier_start(str[j]) && !in_number) {
|
||||
|
|
|
@ -913,7 +913,7 @@ void GridMapEditor::update_palette() {
|
|||
}
|
||||
|
||||
void GridMapEditor::edit(GridMap *p_gridmap) {
|
||||
if (node) {
|
||||
if (node && node->is_connected("cell_size_changed", callable_mp(this, &GridMapEditor::_draw_grids))) {
|
||||
node->disconnect("cell_size_changed", callable_mp(this, &GridMapEditor::_draw_grids));
|
||||
}
|
||||
|
||||
|
|
|
@ -557,10 +557,14 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
|
|||
}
|
||||
|
||||
//erase navigation
|
||||
for (const KeyValue<IndexKey, Octant::NavigationCell> &E : g.navigation_cell_ids) {
|
||||
NavigationServer3D::get_singleton()->free(E.value.region);
|
||||
for (KeyValue<IndexKey, Octant::NavigationCell> &E : g.navigation_cell_ids) {
|
||||
if (E.value.region.is_valid()) {
|
||||
NavigationServer3D::get_singleton()->free(E.value.region);
|
||||
E.value.region = RID();
|
||||
}
|
||||
if (E.value.navigation_mesh_debug_instance.is_valid()) {
|
||||
RS::get_singleton()->free(E.value.navigation_mesh_debug_instance);
|
||||
E.value.navigation_mesh_debug_instance = RID();
|
||||
}
|
||||
}
|
||||
g.navigation_cell_ids.clear();
|
||||
|
|
|
@ -334,8 +334,8 @@ bool CSharpLanguage::is_using_templates() {
|
|||
}
|
||||
|
||||
Ref<Script> CSharpLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const {
|
||||
Ref<CSharpScript> script;
|
||||
script.instantiate();
|
||||
Ref<CSharpScript> scr;
|
||||
scr.instantiate();
|
||||
|
||||
String class_name_no_spaces = p_class_name.replace(" ", "_");
|
||||
String base_class_name = get_base_class_name(p_base_class_name, class_name_no_spaces);
|
||||
|
@ -344,8 +344,8 @@ Ref<Script> CSharpLanguage::make_template(const String &p_template, const String
|
|||
.replace("_BASE_", base_class_name)
|
||||
.replace("_CLASS_", class_name_no_spaces)
|
||||
.replace("_TS_", _get_indentation());
|
||||
script->set_source_code(processed_template);
|
||||
return script;
|
||||
scr->set_source_code(processed_template);
|
||||
return scr;
|
||||
}
|
||||
|
||||
Vector<ScriptLanguage::ScriptTemplate> CSharpLanguage::get_built_in_templates(StringName p_object) {
|
||||
|
@ -780,28 +780,28 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
|
||||
// As scripts are going to be reloaded, must proceed without locking here
|
||||
|
||||
for (Ref<CSharpScript> &script : scripts) {
|
||||
for (Ref<CSharpScript> &scr : scripts) {
|
||||
// If someone removes a script from a node, deletes the script, builds, adds a script to the
|
||||
// same node, then builds again, the script might have no path and also no script_class. In
|
||||
// that case, we can't (and don't need to) reload it.
|
||||
if (script->get_path().is_empty() && !script->valid) {
|
||||
if (scr->get_path().is_empty() && !scr->valid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
to_reload.push_back(script);
|
||||
to_reload.push_back(scr);
|
||||
|
||||
// Script::instances are deleted during managed object disposal, which happens on domain finalize.
|
||||
// Only placeholders are kept. Therefore we need to keep a copy before that happens.
|
||||
|
||||
for (Object *obj : script->instances) {
|
||||
script->pending_reload_instances.insert(obj->get_instance_id());
|
||||
for (Object *obj : scr->instances) {
|
||||
scr->pending_reload_instances.insert(obj->get_instance_id());
|
||||
|
||||
// Since this script instance wasn't a placeholder, add it to the list of placeholders
|
||||
// that will have to be eventually replaced with a script instance in case it turns into one.
|
||||
// This list is not cleared after the reload and the collected instances only leave
|
||||
// the list if the script is instantiated or if it was a tool script but becomes a
|
||||
// non-tool script in a rebuild.
|
||||
script->pending_replace_placeholders.insert(obj->get_instance_id());
|
||||
scr->pending_replace_placeholders.insert(obj->get_instance_id());
|
||||
|
||||
RefCounted *rc = Object::cast_to<RefCounted>(obj);
|
||||
if (rc) {
|
||||
|
@ -810,9 +810,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
for (PlaceHolderScriptInstance *script_instance : script->placeholders) {
|
||||
Object *obj = script_instance->get_owner();
|
||||
script->pending_reload_instances.insert(obj->get_instance_id());
|
||||
for (PlaceHolderScriptInstance *instance : scr->placeholders) {
|
||||
Object *obj = instance->get_owner();
|
||||
scr->pending_reload_instances.insert(obj->get_instance_id());
|
||||
|
||||
RefCounted *rc = Object::cast_to<RefCounted>(obj);
|
||||
if (rc) {
|
||||
|
@ -822,9 +822,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
#endif
|
||||
|
||||
// Save state and remove script from instances
|
||||
RBMap<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state;
|
||||
RBMap<ObjectID, CSharpScript::StateBackup> &owners_map = scr->pending_reload_state;
|
||||
|
||||
for (Object *obj : script->instances) {
|
||||
for (Object *obj : scr->instances) {
|
||||
ERR_CONTINUE(!obj->get_script_instance());
|
||||
|
||||
CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance());
|
||||
|
@ -849,14 +849,14 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
}
|
||||
|
||||
// After the state of all instances is saved, clear scripts and script instances
|
||||
for (Ref<CSharpScript> &script : scripts) {
|
||||
while (script->instances.begin()) {
|
||||
Object *obj = *script->instances.begin();
|
||||
for (Ref<CSharpScript> &scr : scripts) {
|
||||
while (scr->instances.begin()) {
|
||||
Object *obj = *scr->instances.begin();
|
||||
obj->set_script(Ref<RefCounted>()); // Remove script and existing script instances (placeholder are not removed before domain reload)
|
||||
}
|
||||
|
||||
script->was_tool_before_reload = script->tool;
|
||||
script->_clear();
|
||||
scr->was_tool_before_reload = scr->tool;
|
||||
scr->_clear();
|
||||
}
|
||||
|
||||
// Do domain reload
|
||||
|
@ -901,44 +901,44 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
|
||||
List<Ref<CSharpScript>> to_reload_state;
|
||||
|
||||
for (Ref<CSharpScript> &script : to_reload) {
|
||||
for (Ref<CSharpScript> &scr : to_reload) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
script->exports_invalidated = true;
|
||||
scr->exports_invalidated = true;
|
||||
#endif
|
||||
|
||||
if (!script->get_path().is_empty()) {
|
||||
script->reload(p_soft_reload);
|
||||
if (!scr->get_path().is_empty()) {
|
||||
scr->reload(p_soft_reload);
|
||||
|
||||
if (!script->valid) {
|
||||
script->pending_reload_instances.clear();
|
||||
script->pending_reload_state.clear();
|
||||
if (!scr->valid) {
|
||||
scr->pending_reload_instances.clear();
|
||||
scr->pending_reload_state.clear();
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
bool success = GDMonoCache::managed_callbacks.ScriptManagerBridge_TryReloadRegisteredScriptWithClass(script.ptr());
|
||||
bool success = GDMonoCache::managed_callbacks.ScriptManagerBridge_TryReloadRegisteredScriptWithClass(scr.ptr());
|
||||
|
||||
if (!success) {
|
||||
// Couldn't reload
|
||||
script->pending_reload_instances.clear();
|
||||
script->pending_reload_state.clear();
|
||||
scr->pending_reload_instances.clear();
|
||||
scr->pending_reload_state.clear();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
StringName native_name = script->get_instance_base_type();
|
||||
StringName native_name = scr->get_instance_base_type();
|
||||
|
||||
{
|
||||
for (const ObjectID &obj_id : script->pending_reload_instances) {
|
||||
for (const ObjectID &obj_id : scr->pending_reload_instances) {
|
||||
Object *obj = ObjectDB::get_instance(obj_id);
|
||||
|
||||
if (!obj) {
|
||||
script->pending_reload_state.erase(obj_id);
|
||||
scr->pending_reload_state.erase(obj_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ClassDB::is_parent_class(obj->get_class_name(), native_name)) {
|
||||
// No longer inherits the same compatible type, can't reload
|
||||
script->pending_reload_state.erase(obj_id);
|
||||
scr->pending_reload_state.erase(obj_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -946,11 +946,11 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
|
||||
// Check if the script must be instantiated or kept as a placeholder
|
||||
// when the script may not be a tool (see #65266)
|
||||
bool replace_placeholder = script->pending_replace_placeholders.has(obj->get_instance_id());
|
||||
if (!script->is_tool() && script->was_tool_before_reload) {
|
||||
bool replace_placeholder = scr->pending_replace_placeholders.has(obj->get_instance_id());
|
||||
if (!scr->is_tool() && scr->was_tool_before_reload) {
|
||||
// The script was a tool before the rebuild so the removal was intentional.
|
||||
replace_placeholder = false;
|
||||
script->pending_replace_placeholders.erase(obj->get_instance_id());
|
||||
scr->pending_replace_placeholders.erase(obj->get_instance_id());
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
@ -959,20 +959,20 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
// Non-placeholder script instances are removed in godot_icall_Object_Disposed.
|
||||
CRASH_COND(!si->is_placeholder());
|
||||
|
||||
if (replace_placeholder || script->is_tool() || ScriptServer::is_scripting_enabled()) {
|
||||
if (replace_placeholder || scr->is_tool() || ScriptServer::is_scripting_enabled()) {
|
||||
// Replace placeholder with a script instance.
|
||||
|
||||
CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
|
||||
CSharpScript::StateBackup &state_backup = scr->pending_reload_state[obj_id];
|
||||
|
||||
// Backup placeholder script instance state before replacing it with a script instance.
|
||||
si->get_property_state(state_backup.properties);
|
||||
|
||||
ScriptInstance *script_instance = script->instance_create(obj);
|
||||
ScriptInstance *instance = scr->instance_create(obj);
|
||||
|
||||
if (script_instance) {
|
||||
script->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si));
|
||||
script->pending_replace_placeholders.erase(obj->get_instance_id());
|
||||
obj->set_script_instance(script_instance);
|
||||
if (instance) {
|
||||
scr->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si));
|
||||
scr->pending_replace_placeholders.erase(obj->get_instance_id());
|
||||
obj->set_script_instance(instance);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -983,18 +983,18 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
#endif
|
||||
|
||||
// Re-create the script instance.
|
||||
if (replace_placeholder || script->is_tool() || ScriptServer::is_scripting_enabled()) {
|
||||
if (replace_placeholder || scr->is_tool() || ScriptServer::is_scripting_enabled()) {
|
||||
// Create script instance or replace placeholder with a script instance.
|
||||
ScriptInstance *script_instance = script->instance_create(obj);
|
||||
ScriptInstance *instance = scr->instance_create(obj);
|
||||
|
||||
if (script_instance) {
|
||||
script->pending_replace_placeholders.erase(obj->get_instance_id());
|
||||
obj->set_script_instance(script_instance);
|
||||
if (instance) {
|
||||
scr->pending_replace_placeholders.erase(obj->get_instance_id());
|
||||
obj->set_script_instance(instance);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// The script instance could not be instantiated or wasn't in the list of placeholders to replace.
|
||||
obj->set_script(script);
|
||||
obj->set_script(scr);
|
||||
#if DEBUG_ENABLED
|
||||
// If we reached here, the instantiated script must be a placeholder.
|
||||
CRASH_COND(!obj->get_script_instance()->is_placeholder());
|
||||
|
@ -1002,21 +1002,21 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
}
|
||||
}
|
||||
|
||||
to_reload_state.push_back(script);
|
||||
to_reload_state.push_back(scr);
|
||||
}
|
||||
|
||||
for (Ref<CSharpScript> &script : to_reload_state) {
|
||||
for (const ObjectID &obj_id : script->pending_reload_instances) {
|
||||
for (Ref<CSharpScript> &scr : to_reload_state) {
|
||||
for (const ObjectID &obj_id : scr->pending_reload_instances) {
|
||||
Object *obj = ObjectDB::get_instance(obj_id);
|
||||
|
||||
if (!obj) {
|
||||
script->pending_reload_state.erase(obj_id);
|
||||
scr->pending_reload_state.erase(obj_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
ERR_CONTINUE(!obj->get_script_instance());
|
||||
|
||||
CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
|
||||
CSharpScript::StateBackup &state_backup = scr->pending_reload_state[obj_id];
|
||||
|
||||
CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance());
|
||||
|
||||
|
@ -1033,8 +1033,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
|
|||
}
|
||||
}
|
||||
|
||||
script->pending_reload_instances.clear();
|
||||
script->pending_reload_state.clear();
|
||||
scr->pending_reload_instances.clear();
|
||||
scr->pending_reload_state.clear();
|
||||
}
|
||||
|
||||
// Deserialize managed callables
|
||||
|
@ -2144,8 +2144,8 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda
|
|||
_update_exports_values(values, propnames);
|
||||
|
||||
if (changed) {
|
||||
for (PlaceHolderScriptInstance *script_instance : placeholders) {
|
||||
script_instance->update(propnames, values);
|
||||
for (PlaceHolderScriptInstance *instance : placeholders) {
|
||||
instance->update(propnames, values);
|
||||
}
|
||||
} else {
|
||||
p_instance_to_update->update(propnames, values);
|
||||
|
@ -2711,28 +2711,28 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const
|
|||
|
||||
// TODO ignore anything inside bin/ and obj/ in tools builds?
|
||||
|
||||
Ref<CSharpScript> script;
|
||||
Ref<CSharpScript> scr;
|
||||
|
||||
if (GDMonoCache::godot_api_cache_updated) {
|
||||
GDMonoCache::managed_callbacks.ScriptManagerBridge_GetOrCreateScriptBridgeForPath(&p_path, &script);
|
||||
GDMonoCache::managed_callbacks.ScriptManagerBridge_GetOrCreateScriptBridgeForPath(&p_path, &scr);
|
||||
} else {
|
||||
script = Ref<CSharpScript>(memnew(CSharpScript));
|
||||
scr = Ref<CSharpScript>(memnew(CSharpScript));
|
||||
}
|
||||
|
||||
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
|
||||
Error err = script->load_source_code(p_path);
|
||||
Error err = scr->load_source_code(p_path);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot load C# script file '" + p_path + "'.");
|
||||
#endif
|
||||
|
||||
script->set_path(p_original_path);
|
||||
scr->set_path(p_original_path);
|
||||
|
||||
script->reload();
|
||||
scr->reload();
|
||||
|
||||
if (r_error) {
|
||||
*r_error = OK;
|
||||
}
|
||||
|
||||
return script;
|
||||
return scr;
|
||||
}
|
||||
|
||||
void ResourceFormatLoaderCSharpScript::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
<GodotProjectDir Condition=" '$(GodotProjectDir)' == '' ">$(MSBuildProjectDirectory)</GodotProjectDir>
|
||||
<GodotProjectDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotProjectDir>
|
||||
<GodotProjectDirBase64 Condition=" $([MSBuild]::VersionGreaterThanOrEquals($(MSBuildAssemblyVersion), '17.3')) ">$([MSBuild]::ConvertToBase64('$(GodotProjectDir)'))</GodotProjectDirBase64>
|
||||
|
||||
<!-- Custom output paths for Godot projects. In brief, 'bin\' and 'obj\' are moved to '$(GodotProjectDir)\.godot\mono\temp\'. -->
|
||||
<BaseOutputPath>$(GodotProjectDir).godot\mono\temp\bin\</BaseOutputPath>
|
||||
|
@ -29,6 +30,13 @@
|
|||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Condition=" $([MSBuild]::VersionLessThan($(MSBuildAssemblyVersion), '17.3')) " Name="GodotProjectDir" BeforeTargets="Build">
|
||||
<Error Text="Cannot build from path containing '%23', move your project or update dotnet to the latest version." Condition="$(GodotProjectDir.Contains('%23'))" /> <!-- # -->
|
||||
<Error Text="Cannot build from path containing '%3B', move your project or update dotnet to the latest version." Condition="$(GodotProjectDir.Contains('%3B'))" /> <!-- ; -->
|
||||
<Error Text="Cannot build from path containing newlines, move your project or update dotnet to the latest version." Condition="$(GodotProjectDir.Contains('%0A'))" /> <!-- \n -->
|
||||
<Error Text="Cannot build from path containing newlines, move your project or update dotnet to the latest version." Condition="$(GodotProjectDir.Contains('%0D'))" /> <!-- \r -->
|
||||
</Target>
|
||||
|
||||
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" Condition=" '$(GodotSdkImportsMicrosoftNetSdk)' == 'true' " />
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<PropertyGroup>
|
||||
<!-- $(GodotProjectDir) would normally be defined by the Godot.NET.Sdk -->
|
||||
<GodotProjectDir>$(MSBuildProjectDirectory)</GodotProjectDir>
|
||||
<GodotProjectDirBase64 Condition=" $([MSBuild]::VersionGreaterThanOrEquals($(MSBuildAssemblyVersion), '17.3')) ">$([MSBuild]::ConvertToBase64('$(GodotProjectDir)'))</GodotProjectDirBase64>
|
||||
<!-- For compiling GetGodotPropertyDefaultValues. -->
|
||||
<DefineConstants>$(DefineConstants);TOOLS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<ItemGroup>
|
||||
<!-- $(GodotProjectDir) is defined by Godot.NET.Sdk -->
|
||||
<CompilerVisibleProperty Include="GodotProjectDir" />
|
||||
<CompilerVisibleProperty Include="GodotProjectDirBase64" />
|
||||
<CompilerVisibleProperty Include="GodotSourceGenerators" />
|
||||
<CompilerVisibleProperty Include="IsGodotToolsProject" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -22,10 +22,17 @@ namespace Godot.SourceGenerators
|
|||
|
||||
// NOTE: NotNullWhen diagnostics don't work on projects targeting .NET Standard 2.0
|
||||
// ReSharper disable once ReplaceWithStringIsNullOrEmpty
|
||||
if (!context.TryGetGlobalAnalyzerProperty("GodotProjectDir", out string? godotProjectDir)
|
||||
|| godotProjectDir!.Length == 0)
|
||||
if (!context.TryGetGlobalAnalyzerProperty("GodotProjectDirBase64", out string? godotProjectDir) || godotProjectDir!.Length == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Property 'GodotProjectDir' is null or empty.");
|
||||
if (!context.TryGetGlobalAnalyzerProperty("GodotProjectDir", out godotProjectDir) || godotProjectDir!.Length == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Property 'GodotProjectDir' is null or empty.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Workaround for https://github.com/dotnet/roslyn/issues/51692
|
||||
godotProjectDir = Encoding.UTF8.GetString(Convert.FromBase64String(godotProjectDir));
|
||||
}
|
||||
|
||||
Dictionary<INamedTypeSymbol, IEnumerable<ClassDeclarationSyntax>> godotClasses = context
|
||||
|
|
|
@ -523,7 +523,10 @@ void BindingsGenerator::_append_xml_method(StringBuilder &p_xml_output, const Ty
|
|||
p_xml_output.append(target_imethod->proxy_name);
|
||||
p_xml_output.append("\"/>");
|
||||
} else {
|
||||
ERR_PRINT("Cannot resolve method reference in documentation: '" + p_link_target + "'.");
|
||||
if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
|
||||
ERR_PRINT("Cannot resolve method reference in documentation: '" + p_link_target + "'.");
|
||||
}
|
||||
|
||||
_append_xml_undeclared(p_xml_output, p_link_target);
|
||||
}
|
||||
}
|
||||
|
@ -563,7 +566,10 @@ void BindingsGenerator::_append_xml_member(StringBuilder &p_xml_output, const Ty
|
|||
p_xml_output.append(target_iprop->proxy_name);
|
||||
p_xml_output.append("\"/>");
|
||||
} else {
|
||||
ERR_PRINT("Cannot resolve member reference in documentation: '" + p_link_target + "'.");
|
||||
if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
|
||||
ERR_PRINT("Cannot resolve member reference in documentation: '" + p_link_target + "'.");
|
||||
}
|
||||
|
||||
_append_xml_undeclared(p_xml_output, p_link_target);
|
||||
}
|
||||
}
|
||||
|
@ -591,7 +597,10 @@ void BindingsGenerator::_append_xml_signal(StringBuilder &p_xml_output, const Ty
|
|||
p_xml_output.append(target_isignal->proxy_name);
|
||||
p_xml_output.append("\"/>");
|
||||
} else {
|
||||
ERR_PRINT("Cannot resolve signal reference in documentation: '" + p_link_target + "'.");
|
||||
if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
|
||||
ERR_PRINT("Cannot resolve signal reference in documentation: '" + p_link_target + "'.");
|
||||
}
|
||||
|
||||
_append_xml_undeclared(p_xml_output, p_link_target);
|
||||
}
|
||||
}
|
||||
|
@ -613,7 +622,10 @@ void BindingsGenerator::_append_xml_enum(StringBuilder &p_xml_output, const Type
|
|||
p_xml_output.append(target_enum_itype.proxy_name); // Includes nesting class if any
|
||||
p_xml_output.append("\"/>");
|
||||
} else {
|
||||
ERR_PRINT("Cannot resolve enum reference in documentation: '" + p_link_target + "'.");
|
||||
if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
|
||||
ERR_PRINT("Cannot resolve enum reference in documentation: '" + p_link_target + "'.");
|
||||
}
|
||||
|
||||
_append_xml_undeclared(p_xml_output, p_link_target);
|
||||
}
|
||||
}
|
||||
|
@ -673,7 +685,10 @@ void BindingsGenerator::_append_xml_constant(StringBuilder &p_xml_output, const
|
|||
// Also search in @GlobalScope as a last resort if no class was specified
|
||||
_append_xml_constant_in_global_scope(p_xml_output, p_target_cname, p_link_target);
|
||||
} else {
|
||||
ERR_PRINT("Cannot resolve constant reference in documentation: '" + p_link_target + "'.");
|
||||
if (!p_target_itype->is_intentionally_ignored(p_link_target)) {
|
||||
ERR_PRINT("Cannot resolve constant reference in documentation: '" + p_link_target + "'.");
|
||||
}
|
||||
|
||||
_append_xml_undeclared(p_xml_output, p_link_target);
|
||||
}
|
||||
}
|
||||
|
@ -2936,6 +2951,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
|
|||
|
||||
if (method_has_ptr_parameter(method_info)) {
|
||||
// Pointers are not supported.
|
||||
itype.ignored_members.insert(method_info.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -408,6 +408,7 @@ class BindingsGenerator {
|
|||
List<PropertyInterface> properties;
|
||||
List<MethodInterface> methods;
|
||||
List<SignalInterface> signals_;
|
||||
HashSet<String> ignored_members;
|
||||
|
||||
bool has_virtual_methods = false;
|
||||
|
||||
|
@ -471,6 +472,10 @@ class BindingsGenerator {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool is_intentionally_ignored(const String &p_name) const {
|
||||
return ignored_members.has(p_name);
|
||||
}
|
||||
|
||||
private:
|
||||
static DocData::ClassDoc *_get_type_doc(TypeInterface &itype) {
|
||||
String doc_name = itype.name.begins_with("_") ? itype.name.substr(1) : itype.name;
|
||||
|
|
|
@ -66,23 +66,25 @@ String _get_mono_user_dir() {
|
|||
if (EditorPaths::get_singleton()) {
|
||||
return EditorPaths::get_singleton()->get_data_dir().path_join("mono");
|
||||
} else {
|
||||
String settings_path;
|
||||
String settings_path = OS::get_singleton()->get_data_path().path_join(OS::get_singleton()->get_godot_dir_name());
|
||||
|
||||
// Self-contained mode if a `._sc_` or `_sc_` file is present in executable dir.
|
||||
String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
|
||||
|
||||
// On macOS, look outside .app bundle, since .app bundle is read-only.
|
||||
if (OS::get_singleton()->has_feature("macos") && exe_dir.ends_with("MacOS") && exe_dir.path_join("..").simplify_path().ends_with("Contents")) {
|
||||
exe_dir = exe_dir.path_join("../../..").simplify_path();
|
||||
}
|
||||
|
||||
Ref<DirAccess> d = DirAccess::create_for_path(exe_dir);
|
||||
|
||||
if (d->file_exists("._sc_") || d->file_exists("_sc_")) {
|
||||
// contain yourself
|
||||
settings_path = exe_dir.path_join("editor_data");
|
||||
} else {
|
||||
settings_path = OS::get_singleton()->get_data_path().path_join(OS::get_singleton()->get_godot_dir_name());
|
||||
}
|
||||
|
||||
// On macOS, look outside .app bundle, since .app bundle is read-only.
|
||||
// Note: This will not work if Gatekeeper path randomization is active.
|
||||
if (OS::get_singleton()->has_feature("macos") && exe_dir.ends_with("MacOS") && exe_dir.path_join("..").simplify_path().ends_with("Contents")) {
|
||||
exe_dir = exe_dir.path_join("../../..").simplify_path();
|
||||
d = DirAccess::create_for_path(exe_dir);
|
||||
if (d->file_exists("._sc_") || d->file_exists("_sc_")) {
|
||||
// contain yourself
|
||||
settings_path = exe_dir.path_join("editor_data");
|
||||
}
|
||||
}
|
||||
|
||||
return settings_path.path_join("mono");
|
||||
|
|
|
@ -894,9 +894,9 @@ void NavMap::sync() {
|
|||
if (agents_dirty) {
|
||||
// cannot use LocalVector here as RVO library expects std::vector to build KdTree
|
||||
std::vector<RVO::Agent *> raw_agents;
|
||||
raw_agents.reserve(agents.size());
|
||||
for (NavAgent *agent : agents) {
|
||||
raw_agents.push_back(agent->get_agent());
|
||||
raw_agents.reserve(controlled_agents.size());
|
||||
for (NavAgent *controlled_agent : controlled_agents) {
|
||||
raw_agents.push_back(controlled_agent->get_agent());
|
||||
}
|
||||
rvo.buildAgentTree(raw_agents);
|
||||
}
|
||||
|
|
|
@ -178,6 +178,7 @@ void OpenXRActionMap::create_default_action_sets() {
|
|||
Ref<OpenXRAction> grip = action_set->add_new_action("grip", "Grip", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
|
||||
Ref<OpenXRAction> grip_click = action_set->add_new_action("grip_click", "Grip click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
|
||||
Ref<OpenXRAction> grip_touch = action_set->add_new_action("grip_touch", "Grip touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
|
||||
Ref<OpenXRAction> grip_force = action_set->add_new_action("grip_force", "Grip force", OpenXRAction::OPENXR_ACTION_FLOAT, "/user/hand/left,/user/hand/right");
|
||||
Ref<OpenXRAction> primary = action_set->add_new_action("primary", "Primary joystick/thumbstick/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2, "/user/hand/left,/user/hand/right");
|
||||
Ref<OpenXRAction> primary_click = action_set->add_new_action("primary_click", "Primary joystick/thumbstick/trackpad click", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
|
||||
Ref<OpenXRAction> primary_touch = action_set->add_new_action("primary_touch", "Primary joystick/thumbstick/trackpad touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
|
||||
|
@ -349,6 +350,7 @@ void OpenXRActionMap::create_default_action_sets() {
|
|||
profile->add_new_binding(trigger_touch, "/user/hand/left/input/trigger/touch,/user/hand/right/input/trigger/touch");
|
||||
profile->add_new_binding(grip, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value");
|
||||
profile->add_new_binding(grip_click, "/user/hand/left/input/squeeze/value,/user/hand/right/input/squeeze/value"); // this should do a float to bool conversion
|
||||
profile->add_new_binding(grip_force, "/user/hand/left/input/squeeze/force,/user/hand/right/input/squeeze/force"); // grip force seems to be unique to the Valve Index
|
||||
// primary on our index controller is our thumbstick
|
||||
profile->add_new_binding(primary, "/user/hand/left/input/thumbstick,/user/hand/right/input/thumbstick");
|
||||
profile->add_new_binding(primary_click, "/user/hand/left/input/thumbstick/click,/user/hand/right/input/thumbstick/click");
|
||||
|
|
|
@ -376,7 +376,9 @@ void OpenXRInteractionProfileMetaData::_register_core_metadata() {
|
|||
register_io_path("/interaction_profiles/valve/index_controller", "Trigger touch", "/user/hand/right", "/user/hand/right/input/trigger/touch", "", OpenXRAction::OPENXR_ACTION_BOOL);
|
||||
|
||||
register_io_path("/interaction_profiles/valve/index_controller", "Squeeze", "/user/hand/left", "/user/hand/left/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
|
||||
register_io_path("/interaction_profiles/valve/index_controller", "Squeeze force", "/user/hand/left", "/user/hand/left/input/squeeze/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
|
||||
register_io_path("/interaction_profiles/valve/index_controller", "Squeeze", "/user/hand/right", "/user/hand/right/input/squeeze/value", "", OpenXRAction::OPENXR_ACTION_FLOAT);
|
||||
register_io_path("/interaction_profiles/valve/index_controller", "Squeeze force", "/user/hand/right", "/user/hand/right/input/squeeze/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
|
||||
|
||||
register_io_path("/interaction_profiles/valve/index_controller", "Thumbstick", "/user/hand/left", "/user/hand/left/input/thumbstick", "", OpenXRAction::OPENXR_ACTION_VECTOR2);
|
||||
register_io_path("/interaction_profiles/valve/index_controller", "Thumbstick click", "/user/hand/left", "/user/hand/left/input/thumbstick/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
|
||||
|
|
|
@ -80,7 +80,9 @@ public:
|
|||
// this happens right before physics process and normal processing is run.
|
||||
// This is when controller data is queried and made available to game logic.
|
||||
virtual void on_process() {}
|
||||
virtual void on_pre_render() {} // `on_pre_render` is called right before we start rendering our XR viewport.
|
||||
virtual void on_pre_render() {} // `on_pre_render` is called right before we start rendering our XR viewports.
|
||||
virtual void on_pre_draw_viewport(RID p_render_target) {} // `on_pre_draw_viewport` is called right before we start rendering this viewport
|
||||
virtual void on_post_draw_viewport(RID p_render_target) {} // `on_port_draw_viewport` is called right after we start rendering this viewport (note that on Vulkan draw commands may only be queued)
|
||||
|
||||
virtual void on_state_idle() {} // `on_state_idle` is called when the OpenXR session state is changed to idle.
|
||||
virtual void on_state_ready() {} // `on_state_ready` is called when the OpenXR session state is changed to ready, this means OpenXR is ready to setup our session.
|
||||
|
|
|
@ -37,6 +37,28 @@
|
|||
#include "servers/rendering/rendering_server_globals.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
// OpenXR requires us to submit sRGB textures so that it recognises the content
|
||||
// as being in sRGB color space. We do fall back on "normal" textures but this
|
||||
// will likely result in incorrect colors as OpenXR will double the sRGB conversion.
|
||||
// All major XR runtimes support sRGB textures.
|
||||
|
||||
// In OpenGL output of the fragment shader is assumed to be in the color space of
|
||||
// the developers choice, however a linear to sRGB HW conversion can be enabled
|
||||
// through enabling GL_FRAMEBUFFER_SRGB if an sRGB color attachment is used.
|
||||
// This is a global setting.
|
||||
// See: https://www.khronos.org/opengl/wiki/Framebuffer
|
||||
|
||||
// In OpenGLES output of the fragment shader is assumed to be in linear color space
|
||||
// and will be converted by default to sRGB if an sRGB color attachment is used.
|
||||
// The extension GL_EXT_sRGB_write_control was introduced to enable turning this
|
||||
// feature off.
|
||||
// See: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_sRGB_write_control.txt
|
||||
|
||||
// On OpenGLES this is not defined in our standard headers..
|
||||
#ifndef GL_FRAMEBUFFER_SRGB
|
||||
#define GL_FRAMEBUFFER_SRGB 0x8DB9
|
||||
#endif
|
||||
|
||||
HashMap<String, bool *> OpenXROpenGLExtension::get_requested_extensions() {
|
||||
HashMap<String, bool *> request_extensions;
|
||||
|
||||
|
@ -157,8 +179,8 @@ void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_nex
|
|||
}
|
||||
|
||||
void OpenXROpenGLExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) {
|
||||
p_usable_swap_chains.push_back(GL_RGBA8);
|
||||
p_usable_swap_chains.push_back(GL_SRGB8_ALPHA8);
|
||||
p_usable_swap_chains.push_back(GL_RGBA8);
|
||||
}
|
||||
|
||||
void OpenXROpenGLExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_depth_formats) {
|
||||
|
@ -168,6 +190,23 @@ void OpenXROpenGLExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_d
|
|||
p_usable_depth_formats.push_back(GL_DEPTH_COMPONENT24);
|
||||
}
|
||||
|
||||
void OpenXROpenGLExtension::on_pre_draw_viewport(RID p_render_target) {
|
||||
if (srgb_ext_is_available) {
|
||||
hw_linear_to_srgb_is_enabled = glIsEnabled(GL_FRAMEBUFFER_SRGB);
|
||||
if (hw_linear_to_srgb_is_enabled) {
|
||||
// Disable this.
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenXROpenGLExtension::on_post_draw_viewport(RID p_render_target) {
|
||||
if (srgb_ext_is_available && hw_linear_to_srgb_is_enabled) {
|
||||
// Re-enable this.
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenXROpenGLExtension::get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) {
|
||||
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
|
||||
ERR_FAIL_NULL_V(texture_storage, false);
|
||||
|
|
|
@ -79,6 +79,9 @@ public:
|
|||
virtual void on_instance_created(const XrInstance p_instance) override;
|
||||
virtual void *set_session_create_and_get_next_pointer(void *p_next_pointer) override;
|
||||
|
||||
virtual void on_pre_draw_viewport(RID p_render_target) override;
|
||||
virtual void on_post_draw_viewport(RID p_render_target) override;
|
||||
|
||||
virtual void get_usable_swapchain_formats(Vector<int64_t> &p_usable_swap_chains) override;
|
||||
virtual void get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) override;
|
||||
virtual String get_swapchain_format_name(int64_t p_swapchain_format) const override;
|
||||
|
@ -103,6 +106,9 @@ private:
|
|||
Vector<RID> texture_rids;
|
||||
};
|
||||
|
||||
bool srgb_ext_is_available = true;
|
||||
bool hw_linear_to_srgb_is_enabled = false;
|
||||
|
||||
bool check_graphics_api_support(XrVersion p_desired_version);
|
||||
|
||||
#ifdef ANDROID_ENABLED
|
||||
|
|
|
@ -1815,6 +1815,10 @@ bool OpenXRAPI::pre_draw_viewport(RID p_render_target) {
|
|||
}
|
||||
}
|
||||
|
||||
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
||||
wrapper->on_pre_draw_viewport(p_render_target);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1839,7 +1843,9 @@ void OpenXRAPI::post_draw_viewport(RID p_render_target) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Nothing to do here at this point in time...
|
||||
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
|
||||
wrapper->on_post_draw_viewport(p_render_target);
|
||||
}
|
||||
};
|
||||
|
||||
void OpenXRAPI::end_frame() {
|
||||
|
|
|
@ -386,6 +386,8 @@ int64_t TextServerAdvanced::_get_features() const {
|
|||
void TextServerAdvanced::_free_rid(const RID &p_rid) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
if (font_owner.owns(p_rid)) {
|
||||
MutexLock ftlock(ft_mutex);
|
||||
|
||||
FontAdvanced *fd = font_owner.get_or_null(p_rid);
|
||||
{
|
||||
MutexLock lock(fd->mutex);
|
||||
|
@ -1321,45 +1323,48 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
|
|||
// Init dynamic font.
|
||||
#ifdef MODULE_FREETYPE_ENABLED
|
||||
int error = 0;
|
||||
if (!ft_library) {
|
||||
error = FT_Init_FreeType(&ft_library);
|
||||
if (error != 0) {
|
||||
memdelete(fd);
|
||||
ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
|
||||
}
|
||||
{
|
||||
MutexLock ftlock(ft_mutex);
|
||||
if (!ft_library) {
|
||||
error = FT_Init_FreeType(&ft_library);
|
||||
if (error != 0) {
|
||||
memdelete(fd);
|
||||
ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
|
||||
}
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
|
||||
FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
memset(&fd->stream, 0, sizeof(FT_StreamRec));
|
||||
fd->stream.base = (unsigned char *)p_font_data->data_ptr;
|
||||
fd->stream.size = p_font_data->data_size;
|
||||
fd->stream.pos = 0;
|
||||
memset(&fd->stream, 0, sizeof(FT_StreamRec));
|
||||
fd->stream.base = (unsigned char *)p_font_data->data_ptr;
|
||||
fd->stream.size = p_font_data->data_size;
|
||||
fd->stream.pos = 0;
|
||||
|
||||
FT_Open_Args fargs;
|
||||
memset(&fargs, 0, sizeof(FT_Open_Args));
|
||||
fargs.memory_base = (unsigned char *)p_font_data->data_ptr;
|
||||
fargs.memory_size = p_font_data->data_size;
|
||||
fargs.flags = FT_OPEN_MEMORY;
|
||||
fargs.stream = &fd->stream;
|
||||
FT_Open_Args fargs;
|
||||
memset(&fargs, 0, sizeof(FT_Open_Args));
|
||||
fargs.memory_base = (unsigned char *)p_font_data->data_ptr;
|
||||
fargs.memory_size = p_font_data->data_size;
|
||||
fargs.flags = FT_OPEN_MEMORY;
|
||||
fargs.stream = &fd->stream;
|
||||
|
||||
int max_index = 0;
|
||||
FT_Face tmp_face = nullptr;
|
||||
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
|
||||
if (tmp_face && error == 0) {
|
||||
max_index = tmp_face->num_faces - 1;
|
||||
}
|
||||
if (tmp_face) {
|
||||
FT_Done_Face(tmp_face);
|
||||
}
|
||||
int max_index = 0;
|
||||
FT_Face tmp_face = nullptr;
|
||||
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
|
||||
if (tmp_face && error == 0) {
|
||||
max_index = tmp_face->num_faces - 1;
|
||||
}
|
||||
if (tmp_face) {
|
||||
FT_Done_Face(tmp_face);
|
||||
}
|
||||
|
||||
error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
|
||||
if (error) {
|
||||
FT_Done_Face(fd->face);
|
||||
fd->face = nullptr;
|
||||
memdelete(fd);
|
||||
ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");
|
||||
error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
|
||||
if (error) {
|
||||
FT_Done_Face(fd->face);
|
||||
fd->face = nullptr;
|
||||
memdelete(fd);
|
||||
ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");
|
||||
}
|
||||
}
|
||||
|
||||
if (p_font_data->msdf) {
|
||||
|
@ -1788,6 +1793,8 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f
|
|||
}
|
||||
|
||||
_FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontAdvanced *p_font_data) {
|
||||
MutexLock ftlock(ft_mutex);
|
||||
|
||||
for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : p_font_data->cache) {
|
||||
memdelete(E.value);
|
||||
}
|
||||
|
@ -1894,6 +1901,8 @@ int64_t TextServerAdvanced::_font_get_face_count(const RID &p_font_rid) const {
|
|||
fargs.flags = FT_OPEN_MEMORY;
|
||||
fargs.stream = &stream;
|
||||
|
||||
MutexLock ftlock(ft_mutex);
|
||||
|
||||
FT_Face tmp_face = nullptr;
|
||||
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
|
||||
if (error == 0) {
|
||||
|
@ -2285,6 +2294,7 @@ void TextServerAdvanced::_font_clear_size_cache(const RID &p_font_rid) {
|
|||
ERR_FAIL_COND(!fd);
|
||||
|
||||
MutexLock lock(fd->mutex);
|
||||
MutexLock ftlock(ft_mutex);
|
||||
for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) {
|
||||
memdelete(E.value);
|
||||
}
|
||||
|
@ -2296,6 +2306,7 @@ void TextServerAdvanced::_font_remove_size_cache(const RID &p_font_rid, const Ve
|
|||
ERR_FAIL_COND(!fd);
|
||||
|
||||
MutexLock lock(fd->mutex);
|
||||
MutexLock ftlock(ft_mutex);
|
||||
if (fd->cache.has(p_size)) {
|
||||
memdelete(fd->cache[p_size]);
|
||||
fd->cache.erase(p_size);
|
||||
|
|
|
@ -616,6 +616,8 @@ class TextServerAdvanced : public TextServerExtension {
|
|||
|
||||
_FORCE_INLINE_ void _add_featuers(const Dictionary &p_source, Vector<hb_feature_t> &r_ftrs);
|
||||
|
||||
Mutex ft_mutex;
|
||||
|
||||
// HarfBuzz bitmap font interface.
|
||||
|
||||
static hb_font_funcs_t *funcs;
|
||||
|
|
|
@ -113,6 +113,8 @@ int64_t TextServerFallback::_get_features() const {
|
|||
void TextServerFallback::_free_rid(const RID &p_rid) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
if (font_owner.owns(p_rid)) {
|
||||
MutexLock ftlock(ft_mutex);
|
||||
|
||||
FontFallback *fd = font_owner.get_or_null(p_rid);
|
||||
{
|
||||
MutexLock lock(fd->mutex);
|
||||
|
@ -760,45 +762,48 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
|
|||
// Init dynamic font.
|
||||
#ifdef MODULE_FREETYPE_ENABLED
|
||||
int error = 0;
|
||||
if (!ft_library) {
|
||||
error = FT_Init_FreeType(&ft_library);
|
||||
if (error != 0) {
|
||||
memdelete(fd);
|
||||
ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
|
||||
}
|
||||
{
|
||||
MutexLock ftlock(ft_mutex);
|
||||
if (!ft_library) {
|
||||
error = FT_Init_FreeType(&ft_library);
|
||||
if (error != 0) {
|
||||
memdelete(fd);
|
||||
ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
|
||||
}
|
||||
#ifdef MODULE_SVG_ENABLED
|
||||
FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
|
||||
FT_Property_Set(ft_library, "ot-svg", "svg-hooks", get_tvg_svg_in_ot_hooks());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
memset(&fd->stream, 0, sizeof(FT_StreamRec));
|
||||
fd->stream.base = (unsigned char *)p_font_data->data_ptr;
|
||||
fd->stream.size = p_font_data->data_size;
|
||||
fd->stream.pos = 0;
|
||||
memset(&fd->stream, 0, sizeof(FT_StreamRec));
|
||||
fd->stream.base = (unsigned char *)p_font_data->data_ptr;
|
||||
fd->stream.size = p_font_data->data_size;
|
||||
fd->stream.pos = 0;
|
||||
|
||||
FT_Open_Args fargs;
|
||||
memset(&fargs, 0, sizeof(FT_Open_Args));
|
||||
fargs.memory_base = (unsigned char *)p_font_data->data_ptr;
|
||||
fargs.memory_size = p_font_data->data_size;
|
||||
fargs.flags = FT_OPEN_MEMORY;
|
||||
fargs.stream = &fd->stream;
|
||||
FT_Open_Args fargs;
|
||||
memset(&fargs, 0, sizeof(FT_Open_Args));
|
||||
fargs.memory_base = (unsigned char *)p_font_data->data_ptr;
|
||||
fargs.memory_size = p_font_data->data_size;
|
||||
fargs.flags = FT_OPEN_MEMORY;
|
||||
fargs.stream = &fd->stream;
|
||||
|
||||
int max_index = 0;
|
||||
FT_Face tmp_face = nullptr;
|
||||
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
|
||||
if (tmp_face && error == 0) {
|
||||
max_index = tmp_face->num_faces - 1;
|
||||
}
|
||||
if (tmp_face) {
|
||||
FT_Done_Face(tmp_face);
|
||||
}
|
||||
int max_index = 0;
|
||||
FT_Face tmp_face = nullptr;
|
||||
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
|
||||
if (tmp_face && error == 0) {
|
||||
max_index = tmp_face->num_faces - 1;
|
||||
}
|
||||
if (tmp_face) {
|
||||
FT_Done_Face(tmp_face);
|
||||
}
|
||||
|
||||
error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
|
||||
if (error) {
|
||||
FT_Done_Face(fd->face);
|
||||
fd->face = nullptr;
|
||||
memdelete(fd);
|
||||
ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");
|
||||
error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
|
||||
if (error) {
|
||||
FT_Done_Face(fd->face);
|
||||
fd->face = nullptr;
|
||||
memdelete(fd);
|
||||
ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");
|
||||
}
|
||||
}
|
||||
|
||||
if (p_font_data->msdf) {
|
||||
|
@ -909,6 +914,8 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
|
|||
}
|
||||
|
||||
_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontFallback *p_font_data) {
|
||||
MutexLock ftlock(ft_mutex);
|
||||
|
||||
for (const KeyValue<Vector2i, FontForSizeFallback *> &E : p_font_data->cache) {
|
||||
memdelete(E.value);
|
||||
}
|
||||
|
@ -1012,6 +1019,8 @@ int64_t TextServerFallback::_font_get_face_count(const RID &p_font_rid) const {
|
|||
fargs.flags = FT_OPEN_MEMORY;
|
||||
fargs.stream = &stream;
|
||||
|
||||
MutexLock ftlock(ft_mutex);
|
||||
|
||||
FT_Face tmp_face = nullptr;
|
||||
error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
|
||||
if (error == 0) {
|
||||
|
@ -1393,6 +1402,7 @@ void TextServerFallback::_font_clear_size_cache(const RID &p_font_rid) {
|
|||
ERR_FAIL_COND(!fd);
|
||||
|
||||
MutexLock lock(fd->mutex);
|
||||
MutexLock ftlock(ft_mutex);
|
||||
for (const KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {
|
||||
memdelete(E.value);
|
||||
}
|
||||
|
@ -1404,6 +1414,7 @@ void TextServerFallback::_font_remove_size_cache(const RID &p_font_rid, const Ve
|
|||
ERR_FAIL_COND(!fd);
|
||||
|
||||
MutexLock lock(fd->mutex);
|
||||
MutexLock ftlock(ft_mutex);
|
||||
if (fd->cache.has(p_size)) {
|
||||
memdelete(fd->cache[p_size]);
|
||||
fd->cache.erase(p_size);
|
||||
|
|
|
@ -531,6 +531,8 @@ class TextServerFallback : public TextServerExtension {
|
|||
|
||||
void _realign(ShapedTextDataFallback *p_sd) const;
|
||||
|
||||
Mutex ft_mutex;
|
||||
|
||||
protected:
|
||||
static void _bind_methods(){};
|
||||
|
||||
|
|
|
@ -74,10 +74,14 @@ import android.util.Log;
|
|||
import android.view.Display;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.Window;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsetsAnimation;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
|
@ -291,14 +295,64 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
|
|||
editText.setView(mRenderView);
|
||||
io.setEdit(editText);
|
||||
|
||||
view.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
||||
Point fullSize = new Point();
|
||||
activity.getWindowManager().getDefaultDisplay().getSize(fullSize);
|
||||
Rect gameSize = new Rect();
|
||||
mRenderView.getView().getWindowVisibleDisplayFrame(gameSize);
|
||||
final int keyboardHeight = fullSize.y - gameSize.bottom;
|
||||
GodotLib.setVirtualKeyboardHeight(keyboardHeight);
|
||||
});
|
||||
// Listeners for keyboard height.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// Report the height of virtual keyboard as it changes during the animation.
|
||||
final View decorView = activity.getWindow().getDecorView();
|
||||
decorView.setWindowInsetsAnimationCallback(new WindowInsetsAnimation.Callback(WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP) {
|
||||
int startBottom, endBottom;
|
||||
@Override
|
||||
public void onPrepare(@NonNull WindowInsetsAnimation animation) {
|
||||
startBottom = decorView.getRootWindowInsets().getInsets(WindowInsets.Type.ime()).bottom;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public WindowInsetsAnimation.Bounds onStart(@NonNull WindowInsetsAnimation animation, @NonNull WindowInsetsAnimation.Bounds bounds) {
|
||||
endBottom = decorView.getRootWindowInsets().getInsets(WindowInsets.Type.ime()).bottom;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public WindowInsets onProgress(@NonNull WindowInsets windowInsets, @NonNull List<WindowInsetsAnimation> list) {
|
||||
// Find the IME animation.
|
||||
WindowInsetsAnimation imeAnimation = null;
|
||||
for (WindowInsetsAnimation animation : list) {
|
||||
if ((animation.getTypeMask() & WindowInsets.Type.ime()) != 0) {
|
||||
imeAnimation = animation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Update keyboard height based on IME animation.
|
||||
if (imeAnimation != null) {
|
||||
float interpolatedFraction = imeAnimation.getInterpolatedFraction();
|
||||
// Linear interpolation between start and end values.
|
||||
float keyboardHeight = startBottom * (1.0f - interpolatedFraction) + endBottom * interpolatedFraction;
|
||||
GodotLib.setVirtualKeyboardHeight((int)keyboardHeight);
|
||||
}
|
||||
return windowInsets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnd(@NonNull WindowInsetsAnimation animation) {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Infer the virtual keyboard height using visible area.
|
||||
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
// Don't allocate a new Rect every time the callback is called.
|
||||
final Rect visibleSize = new Rect();
|
||||
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
final SurfaceView view = mRenderView.getView();
|
||||
view.getWindowVisibleDisplayFrame(visibleSize);
|
||||
final int keyboardHeight = view.getHeight() - visibleSize.bottom;
|
||||
GodotLib.setVirtualKeyboardHeight(keyboardHeight);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mRenderView.queueOnRenderThread(() -> {
|
||||
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
|
||||
|
|
|
@ -76,6 +76,13 @@ internal enum class StorageScope {
|
|||
return UNKNOWN
|
||||
}
|
||||
|
||||
// If we have 'All Files Access' permission, we can access all directories without
|
||||
// restriction.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
&& Environment.isExternalStorageManager()) {
|
||||
return APP
|
||||
}
|
||||
|
||||
val canonicalPathFile = pathFile.canonicalPath
|
||||
|
||||
if (internalAppDir != null && canonicalPathFile.startsWith(internalAppDir)) {
|
||||
|
@ -90,7 +97,7 @@ internal enum class StorageScope {
|
|||
return APP
|
||||
}
|
||||
|
||||
var rootDir: String? = System.getenv("ANDROID_ROOT")
|
||||
val rootDir: String? = System.getenv("ANDROID_ROOT")
|
||||
if (rootDir != null && canonicalPathFile.startsWith(rootDir)) {
|
||||
return APP
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ def configure(env: "Environment"):
|
|||
'Unsupported CPU architecture "%s" for Linux / *BSD. Supported architectures are: %s.'
|
||||
% (env["arch"], ", ".join(supported_arches))
|
||||
)
|
||||
sys.exit()
|
||||
sys.exit(255)
|
||||
|
||||
## Build type
|
||||
|
||||
|
@ -208,7 +208,7 @@ def configure(env: "Environment"):
|
|||
"freetype, libpng, zlib, graphite, harfbuzz.\n"
|
||||
"Please specify `builtin_<name>=no` for all of them, or none."
|
||||
)
|
||||
sys.exit()
|
||||
sys.exit(255)
|
||||
|
||||
if not env["builtin_freetype"]:
|
||||
env.ParseConfig("pkg-config freetype2 --cflags --libs")
|
||||
|
@ -307,11 +307,12 @@ def configure(env: "Environment"):
|
|||
if not env["use_sowrap"]:
|
||||
if os.system("pkg-config --exists libpulse") == 0: # 0 means found
|
||||
env.ParseConfig("pkg-config libpulse --cflags --libs")
|
||||
env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED", "_REENTRANT"])
|
||||
env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED"])
|
||||
else:
|
||||
print("Warning: PulseAudio development libraries not found. Disabling the PulseAudio audio driver.")
|
||||
env["pulseaudio"] = False
|
||||
env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED", "_REENTRANT"])
|
||||
else:
|
||||
env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED", "_REENTRANT"])
|
||||
|
||||
if env["dbus"]:
|
||||
if not env["use_sowrap"]:
|
||||
|
|
|
@ -242,17 +242,17 @@ def configure(env: "Environment"):
|
|||
env.Append(LINKFLAGS=["-lMoltenVK"])
|
||||
mvk_found = False
|
||||
|
||||
mkv_list = [get_mvk_sdk_path(), "/opt/homebrew/lib", "/usr/local/homebrew/lib", "/opt/local/lib"]
|
||||
mvk_list = [get_mvk_sdk_path(), "/opt/homebrew/lib", "/usr/local/homebrew/lib", "/opt/local/lib"]
|
||||
if env["vulkan_sdk_path"] != "":
|
||||
mkv_list.insert(0, os.path.expanduser(env["vulkan_sdk_path"]))
|
||||
mkv_list.insert(
|
||||
mvk_list.insert(0, os.path.expanduser(env["vulkan_sdk_path"]))
|
||||
mvk_list.insert(
|
||||
0,
|
||||
os.path.join(
|
||||
os.path.expanduser(env["vulkan_sdk_path"]), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/"
|
||||
),
|
||||
)
|
||||
|
||||
for mvk_path in mkv_list:
|
||||
for mvk_path in mvk_list:
|
||||
if mvk_path and os.path.isfile(os.path.join(mvk_path, "libMoltenVK.a")):
|
||||
mvk_found = True
|
||||
print("MoltenVK found at: " + mvk_path)
|
||||
|
|
|
@ -75,7 +75,7 @@ void WebToolsEditorPlugin::_download_zip() {
|
|||
const String project_name_safe = project_name.to_lower().replace(" ", "_");
|
||||
const String datetime_safe =
|
||||
Time::get_singleton()->get_datetime_string_from_system(false, true).replace(" ", "_");
|
||||
const String output_name = OS::get_singleton()->get_safe_dir_name(vformat("%s_%s.zip"));
|
||||
const String output_name = OS::get_singleton()->get_safe_dir_name(vformat("%s_%s.zip", project_name_safe, datetime_safe));
|
||||
const String output_path = String("/tmp").path_join(output_name);
|
||||
|
||||
zipFile zip = zipOpen2(output_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io);
|
||||
|
|
|
@ -3418,9 +3418,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
|||
gr_mem = alt_mem;
|
||||
}
|
||||
}
|
||||
if (wParam == VK_LWIN || wParam == VK_RWIN) {
|
||||
meta_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN);
|
||||
}
|
||||
|
||||
if (windows[window_id].ime_suppress_next_keyup && (uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP)) {
|
||||
windows[window_id].ime_suppress_next_keyup = false;
|
||||
|
|
|
@ -301,13 +301,13 @@ void AnimatedSprite2D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) {
|
|||
frames->get_animation_list(&al);
|
||||
if (al.size() == 0) {
|
||||
set_animation(StringName());
|
||||
set_autoplay(String());
|
||||
autoplay = String();
|
||||
} else {
|
||||
if (!frames->has_animation(animation)) {
|
||||
set_animation(al[0]);
|
||||
}
|
||||
if (!frames->has_animation(autoplay)) {
|
||||
set_autoplay(String());
|
||||
autoplay = String();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,11 +151,18 @@ bool ShapeCast2D::is_enabled() const {
|
|||
}
|
||||
|
||||
void ShapeCast2D::set_shape(const Ref<Shape2D> &p_shape) {
|
||||
if (p_shape == shape) {
|
||||
return;
|
||||
}
|
||||
if (shape.is_valid()) {
|
||||
shape->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast2D::_shape_changed));
|
||||
}
|
||||
shape = p_shape;
|
||||
if (p_shape.is_valid()) {
|
||||
shape->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast2D::_redraw_shape));
|
||||
if (shape.is_valid()) {
|
||||
shape->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast2D::_shape_changed));
|
||||
shape_rid = shape->get_rid();
|
||||
}
|
||||
|
||||
update_configuration_warnings();
|
||||
queue_redraw();
|
||||
}
|
||||
|
@ -186,7 +193,7 @@ bool ShapeCast2D::get_exclude_parent_body() const {
|
|||
return exclude_parent_body;
|
||||
}
|
||||
|
||||
void ShapeCast2D::_redraw_shape() {
|
||||
void ShapeCast2D::_shape_changed() {
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ class ShapeCast2D : public Node2D {
|
|||
real_t collision_unsafe_fraction = 1.0;
|
||||
|
||||
Array _get_collision_result() const;
|
||||
void _redraw_shape();
|
||||
void _shape_changed();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
|
|
@ -331,16 +331,14 @@ void ShapeCast3D::set_shape(const Ref<Shape3D> &p_shape) {
|
|||
if (p_shape == shape) {
|
||||
return;
|
||||
}
|
||||
if (!shape.is_null()) {
|
||||
if (shape.is_valid()) {
|
||||
shape->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast3D::_shape_changed));
|
||||
shape->unregister_owner(this);
|
||||
}
|
||||
shape = p_shape;
|
||||
if (!shape.is_null()) {
|
||||
if (shape.is_valid()) {
|
||||
shape->register_owner(this);
|
||||
shape->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &ShapeCast3D::_shape_changed));
|
||||
}
|
||||
if (p_shape.is_valid()) {
|
||||
shape_rid = shape->get_rid();
|
||||
}
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ private:
|
|||
|
||||
/* Line numbers */
|
||||
int line_number_gutter = -1;
|
||||
int line_number_digits = 0;
|
||||
int line_number_digits = 1;
|
||||
String line_number_padding = " ";
|
||||
Color line_number_color = Color(1, 1, 1);
|
||||
void _line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue