diff options
46 files changed, 825 insertions, 1066 deletions
diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp index ef0b590030..6c680e82c9 100644 --- a/core/extension/gdnative_interface.cpp +++ b/core/extension/gdnative_interface.cpp @@ -251,27 +251,6 @@ static GDNativeBool gdnative_variant_booleanize(const GDNativeVariantPtr p_self) return self->booleanize(); } -static void gdnative_variant_sub(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_dst) { - const Variant *a = (const Variant *)p_a; - const Variant *b = (const Variant *)p_b; - memnew_placement(r_dst, Variant); - Variant::sub(*a, *b, *(Variant *)r_dst); -} - -static void gdnative_variant_blend(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst) { - const Variant *a = (const Variant *)p_a; - const Variant *b = (const Variant *)p_b; - memnew_placement(r_dst, Variant); - Variant::blend(*a, *b, p_c, *(Variant *)r_dst); -} - -static void gdnative_variant_interpolate(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst) { - const Variant *a = (const Variant *)p_a; - const Variant *b = (const Variant *)p_b; - memnew_placement(r_dst, Variant); - Variant::interpolate(*a, *b, p_c, *(Variant *)r_dst); -} - static void gdnative_variant_duplicate(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep) { const Variant *self = (const Variant *)p_self; memnew_placement(r_ret, Variant(self->duplicate(p_deep))); @@ -970,9 +949,6 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) { gdni.variant_recursive_hash = gdnative_variant_recursive_hash; gdni.variant_hash_compare = gdnative_variant_hash_compare; gdni.variant_booleanize = gdnative_variant_booleanize; - gdni.variant_sub = gdnative_variant_sub; - gdni.variant_blend = gdnative_variant_blend; - gdni.variant_interpolate = gdnative_variant_interpolate; gdni.variant_duplicate = gdnative_variant_duplicate; gdni.variant_stringify = gdnative_variant_stringify; diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h index cb2adcb562..39378d8261 100644 --- a/core/extension/gdnative_interface.h +++ b/core/extension/gdnative_interface.h @@ -427,9 +427,6 @@ typedef struct { GDNativeInt (*variant_recursive_hash)(const GDNativeVariantPtr p_self, GDNativeInt p_recursion_count); GDNativeBool (*variant_hash_compare)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other); GDNativeBool (*variant_booleanize)(const GDNativeVariantPtr p_self); - void (*variant_sub)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_dst); - void (*variant_blend)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst); - void (*variant_interpolate)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst); void (*variant_duplicate)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep); void (*variant_stringify)(const GDNativeVariantPtr p_self, GDNativeStringPtr r_ret); diff --git a/core/variant/variant.h b/core/variant/variant.h index 9b213a7682..b0738e7d44 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -552,9 +552,6 @@ public: void zero(); Variant duplicate(bool p_deep = false) const; Variant recursive_duplicate(bool p_deep, int recursion_count) const; - static void blend(const Variant &a, const Variant &b, float c, Variant &r_dst); - static void interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst); - static void sub(const Variant &a, const Variant &b, Variant &r_dst); /* Built-In Methods */ diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp index 57b953f7f0..ff67b187ef 100644 --- a/core/variant/variant_setget.cpp +++ b/core/variant/variant_setget.cpp @@ -1911,572 +1911,6 @@ Variant Variant::recursive_duplicate(bool p_deep, int recursion_count) const { } } -void Variant::sub(const Variant &a, const Variant &b, Variant &r_dst) { - if (a.type != b.type) { - return; - } - - switch (a.type) { - case NIL: { - r_dst = Variant(); - } - return; - case INT: { - int64_t va = a._data._int; - int64_t vb = b._data._int; - r_dst = int(va - vb); - } - return; - case FLOAT: { - double ra = a._data._float; - double rb = b._data._float; - r_dst = ra - rb; - } - return; - case VECTOR2: { - r_dst = *reinterpret_cast<const Vector2 *>(a._data._mem) - *reinterpret_cast<const Vector2 *>(b._data._mem); - } - return; - case VECTOR2I: { - int32_t vax = reinterpret_cast<const Vector2i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector2i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector2i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector2i *>(b._data._mem)->y; - r_dst = Vector2i(int32_t(vax - vbx), int32_t(vay - vby)); - } - return; - case RECT2: { - const Rect2 *ra = reinterpret_cast<const Rect2 *>(a._data._mem); - const Rect2 *rb = reinterpret_cast<const Rect2 *>(b._data._mem); - r_dst = Rect2(ra->position - rb->position, ra->size - rb->size); - } - return; - case RECT2I: { - const Rect2i *ra = reinterpret_cast<const Rect2i *>(a._data._mem); - const Rect2i *rb = reinterpret_cast<const Rect2i *>(b._data._mem); - - int32_t vax = ra->position.x; - int32_t vay = ra->position.y; - int32_t vbx = ra->size.x; - int32_t vby = ra->size.y; - int32_t vcx = rb->position.x; - int32_t vcy = rb->position.y; - int32_t vdx = rb->size.x; - int32_t vdy = rb->size.y; - - r_dst = Rect2i(int32_t(vax - vbx), int32_t(vay - vby), int32_t(vcx - vdx), int32_t(vcy - vdy)); - } - return; - case VECTOR3: { - r_dst = *reinterpret_cast<const Vector3 *>(a._data._mem) - *reinterpret_cast<const Vector3 *>(b._data._mem); - } - return; - case VECTOR3I: { - int32_t vax = reinterpret_cast<const Vector3i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector3i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector3i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector3i *>(b._data._mem)->y; - int32_t vaz = reinterpret_cast<const Vector3i *>(a._data._mem)->z; - int32_t vbz = reinterpret_cast<const Vector3i *>(b._data._mem)->z; - r_dst = Vector3i(int32_t(vax - vbx), int32_t(vay - vby), int32_t(vaz - vbz)); - } - return; - case AABB: { - const ::AABB *ra = reinterpret_cast<const ::AABB *>(a._data._mem); - const ::AABB *rb = reinterpret_cast<const ::AABB *>(b._data._mem); - r_dst = ::AABB(ra->position - rb->position, ra->size - rb->size); - } - return; - case QUATERNION: { - Quaternion empty_rot; - const Quaternion *qa = reinterpret_cast<const Quaternion *>(a._data._mem); - const Quaternion *qb = reinterpret_cast<const Quaternion *>(b._data._mem); - r_dst = (*qb).inverse() * *qa; - } - return; - case COLOR: { - const Color *ca = reinterpret_cast<const Color *>(a._data._mem); - const Color *cb = reinterpret_cast<const Color *>(b._data._mem); - float new_r = ca->r - cb->r; - float new_g = ca->g - cb->g; - float new_b = ca->b - cb->b; - float new_a = ca->a - cb->a; - new_r = new_r > 1.0 ? 1.0 : new_r; - new_g = new_g > 1.0 ? 1.0 : new_g; - new_b = new_b > 1.0 ? 1.0 : new_b; - new_a = new_a > 1.0 ? 1.0 : new_a; - r_dst = Color(new_r, new_g, new_b, new_a); - } - return; - default: { - r_dst = a; - } - return; - } -} - -void Variant::blend(const Variant &a, const Variant &b, float c, Variant &r_dst) { - if (a.type != b.type) { - if (a.is_num() && b.is_num()) { - real_t va = a; - real_t vb = b; - r_dst = va + vb * c; - } else { - r_dst = a; - } - return; - } - - switch (a.type) { - case NIL: { - r_dst = Variant(); - } - return; - case INT: { - int64_t va = a._data._int; - int64_t vb = b._data._int; - r_dst = int(va + vb * c + 0.5); - } - return; - case FLOAT: { - double ra = a._data._float; - double rb = b._data._float; - r_dst = ra + rb * c; - } - return; - case VECTOR2: { - r_dst = *reinterpret_cast<const Vector2 *>(a._data._mem) + *reinterpret_cast<const Vector2 *>(b._data._mem) * c; - } - return; - case VECTOR2I: { - int32_t vax = reinterpret_cast<const Vector2i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector2i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector2i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector2i *>(b._data._mem)->y; - r_dst = Vector2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5)); - } - return; - case RECT2: { - const Rect2 *ra = reinterpret_cast<const Rect2 *>(a._data._mem); - const Rect2 *rb = reinterpret_cast<const Rect2 *>(b._data._mem); - r_dst = Rect2(ra->position + rb->position * c, ra->size + rb->size * c); - } - return; - case RECT2I: { - const Rect2i *ra = reinterpret_cast<const Rect2i *>(a._data._mem); - const Rect2i *rb = reinterpret_cast<const Rect2i *>(b._data._mem); - - int32_t vax = ra->position.x; - int32_t vay = ra->position.y; - int32_t vbx = ra->size.x; - int32_t vby = ra->size.y; - int32_t vcx = rb->position.x; - int32_t vcy = rb->position.y; - int32_t vdx = rb->size.x; - int32_t vdy = rb->size.y; - - r_dst = Rect2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vcx + vdx * c + 0.5), int32_t(vcy + vdy * c + 0.5)); - } - return; - case VECTOR3: { - r_dst = *reinterpret_cast<const Vector3 *>(a._data._mem) + *reinterpret_cast<const Vector3 *>(b._data._mem) * c; - } - return; - case VECTOR3I: { - int32_t vax = reinterpret_cast<const Vector3i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector3i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector3i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector3i *>(b._data._mem)->y; - int32_t vaz = reinterpret_cast<const Vector3i *>(a._data._mem)->z; - int32_t vbz = reinterpret_cast<const Vector3i *>(b._data._mem)->z; - r_dst = Vector3i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vaz + vbz * c + 0.5)); - } - return; - case AABB: { - const ::AABB *ra = reinterpret_cast<const ::AABB *>(a._data._mem); - const ::AABB *rb = reinterpret_cast<const ::AABB *>(b._data._mem); - r_dst = ::AABB(ra->position + rb->position * c, ra->size + rb->size * c); - } - return; - case QUATERNION: { - Quaternion empty_rot; - const Quaternion *qa = reinterpret_cast<const Quaternion *>(a._data._mem); - const Quaternion *qb = reinterpret_cast<const Quaternion *>(b._data._mem); - r_dst = *qa * empty_rot.slerp(*qb, c); - } - return; - case COLOR: { - const Color *ca = reinterpret_cast<const Color *>(a._data._mem); - const Color *cb = reinterpret_cast<const Color *>(b._data._mem); - float new_r = ca->r + cb->r * c; - float new_g = ca->g + cb->g * c; - float new_b = ca->b + cb->b * c; - float new_a = ca->a + cb->a * c; - new_r = new_r > 1.0 ? 1.0 : new_r; - new_g = new_g > 1.0 ? 1.0 : new_g; - new_b = new_b > 1.0 ? 1.0 : new_b; - new_a = new_a > 1.0 ? 1.0 : new_a; - r_dst = Color(new_r, new_g, new_b, new_a); - } - return; - default: { - r_dst = c < 0.5 ? a : b; - } - return; - } -} - -void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst) { - if (a.type != b.type) { - if (a.is_num() && b.is_num()) { - //not as efficient but.. - real_t va = a; - real_t vb = b; - r_dst = va + (vb - va) * c; - - } else { - r_dst = a; - } - return; - } - - switch (a.type) { - case NIL: { - r_dst = Variant(); - } - return; - case BOOL: { - r_dst = a; - } - return; - case INT: { - int64_t va = a._data._int; - int64_t vb = b._data._int; - r_dst = int(va + (vb - va) * c); - } - return; - case FLOAT: { - real_t va = a._data._float; - real_t vb = b._data._float; - r_dst = va + (vb - va) * c; - } - return; - case STRING: { - //this is pretty funny and bizarre, but artists like to use it for typewriter effects - String sa = *reinterpret_cast<const String *>(a._data._mem); - String sb = *reinterpret_cast<const String *>(b._data._mem); - String dst; - int sa_len = sa.length(); - int sb_len = sb.length(); - int csize = sa_len + (sb_len - sa_len) * c; - if (csize == 0) { - r_dst = ""; - return; - } - dst.resize(csize + 1); - dst[csize] = 0; - int split = csize / 2; - - for (int i = 0; i < csize; i++) { - char32_t chr = ' '; - - if (i < split) { - if (i < sa.length()) { - chr = sa[i]; - } else if (i < sb.length()) { - chr = sb[i]; - } - - } else { - if (i < sb.length()) { - chr = sb[i]; - } else if (i < sa.length()) { - chr = sa[i]; - } - } - - dst[i] = chr; - } - - r_dst = dst; - } - return; - case VECTOR2: { - r_dst = reinterpret_cast<const Vector2 *>(a._data._mem)->lerp(*reinterpret_cast<const Vector2 *>(b._data._mem), c); - } - return; - case VECTOR2I: { - int32_t vax = reinterpret_cast<const Vector2i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector2i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector2i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector2i *>(b._data._mem)->y; - r_dst = Vector2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5)); - } - return; - - case RECT2: { - r_dst = Rect2(reinterpret_cast<const Rect2 *>(a._data._mem)->position.lerp(reinterpret_cast<const Rect2 *>(b._data._mem)->position, c), reinterpret_cast<const Rect2 *>(a._data._mem)->size.lerp(reinterpret_cast<const Rect2 *>(b._data._mem)->size, c)); - } - return; - case RECT2I: { - const Rect2i *ra = reinterpret_cast<const Rect2i *>(a._data._mem); - const Rect2i *rb = reinterpret_cast<const Rect2i *>(b._data._mem); - - int32_t vax = ra->position.x; - int32_t vay = ra->position.y; - int32_t vbx = ra->size.x; - int32_t vby = ra->size.y; - int32_t vcx = rb->position.x; - int32_t vcy = rb->position.y; - int32_t vdx = rb->size.x; - int32_t vdy = rb->size.y; - - r_dst = Rect2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vcx + vdx * c + 0.5), int32_t(vcy + vdy * c + 0.5)); - } - return; - - case VECTOR3: { - r_dst = reinterpret_cast<const Vector3 *>(a._data._mem)->lerp(*reinterpret_cast<const Vector3 *>(b._data._mem), c); - } - return; - case VECTOR3I: { - int32_t vax = reinterpret_cast<const Vector3i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector3i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector3i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector3i *>(b._data._mem)->y; - int32_t vaz = reinterpret_cast<const Vector3i *>(a._data._mem)->z; - int32_t vbz = reinterpret_cast<const Vector3i *>(b._data._mem)->z; - r_dst = Vector3i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vaz + vbz * c + 0.5)); - } - return; - - case TRANSFORM2D: { - r_dst = a._data._transform2d->interpolate_with(*b._data._transform2d, c); - } - return; - case PLANE: { - r_dst = a; - } - return; - case QUATERNION: { - r_dst = reinterpret_cast<const Quaternion *>(a._data._mem)->slerp(*reinterpret_cast<const Quaternion *>(b._data._mem), c); - } - return; - case AABB: { - r_dst = ::AABB(a._data._aabb->position.lerp(b._data._aabb->position, c), a._data._aabb->size.lerp(b._data._aabb->size, c)); - } - return; - case BASIS: { - r_dst = a._data._basis->lerp(*b._data._basis, c); - } - return; - case TRANSFORM3D: { - r_dst = a._data._transform3d->interpolate_with(*b._data._transform3d, c); - } - return; - case COLOR: { - r_dst = reinterpret_cast<const Color *>(a._data._mem)->lerp(*reinterpret_cast<const Color *>(b._data._mem), c); - } - return; - case STRING_NAME: { - r_dst = a; - } - return; - case NODE_PATH: { - r_dst = a; - } - return; - case RID: { - r_dst = a; - } - return; - case OBJECT: { - r_dst = a; - } - return; - case DICTIONARY: { - } - return; - case ARRAY: { - r_dst = a; - } - return; - case PACKED_BYTE_ARRAY: { - r_dst = a; - } - return; - case PACKED_INT32_ARRAY: { - const Vector<int32_t> *arr_a = &PackedArrayRef<int32_t>::get_array(a._data.packed_array); - const Vector<int32_t> *arr_b = &PackedArrayRef<int32_t>::get_array(b._data.packed_array); - int32_t sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<int32_t> v; - v.resize(sz); - { - int32_t *vw = v.ptrw(); - const int32_t *ar = arr_a->ptr(); - const int32_t *br = arr_b->ptr(); - - Variant va; - for (int32_t i = 0; i < sz; i++) { - Variant::interpolate(ar[i], br[i], c, va); - vw[i] = va; - } - } - r_dst = v; - } - } - return; - case PACKED_INT64_ARRAY: { - const Vector<int64_t> *arr_a = &PackedArrayRef<int64_t>::get_array(a._data.packed_array); - const Vector<int64_t> *arr_b = &PackedArrayRef<int64_t>::get_array(b._data.packed_array); - int64_t sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<int64_t> v; - v.resize(sz); - { - int64_t *vw = v.ptrw(); - const int64_t *ar = arr_a->ptr(); - const int64_t *br = arr_b->ptr(); - - Variant va; - for (int64_t i = 0; i < sz; i++) { - Variant::interpolate(ar[i], br[i], c, va); - vw[i] = va; - } - } - r_dst = v; - } - } - return; - case PACKED_FLOAT32_ARRAY: { - const Vector<float> *arr_a = &PackedArrayRef<float>::get_array(a._data.packed_array); - const Vector<float> *arr_b = &PackedArrayRef<float>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<float> v; - v.resize(sz); - { - float *vw = v.ptrw(); - const float *ar = arr_a->ptr(); - const float *br = arr_b->ptr(); - - Variant va; - for (int i = 0; i < sz; i++) { - Variant::interpolate(ar[i], br[i], c, va); - vw[i] = va; - } - } - r_dst = v; - } - } - return; - case PACKED_FLOAT64_ARRAY: { - const Vector<double> *arr_a = &PackedArrayRef<double>::get_array(a._data.packed_array); - const Vector<double> *arr_b = &PackedArrayRef<double>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<double> v; - v.resize(sz); - { - double *vw = v.ptrw(); - const double *ar = arr_a->ptr(); - const double *br = arr_b->ptr(); - - Variant va; - for (int i = 0; i < sz; i++) { - Variant::interpolate(ar[i], br[i], c, va); - vw[i] = va; - } - } - r_dst = v; - } - } - return; - case PACKED_STRING_ARRAY: { - r_dst = a; - } - return; - case PACKED_VECTOR2_ARRAY: { - const Vector<Vector2> *arr_a = &PackedArrayRef<Vector2>::get_array(a._data.packed_array); - const Vector<Vector2> *arr_b = &PackedArrayRef<Vector2>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<Vector2> v; - v.resize(sz); - { - Vector2 *vw = v.ptrw(); - const Vector2 *ar = arr_a->ptr(); - const Vector2 *br = arr_b->ptr(); - - for (int i = 0; i < sz; i++) { - vw[i] = ar[i].lerp(br[i], c); - } - } - r_dst = v; - } - } - return; - case PACKED_VECTOR3_ARRAY: { - const Vector<Vector3> *arr_a = &PackedArrayRef<Vector3>::get_array(a._data.packed_array); - const Vector<Vector3> *arr_b = &PackedArrayRef<Vector3>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<Vector3> v; - v.resize(sz); - { - Vector3 *vw = v.ptrw(); - const Vector3 *ar = arr_a->ptr(); - const Vector3 *br = arr_b->ptr(); - - for (int i = 0; i < sz; i++) { - vw[i] = ar[i].lerp(br[i], c); - } - } - r_dst = v; - } - } - return; - case PACKED_COLOR_ARRAY: { - const Vector<Color> *arr_a = &PackedArrayRef<Color>::get_array(a._data.packed_array); - const Vector<Color> *arr_b = &PackedArrayRef<Color>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<Color> v; - v.resize(sz); - { - Color *vw = v.ptrw(); - const Color *ar = arr_a->ptr(); - const Color *br = arr_b->ptr(); - - for (int i = 0; i < sz; i++) { - vw[i] = ar[i].lerp(br[i], c); - } - } - r_dst = v; - } - } - return; - default: { - r_dst = a; - } - } -} - void Variant::_register_variant_setters_getters() { register_named_setters_getters(); register_indexed_setters_getters(); diff --git a/doc/classes/AnimatedSprite2D.xml b/doc/classes/AnimatedSprite2D.xml index b207eda27f..afbe34816a 100644 --- a/doc/classes/AnimatedSprite2D.xml +++ b/doc/classes/AnimatedSprite2D.xml @@ -5,6 +5,8 @@ </brief_description> <description> [AnimatedSprite2D] is similar to the [Sprite2D] node, except it carries multiple textures as animation frames. Animations are created using a [SpriteFrames] resource, which allows you to import image files (or a folder containing said files) to provide the animation frames for the sprite. The [SpriteFrames] resource can be configured in the editor via the SpriteFrames bottom panel. + After setting up [member frames], [method play] may be called. It's also possible to select an [member animation] and toggle [member playing], even within the editor. + To pause the current animation, call [method stop] or set [member playing] to [code]false[/code]. Alternatively, setting [member speed_scale] to [code]0[/code] also preserves the current frame's elapsed time. [b]Note:[/b] You can associate a set of normal or specular maps by creating additional [SpriteFrames] resources with a [code]_normal[/code] or [code]_specular[/code] suffix. For example, having 3 [SpriteFrames] resources [code]run[/code], [code]run_normal[/code], and [code]run_specular[/code] will make it so the [code]run[/code] animation uses normal and specular maps. </description> <tutorials> @@ -17,13 +19,14 @@ <param index="0" name="anim" type="StringName" default="&""" /> <param index="1" name="backwards" type="bool" default="false" /> <description> - Plays the animation named [param anim]. If no [param anim] is provided, the current animation is played. If [code]backwards[/code] is [code]true[/code], the animation will be played in reverse. + Plays the animation named [param anim]. If no [param anim] is provided, the current animation is played. If [param backwards] is [code]true[/code], the animation is played in reverse. </description> </method> <method name="stop"> <return type="void" /> <description> - Stops the current animation (does not reset the frame counter). + Stops the current [member animation] at the current [member frame]. + [b]Note:[/b] This method resets the current frame's elapsed time. If this behavior is undesired, consider setting [member speed_scale] to [code]0[/code], instead. </description> </method> </methods> @@ -50,10 +53,10 @@ The texture's drawing offset. </member> <member name="playing" type="bool" setter="set_playing" getter="is_playing" default="false"> - If [code]true[/code], the [member animation] is currently playing. + If [code]true[/code], the [member animation] is currently playing. Setting this property to [code]false[/code] is the equivalent of calling [method stop]. </member> <member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0"> - The animation speed is multiplied by this value. + The animation speed is multiplied by this value. If set to a negative value, the animation is played in reverse. If set to [code]0[/code], the animation is paused, preserving the current frame's elapsed time. </member> </members> <signals> diff --git a/doc/classes/AnimatedSprite3D.xml b/doc/classes/AnimatedSprite3D.xml index 58d3ca6ad3..09baf882fb 100644 --- a/doc/classes/AnimatedSprite3D.xml +++ b/doc/classes/AnimatedSprite3D.xml @@ -4,7 +4,9 @@ 2D sprite node in 3D world, that can use multiple 2D textures for animation. </brief_description> <description> - Animations are created using a [SpriteFrames] resource, which can be configured in the editor via the SpriteFrames panel. + [AnimatedSprite3D] is similar to the [Sprite3D] node, except it carries multiple textures as animation [member frames]. Animations are created using a [SpriteFrames] resource, which allows you to import image files (or a folder containing said files) to provide the animation frames for the sprite. The [SpriteFrames] resource can be configured in the editor via the SpriteFrames bottom panel. + After setting up [member frames], [method play] may be called. It's also possible to select an [member animation] and toggle [member playing], even within the editor. + To pause the current animation, call [method stop] or set [member playing] to [code]false[/code]. Alternatively, setting [member speed_scale] to [code]0[/code] also preserves the current frame's elapsed time. </description> <tutorials> <link title="2D Sprite animation (also applies to 3D)">$DOCS_URL/tutorials/2d/2d_sprite_animation.html</link> @@ -15,13 +17,14 @@ <param index="0" name="anim" type="StringName" default="&""" /> <param index="1" name="backwards" type="bool" default="false" /> <description> - Plays the animation named [param anim]. If no [param anim] is provided, the current animation is played. If [param backwards] is [code]true[/code], the animation will be played in reverse. + Plays the animation named [param anim]. If no [param anim] is provided, the current animation is played. If [param backwards] is [code]true[/code], the animation is played in reverse. </description> </method> <method name="stop"> <return type="void" /> <description> - Stops the current animation (does not reset the frame counter). + Stops the current [member animation] at the current [member frame]. + [b]Note:[/b] This method resets the current frame's elapsed time. If this behavior is undesired, consider setting [member speed_scale] to [code]0[/code], instead. </description> </method> </methods> @@ -36,10 +39,10 @@ The [SpriteFrames] resource containing the animation(s). </member> <member name="playing" type="bool" setter="set_playing" getter="is_playing" default="false"> - If [code]true[/code], the [member animation] is currently playing. + If [code]true[/code], the [member animation] is currently playing. Setting this property to [code]false[/code] is the equivalent of calling [method stop]. </member> <member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0"> - The animation speed is multiplied by this value. + The animation speed is multiplied by this value. If set to a negative value, the animation is played in reverse. If set to [code]0[/code], the animation is paused, preserving the current frame's elapsed time. </member> </members> <signals> diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index ee28675d89..cbb58a3e1e 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -28,7 +28,7 @@ <return type="Texture2D" /> <param index="0" name="param" type="int" enum="BaseMaterial3D.TextureParam" /> <description> - Returns the [Texture] associated with the specified [enum TextureParam]. + Returns the [Texture2D] associated with the specified [enum TextureParam]. </description> </method> <method name="set_feature"> diff --git a/doc/classes/Cubemap.xml b/doc/classes/Cubemap.xml index 7173388027..0cdebeda95 100644 --- a/doc/classes/Cubemap.xml +++ b/doc/classes/Cubemap.xml @@ -4,9 +4,9 @@ 6-sided texture typically used in 3D rendering. </brief_description> <description> - A cubemap is a 6-sided texture typically used for faking reflections in 3D rendering. It can be used to make an object look as if it's reflecting its surroundings. This usually delivers much better performance than other reflection methods. + A cubemap is made of 6 textures organized in layers. They are typically used for faking reflections (see [ReflectionProbe]) in 3D rendering. It can be used to make an object look as if it's reflecting its surroundings. This usually delivers much better performance than other reflection methods. This resource is typically used as a uniform in custom shaders. Few core Godot methods make use of Cubemap resources. - [b]Note:[/b] Godot doesn't support using cubemaps as a [PanoramaSkyMaterial]. You can use [url=https://danilw.github.io/GLSL-howto/cubemap_to_panorama_js/cubemap_to_panorama.html]this tool[/url] to convert a cube map to an equirectangular sky map. + [b]Note:[/b] Godot doesn't support using cubemaps in a [PanoramaSkyMaterial]. You can use [url=https://danilw.github.io/GLSL-howto/cubemap_to_panorama_js/cubemap_to_panorama.html]this tool[/url] to convert a cubemap to an equirectangular sky map. </description> <tutorials> </tutorials> diff --git a/doc/classes/CubemapArray.xml b/doc/classes/CubemapArray.xml index 4fca842b5a..7f001155e4 100644 --- a/doc/classes/CubemapArray.xml +++ b/doc/classes/CubemapArray.xml @@ -1,8 +1,13 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="CubemapArray" inherits="ImageTextureLayered" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A single composite texture resource which consists of multiple [Cubemap]s. </brief_description> <description> + [CubemapArray]s are made of an array of [Cubemap]s. Accordingly, like [Cubemap]s they are made of multiple textures the amount of which must be divisible by 6 (one image for each face of the cube). The primary benefit of [CubemapArray]s is that they can be accessed in shader code using a single texture reference. In other words, you can pass multiple [Cubemap]s into a shader using a single [CubemapArray]. + Generally, [CubemapArray]s provide a more efficent way for storing multiple [Cubemap]s, than storing multiple [Cubemap]s themselves in an array. + Internally Godot, uses [CubemapArray]s for many effects including the [Sky], if you set [member ProjectSettings.rendering/reflections/sky_reflections/texture_array_reflections] to [code]true[/code]. + [b]Note:[/b] [CubemapArray] is not supported in the OpenGL 3 rendering backend. </description> <tutorials> </tutorials> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index 695f2cbc66..34a639d2de 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -86,7 +86,7 @@ This is useful to simulate [url=https://en.wikipedia.org/wiki/Aerial_perspective]aerial perspective[/url] in large scenes with low density fog. However, it is not very useful for high-density fog, as the sky will shine through. When set to [code]1.0[/code], the fog color comes completely from the [Sky]. If set to [code]0.0[/code], aerial perspective is disabled. </member> <member name="fog_density" type="float" setter="set_fog_density" getter="get_fog_density" default="0.01"> - The exponential fog density to use. Higher values result in a more dense fog. + The [i]exponential[/i] fog density to use. Higher values result in a more dense fog. Fog rendering is exponential as in real life. </member> <member name="fog_enabled" type="bool" setter="set_fog_enabled" getter="is_fog_enabled" default="false"> If [code]true[/code], fog effects are enabled. @@ -292,13 +292,15 @@ The [Color] of the volumetric fog when interacting with lights. Mist and fog have an albedo close to [code]Color(1, 1, 1, 1)[/code] while smoke has a darker albedo. </member> <member name="volumetric_fog_ambient_inject" type="float" setter="set_volumetric_fog_ambient_inject" getter="get_volumetric_fog_ambient_inject" default="0.0"> - Scales the strength of ambient light used in the volumetric fog. A value of [code]0[/code] means that ambient light will not impact the volumetric fog. + Scales the strength of ambient light used in the volumetric fog. A value of [code]0.0[/code] means that ambient light will not impact the volumetric fog. [member volumetric_fog_ambient_inject] has a small performance cost when set above [code]0.0[/code]. + [b]Note:[/b] This has no visible effect if [member volumetric_fog_density] is [code]0.0[/code] or if [member volumetric_fog_albedo] is a fully black color. </member> <member name="volumetric_fog_anisotropy" type="float" setter="set_volumetric_fog_anisotropy" getter="get_volumetric_fog_anisotropy" default="0.2"> - The direction of scattered light as it goes through the volumetric fog. A value close [code]1[/code] means almost all light is scattered forward. A value close to [code]0[/code] means light is scattered equally in all directions. A value close to [code]-1[/code] means light is scattered mostly backward. Fog and mist scatter light slightly forward, while smoke scatters light equally in all directions. + The direction of scattered light as it goes through the volumetric fog. A value close to [code]1.0[/code] means almost all light is scattered forward. A value close to [code]0.0[/code] means light is scattered equally in all directions. A value close to [code]-1.0[/code] means light is scattered mostly backward. Fog and mist scatter light slightly forward, while smoke scatters light equally in all directions. </member> <member name="volumetric_fog_density" type="float" setter="set_volumetric_fog_density" getter="get_volumetric_fog_density" default="0.05"> - The base density of the volumetric fog. Set this to the lowest density you want to have globally. + The base [i]exponential[/i] density of the volumetric fog. Set this to the lowest density you want to have globally. [FogVolume]s can be used to add to or subtract from this density in specific areas. Fog rendering is exponential as in real life. + A value of [code]0.0[/code] disables global volumetric fog while allowing [FogVolume]s to display volumetric fog in specific areas. </member> <member name="volumetric_fog_detail_spread" type="float" setter="set_volumetric_fog_detail_spread" getter="get_volumetric_fog_detail_spread" default="2.0"> The distribution of size down the length of the froxel buffer. A higher value compresses the froxels closer to the camera and places more detail closer to the camera. @@ -313,20 +315,22 @@ Enables the volumetric fog effect. Volumetric fog uses a screen-aligned froxel buffer to calculate accurate volumetric scattering in the short to medium range. Volumetric fog interacts with [FogVolume]s and lights to calculate localized and global fog. Volumetric fog uses a PBR single-scattering model based on extinction, scattering, and emission which it exposes to users as density, albedo, and emission. </member> <member name="volumetric_fog_gi_inject" type="float" setter="set_volumetric_fog_gi_inject" getter="get_volumetric_fog_gi_inject" default="1.0"> - Scales the strength of Global Illumination used in the volumetric fog. A value of [code]0.0[/code] means that Global Illumination will not impact the volumetric fog. + Scales the strength of Global Illumination used in the volumetric fog's albedo color. A value of [code]0.0[/code] means that Global Illumination will not impact the volumetric fog. [member volumetric_fog_gi_inject] has a small performance cost when set above [code]0.0[/code]. + [b]Note:[/b] This has no visible effect if [member volumetric_fog_density] is [code]0.0[/code] or if [member volumetric_fog_albedo] is a fully black color. [b]Note:[/b] Only [VoxelGI] and SDFGI ([member Environment.sdfgi_enabled]) are taken into account when using [member volumetric_fog_gi_inject]. Global illumination from [LightmapGI], [ReflectionProbe] and SSIL (see [member ssil_enabled]) will be ignored by volumetric fog. </member> <member name="volumetric_fog_length" type="float" setter="set_volumetric_fog_length" getter="get_volumetric_fog_length" default="64.0"> - The distance over which the volumetric fog is computed. Increase to compute fog over a greater range, decrease to add more detail when a long range is not needed. For best quality fog, keep this as low as possible. + The distance over which the volumetric fog is computed. Increase to compute fog over a greater range, decrease to add more detail when a long range is not needed. For best quality fog, keep this as low as possible. See also [member ProjectSettings.rendering/environment/volumetric_fog/volume_depth]. </member> <member name="volumetric_fog_sky_affect" type="float" setter="set_volumetric_fog_sky_affect" getter="get_volumetric_fog_sky_affect" default="1.0"> The factor to use when affecting the sky with volumetric fog. [code]1.0[/code] means that volumetric fog can fully obscure the sky. Lower values reduce the impact of volumetric fog on sky rendering, with [code]0.0[/code] not affecting sky rendering at all. + [b]Note:[/b] [member volumetric_fog_sky_affect] also affects [FogVolume]s, even if [member volumetric_fog_density] is [code]0.0[/code]. If you notice [FogVolume]s are disappearing when looking towards the sky, set [member volumetric_fog_sky_affect] to [code]1.0[/code]. </member> <member name="volumetric_fog_temporal_reprojection_amount" type="float" setter="set_volumetric_fog_temporal_reprojection_amount" getter="get_volumetric_fog_temporal_reprojection_amount" default="0.9"> The amount by which to blend the last frame with the current frame. A higher number results in smoother volumetric fog, but makes "ghosting" much worse. A lower value reduces ghosting but can result in the per-frame temporal jitter becoming visible. </member> <member name="volumetric_fog_temporal_reprojection_enabled" type="bool" setter="set_volumetric_fog_temporal_reprojection_enabled" getter="is_volumetric_fog_temporal_reprojection_enabled" default="true"> - Enables temporal reprojection in the volumetric fog. Temporal reprojection blends the current frame's volumetric fog with the last frame's volumetric fog to smooth out jagged edges. The performance cost is minimal, however it does lead to moving [FogVolume]s and [Light3D]s "ghosting" and leaving a trail behind them. When temporal reprojection is enabled, try to avoid moving [FogVolume]s or [Light3D]s too fast. + Enables temporal reprojection in the volumetric fog. Temporal reprojection blends the current frame's volumetric fog with the last frame's volumetric fog to smooth out jagged edges. The performance cost is minimal; however, it leads to moving [FogVolume]s and [Light3D]s "ghosting" and leaving a trail behind them. When temporal reprojection is enabled, try to avoid moving [FogVolume]s or [Light3D]s too fast. Short-lived dynamic lighting effects should have [member Light3D.light_volumetric_fog_energy] set to [code]0.0[/code] to avoid ghosting. </member> </members> <constants> diff --git a/doc/classes/FogMaterial.xml b/doc/classes/FogMaterial.xml index e63d4ddf3e..7428d6169a 100644 --- a/doc/classes/FogMaterial.xml +++ b/doc/classes/FogMaterial.xml @@ -5,27 +5,28 @@ </brief_description> <description> A [Material] resource that can be used by [FogVolume]s to draw volumetric effects. + If you need more advanced effects, use a custom [url=$DOCS_URL/tutorials/shaders/shader_reference/fog_shader.html]fog shader[/url]. </description> <tutorials> </tutorials> <members> <member name="albedo" type="Color" setter="set_albedo" getter="get_albedo" default="Color(1, 1, 1, 1)"> - Sets the single-scattering [Color] of the [FogVolume]. Internally [member albedo] is converted into single-scattering which is additively blended with other [FogVolume]s and the [member Environment.volumetric_fog_albedo]. + The single-scattering [Color] of the [FogVolume]. Internally, [member albedo] is converted into single-scattering, which is additively blended with other [FogVolume]s and the [member Environment.volumetric_fog_albedo]. </member> <member name="density" type="float" setter="set_density" getter="get_density" default="1.0"> - Sets the density of the [FogVolume]. Denser objects are more opaque, but may suffer from under-sampling artifacts that look like stripes. + The density of the [FogVolume]. Denser objects are more opaque, but may suffer from under-sampling artifacts that look like stripes. Negative values can be used to subtract fog from other [FogVolume]s or global volumetric fog. </member> <member name="density_texture" type="Texture3D" setter="set_density_texture" getter="get_density_texture"> - Sets a 3D texture that is used to scale the [member density] of the [FogVolume]. + The 3D texture that is used to scale the [member density] of the [FogVolume]. This can be used to vary fog density within the [FogVolume] with any kind of static pattern. For animated effects, consider using a custom [url=$DOCS_URL/tutorials/shaders/shader_reference/fog_shader.html]fog shader[/url]. </member> <member name="edge_fade" type="float" setter="set_edge_fade" getter="get_edge_fade" default="0.1"> - Sets the hardness of the edges of the [FogVolume]. A higher number will result in softer edges while a lower number will result in harder edges. + The hardness of the edges of the [FogVolume]. A higher value will result in softer edges, while a lower value will result in harder edges. </member> <member name="emission" type="Color" setter="set_emission" getter="get_emission" default="Color(0, 0, 0, 1)"> - Sets the [Color] of the light emitted by the [FogVolume]. Emitted light will not cast light or shadows on other objects, but can be useful for modulating the [Color] of the [FogVolume] independently from light sources. + The [Color] of the light emitted by the [FogVolume]. Emitted light will not cast light or shadows on other objects, but can be useful for modulating the [Color] of the [FogVolume] independently from light sources. </member> <member name="height_falloff" type="float" setter="set_height_falloff" getter="get_height_falloff" default="0.0"> - Sets the rate by which the height-based fog decreases in density as height increases in world space. A high falloff will result in a sharp transition, while a low falloff will result in a smoother transition. A value of [code]0[/code] results in uniform-density fog. The height threshold is determined by the height of the associated [FogVolume]. + The rate by which the height-based fog decreases in density as height increases in world space. A high falloff will result in a sharp transition, while a low falloff will result in a smoother transition. A value of [code]0.0[/code] results in uniform-density fog. The height threshold is determined by the height of the associated [FogVolume]. </member> </members> </class> diff --git a/doc/classes/FogVolume.xml b/doc/classes/FogVolume.xml index 3f2ee3035c..d9fa2e6ebb 100644 --- a/doc/classes/FogVolume.xml +++ b/doc/classes/FogVolume.xml @@ -4,22 +4,23 @@ A node used to add local fog with the volumetric fog effect. </brief_description> <description> - [FogVolume]s are used to add localized fog into the global volumetric fog effect. + [FogVolume]s are used to add localized fog into the global volumetric fog effect. [FogVolume]s can also remove volumetric fog from specific areas if using a [FogMaterial] with a negative [member FogMaterial.density]. Performance of [FogVolume]s is directly related to their relative size on the screen and the complexity of their attached [FogMaterial]. It is best to keep [FogVolume]s relatively small and simple where possible. + [b]Note:[/b] [FogVolume]s only have a visible effect if [member Environment.volumetric_fog_enabled] is [code]true[/code]. If you don't want fog to be globally visible (but only within [FogVolume] nodes), set [member Environment.volumetric_fog_density] to [code]0.0[/code]. </description> <tutorials> </tutorials> <members> <member name="extents" type="Vector3" setter="set_extents" getter="get_extents" default="Vector3(1, 1, 1)"> - Sets the size of the [FogVolume] when [member shape] is [constant RenderingServer.FOG_VOLUME_SHAPE_ELLIPSOID], [constant RenderingServer.FOG_VOLUME_SHAPE_CONE], [constant RenderingServer.FOG_VOLUME_SHAPE_CYLINDER] or [constant RenderingServer.FOG_VOLUME_SHAPE_BOX]. + The size of the [FogVolume] when [member shape] is [constant RenderingServer.FOG_VOLUME_SHAPE_ELLIPSOID], [constant RenderingServer.FOG_VOLUME_SHAPE_CONE], [constant RenderingServer.FOG_VOLUME_SHAPE_CYLINDER] or [constant RenderingServer.FOG_VOLUME_SHAPE_BOX]. [b]Note:[/b] Thin fog volumes may appear to flicker when the camera moves or rotates. This can be alleviated by increasing [member ProjectSettings.rendering/environment/volumetric_fog/volume_depth] (at a performance cost) or by decreasing [member Environment.volumetric_fog_length] (at no performance cost, but at the cost of lower fog range). Alternatively, the [FogVolume] can be made thicker and use a lower density in the [member material]. [b]Note:[/b] If [member shape] is [constant RenderingServer.FOG_VOLUME_SHAPE_CONE] or [constant RenderingServer.FOG_VOLUME_SHAPE_CYLINDER], the cone/cylinder will be adjusted to fit within the extents. Non-uniform scaling of cone/cylinder shapes via the [member extents] property is not supported, but you can scale the [FogVolume] node instead. </member> <member name="material" type="Material" setter="set_material" getter="get_material"> - Sets the [Material] to be used by the [FogVolume]. Can be either a [FogMaterial] or a custom [ShaderMaterial]. + The [Material] used by the [FogVolume]. Can be either a built-in [FogMaterial] or a custom [ShaderMaterial]. </member> <member name="shape" type="int" setter="set_shape" getter="get_shape" enum="RenderingServer.FogVolumeShape" default="3"> - Sets the shape of the [FogVolume] to either [constant RenderingServer.FOG_VOLUME_SHAPE_ELLIPSOID], [constant RenderingServer.FOG_VOLUME_SHAPE_CONE], [constant RenderingServer.FOG_VOLUME_SHAPE_CYLINDER], [constant RenderingServer.FOG_VOLUME_SHAPE_BOX] or [constant RenderingServer.FOG_VOLUME_SHAPE_WORLD]. + The shape of the [FogVolume]. This can be set to either [constant RenderingServer.FOG_VOLUME_SHAPE_ELLIPSOID], [constant RenderingServer.FOG_VOLUME_SHAPE_CONE], [constant RenderingServer.FOG_VOLUME_SHAPE_CYLINDER], [constant RenderingServer.FOG_VOLUME_SHAPE_BOX] or [constant RenderingServer.FOG_VOLUME_SHAPE_WORLD]. </member> </members> </class> diff --git a/doc/classes/ImageTextureLayered.xml b/doc/classes/ImageTextureLayered.xml index f5b338542b..f5786f070a 100644 --- a/doc/classes/ImageTextureLayered.xml +++ b/doc/classes/ImageTextureLayered.xml @@ -11,6 +11,8 @@ <return type="int" enum="Error" /> <param index="0" name="images" type="Image[]" /> <description> + Creates an [ImageTextureLayered] from an array of [Image]s. The first image decides the width, height, image format and mipmapping setting. The other images must have the same width, height, image format and mipmapping setting. + Each [Image] represents one [code]layer[/code]. </description> </method> <method name="update_layer"> @@ -18,6 +20,10 @@ <param index="0" name="image" type="Image" /> <param index="1" name="layer" type="int" /> <description> + Replaces the existing [Image] data at the given [code]layer[/code] with this new image. + The given [Image] must have the same width, height, image format and mipmapping setting (a [code]bool[/code] value) as the rest of the referenced images. + If the image format is unsupported, it will be decompressed and converted to a similar and supported [enum Image.Format]. + The update is immediate: synced with the draw. </description> </method> </methods> diff --git a/doc/classes/PanoramaSkyMaterial.xml b/doc/classes/PanoramaSkyMaterial.xml index 21c7f29585..0c27037f28 100644 --- a/doc/classes/PanoramaSkyMaterial.xml +++ b/doc/classes/PanoramaSkyMaterial.xml @@ -4,9 +4,9 @@ A [Material] used with [Sky] to draw a background texture. </brief_description> <description> - A resource referenced in a [Sky] that is used to draw a background. The Panorama sky material functions similar to skyboxes in other engines, except it uses an equirectangular sky map instead of a cube map. + A resource referenced in a [Sky] that is used to draw a background. The Panorama sky material functions similar to skyboxes in other engines, except it uses an equirectangular sky map instead of a cubemap. Using an HDR panorama is strongly recommended for accurate, high-quality reflections. Godot supports the Radiance HDR ([code].hdr[/code]) and OpenEXR ([code].exr[/code]) image formats for this purpose. - You can use [url=https://danilw.github.io/GLSL-howto/cubemap_to_panorama_js/cubemap_to_panorama.html]this tool[/url] to convert a cube map to an equirectangular sky map. + You can use [url=https://danilw.github.io/GLSL-howto/cubemap_to_panorama_js/cubemap_to_panorama.html]this tool[/url] to convert a cubemap to an equirectangular sky map. </description> <tutorials> </tutorials> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 1f07a13c94..ffd3dbaf80 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1782,7 +1782,7 @@ Enables filtering of the volumetric fog effect prior to integration. This substantially blurs the fog which reduces fine details but also smooths out harsh edges and aliasing artifacts. Disable when more detail is required. </member> <member name="rendering/environment/volumetric_fog/volume_depth" type="int" setter="" getter="" default="64"> - Number of slices to use along the depth of the froxel buffer for volumetric fog. A lower number will be more efficient but may result in artifacts appearing during camera movement. + Number of slices to use along the depth of the froxel buffer for volumetric fog. A lower number will be more efficient but may result in artifacts appearing during camera movement. See also [member Environment.volumetric_fog_length]. </member> <member name="rendering/environment/volumetric_fog/volume_size" type="int" setter="" getter="" default="64"> Base size used to determine size of froxel buffer in the camera X-axis and Y-axis. The final size is scaled by the aspect ratio of the screen, so actual values may differ from what is set. Set a larger size for more detailed fog, set a smaller size for better performance. diff --git a/doc/classes/Texture2DArray.xml b/doc/classes/Texture2DArray.xml index 6f059b5fbf..113f37f974 100644 --- a/doc/classes/Texture2DArray.xml +++ b/doc/classes/Texture2DArray.xml @@ -1,8 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="Texture2DArray" inherits="ImageTextureLayered" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + A single texture resource which consists of multiple, separate images. Each image has the same dimensions and number of mipmap levels. </brief_description> <description> + A Texture2DArray is different from a Texture3D: The Texture2DArray does not support trilinear interpolation between the [Image]s, i.e. no blending. + A Texture2DArray is also different from an [AtlasTexture]: In a Texture2DArray, all images are treated separately. In an atlas, the regions (i.e. the single images) can be of different sizes. Furthermore, you usually need to add a padding around the regions, to prevent accidental UV mapping to more than one region. The same goes for mipmapping: Mipmap chains are handled separately for each layer. In an atlas, the slicing has to be done manually in the fragment shader. </description> <tutorials> </tutorials> diff --git a/doc/classes/TextureLayered.xml b/doc/classes/TextureLayered.xml index 7b528e2082..5e6afcbc5c 100644 --- a/doc/classes/TextureLayered.xml +++ b/doc/classes/TextureLayered.xml @@ -1,10 +1,15 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="TextureLayered" inherits="Texture" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Base class for 3D texture types. + Base class for texture types which contain the data of multiple [Image]s. Each image is of the same size and format. </brief_description> <description> - Base class for [Texture2DArray], [Cubemap] and [CubemapArray]. Cannot be used directly, but contains all the functions necessary for accessing the derived resource types. Data is set on a per-layer basis. For [Texture2DArray]s, the layer specifies the array layer. + Base class for [Texture2DArray], [Cubemap] and [CubemapArray]. Cannot be used directly, but contains all the functions necessary for accessing the derived resource types. + Data is set on a per-layer basis. For [Texture2DArray]s, the layer specifies the array layer. + All images need to have the same width, height and number of mipmap levels. + A [TextureLayered] can be loaded with [code]method ResourceFormatLoader.load[/code]. + To create such a texture file yourself, re-import your image files using the Godot Editor import presets. + Internally, Godot maps these files to their respective counterparts in the target rendering driver (GLES3, Vulkan). </description> <tutorials> </tutorials> @@ -72,6 +77,7 @@ <method name="get_layers" qualifiers="const"> <return type="int" /> <description> + Returns the number of referenced [Image]s. </description> </method> <method name="get_width" qualifiers="const"> @@ -83,6 +89,7 @@ <method name="has_mipmaps" qualifiers="const"> <return type="bool" /> <description> + Returns [code]true[/code] if the layers have generated mipmaps. </description> </method> </methods> diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 8d4954136e..0ffede0992 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -198,7 +198,8 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ int item_count = 0; bool backbuffer_cleared = false; bool time_used = false; - bool material_screen_texture_found = false; + bool material_screen_texture_cached = false; + bool material_screen_texture_mipmaps_cached = false; Rect2 back_buffer_rect; bool backbuffer_copy = false; bool backbuffer_gen_mipmaps = false; @@ -223,10 +224,12 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ GLES3::CanvasMaterialData *md = static_cast<GLES3::CanvasMaterialData *>(material_storage->material_get_data(material, RS::SHADER_CANVAS_ITEM)); if (md && md->shader_data->valid) { if (md->shader_data->uses_screen_texture && canvas_group_owner == nullptr) { - if (!material_screen_texture_found) { + if (!material_screen_texture_cached) { backbuffer_copy = true; back_buffer_rect = Rect2(); backbuffer_gen_mipmaps = md->shader_data->uses_screen_texture_mipmaps; + } else if (!material_screen_texture_mipmaps_cached) { + backbuffer_gen_mipmaps = md->shader_data->uses_screen_texture_mipmaps; } } @@ -287,7 +290,16 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps); backbuffer_copy = false; - material_screen_texture_found = true; //after a backbuffer copy, screen texture makes no further copies + backbuffer_gen_mipmaps = false; + material_screen_texture_cached = true; // After a backbuffer copy, screen texture makes no further copies. + material_screen_texture_mipmaps_cached = backbuffer_gen_mipmaps; + } + + if (backbuffer_gen_mipmaps) { + texture_storage->render_target_gen_back_buffer_mipmaps(p_to_render_target, back_buffer_rect); + + backbuffer_gen_mipmaps = false; + material_screen_texture_mipmaps_cached = true; } // just add all items for now diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 70b5501692..c2e04e4be9 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -6050,10 +6050,9 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { real_t to_diff = fmod(b - a, Math_TAU); to_v = a + fmod(2.0 * to_diff, Math_TAU) - to_diff; } - Variant delta_v; - Variant::sub(to_v, from_v, delta_v); + Variant delta_v = Animation::subtract_variant(to_v, from_v); double duration = to_t - from_t; - double fixed_duration = duration - 0.01; // Prevent to overwrap keys... + double fixed_duration = duration - UNIT_EPSILON; // Prevent to overwrap keys... for (double delta_t = dur_step; delta_t < fixed_duration; delta_t += dur_step) { Pair<real_t, Variant> keydata; keydata.first = from_t + delta_t; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 177bc6d2b2..b89bd23859 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -690,7 +690,6 @@ void EditorFileSystem::scan() { _update_extensions(); - abort_scan = false; if (!use_threads) { scanning = true; scan_total = 0; @@ -1162,8 +1161,6 @@ void EditorFileSystem::scan_changes() { scanning_changes = true; scanning_changes_done = false; - abort_scan = false; - if (!use_threads) { if (filesystem) { EditorProgressBG pr("sources", TTR("ScanSources"), 1000); @@ -1195,8 +1192,6 @@ void EditorFileSystem::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { Thread &active_thread = thread.is_started() ? thread : thread_sources; if (use_threads && active_thread.is_started()) { - //abort thread if in progress - abort_scan = true; while (scanning) { OS::get_singleton()->delay_usec(1000); } diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index f4e69b95e7..e06c6e4593 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -168,7 +168,6 @@ class EditorFileSystem : public Node { EditorFileSystemDirectory *new_filesystem = nullptr; - bool abort_scan = false; bool scanning = false; bool importing = false; bool first_scan = true; diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 9e7314bf50..08c731cba5 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -3038,7 +3038,7 @@ void EditorInspector::update_tree() { bool movable = true; bool numbered = false; bool foldable = use_folding; - String add_button_text; + String add_button_text = TTR("Add Element"); String swap_method; for (int i = (p.type == Variant::NIL ? 1 : 2); i < class_name_components.size(); i++) { if (class_name_components[i].begins_with("page_size") && class_name_components[i].get_slice_count("=") == 2) { diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index 0236e134fb..66dea6cf1b 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -397,7 +397,10 @@ void DisplayServerX11::mouse_set_mode(MouseMode p_mode) { if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { //flush pending motion events _flush_mouse_motion(); - WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID; + WindowID window_id = _get_focused_window_or_popup(); + if (!windows.has(window_id)) { + window_id = MAIN_WINDOW_ID; + } WindowData &window = windows[window_id]; if (XGrabPointer( @@ -433,7 +436,11 @@ void DisplayServerX11::warp_mouse(const Point2i &p_position) { if (mouse_mode == MOUSE_MODE_CAPTURED) { last_mouse_pos = p_position; } else { - WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID; + WindowID window_id = _get_focused_window_or_popup(); + if (!windows.has(window_id)) { + window_id = MAIN_WINDOW_ID; + } + XWarpPointer(x11_display, None, windows[window_id].x11_window, 0, 0, 0, 0, (int)p_position.x, (int)p_position.y); } @@ -3181,6 +3188,15 @@ void DisplayServerX11::_window_changed(XEvent *event) { } } +DisplayServer::WindowID DisplayServerX11::_get_focused_window_or_popup() const { + const List<WindowID>::Element *E = popup_list.back(); + if (E) { + return E->get(); + } + + return last_focused_window; +} + void DisplayServerX11::_dispatch_input_events(const Ref<InputEvent> &p_event) { static_cast<DisplayServerX11 *>(get_singleton())->_dispatch_input_event(p_event); } @@ -3936,7 +3952,11 @@ void DisplayServerX11::process_events() { // The X11 API requires filtering one-by-one through the motion // notify events, in order to figure out which event is the one // generated by warping the mouse pointer. - WindowID focused_window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID; + WindowID focused_window_id = _get_focused_window_or_popup(); + if (!windows.has(focused_window_id)) { + focused_window_id = MAIN_WINDOW_ID; + } + while (true) { if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == windows[focused_window_id].size.width / 2 && event.xmotion.y == windows[focused_window_id].size.height / 2) { //this is likely the warp event since it was warped here diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h index ea03b2328c..a5fa7613bc 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/display_server_x11.h @@ -284,6 +284,8 @@ class DisplayServerX11 : public DisplayServer { Context context = CONTEXT_ENGINE; + WindowID _get_focused_window_or_popup() const; + void _send_window_event(const WindowData &wd, WindowEvent p_event); static void _dispatch_input_events(const Ref<InputEvent> &p_event); void _dispatch_input_event(const Ref<InputEvent> &p_event); diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index bb5dce641a..da377c9171 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -189,6 +189,8 @@ private: Point2i _get_native_screen_position(int p_screen) const; static void _displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info); + WindowID _get_focused_window_or_popup() const; + static void _dispatch_input_events(const Ref<InputEvent> &p_event); void _dispatch_input_event(const Ref<InputEvent> &p_event); void _push_input(const Ref<InputEvent> &p_event); diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 91c6da5d13..05f89c70aa 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -317,6 +317,15 @@ void DisplayServerMacOS::_displays_arrangement_changed(CGDirectDisplayID display } } +DisplayServer::WindowID DisplayServerMacOS::_get_focused_window_or_popup() const { + const List<WindowID>::Element *E = popup_list.back(); + if (E) { + return E->get(); + } + + return last_focused_window; +} + void DisplayServerMacOS::_dispatch_input_events(const Ref<InputEvent> &p_event) { ((DisplayServerMacOS *)(get_singleton()))->_dispatch_input_event(p_event); } @@ -1828,7 +1837,10 @@ void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) { return; } - WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID; + WindowID window_id = _get_focused_window_or_popup(); + if (!windows.has(window_id)) { + window_id = MAIN_WINDOW_ID; + } WindowData &wd = windows[window_id]; if (p_mode == MOUSE_MODE_CAPTURED) { // Apple Docs state that the display parameter is not used. @@ -1943,7 +1955,10 @@ void DisplayServerMacOS::warp_mouse(const Point2i &p_position) { _THREAD_SAFE_METHOD_ if (mouse_mode != MOUSE_MODE_CAPTURED) { - WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID; + WindowID window_id = _get_focused_window_or_popup(); + if (!windows.has(window_id)) { + window_id = MAIN_WINDOW_ID; + } WindowData &wd = windows[window_id]; // Local point in window coords. @@ -3537,7 +3552,7 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM [apple_menu addItem:[NSMenuItem separatorItem]]; - title = [NSString stringWithFormat:NSLocalizedString(@"\t\tQuit %@", nil), nsappname]; + title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname]; [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; // Add items to the menu bar. diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 237215c198..4553f31480 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -104,7 +104,10 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) { // Mouse is grabbed (captured or confined). - WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID; + WindowID window_id = _get_focused_window_or_popup(); + if (!windows.has(window_id)) { + window_id = MAIN_WINDOW_ID; + } WindowData &wd = windows[window_id]; @@ -119,11 +122,15 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { ClientToScreen(wd.hWnd, &pos); SetCursorPos(pos.x, pos.y); SetCapture(wd.hWnd); + + _register_raw_input_devices(window_id); } } else { // Mouse is free to move around (not captured or confined). ReleaseCapture(); ClipCursor(nullptr); + + _register_raw_input_devices(INVALID_WINDOW_ID); } if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) { @@ -139,6 +146,37 @@ void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) { } } +DisplayServer::WindowID DisplayServerWindows::_get_focused_window_or_popup() const { + const List<WindowID>::Element *E = popup_list.back(); + if (E) { + return E->get(); + } + + return last_focused_window; +} + +void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window) { + use_raw_input = true; + + RAWINPUTDEVICE rid[1] = {}; + rid[0].usUsagePage = 0x01; + rid[0].usUsage = 0x02; + rid[0].dwFlags = 0; + + if (p_target_window != INVALID_WINDOW_ID && windows.has(p_target_window)) { + // Follow the defined window + rid[0].hwndTarget = windows[p_target_window].hWnd; + } else { + // Follow the keyboard focus + rid[0].hwndTarget = 0; + } + + if (RegisterRawInputDevices(rid, 1, sizeof(rid[0])) == FALSE) { + // Registration failed. + use_raw_input = false; + } +} + bool DisplayServerWindows::tts_is_speaking() const { ERR_FAIL_COND_V(!tts, false); return tts->is_speaking(); @@ -194,7 +232,9 @@ DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const { void DisplayServerWindows::warp_mouse(const Point2i &p_position) { _THREAD_SAFE_METHOD_ - if (!windows.has(last_focused_window)) { + WindowID window_id = _get_focused_window_or_popup(); + + if (!windows.has(window_id)) { return; // No focused window? } @@ -205,7 +245,7 @@ void DisplayServerWindows::warp_mouse(const Point2i &p_position) { POINT p; p.x = p_position.x; p.y = p_position.y; - ClientToScreen(windows[last_focused_window].hWnd, &p); + ClientToScreen(windows[window_id].hWnd, &p); SetCursorPos(p.x, p.y); } @@ -2483,7 +2523,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA old_y = coords.y; } - if (windows[window_id].window_has_focus && mm->get_relative() != Vector2()) { + if ((windows[window_id].window_has_focus || windows[window_id].is_popup) && mm->get_relative() != Vector2()) { Input::get_singleton()->parse_input_event(mm); } } @@ -3773,19 +3813,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win return; } - use_raw_input = true; - - RAWINPUTDEVICE Rid[1]; - - Rid[0].usUsagePage = 0x01; - Rid[0].usUsage = 0x02; - Rid[0].dwFlags = 0; - Rid[0].hwndTarget = 0; - - if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) { - // Registration failed. - use_raw_input = false; - } + _register_raw_input_devices(INVALID_WINDOW_ID); #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index fd64a02020..d85d6364bd 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -466,6 +466,8 @@ class DisplayServerWindows : public DisplayServer { void _update_real_mouse_position(WindowID p_window); void _set_mouse_mode_impl(MouseMode p_mode); + WindowID _get_focused_window_or_popup() const; + void _register_raw_input_devices(WindowID p_target_window); void _process_activate_event(WindowID p_window_id, WPARAM wParam, LPARAM lParam); void _process_key_events(); diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index 7fe464d2f4..afa4633848 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -63,9 +63,13 @@ Rect2 AnimatedSprite2D::_edit_get_rect() const { } bool AnimatedSprite2D::_edit_use_rect() const { - if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { return false; } + if (frame < 0 || frame >= frames->get_frame_count(animation)) { + return false; + } + Ref<Texture2D> t; if (animation) { t = frames->get_frame(animation, frame); @@ -79,7 +83,10 @@ Rect2 AnimatedSprite2D::get_anchorable_rect() const { } Rect2 AnimatedSprite2D::_get_rect() const { - if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { + return Rect2(); + } + if (frame < 0 || frame >= frames->get_frame_count(animation)) { return Rect2(); } @@ -161,29 +168,22 @@ void AnimatedSprite2D::_validate_property(PropertyInfo &p_property) const { void AnimatedSprite2D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_INTERNAL_PROCESS: { - if (frames.is_null()) { + if (frames.is_null() || !frames->has_animation(animation)) { return; } - if (!frames->has_animation(animation)) { - return; - } - if (frame < 0) { - return; + + double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); + if (speed == 0) { + return; // Do nothing. } + int last_frame = frames->get_frame_count(animation) - 1; double remaining = get_process_delta_time(); - while (remaining) { - double speed = frames->get_animation_speed(animation) * speed_scale; - if (speed == 0) { - return; // Do nothing. - } - if (timeout <= 0) { timeout = _get_frame_duration(); - int last_frame = frames->get_frame_count(animation) - 1; - if (!backwards) { + if (!playing_backwards) { // Forward. if (frame >= last_frame) { if (frames->get_animation_loop(animation)) { @@ -229,13 +229,7 @@ void AnimatedSprite2D::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { - if (frames.is_null()) { - return; - } - if (frame < 0) { - return; - } - if (!frames->has_animation(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { return; } @@ -327,9 +321,14 @@ int AnimatedSprite2D::get_frame() const { } void AnimatedSprite2D::set_speed_scale(double p_speed_scale) { + if (speed_scale == p_speed_scale) { + return; + } + double elapsed = _get_frame_duration() - timeout; - speed_scale = MAX(p_speed_scale, 0.0f); + speed_scale = p_speed_scale; + playing_backwards = signbit(speed_scale) != backwards; // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed. _reset_timeout(); @@ -398,12 +397,13 @@ bool AnimatedSprite2D::is_playing() const { return playing; } -void AnimatedSprite2D::play(const StringName &p_animation, const bool p_backwards) { +void AnimatedSprite2D::play(const StringName &p_animation, bool p_backwards) { backwards = p_backwards; + playing_backwards = signbit(speed_scale) != backwards; if (p_animation) { set_animation(p_animation); - if (frames.is_valid() && backwards && get_frame() == 0) { + if (frames.is_valid() && playing_backwards && get_frame() == 0) { set_frame(frames->get_frame_count(p_animation) - 1); } } @@ -418,7 +418,7 @@ void AnimatedSprite2D::stop() { double AnimatedSprite2D::_get_frame_duration() { if (frames.is_valid() && frames->has_animation(animation)) { - double speed = frames->get_animation_speed(animation) * speed_scale; + double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); if (speed > 0) { return 1.0 / speed; } diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h index 0a19e250d8..be1cc5353e 100644 --- a/scene/2d/animated_sprite_2d.h +++ b/scene/2d/animated_sprite_2d.h @@ -39,6 +39,7 @@ class AnimatedSprite2D : public Node2D { Ref<SpriteFrames> frames; bool playing = false; + bool playing_backwards = false; bool backwards = false; StringName animation = "default"; int frame = 0; @@ -81,7 +82,7 @@ public: void set_sprite_frames(const Ref<SpriteFrames> &p_frames); Ref<SpriteFrames> get_sprite_frames() const; - void play(const StringName &p_animation = StringName(), const bool p_backwards = false); + void play(const StringName &p_animation = StringName(), bool p_backwards = false); void stop(); void set_playing(bool p_playing); diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 7a89bf81bb..380172f396 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -447,7 +447,7 @@ void Sprite3D::_draw() { if (get_base() != get_mesh()) { set_base(get_mesh()); } - if (!texture.is_valid()) { + if (texture.is_null()) { set_base(RID()); return; } @@ -807,15 +807,7 @@ void AnimatedSprite3D::_draw() { set_base(get_mesh()); } - if (frames.is_null()) { - return; - } - - if (frame < 0) { - return; - } - - if (!frames->has_animation(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { return; } @@ -1050,29 +1042,22 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &p_property) const { void AnimatedSprite3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_INTERNAL_PROCESS: { - if (frames.is_null()) { - return; - } - if (!frames->has_animation(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { return; } - if (frame < 0) { - return; + + double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); + if (speed == 0) { + return; // Do nothing. } + int last_frame = frames->get_frame_count(animation) - 1; double remaining = get_process_delta_time(); - while (remaining) { - double speed = frames->get_animation_speed(animation) * speed_scale; - if (speed == 0) { - return; // Do nothing. - } - if (timeout <= 0) { timeout = _get_frame_duration(); - int last_frame = frames->get_frame_count(animation) - 1; - if (!backwards) { + if (!playing_backwards) { // Forward. if (frame >= last_frame) { if (frames->get_animation_loop(animation)) { @@ -1177,9 +1162,14 @@ int AnimatedSprite3D::get_frame() const { } void AnimatedSprite3D::set_speed_scale(double p_speed_scale) { + if (speed_scale == p_speed_scale) { + return; + } + double elapsed = _get_frame_duration() - timeout; - speed_scale = MAX(p_speed_scale, 0.0f); + speed_scale = p_speed_scale; + playing_backwards = signbit(speed_scale) != backwards; // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed. _reset_timeout(); @@ -1191,7 +1181,10 @@ double AnimatedSprite3D::get_speed_scale() const { } Rect2 AnimatedSprite3D::get_item_rect() const { - if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) { + if (frames.is_null() || !frames->has_animation(animation)) { + return Rect2(0, 0, 1, 1); + } + if (frame < 0 || frame >= frames->get_frame_count(animation)) { return Rect2(0, 0, 1, 1); } @@ -1236,12 +1229,13 @@ bool AnimatedSprite3D::is_playing() const { return playing; } -void AnimatedSprite3D::play(const StringName &p_animation, const bool p_backwards) { +void AnimatedSprite3D::play(const StringName &p_animation, bool p_backwards) { backwards = p_backwards; + playing_backwards = signbit(speed_scale) != backwards; if (p_animation) { set_animation(p_animation); - if (frames.is_valid() && backwards && get_frame() == 0) { + if (frames.is_valid() && playing_backwards && get_frame() == 0) { set_frame(frames->get_frame_count(p_animation) - 1); } } @@ -1256,7 +1250,7 @@ void AnimatedSprite3D::stop() { double AnimatedSprite3D::_get_frame_duration() { if (frames.is_valid() && frames->has_animation(animation)) { - double speed = frames->get_animation_speed(animation) * speed_scale; + double speed = frames->get_animation_speed(animation) * Math::abs(speed_scale); if (speed > 0) { return 1.0 / speed; } diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index e6a546a76d..f6ad1bbdb8 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -209,6 +209,7 @@ class AnimatedSprite3D : public SpriteBase3D { Ref<SpriteFrames> frames; bool playing = false; + bool playing_backwards = false; bool backwards = false; StringName animation = "default"; int frame = 0; @@ -237,7 +238,7 @@ public: void set_sprite_frames(const Ref<SpriteFrames> &p_frames); Ref<SpriteFrames> get_sprite_frames() const; - void play(const StringName &p_animation = StringName(), const bool p_backwards = false); + void play(const StringName &p_animation = StringName(), bool p_backwards = false); void stop(); void set_playing(bool p_playing); diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 72710cf802..ce9406883d 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -650,15 +650,14 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double double c = Math::ease(p_time / first_key_time, transition); Variant first_value = a->track_get_key_value(i, first_key); first_value = _post_process_key_value(a, i, first_value, nc->node); - Variant interp_value; - Variant::interpolate(pa->capture, first_value, c, interp_value); + Variant interp_value = Animation::interpolate_variant(pa->capture, first_value, c); if (pa->accum_pass != accum_pass) { ERR_CONTINUE(cache_update_prop_size >= NODE_CACHE_UPDATE_MAX); cache_update_prop[cache_update_prop_size++] = pa; pa->value_accum = interp_value; pa->accum_pass = accum_pass; } else { - Variant::interpolate(pa->value_accum, interp_value, p_interp, pa->value_accum); + pa->value_accum = Animation::interpolate_variant(pa->value_accum, interp_value, p_interp); } continue; //handled @@ -679,7 +678,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double pa->value_accum = value; pa->accum_pass = accum_pass; } else { - Variant::interpolate(pa->value_accum, value, p_interp, pa->value_accum); + pa->value_accum = Animation::interpolate_variant(pa->value_accum, value, p_interp); } } else if (p_is_current && p_delta != 0) { diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index c1e674dad5..776706d7f3 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -1383,8 +1383,13 @@ void AnimationTree::_process_graph(double p_delta) { } t->value = Math::fposmod(rot_a + (rot_b - rot_init) * (float)blend, (float)Math_TAU); } else { - Variant::sub(value, t->init_value, value); - Variant::blend(t->value, value, blend, t->value); + if (t->init_value.get_type() == Variant::BOOL) { + value = Animation::subtract_variant(value.operator real_t(), t->init_value.operator real_t()); + t->value = Animation::blend_variant(t->value.operator real_t(), value.operator real_t(), blend); + } else { + value = Animation::subtract_variant(value, t->init_value); + t->value = Animation::blend_variant(t->value, value, blend); + } } } else { if (blend < CMP_EPSILON) { @@ -1703,7 +1708,11 @@ void AnimationTree::_process_graph(double p_delta) { case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); - t->object->set_indexed(t->subpath, t->value); + if (t->init_value.get_type() == Variant::BOOL) { + t->object->set_indexed(t->subpath, t->value.operator real_t() >= 0.5); + } else { + t->object->set_indexed(t->subpath, t->value); + } } break; case Animation::TYPE_BEZIER: { diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 5b18d4e457..beadfbaeef 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -32,6 +32,7 @@ #include "scene/animation/easing_equations.h" #include "scene/main/node.h" +#include "scene/resources/animation.h" Tween::interpolater Tween::interpolaters[Tween::TRANS_MAX][Tween::EASE_MAX] = { { &linear::in, &linear::in, &linear::in, &linear::in }, // Linear is the same for each easing. @@ -375,264 +376,14 @@ Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, f ERR_FAIL_INDEX_V(p_trans, TransitionType::TRANS_MAX, Variant()); ERR_FAIL_INDEX_V(p_ease, EaseType::EASE_MAX, Variant()); -// Helper macro to run equation on sub-elements of the value (e.g. x and y of Vector2). -#define APPLY_EQUATION(element) \ - r.element = run_equation(p_trans, p_ease, p_time, i.element, d.element, p_duration); - - switch (p_initial_val.get_type()) { - case Variant::BOOL: { - return (run_equation(p_trans, p_ease, p_time, p_initial_val, p_delta_val, p_duration)) >= 0.5; - } - - case Variant::INT: { - return (int)run_equation(p_trans, p_ease, p_time, (int)p_initial_val, (int)p_delta_val, p_duration); - } - - case Variant::FLOAT: { - return run_equation(p_trans, p_ease, p_time, (real_t)p_initial_val, (real_t)p_delta_val, p_duration); - } - - case Variant::VECTOR2: { - Vector2 i = p_initial_val; - Vector2 d = p_delta_val; - Vector2 r; - - APPLY_EQUATION(x); - APPLY_EQUATION(y); - return r; - } - - case Variant::VECTOR2I: { - Vector2i i = p_initial_val; - Vector2i d = p_delta_val; - Vector2i r; - - APPLY_EQUATION(x); - APPLY_EQUATION(y); - return r; - } - - case Variant::RECT2: { - Rect2 i = p_initial_val; - Rect2 d = p_delta_val; - Rect2 r; - - APPLY_EQUATION(position.x); - APPLY_EQUATION(position.y); - APPLY_EQUATION(size.x); - APPLY_EQUATION(size.y); - return r; - } - - case Variant::RECT2I: { - Rect2i i = p_initial_val; - Rect2i d = p_delta_val; - Rect2i r; - - APPLY_EQUATION(position.x); - APPLY_EQUATION(position.y); - APPLY_EQUATION(size.x); - APPLY_EQUATION(size.y); - return r; - } - - case Variant::VECTOR3: { - Vector3 i = p_initial_val; - Vector3 d = p_delta_val; - Vector3 r; - - APPLY_EQUATION(x); - APPLY_EQUATION(y); - APPLY_EQUATION(z); - return r; - } - - case Variant::VECTOR3I: { - Vector3i i = p_initial_val; - Vector3i d = p_delta_val; - Vector3i r; - - APPLY_EQUATION(x); - APPLY_EQUATION(y); - APPLY_EQUATION(z); - return r; - } - - case Variant::TRANSFORM2D: { - Transform2D i = p_initial_val; - Transform2D d = p_delta_val; - Transform2D r; - - APPLY_EQUATION(columns[0][0]); - APPLY_EQUATION(columns[0][1]); - APPLY_EQUATION(columns[1][0]); - APPLY_EQUATION(columns[1][1]); - APPLY_EQUATION(columns[2][0]); - APPLY_EQUATION(columns[2][1]); - return r; - } - case Variant::VECTOR4: { - Vector4 i = p_initial_val; - Vector4 d = p_delta_val; - Vector4 r; - - APPLY_EQUATION(x); - APPLY_EQUATION(y); - APPLY_EQUATION(z); - APPLY_EQUATION(w); - return r; - } - - case Variant::QUATERNION: { - Quaternion i = p_initial_val; - Quaternion d = p_delta_val; - Quaternion r = i * d; - r = i.slerp(r, run_equation(p_trans, p_ease, p_time, 0.0, 1.0, p_duration)); - return r; - } - - case Variant::AABB: { - AABB i = p_initial_val; - AABB d = p_delta_val; - AABB r; - - APPLY_EQUATION(position.x); - APPLY_EQUATION(position.y); - APPLY_EQUATION(position.z); - APPLY_EQUATION(size.x); - APPLY_EQUATION(size.y); - APPLY_EQUATION(size.z); - return r; - } - - case Variant::BASIS: { - Basis i = p_initial_val; - Basis d = p_delta_val; - Basis r; - - APPLY_EQUATION(rows[0][0]); - APPLY_EQUATION(rows[0][1]); - APPLY_EQUATION(rows[0][2]); - APPLY_EQUATION(rows[1][0]); - APPLY_EQUATION(rows[1][1]); - APPLY_EQUATION(rows[1][2]); - APPLY_EQUATION(rows[2][0]); - APPLY_EQUATION(rows[2][1]); - APPLY_EQUATION(rows[2][2]); - return r; - } - - case Variant::TRANSFORM3D: { - Transform3D i = p_initial_val; - Transform3D d = p_delta_val; - Transform3D r; - - APPLY_EQUATION(basis.rows[0][0]); - APPLY_EQUATION(basis.rows[0][1]); - APPLY_EQUATION(basis.rows[0][2]); - APPLY_EQUATION(basis.rows[1][0]); - APPLY_EQUATION(basis.rows[1][1]); - APPLY_EQUATION(basis.rows[1][2]); - APPLY_EQUATION(basis.rows[2][0]); - APPLY_EQUATION(basis.rows[2][1]); - APPLY_EQUATION(basis.rows[2][2]); - APPLY_EQUATION(origin.x); - APPLY_EQUATION(origin.y); - APPLY_EQUATION(origin.z); - return r; - } - - case Variant::COLOR: { - Color i = p_initial_val; - Color d = p_delta_val; - Color r; - - APPLY_EQUATION(r); - APPLY_EQUATION(g); - APPLY_EQUATION(b); - APPLY_EQUATION(a); - return r; - } - - default: { - return p_initial_val; - } - }; -#undef APPLY_EQUATION -} - -Variant Tween::calculate_delta_value(Variant p_intial_val, Variant p_final_val) { - ERR_FAIL_COND_V_MSG(p_intial_val.get_type() != p_final_val.get_type(), p_intial_val, "Type mismatch between initial and final value: " + Variant::get_type_name(p_intial_val.get_type()) + " and " + Variant::get_type_name(p_final_val.get_type())); - - switch (p_intial_val.get_type()) { - case Variant::BOOL: { - return (int)p_final_val - (int)p_intial_val; - } - - case Variant::RECT2: { - Rect2 i = p_intial_val; - Rect2 f = p_final_val; - return Rect2(f.position - i.position, f.size - i.size); - } - - case Variant::RECT2I: { - Rect2i i = p_intial_val; - Rect2i f = p_final_val; - return Rect2i(f.position - i.position, f.size - i.size); - } - - case Variant::TRANSFORM2D: { - Transform2D i = p_intial_val; - Transform2D f = p_final_val; - return Transform2D(f.columns[0][0] - i.columns[0][0], - f.columns[0][1] - i.columns[0][1], - f.columns[1][0] - i.columns[1][0], - f.columns[1][1] - i.columns[1][1], - f.columns[2][0] - i.columns[2][0], - f.columns[2][1] - i.columns[2][1]); - } - - case Variant::AABB: { - AABB i = p_intial_val; - AABB f = p_final_val; - return AABB(f.position - i.position, f.size - i.size); - } - - case Variant::BASIS: { - Basis i = p_intial_val; - Basis f = p_final_val; - return Basis(f.rows[0][0] - i.rows[0][0], - f.rows[0][1] - i.rows[0][1], - f.rows[0][2] - i.rows[0][2], - f.rows[1][0] - i.rows[1][0], - f.rows[1][1] - i.rows[1][1], - f.rows[1][2] - i.rows[1][2], - f.rows[2][0] - i.rows[2][0], - f.rows[2][1] - i.rows[2][1], - f.rows[2][2] - i.rows[2][2]); - } - - case Variant::TRANSFORM3D: { - Transform3D i = p_intial_val; - Transform3D f = p_final_val; - return Transform3D(f.basis.rows[0][0] - i.basis.rows[0][0], - f.basis.rows[0][1] - i.basis.rows[0][1], - f.basis.rows[0][2] - i.basis.rows[0][2], - f.basis.rows[1][0] - i.basis.rows[1][0], - f.basis.rows[1][1] - i.basis.rows[1][1], - f.basis.rows[1][2] - i.basis.rows[1][2], - f.basis.rows[2][0] - i.basis.rows[2][0], - f.basis.rows[2][1] - i.basis.rows[2][1], - f.basis.rows[2][2] - i.basis.rows[2][2], - f.origin.x - i.origin.x, - f.origin.y - i.origin.y, - f.origin.z - i.origin.z); - } + // Special case for bool. + if (p_initial_val.get_type() == Variant::BOOL) { + return run_equation(p_trans, p_ease, p_time, p_initial_val, p_delta_val, p_duration) >= 0.5; + } - default: { - return Variant::evaluate(Variant::OP_SUBTRACT, p_final_val, p_intial_val); - } - }; + Variant ret = Animation::add_variant(p_initial_val, p_delta_val); + ret = Animation::interpolate_variant(p_initial_val, ret, run_equation(p_trans, p_ease, p_time, 0.0, 1.0, p_duration)); + return ret; } void Tween::_bind_methods() { @@ -748,10 +499,10 @@ void PropertyTweener::start() { } if (relative) { - final_val = Variant::evaluate(Variant::Operator::OP_ADD, initial_val, base_final_val); + final_val = Animation::add_variant(initial_val, base_final_val); } - delta_val = tween->calculate_delta_value(initial_val, final_val); + delta_val = Animation::subtract_variant(final_val, initial_val); } bool PropertyTweener::step(float &r_delta) { @@ -973,7 +724,7 @@ void MethodTweener::_bind_methods() { MethodTweener::MethodTweener(Callable p_callback, Variant p_from, Variant p_to, float p_duration) { callback = p_callback; initial_val = p_from; - delta_val = tween->calculate_delta_value(p_from, p_to); + delta_val = Animation::subtract_variant(p_to, p_from); final_val = p_to; duration = p_duration; } diff --git a/scene/animation/tween.h b/scene/animation/tween.h index b57ec2e5e7..da7a8b5d71 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -164,7 +164,6 @@ public: static real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d); static Variant interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease); - Variant calculate_delta_value(Variant p_intial_val, Variant p_final_val); bool step(float p_delta); bool can_process(bool p_tree_paused) const; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 5295de5c09..879d494909 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -795,11 +795,20 @@ void Viewport::_set_size(const Size2i &p_size, const Size2i &p_size_2d_override, stretch_transform = p_stretch_transform; to_screen_rect = p_to_screen_rect; - if (p_allocated) { - RS::get_singleton()->viewport_set_size(viewport, size.width, size.height); - } else { - RS::get_singleton()->viewport_set_size(viewport, 0, 0); - } +#ifndef _3D_DISABLED + if (!use_xr) { +#endif + + if (p_allocated) { + RS::get_singleton()->viewport_set_size(viewport, size.width, size.height); + } else { + RS::get_singleton()->viewport_set_size(viewport, 0, 0); + } + +#ifndef _3D_DISABLED + } // if (!use_xr) +#endif + _update_global_transform(); update_configuration_warnings(); @@ -813,6 +822,19 @@ void Viewport::_set_size(const Size2i &p_size, const Size2i &p_size_2d_override, } Size2i Viewport::_get_size() const { +#ifndef _3D_DISABLED + if (use_xr) { + if (XRServer::get_singleton() != nullptr) { + Ref<XRInterface> xr_interface = XRServer::get_singleton()->get_primary_interface(); + if (xr_interface.is_valid() && xr_interface->is_initialized()) { + Size2 xr_size = xr_interface->get_render_target_size(); + return (Size2i)xr_size; + } + } + return Size2i(); + } +#endif // _3D_DISABLED + return size; } @@ -3612,9 +3634,20 @@ void Viewport::_propagate_exit_world_3d(Node *p_node) { } void Viewport::set_use_xr(bool p_use_xr) { - use_xr = p_use_xr; + if (use_xr != p_use_xr) { + use_xr = p_use_xr; - RS::get_singleton()->viewport_set_use_xr(viewport, use_xr); + RS::get_singleton()->viewport_set_use_xr(viewport, use_xr); + + if (!use_xr) { + // Set viewport to previous size when exiting XR. + if (size_allocated) { + RS::get_singleton()->viewport_set_size(viewport, size.width, size.height); + } else { + RS::get_singleton()->viewport_set_size(viewport, 0, 0); + } + } + } } bool Viewport::is_using_xr() { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index afea3ea56c..c7c474c70f 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -568,7 +568,7 @@ public: bool is_input_disabled() const; Vector2 get_mouse_position() const; - virtual void warp_mouse(const Vector2 &p_position); + void warp_mouse(const Vector2 &p_position); void set_physics_object_picking(bool p_enable); bool get_physics_object_picking(); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 04f56bb874..ebe9587b31 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -986,18 +986,6 @@ DisplayServer::WindowID Window::get_window_id() const { return window_id; } -void Window::warp_mouse(const Vector2 &p_position) { - Transform2D xform = get_screen_transform(); - Vector2 gpos = xform.xform(p_position); - - if (transient_parent && !transient_parent->is_embedding_subwindows()) { - Transform2D window_trans = Transform2D().translated(get_position() + (transient_parent->get_visible_rect().size - transient_parent->get_real_size())); - gpos = window_trans.xform(gpos); - } - - Input::get_singleton()->warp_mouse(gpos); -} - void Window::set_wrap_controls(bool p_enable) { wrap_controls = p_enable; if (wrap_controls) { @@ -1153,7 +1141,7 @@ void Window::popup_centered_clamped(const Size2i &p_size, float p_fallback_ratio Rect2 parent_rect; if (is_embedded()) { - parent_rect = get_parent_viewport()->get_visible_rect(); + parent_rect = _get_embedder()->get_visible_rect(); } else { DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id(); int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id); @@ -1179,7 +1167,7 @@ void Window::popup_centered(const Size2i &p_minsize) { Rect2 parent_rect; if (is_embedded()) { - parent_rect = get_parent_viewport()->get_visible_rect(); + parent_rect = _get_embedder()->get_visible_rect(); } else { DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id(); int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id); @@ -1207,7 +1195,7 @@ void Window::popup_centered_ratio(float p_ratio) { Rect2 parent_rect; if (is_embedded()) { - parent_rect = get_parent_viewport()->get_visible_rect(); + parent_rect = _get_embedder()->get_visible_rect(); } else { DisplayServer::WindowID parent_id = get_parent_visible_window()->get_window_id(); int parent_screen = DisplayServer::get_singleton()->window_get_current_screen(parent_id); diff --git a/scene/main/window.h b/scene/main/window.h index 8113117103..8c6ca65436 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -254,8 +254,6 @@ public: void set_use_font_oversampling(bool p_oversampling); bool is_using_font_oversampling() const; - void warp_mouse(const Vector2 &p_position) override; - void set_wrap_controls(bool p_enable); bool is_wrapping_controls() const; void child_controls_changed(); diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 9d5bc18c96..4f16f75389 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -2317,9 +2317,7 @@ Quaternion Animation::_interpolate(const Quaternion &p_a, const Quaternion &p_b, } Variant Animation::_interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const { - Variant dst; - Variant::interpolate(p_a, p_b, p_c, dst); - return dst; + return interpolate_variant(p_a, p_b, p_c); } real_t Animation::_interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const { @@ -5563,6 +5561,466 @@ bool Animation::_fetch_compressed_by_index(uint32_t p_compressed_track, int p_in return false; } +// Helper math fuctions for Variant. +Variant Animation::add_variant(const Variant &a, const Variant &b) { + if (a.get_type() != b.get_type()) { + return a; + } + + switch (a.get_type()) { + case Variant::NIL: { + return Variant(); + } + case Variant::BOOL: { + return (a.operator real_t()) + (b.operator real_t()); // It is cast for interpolation. + } + case Variant::RECT2: { + const Rect2 ra = a.operator Rect2(); + const Rect2 rb = b.operator Rect2(); + return Rect2(ra.position + rb.position, ra.size + rb.size); + } + case Variant::RECT2I: { + const Rect2i ra = a.operator Rect2i(); + const Rect2i rb = b.operator Rect2i(); + return Rect2i(ra.position + rb.position, ra.size + rb.size); + } + case Variant::PLANE: { + const Plane pa = a.operator Plane(); + const Plane pb = b.operator Plane(); + return Plane(pa.normal + pb.normal, pa.d + pb.d); + } + case Variant::AABB: { + const ::AABB aa = a.operator ::AABB(); + const ::AABB ab = b.operator ::AABB(); + return ::AABB(aa.position + ab.position, aa.size + ab.size); + } + case Variant::QUATERNION: { + return (a.operator Quaternion()) * (b.operator Quaternion()); + } + case Variant::TRANSFORM2D: { + return (a.operator Transform2D()) * (b.operator Transform2D()); + } + case Variant::TRANSFORM3D: { + return (a.operator Transform3D()) * (b.operator Transform3D()); + } + default: { + return Variant::evaluate(Variant::OP_ADD, a, b); + } + } +} + +Variant Animation::subtract_variant(const Variant &a, const Variant &b) { + if (a.get_type() != b.get_type()) { + return a; + } + + switch (a.get_type()) { + case Variant::NIL: { + return Variant(); + } + case Variant::BOOL: { + return (a.operator real_t()) - (b.operator real_t()); // It is cast for interpolation. + } + case Variant::RECT2: { + const Rect2 ra = a.operator Rect2(); + const Rect2 rb = b.operator Rect2(); + return Rect2(ra.position - rb.position, ra.size - rb.size); + } + case Variant::RECT2I: { + const Rect2i ra = a.operator Rect2i(); + const Rect2i rb = b.operator Rect2i(); + return Rect2i(ra.position - rb.position, ra.size - rb.size); + } + case Variant::PLANE: { + const Plane pa = a.operator Plane(); + const Plane pb = b.operator Plane(); + return Plane(pa.normal - pb.normal, pa.d - pb.d); + } + case Variant::AABB: { + const ::AABB aa = a.operator ::AABB(); + const ::AABB ab = b.operator ::AABB(); + return ::AABB(aa.position - ab.position, aa.size - ab.size); + } + case Variant::QUATERNION: { + return (b.operator Quaternion()).inverse() * (a.operator Quaternion()); + } + case Variant::TRANSFORM2D: { + return (b.operator Transform2D()).inverse() * (a.operator Transform2D()); + } + case Variant::TRANSFORM3D: { + return (b.operator Transform3D()).inverse() * (a.operator Transform3D()); + } + default: { + return Variant::evaluate(Variant::OP_SUBTRACT, a, b); + } + } +} + +Variant Animation::blend_variant(const Variant &a, const Variant &b, float c) { + if (a.get_type() != b.get_type()) { + if (a.is_num() && b.is_num()) { + real_t va = a; + real_t vb = b; + return va + vb * c; + } + return a; + } + + switch (a.get_type()) { + case Variant::NIL: { + return Variant(); + } + case Variant::INT: { + return int((a.operator int64_t()) + (b.operator int64_t()) * c + 0.5); + } + case Variant::FLOAT: { + return (a.operator double()) + (b.operator double()) * c; + } + case Variant::VECTOR2: { + return (a.operator Vector2()) + (b.operator Vector2()) * c; + } + case Variant::VECTOR2I: { + const Vector2i va = a.operator Vector2i(); + const Vector2i vb = b.operator Vector2i(); + return Vector2i(int32_t(va.x + vb.x * c + 0.5), int32_t(va.y + vb.y * c + 0.5)); + } + case Variant::RECT2: { + const Rect2 ra = a.operator Rect2(); + const Rect2 rb = b.operator Rect2(); + return Rect2(ra.position + rb.position * c, ra.size + rb.size * c); + } + case Variant::RECT2I: { + const Rect2i ra = a.operator Rect2i(); + const Rect2i rb = b.operator Rect2i(); + return Rect2i(int32_t(ra.position.x + rb.position.x * c + 0.5), int32_t(ra.position.y + rb.position.y * c + 0.5), int32_t(ra.size.x + rb.size.x * c + 0.5), int32_t(ra.size.y + rb.size.y * c + 0.5)); + } + case Variant::VECTOR3: { + return (a.operator Vector3()) + (b.operator Vector3()) * c; + } + case Variant::VECTOR3I: { + const Vector3i va = a.operator Vector3i(); + const Vector3i vb = b.operator Vector3i(); + return Vector3i(int32_t(va.x + vb.x * c + 0.5), int32_t(va.y + vb.y * c + 0.5), int32_t(va.z + vb.z * c + 0.5)); + } + case Variant::VECTOR4: { + return (a.operator Vector4()) + (b.operator Vector4()) * c; + } + case Variant::VECTOR4I: { + const Vector4i va = a.operator Vector4i(); + const Vector4i vb = b.operator Vector4i(); + return Vector4i(int32_t(va.x + vb.x * c + 0.5), int32_t(va.y + vb.y * c + 0.5), int32_t(va.z + vb.z * c + 0.5), int32_t(va.w + vb.w * c + 0.5)); + } + case Variant::PLANE: { + const Plane pa = a.operator Plane(); + const Plane pb = b.operator Plane(); + return Plane(pa.normal + pb.normal * c, pa.d + pb.d * c); + } + case Variant::COLOR: { + return (a.operator Color()) + (b.operator Color()) * c; + } + case Variant::AABB: { + const ::AABB aa = a.operator ::AABB(); + const ::AABB ab = b.operator ::AABB(); + return ::AABB(aa.position + ab.position * c, aa.size + ab.size * c); + } + case Variant::BASIS: { + return (a.operator Basis()) + (b.operator Basis()) * c; + } + case Variant::QUATERNION: { + return (a.operator Quaternion()) * Quaternion().slerp((b.operator Quaternion()), c); + } + case Variant::TRANSFORM2D: { + return (a.operator Transform2D()) * Transform2D().interpolate_with((b.operator Transform2D()), c); + } + case Variant::TRANSFORM3D: { + return (a.operator Transform3D()) * Transform3D().interpolate_with((b.operator Transform3D()), c); + } + default: { + return c < 0.5 ? a : b; + } + } +} + +Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float c) { + if (a.get_type() != b.get_type()) { + if (a.is_num() && b.is_num()) { + real_t va = a; + real_t vb = b; + return va + (vb - va) * c; + } + return a; + } + + switch (a.get_type()) { + case Variant::NIL: { + return Variant(); + } + case Variant::INT: { + const int64_t va = a.operator int64_t(); + return int(va + ((b.operator int64_t()) - va) * c); + } + case Variant::FLOAT: { + const real_t va = a.operator real_t(); + return va + ((b.operator real_t()) - va) * c; + } + case Variant::VECTOR2: { + return (a.operator Vector2()).lerp(b.operator Vector2(), c); + } + case Variant::VECTOR2I: { + const Vector2i va = a.operator Vector2i(); + const Vector2i vb = b.operator Vector2i(); + return Vector2i(int32_t(va.x + (vb.x - va.x) * c), int32_t(va.y + (vb.y - va.y) * c)); + } + case Variant::RECT2: { + const Rect2 ra = a.operator Rect2(); + const Rect2 rb = b.operator Rect2(); + return Rect2(ra.position.lerp(rb.position, c), ra.size.lerp(rb.size, c)); + } + case Variant::RECT2I: { + const Rect2i ra = a.operator Rect2i(); + const Rect2i rb = b.operator Rect2i(); + return Rect2i(int32_t(ra.position.x + (rb.position.x - ra.position.x) * c), int32_t(ra.position.y + (rb.position.y - ra.position.y) * c), int32_t(ra.size.x + (rb.size.x - ra.size.x) * c), int32_t(ra.size.y + (rb.size.y - ra.size.y) * c)); + } + case Variant::VECTOR3: { + return (a.operator Vector3()).lerp(b.operator Vector3(), c); + } + case Variant::VECTOR3I: { + const Vector3i va = a.operator Vector3i(); + const Vector3i vb = b.operator Vector3i(); + return Vector3i(int32_t(va.x + (vb.x - va.x) * c), int32_t(va.y + (vb.y - va.y) * c), int32_t(va.z + (vb.z - va.z) * c)); + } + case Variant::VECTOR4: { + return (a.operator Vector4()).lerp(b.operator Vector4(), c); + } + case Variant::VECTOR4I: { + const Vector4i va = a.operator Vector4i(); + const Vector4i vb = b.operator Vector4i(); + return Vector4i(int32_t(va.x + (vb.x - va.x) * c), int32_t(va.y + (vb.y - va.y) * c), int32_t(va.z + (vb.z - va.z) * c), int32_t(va.w + (vb.w - va.w) * c)); + } + case Variant::PLANE: { + const Plane pa = a.operator Plane(); + const Plane pb = b.operator Plane(); + return Plane(pa.normal.lerp(pb.normal, c), pa.d + (pb.d - pa.d) * c); + } + case Variant::COLOR: { + return (a.operator Color()).lerp(b.operator Color(), c); + } + case Variant::AABB: { + const ::AABB aa = a.operator ::AABB(); + const ::AABB ab = b.operator ::AABB(); + return ::AABB(aa.position.lerp(ab.position, c), aa.size.lerp(ab.size, c)); + } + case Variant::BASIS: { + return (a.operator Basis()).lerp(b.operator Basis(), c); + } + case Variant::QUATERNION: { + return (a.operator Quaternion()).slerp(b.operator Quaternion(), c); + } + case Variant::TRANSFORM2D: { + return (a.operator Transform2D()).interpolate_with(b.operator Transform2D(), c); + } + case Variant::TRANSFORM3D: { + return (a.operator Transform3D()).interpolate_with(b.operator Transform3D(), c); + } + case Variant::STRING: { + // This is pretty funny and bizarre, but artists like to use it for typewriter effects. + const String sa = a.operator String(); + const String sb = b.operator String(); + String dst; + int sa_len = sa.length(); + int sb_len = sb.length(); + int csize = sa_len + (sb_len - sa_len) * c; + if (csize == 0) { + return ""; + } + dst.resize(csize + 1); + dst[csize] = 0; + int split = csize / 2; + + for (int i = 0; i < csize; i++) { + char32_t chr = ' '; + + if (i < split) { + if (i < sa.length()) { + chr = sa[i]; + } else if (i < sb.length()) { + chr = sb[i]; + } + + } else { + if (i < sb.length()) { + chr = sb[i]; + } else if (i < sa.length()) { + chr = sa[i]; + } + } + + dst[i] = chr; + } + + return dst; + } + case Variant::PACKED_INT32_ARRAY: { + const Vector<int32_t> *arr_a = Object::cast_to<Vector<int32_t>>(a); + const Vector<int32_t> *arr_b = Object::cast_to<Vector<int32_t>>(b); + int32_t sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<int32_t> v; + v.resize(sz); + { + int32_t *vw = v.ptrw(); + const int32_t *ar = arr_a->ptr(); + const int32_t *br = arr_b->ptr(); + + Variant va; + for (int32_t i = 0; i < sz; i++) { + va = interpolate_variant(ar[i], br[i], c); + vw[i] = va; + } + } + return v; + } + } + case Variant::PACKED_INT64_ARRAY: { + const Vector<int64_t> *arr_a = Object::cast_to<Vector<int64_t>>(a); + const Vector<int64_t> *arr_b = Object::cast_to<Vector<int64_t>>(b); + int64_t sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<int64_t> v; + v.resize(sz); + { + int64_t *vw = v.ptrw(); + const int64_t *ar = arr_a->ptr(); + const int64_t *br = arr_b->ptr(); + + Variant va; + for (int64_t i = 0; i < sz; i++) { + va = interpolate_variant(ar[i], br[i], c); + vw[i] = va; + } + } + return v; + } + } + case Variant::PACKED_FLOAT32_ARRAY: { + const Vector<float> *arr_a = Object::cast_to<Vector<float>>(a); + const Vector<float> *arr_b = Object::cast_to<Vector<float>>(b); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<float> v; + v.resize(sz); + { + float *vw = v.ptrw(); + const float *ar = arr_a->ptr(); + const float *br = arr_b->ptr(); + + Variant va; + for (int i = 0; i < sz; i++) { + va = interpolate_variant(ar[i], br[i], c); + vw[i] = va; + } + } + return v; + } + } + case Variant::PACKED_FLOAT64_ARRAY: { + const Vector<double> *arr_a = Object::cast_to<Vector<double>>(a); + const Vector<double> *arr_b = Object::cast_to<Vector<double>>(b); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<double> v; + v.resize(sz); + { + double *vw = v.ptrw(); + const double *ar = arr_a->ptr(); + const double *br = arr_b->ptr(); + + Variant va; + for (int i = 0; i < sz; i++) { + va = interpolate_variant(ar[i], br[i], c); + vw[i] = va; + } + } + return v; + } + } + case Variant::PACKED_VECTOR2_ARRAY: { + const Vector<Vector2> *arr_a = Object::cast_to<Vector<Vector2>>(a); + const Vector<Vector2> *arr_b = Object::cast_to<Vector<Vector2>>(b); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<Vector2> v; + v.resize(sz); + { + Vector2 *vw = v.ptrw(); + const Vector2 *ar = arr_a->ptr(); + const Vector2 *br = arr_b->ptr(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].lerp(br[i], c); + } + } + return v; + } + } + case Variant::PACKED_VECTOR3_ARRAY: { + const Vector<Vector3> *arr_a = Object::cast_to<Vector<Vector3>>(a); + const Vector<Vector3> *arr_b = Object::cast_to<Vector<Vector3>>(b); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<Vector3> v; + v.resize(sz); + { + Vector3 *vw = v.ptrw(); + const Vector3 *ar = arr_a->ptr(); + const Vector3 *br = arr_b->ptr(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].lerp(br[i], c); + } + } + return v; + } + } + case Variant::PACKED_COLOR_ARRAY: { + const Vector<Color> *arr_a = Object::cast_to<Vector<Color>>(a); + const Vector<Color> *arr_b = Object::cast_to<Vector<Color>>(b); + int sz = arr_a->size(); + if (sz == 0 || arr_b->size() != sz) { + return a; + } else { + Vector<Color> v; + v.resize(sz); + { + Color *vw = v.ptrw(); + const Color *ar = arr_a->ptr(); + const Color *br = arr_b->ptr(); + + for (int i = 0; i < sz; i++) { + vw[i] = ar[i].lerp(br[i], c); + } + } + return v; + } + } + default: { + return c < 0.5 ? a : b; + } + } +} + Animation::Animation() {} Animation::~Animation() { diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 46a88df130..112a6c28aa 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -496,6 +496,12 @@ public: void optimize(real_t p_allowed_velocity_err = 0.01, real_t p_allowed_angular_err = 0.01, int p_precision = 3); void compress(uint32_t p_page_size = 8192, uint32_t p_fps = 120, float p_split_tolerance = 4.0); // 4.0 seems to be the split tolerance sweet spot from many tests + // Helper math fuctions for Variant. + static Variant add_variant(const Variant &a, const Variant &b); + static Variant subtract_variant(const Variant &a, const Variant &b); + static Variant blend_variant(const Variant &a, const Variant &b, float c); + static Variant interpolate_variant(const Variant &a, const Variant &b, float c); + Animation(); ~Animation(); }; diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index aa9772a483..c5818d9290 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -189,7 +189,7 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item * RenderingServerDefault::redraw_request(); } - if (ci->commands != nullptr) { + if (ci->commands != nullptr || ci->copy_back_buffer) { ci->final_transform = xform; ci->final_modulate = modulate * ci->self_modulate; ci->global_rect_cache = global_rect; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index ab3e3ebe51..0210151420 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1362,9 +1362,12 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p default_repeat = p_default_repeat; } - //fill the list until rendering is possible. - bool material_screen_texture_found = false; Item *ci = p_item_list; + + //fill the list until rendering is possible. + bool material_screen_texture_cached = false; + bool material_screen_texture_mipmaps_cached = false; + Rect2 back_buffer_rect; bool backbuffer_copy = false; bool backbuffer_gen_mipmaps = false; @@ -1393,10 +1396,12 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p CanvasMaterialData *md = static_cast<CanvasMaterialData *>(material_storage->material_get_data(material, RendererRD::MaterialStorage::SHADER_TYPE_2D)); if (md && md->shader_data->valid) { if (md->shader_data->uses_screen_texture && canvas_group_owner == nullptr) { - if (!material_screen_texture_found) { + if (!material_screen_texture_cached) { backbuffer_copy = true; back_buffer_rect = Rect2(); backbuffer_gen_mipmaps = md->shader_data->uses_screen_texture_mipmaps; + } else if (!material_screen_texture_mipmaps_cached) { + backbuffer_gen_mipmaps = md->shader_data->uses_screen_texture_mipmaps; } } @@ -1486,7 +1491,15 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p backbuffer_copy = false; backbuffer_gen_mipmaps = false; - material_screen_texture_found = true; //after a backbuffer copy, screen texture makes no further copies + material_screen_texture_cached = true; // After a backbuffer copy, screen texture makes no further copies. + material_screen_texture_mipmaps_cached = backbuffer_gen_mipmaps; + } + + if (backbuffer_gen_mipmaps) { + texture_storage->render_target_gen_back_buffer_mipmaps(p_to_render_target, back_buffer_rect); + + backbuffer_gen_mipmaps = false; + material_screen_texture_mipmaps_cached = true; } items[item_count++] = ci; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index eacd9bbbc2..54d07dd3e1 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -179,7 +179,7 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) { // to compensate for the loss of sharpness. const float texture_mipmap_bias = log2f(MIN(scaling_3d_scale, 1.0)) + p_viewport->texture_mipmap_bias; - p_viewport->render_buffers->configure(p_viewport->render_target, Size2i(render_width, render_height), Size2(width, height), p_viewport->fsr_sharpness, texture_mipmap_bias, p_viewport->msaa_3d, p_viewport->screen_space_aa, p_viewport->use_taa, p_viewport->use_debanding, p_viewport->get_view_count()); + p_viewport->render_buffers->configure(p_viewport->render_target, Size2i(render_width, render_height), Size2(width, height), p_viewport->fsr_sharpness, texture_mipmap_bias, p_viewport->msaa_3d, p_viewport->screen_space_aa, p_viewport->use_taa, p_viewport->use_debanding, p_viewport->view_count); } } } @@ -616,14 +616,7 @@ void RendererViewport::draw_viewports() { if (xr_interface.is_valid()) { // Override our size, make sure it matches our required size and is created as a stereo target Size2 xr_size = xr_interface->get_render_target_size(); - - // Would have been nice if we could call viewport_set_size here, - // but alas that takes our RID and we now have our pointer, - // also we only check if view_count changes in render_target_set_size so we need to call that for this to reliably change - vp->occlusion_buffer_dirty = vp->occlusion_buffer_dirty || (vp->size != xr_size); - vp->size = xr_size; - uint32_t view_count = xr_interface->get_view_count(); - RSG::texture_storage->render_target_set_size(vp->render_target, vp->size.x, vp->size.y, view_count); + _viewport_set_size(vp, xr_size.width, xr_size.height, xr_interface->get_view_count()); // Inform xr interface we're about to render its viewport, if this returns false we don't render visible = xr_interface->pre_draw_viewport(vp->render_target); @@ -686,12 +679,17 @@ void RendererViewport::draw_viewports() { // commit our eyes Vector<BlitToScreen> blits = xr_interface->post_draw_viewport(vp->render_target, vp->viewport_to_screen_rect); if (vp->viewport_to_screen != DisplayServer::INVALID_WINDOW_ID && blits.size() > 0) { - if (!blit_to_screen_list.has(vp->viewport_to_screen)) { - blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>(); - } + if (OS::get_singleton()->get_current_rendering_driver_name() == "opengl3") { + RSG::rasterizer->blit_render_targets_to_screen(vp->viewport_to_screen, blits.ptr(), blits.size()); + RSG::rasterizer->end_frame(true); + } else { + if (!blit_to_screen_list.has(vp->viewport_to_screen)) { + blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>(); + } - for (int b = 0; b < blits.size(); b++) { - blit_to_screen_list[vp->viewport_to_screen].push_back(blits[b]); + for (int b = 0; b < blits.size(); b++) { + blit_to_screen_list[vp->viewport_to_screen].push_back(blits[b]); + } } } } else { @@ -777,7 +775,13 @@ void RendererViewport::viewport_set_use_xr(RID p_viewport, bool p_use_xr) { } viewport->use_xr = p_use_xr; - _configure_3d_render_buffers(viewport); + + // Re-configure the 3D render buffers when disabling XR. They'll get + // re-configured when enabling XR in draw_viewports(). + if (!p_use_xr) { + viewport->view_count = 1; + _configure_3d_render_buffers(viewport); + } } void RendererViewport::viewport_set_scaling_3d_mode(RID p_viewport, RS::ViewportScaling3DMode p_mode) { @@ -823,34 +827,27 @@ void RendererViewport::viewport_set_scaling_3d_scale(RID p_viewport, float p_sca _configure_3d_render_buffers(viewport); } -uint32_t RendererViewport::Viewport::get_view_count() { - uint32_t view_count = 1; - - if (use_xr && XRServer::get_singleton() != nullptr) { - Ref<XRInterface> xr_interface; - - xr_interface = XRServer::get_singleton()->get_primary_interface(); - if (xr_interface.is_valid()) { - view_count = xr_interface->get_view_count(); - } - } - - return view_count; -} - void RendererViewport::viewport_set_size(RID p_viewport, int p_width, int p_height) { ERR_FAIL_COND(p_width < 0 && p_height < 0); Viewport *viewport = viewport_owner.get_or_null(p_viewport); ERR_FAIL_COND(!viewport); + ERR_FAIL_COND_MSG(viewport->use_xr, "Cannot set viewport size when using XR"); - viewport->size = Size2(p_width, p_height); + _viewport_set_size(viewport, p_width, p_height, 1); +} - uint32_t view_count = viewport->get_view_count(); - RSG::texture_storage->render_target_set_size(viewport->render_target, p_width, p_height, view_count); - _configure_3d_render_buffers(viewport); +void RendererViewport::_viewport_set_size(Viewport *p_viewport, int p_width, int p_height, uint32_t p_view_count) { + Size2i new_size(p_width, p_height); + if (p_viewport->size != new_size || p_viewport->view_count != p_view_count) { + p_viewport->size = new_size; + p_viewport->view_count = p_view_count; - viewport->occlusion_buffer_dirty = true; + RSG::texture_storage->render_target_set_size(p_viewport->render_target, p_width, p_height, p_view_count); + _configure_3d_render_buffers(p_viewport); + + p_viewport->occlusion_buffer_dirty = true; + } } void RendererViewport::viewport_set_active(RID p_viewport, bool p_active) { @@ -890,7 +887,7 @@ void RendererViewport::viewport_attach_to_screen(RID p_viewport, const Rect2 &p_ // If using OpenGL we can optimize this operation by rendering directly to system_fbo // instead of rendering to fbo and copying to system_fbo after if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) { - RSG::texture_storage->render_target_set_size(viewport->render_target, p_rect.size.x, p_rect.size.y, viewport->get_view_count()); + RSG::texture_storage->render_target_set_size(viewport->render_target, p_rect.size.x, p_rect.size.y, viewport->view_count); RSG::texture_storage->render_target_set_position(viewport->render_target, p_rect.position.x, p_rect.position.y); } @@ -900,7 +897,7 @@ void RendererViewport::viewport_attach_to_screen(RID p_viewport, const Rect2 &p_ // if render_direct_to_screen was used, reset size and position if (RSG::rasterizer->is_low_end() && viewport->viewport_render_direct_to_screen) { RSG::texture_storage->render_target_set_position(viewport->render_target, 0, 0); - RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->get_view_count()); + RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->view_count); } viewport->viewport_to_screen_rect = Rect2(); @@ -919,7 +916,7 @@ void RendererViewport::viewport_set_render_direct_to_screen(RID p_viewport, bool // if disabled, reset render_target size and position if (!p_enable) { RSG::texture_storage->render_target_set_position(viewport->render_target, 0, 0); - RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->get_view_count()); + RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->size.x, viewport->size.y, viewport->view_count); } RSG::texture_storage->render_target_set_direct_to_screen(viewport->render_target, p_enable); @@ -927,7 +924,7 @@ void RendererViewport::viewport_set_render_direct_to_screen(RID p_viewport, bool // if attached to screen already, setup screen size and position, this needs to happen after setting flag to avoid an unnecessary buffer allocation if (RSG::rasterizer->is_low_end() && viewport->viewport_to_screen_rect != Rect2() && p_enable) { - RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->viewport_to_screen_rect.size.x, viewport->viewport_to_screen_rect.size.y, viewport->get_view_count()); + RSG::texture_storage->render_target_set_size(viewport->render_target, viewport->viewport_to_screen_rect.size.x, viewport->viewport_to_screen_rect.size.y, viewport->view_count); RSG::texture_storage->render_target_set_position(viewport->render_target, viewport->viewport_to_screen_rect.position.x, viewport->viewport_to_screen_rect.position.y); } } diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index a123c70372..08ba6abc74 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -54,6 +54,7 @@ public: Size2i internal_size; Size2i size; + uint32_t view_count; RID camera; RID scenario; @@ -150,6 +151,7 @@ public: RendererScene::RenderInfo render_info; Viewport() { + view_count = 1; update_mode = RS::VIEWPORT_UPDATE_WHEN_VISIBLE; clear_mode = RS::VIEWPORT_CLEAR_ALWAYS; transparent_bg = false; @@ -176,8 +178,6 @@ public: time_gpu_begin = 0; time_gpu_end = 0; } - - uint32_t get_view_count(); }; HashMap<String, RID> timestamp_vp_map; @@ -196,6 +196,7 @@ public: private: Vector<Viewport *> _sort_active_viewports(); + void _viewport_set_size(Viewport *p_viewport, int p_width, int p_height, uint32_t p_view_count); void _configure_3d_render_buffers(Viewport *p_viewport); void _draw_3d(Viewport *p_viewport); void _draw_viewport(Viewport *p_viewport); |