diff options
Diffstat (limited to 'scene/resources')
-rw-r--r-- | scene/resources/animation.cpp | 489 | ||||
-rw-r--r-- | scene/resources/animation.h | 13 | ||||
-rw-r--r-- | scene/resources/animation_library.cpp | 13 | ||||
-rw-r--r-- | scene/resources/animation_library.h | 2 | ||||
-rw-r--r-- | scene/resources/curve.cpp | 331 | ||||
-rw-r--r-- | scene/resources/curve.h | 15 | ||||
-rw-r--r-- | scene/resources/font.cpp | 161 | ||||
-rw-r--r-- | scene/resources/font.h | 27 | ||||
-rw-r--r-- | scene/resources/material.cpp | 24 | ||||
-rw-r--r-- | scene/resources/mesh.cpp | 100 | ||||
-rw-r--r-- | scene/resources/packed_scene.cpp | 2 | ||||
-rw-r--r-- | scene/resources/resource_format_text.cpp | 2 | ||||
-rw-r--r-- | scene/resources/shader.cpp | 2 | ||||
-rw-r--r-- | scene/resources/shader_include.cpp | 2 | ||||
-rw-r--r-- | scene/resources/shape_2d.cpp | 5 | ||||
-rw-r--r-- | scene/resources/shape_3d.cpp | 5 | ||||
-rw-r--r-- | scene/resources/style_box.cpp | 12 | ||||
-rw-r--r-- | scene/resources/texture.cpp | 126 | ||||
-rw-r--r-- | scene/resources/visual_shader.cpp | 2 |
19 files changed, 795 insertions, 538 deletions
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index ed9a709382..077a53464e 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -2709,7 +2709,7 @@ void Animation::value_track_set_update_mode(int p_track, UpdateMode p_mode) { ERR_FAIL_INDEX(p_track, tracks.size()); Track *t = tracks[p_track]; ERR_FAIL_COND(t->type != TYPE_VALUE); - ERR_FAIL_INDEX((int)p_mode, 4); + ERR_FAIL_INDEX((int)p_mode, 3); ValueTrack *vt = static_cast<ValueTrack *>(t); vt->update_mode = p_mode; @@ -2726,40 +2726,60 @@ Animation::UpdateMode Animation::value_track_get_update_mode(int p_track) const } template <class T> -void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const { - if (to_time == length) { - to_time = length + CMP_EPSILON; //include a little more if at the end +void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices, bool p_is_backward) const { + int len = p_array.size(); + if (len == 0) { + return; } - int to = _find(p_array, to_time); - - // can't really send the events == time, will be sent in the next frame. - // if event>=len then it will probably never be requested by the anim player. - - if (to >= 0 && p_array[to].time >= to_time) { - to--; - } + int from = 0; + int to = len - 1; - if (to < 0) { - return; // not bother + if (!p_is_backward) { + while (p_array[from].time < from_time || Math::is_equal_approx(p_array[from].time, from_time)) { + from++; + if (to < from) { + return; + } + } + while (p_array[to].time > to_time && !Math::is_equal_approx(p_array[to].time, to_time)) { + to--; + if (to < from) { + return; + } + } + } else { + while (p_array[from].time < from_time && !Math::is_equal_approx(p_array[from].time, from_time)) { + from++; + if (to < from) { + return; + } + } + while (p_array[to].time > to_time || Math::is_equal_approx(p_array[to].time, to_time)) { + to--; + if (to < from) { + return; + } + } } - int from = _find(p_array, from_time); - - // position in the right first event.+ - if (from < 0 || p_array[from].time < from_time) { - from++; + if (from == to) { + p_indices->push_back(from); + return; } - int max = p_array.size(); - - for (int i = from; i <= to; i++) { - ERR_CONTINUE(i < 0 || i >= max); // shouldn't happen - p_indices->push_back(i); + if (!p_is_backward) { + for (int i = from; i <= to; i++) { + p_indices->push_back(i); + } + } else { + for (int i = to; i >= to; i--) { + p_indices->push_back(i); + } } } -void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const { +void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, Animation::LoopedFlag p_looped_flag) const { ERR_FAIL_INDEX(p_track, tracks.size()); if (p_delta == 0) { @@ -2771,7 +2791,9 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl double from_time = p_time - p_delta; double to_time = p_time; + bool is_backward = false; if (from_time > to_time) { + is_backward = true; SWAP(from_time, to_time); } @@ -2800,7 +2822,10 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl } if (from_time > to_time) { - // handle loop by splitting + // Handle loop by splitting. + double anim_end = length + CMP_EPSILON; + double anim_start = -CMP_EPSILON; + switch (t->type) { case TYPE_POSITION_3D: { const PositionTrack *tt = static_cast<const PositionTrack *>(t); @@ -2808,8 +2833,13 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices); _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices); } else { - _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices); - _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices); + if (!is_backward) { + _track_get_key_indices_in_range(tt->positions, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(tt->positions, anim_start, to_time, p_indices, is_backward); + } else { + _track_get_key_indices_in_range(tt->positions, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(tt->positions, from_time, anim_end, p_indices, is_backward); + } } } break; case TYPE_ROTATION_3D: { @@ -2818,8 +2848,13 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices); _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices); } else { - _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices); - _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices); + if (!is_backward) { + _track_get_key_indices_in_range(rt->rotations, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(rt->rotations, anim_start, to_time, p_indices, is_backward); + } else { + _track_get_key_indices_in_range(rt->rotations, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(rt->rotations, from_time, anim_end, p_indices, is_backward); + } } } break; case TYPE_SCALE_3D: { @@ -2828,8 +2863,13 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices); _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices); } else { - _track_get_key_indices_in_range(st->scales, from_time, length, p_indices); - _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices); + if (!is_backward) { + _track_get_key_indices_in_range(st->scales, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(st->scales, anim_start, to_time, p_indices, is_backward); + } else { + _track_get_key_indices_in_range(st->scales, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(st->scales, from_time, anim_end, p_indices, is_backward); + } } } break; case TYPE_BLEND_SHAPE: { @@ -2838,38 +2878,83 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices); _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices); } else { - _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices); - _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices); + if (!is_backward) { + _track_get_key_indices_in_range(bst->blend_shapes, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(bst->blend_shapes, anim_start, to_time, p_indices, is_backward); + } else { + _track_get_key_indices_in_range(bst->blend_shapes, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(bst->blend_shapes, from_time, anim_end, p_indices, is_backward); + } } } break; case TYPE_VALUE: { const ValueTrack *vt = static_cast<const ValueTrack *>(t); - _track_get_key_indices_in_range(vt->values, from_time, length, p_indices); - _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices); + if (!is_backward) { + _track_get_key_indices_in_range(vt->values, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(vt->values, anim_start, to_time, p_indices, is_backward); + } else { + _track_get_key_indices_in_range(vt->values, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(vt->values, from_time, anim_end, p_indices, is_backward); + } } break; case TYPE_METHOD: { const MethodTrack *mt = static_cast<const MethodTrack *>(t); - _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices); - _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices); + if (!is_backward) { + _track_get_key_indices_in_range(mt->methods, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(mt->methods, anim_start, to_time, p_indices, is_backward); + } else { + _track_get_key_indices_in_range(mt->methods, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(mt->methods, from_time, anim_end, p_indices, is_backward); + } } break; case TYPE_BEZIER: { const BezierTrack *bz = static_cast<const BezierTrack *>(t); - _track_get_key_indices_in_range(bz->values, from_time, length, p_indices); - _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices); + if (!is_backward) { + _track_get_key_indices_in_range(bz->values, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(bz->values, anim_start, to_time, p_indices, is_backward); + } else { + _track_get_key_indices_in_range(bz->values, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(bz->values, from_time, anim_end, p_indices, is_backward); + } } break; case TYPE_AUDIO: { const AudioTrack *ad = static_cast<const AudioTrack *>(t); - _track_get_key_indices_in_range(ad->values, from_time, length, p_indices); - _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices); + if (!is_backward) { + _track_get_key_indices_in_range(ad->values, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(ad->values, anim_start, to_time, p_indices, is_backward); + } else { + _track_get_key_indices_in_range(ad->values, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(ad->values, from_time, anim_end, p_indices, is_backward); + } } break; case TYPE_ANIMATION: { const AnimationTrack *an = static_cast<const AnimationTrack *>(t); - _track_get_key_indices_in_range(an->values, from_time, length, p_indices); - _track_get_key_indices_in_range(an->values, 0, to_time, p_indices); + if (!is_backward) { + _track_get_key_indices_in_range(an->values, from_time, anim_end, p_indices, is_backward); + _track_get_key_indices_in_range(an->values, anim_start, to_time, p_indices, is_backward); + } else { + _track_get_key_indices_in_range(an->values, anim_start, to_time, p_indices, is_backward); + _track_get_key_indices_in_range(an->values, from_time, anim_end, p_indices, is_backward); + } } break; } return; } + + // Not from_time > to_time but most recent of looping... + if (p_looped_flag != Animation::LOOPED_FLAG_NONE) { + if (!is_backward && Math::is_equal_approx(from_time, 0)) { + int edge = track_find_key(p_track, 0, true); + if (edge >= 0) { + p_indices->push_back(edge); + } + } else if (is_backward && Math::is_equal_approx(to_time, length)) { + int edge = track_find_key(p_track, length, true); + if (edge >= 0) { + p_indices->push_back(edge); + } + } + } } break; case LOOP_PINGPONG: { if (from_time > length || from_time < 0) { @@ -2879,162 +2964,164 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl to_time = Math::pingpong(to_time, length); } - if ((int)Math::floor(abs(p_delta) / length) % 2 == 0) { - if (p_pingponged == -1) { - // handle loop by splitting - to_time = MAX(CMP_EPSILON, to_time); // To avoid overlapping keys at the turnaround point, one of the point will needs to be shifted slightly. - switch (t->type) { - case TYPE_POSITION_3D: { - const PositionTrack *tt = static_cast<const PositionTrack *>(t); - if (tt->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices); - _get_compressed_key_indices_in_range<3>(tt->compressed_track, CMP_EPSILON, to_time, p_indices); - } else { - _track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices); - _track_get_key_indices_in_range(tt->positions, CMP_EPSILON, to_time, p_indices); - } - } break; - case TYPE_ROTATION_3D: { - const RotationTrack *rt = static_cast<const RotationTrack *>(t); - if (rt->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices); - _get_compressed_key_indices_in_range<3>(rt->compressed_track, CMP_EPSILON, to_time, p_indices); - } else { - _track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices); - _track_get_key_indices_in_range(rt->rotations, CMP_EPSILON, to_time, p_indices); - } - } break; - case TYPE_SCALE_3D: { - const ScaleTrack *st = static_cast<const ScaleTrack *>(t); - if (st->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, from_time, p_indices); - _get_compressed_key_indices_in_range<3>(st->compressed_track, CMP_EPSILON, to_time, p_indices); - } else { - _track_get_key_indices_in_range(st->scales, 0, from_time, p_indices); - _track_get_key_indices_in_range(st->scales, CMP_EPSILON, to_time, p_indices); - } - } break; - case TYPE_BLEND_SHAPE: { - const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t); - if (bst->compressed_track >= 0) { - _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, from_time, p_indices); - _get_compressed_key_indices_in_range<1>(bst->compressed_track, CMP_EPSILON, to_time, p_indices); - } else { - _track_get_key_indices_in_range(bst->blend_shapes, 0, from_time, p_indices); - _track_get_key_indices_in_range(bst->blend_shapes, CMP_EPSILON, to_time, p_indices); - } - } break; - case TYPE_VALUE: { - const ValueTrack *vt = static_cast<const ValueTrack *>(t); - _track_get_key_indices_in_range(vt->values, 0, from_time, p_indices); - _track_get_key_indices_in_range(vt->values, CMP_EPSILON, to_time, p_indices); - } break; - case TYPE_METHOD: { - const MethodTrack *mt = static_cast<const MethodTrack *>(t); - _track_get_key_indices_in_range(mt->methods, 0, from_time, p_indices); - _track_get_key_indices_in_range(mt->methods, CMP_EPSILON, to_time, p_indices); - } break; - case TYPE_BEZIER: { - const BezierTrack *bz = static_cast<const BezierTrack *>(t); - _track_get_key_indices_in_range(bz->values, 0, from_time, p_indices); - _track_get_key_indices_in_range(bz->values, CMP_EPSILON, to_time, p_indices); - } break; - case TYPE_AUDIO: { - const AudioTrack *ad = static_cast<const AudioTrack *>(t); - _track_get_key_indices_in_range(ad->values, 0, from_time, p_indices); - _track_get_key_indices_in_range(ad->values, CMP_EPSILON, to_time, p_indices); - } break; - case TYPE_ANIMATION: { - const AnimationTrack *an = static_cast<const AnimationTrack *>(t); - _track_get_key_indices_in_range(an->values, 0, from_time, p_indices); - _track_get_key_indices_in_range(an->values, CMP_EPSILON, to_time, p_indices); - } break; - } - return; + if (p_looped_flag == Animation::LOOPED_FLAG_START) { + // Handle loop by splitting. + switch (t->type) { + case TYPE_POSITION_3D: { + const PositionTrack *tt = static_cast<const PositionTrack *>(t); + if (tt->compressed_track >= 0) { + _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices); + _get_compressed_key_indices_in_range<3>(tt->compressed_track, CMP_EPSILON, to_time, p_indices); + } else { + _track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices, true); + _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices, false); + } + } break; + case TYPE_ROTATION_3D: { + const RotationTrack *rt = static_cast<const RotationTrack *>(t); + if (rt->compressed_track >= 0) { + _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices); + _get_compressed_key_indices_in_range<3>(rt->compressed_track, CMP_EPSILON, to_time, p_indices); + } else { + _track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices, true); + _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices, false); + } + } break; + case TYPE_SCALE_3D: { + const ScaleTrack *st = static_cast<const ScaleTrack *>(t); + if (st->compressed_track >= 0) { + _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, from_time, p_indices); + _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices); + } else { + _track_get_key_indices_in_range(st->scales, 0, from_time, p_indices, true); + _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices, false); + } + } break; + case TYPE_BLEND_SHAPE: { + const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t); + if (bst->compressed_track >= 0) { + _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, from_time, p_indices); + _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices); + } else { + _track_get_key_indices_in_range(bst->blend_shapes, 0, from_time, p_indices, true); + _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices, false); + } + } break; + case TYPE_VALUE: { + const ValueTrack *vt = static_cast<const ValueTrack *>(t); + _track_get_key_indices_in_range(vt->values, 0, from_time, p_indices, true); + _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices, false); + } break; + case TYPE_METHOD: { + const MethodTrack *mt = static_cast<const MethodTrack *>(t); + _track_get_key_indices_in_range(mt->methods, 0, from_time, p_indices, true); + _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices, false); + } break; + case TYPE_BEZIER: { + const BezierTrack *bz = static_cast<const BezierTrack *>(t); + _track_get_key_indices_in_range(bz->values, 0, from_time, p_indices, true); + _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices, false); + } break; + case TYPE_AUDIO: { + const AudioTrack *ad = static_cast<const AudioTrack *>(t); + _track_get_key_indices_in_range(ad->values, 0, from_time, p_indices, true); + _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices, false); + } break; + case TYPE_ANIMATION: { + const AnimationTrack *an = static_cast<const AnimationTrack *>(t); + _track_get_key_indices_in_range(an->values, 0, from_time, p_indices, true); + _track_get_key_indices_in_range(an->values, 0, to_time, p_indices, false); + } break; } - if (p_pingponged == 1) { - // handle loop by splitting - to_time = MIN(length - CMP_EPSILON, to_time); - switch (t->type) { - case TYPE_POSITION_3D: { - const PositionTrack *tt = static_cast<const PositionTrack *>(t); - if (tt->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<3>(tt->compressed_track, to_time, length - CMP_EPSILON, p_indices); - } else { - _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices); - _track_get_key_indices_in_range(tt->positions, to_time, length - CMP_EPSILON, p_indices); - } - } break; - case TYPE_ROTATION_3D: { - const RotationTrack *rt = static_cast<const RotationTrack *>(t); - if (rt->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<3>(rt->compressed_track, to_time, length, p_indices); - } else { - _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices); - _track_get_key_indices_in_range(rt->rotations, to_time, length - CMP_EPSILON, p_indices); - } - } break; - case TYPE_SCALE_3D: { - const ScaleTrack *st = static_cast<const ScaleTrack *>(t); - if (st->compressed_track >= 0) { - _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<3>(st->compressed_track, to_time, length, p_indices); - } else { - _track_get_key_indices_in_range(st->scales, from_time, length, p_indices); - _track_get_key_indices_in_range(st->scales, to_time, length - CMP_EPSILON, p_indices); - } - } break; - case TYPE_BLEND_SHAPE: { - const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t); - if (bst->compressed_track >= 0) { - _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices); - _get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length - CMP_EPSILON, p_indices); - } else { - _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices); - _track_get_key_indices_in_range(bst->blend_shapes, to_time, length - CMP_EPSILON, p_indices); - } - } break; - case TYPE_VALUE: { - const ValueTrack *vt = static_cast<const ValueTrack *>(t); - _track_get_key_indices_in_range(vt->values, from_time, length, p_indices); - _track_get_key_indices_in_range(vt->values, to_time, length - CMP_EPSILON, p_indices); - } break; - case TYPE_METHOD: { - const MethodTrack *mt = static_cast<const MethodTrack *>(t); - _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices); - _track_get_key_indices_in_range(mt->methods, to_time, length - CMP_EPSILON, p_indices); - } break; - case TYPE_BEZIER: { - const BezierTrack *bz = static_cast<const BezierTrack *>(t); - _track_get_key_indices_in_range(bz->values, from_time, length, p_indices); - _track_get_key_indices_in_range(bz->values, to_time, length - CMP_EPSILON, p_indices); - } break; - case TYPE_AUDIO: { - const AudioTrack *ad = static_cast<const AudioTrack *>(t); - _track_get_key_indices_in_range(ad->values, from_time, length, p_indices); - _track_get_key_indices_in_range(ad->values, to_time, length - CMP_EPSILON, p_indices); - } break; - case TYPE_ANIMATION: { - const AnimationTrack *an = static_cast<const AnimationTrack *>(t); - _track_get_key_indices_in_range(an->values, from_time, length, p_indices); - _track_get_key_indices_in_range(an->values, to_time, length - CMP_EPSILON, p_indices); - } break; - } - return; + return; + } + if (p_looped_flag == Animation::LOOPED_FLAG_END) { + // Handle loop by splitting. + switch (t->type) { + case TYPE_POSITION_3D: { + const PositionTrack *tt = static_cast<const PositionTrack *>(t); + if (tt->compressed_track >= 0) { + _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices); + _get_compressed_key_indices_in_range<3>(tt->compressed_track, to_time, length, p_indices); + } else { + _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices, false); + _track_get_key_indices_in_range(tt->positions, to_time, length, p_indices, true); + } + } break; + case TYPE_ROTATION_3D: { + const RotationTrack *rt = static_cast<const RotationTrack *>(t); + if (rt->compressed_track >= 0) { + _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices); + _get_compressed_key_indices_in_range<3>(rt->compressed_track, to_time, length, p_indices); + } else { + _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices, false); + _track_get_key_indices_in_range(rt->rotations, to_time, length, p_indices, true); + } + } break; + case TYPE_SCALE_3D: { + const ScaleTrack *st = static_cast<const ScaleTrack *>(t); + if (st->compressed_track >= 0) { + _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices); + _get_compressed_key_indices_in_range<3>(st->compressed_track, to_time, length, p_indices); + } else { + _track_get_key_indices_in_range(st->scales, from_time, length, p_indices, false); + _track_get_key_indices_in_range(st->scales, to_time, length, p_indices, true); + } + } break; + case TYPE_BLEND_SHAPE: { + const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t); + if (bst->compressed_track >= 0) { + _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices); + _get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length - CMP_EPSILON, p_indices); + } else { + _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices, false); + _track_get_key_indices_in_range(bst->blend_shapes, to_time, length, p_indices, true); + } + } break; + case TYPE_VALUE: { + const ValueTrack *vt = static_cast<const ValueTrack *>(t); + _track_get_key_indices_in_range(vt->values, from_time, length, p_indices, false); + _track_get_key_indices_in_range(vt->values, to_time, length, p_indices, true); + } break; + case TYPE_METHOD: { + const MethodTrack *mt = static_cast<const MethodTrack *>(t); + _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices, false); + _track_get_key_indices_in_range(mt->methods, to_time, length, p_indices, true); + } break; + case TYPE_BEZIER: { + const BezierTrack *bz = static_cast<const BezierTrack *>(t); + _track_get_key_indices_in_range(bz->values, from_time, length, p_indices, false); + _track_get_key_indices_in_range(bz->values, to_time, length, p_indices, true); + } break; + case TYPE_AUDIO: { + const AudioTrack *ad = static_cast<const AudioTrack *>(t); + _track_get_key_indices_in_range(ad->values, from_time, length, p_indices, false); + _track_get_key_indices_in_range(ad->values, to_time, length, p_indices, true); + } break; + case TYPE_ANIMATION: { + const AnimationTrack *an = static_cast<const AnimationTrack *>(t); + _track_get_key_indices_in_range(an->values, from_time, length, p_indices, false); + _track_get_key_indices_in_range(an->values, to_time, length, p_indices, true); + } break; } + return; + } + + // The edge will be pingponged in the next frame and processed there, so let's ignore it now... + if (!is_backward && Math::is_equal_approx(to_time, length)) { + to_time = length - CMP_EPSILON; + } else if (is_backward && Math::is_equal_approx(from_time, 0)) { + from_time = CMP_EPSILON; } } break; } - switch (t->type) { case TYPE_POSITION_3D: { const PositionTrack *tt = static_cast<const PositionTrack *>(t); if (tt->compressed_track >= 0) { _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, to_time - from_time, p_indices); } else { - _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices); + _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices, is_backward); } } break; case TYPE_ROTATION_3D: { @@ -3042,7 +3129,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl if (rt->compressed_track >= 0) { _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, to_time - from_time, p_indices); } else { - _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices); + _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices, is_backward); } } break; case TYPE_SCALE_3D: { @@ -3050,7 +3137,7 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl if (st->compressed_track >= 0) { _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, to_time - from_time, p_indices); } else { - _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices); + _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices, is_backward); } } break; case TYPE_BLEND_SHAPE: { @@ -3058,28 +3145,28 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl if (bst->compressed_track >= 0) { _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, to_time - from_time, p_indices); } else { - _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices); + _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices, is_backward); } } break; case TYPE_VALUE: { const ValueTrack *vt = static_cast<const ValueTrack *>(t); - _track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices); + _track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices, is_backward); } break; case TYPE_METHOD: { const MethodTrack *mt = static_cast<const MethodTrack *>(t); - _track_get_key_indices_in_range(mt->methods, from_time, to_time, p_indices); + _track_get_key_indices_in_range(mt->methods, from_time, to_time, p_indices, is_backward); } break; case TYPE_BEZIER: { const BezierTrack *bz = static_cast<const BezierTrack *>(t); - _track_get_key_indices_in_range(bz->values, from_time, to_time, p_indices); + _track_get_key_indices_in_range(bz->values, from_time, to_time, p_indices, is_backward); } break; case TYPE_AUDIO: { const AudioTrack *ad = static_cast<const AudioTrack *>(t); - _track_get_key_indices_in_range(ad->values, from_time, to_time, p_indices); + _track_get_key_indices_in_range(ad->values, from_time, to_time, p_indices, is_backward); } break; case TYPE_ANIMATION: { const AnimationTrack *an = static_cast<const AnimationTrack *>(t); - _track_get_key_indices_in_range(an->values, from_time, to_time, p_indices); + _track_get_key_indices_in_range(an->values, from_time, to_time, p_indices, is_backward); } break; } } @@ -3809,12 +3896,15 @@ void Animation::_bind_methods() { BIND_ENUM_CONSTANT(UPDATE_CONTINUOUS); BIND_ENUM_CONSTANT(UPDATE_DISCRETE); - BIND_ENUM_CONSTANT(UPDATE_TRIGGER); BIND_ENUM_CONSTANT(UPDATE_CAPTURE); BIND_ENUM_CONSTANT(LOOP_NONE); BIND_ENUM_CONSTANT(LOOP_LINEAR); BIND_ENUM_CONSTANT(LOOP_PINGPONG); + + BIND_ENUM_CONSTANT(LOOPED_FLAG_NONE); + BIND_ENUM_CONSTANT(LOOPED_FLAG_END); + BIND_ENUM_CONSTANT(LOOPED_FLAG_START); } void Animation::clear() { @@ -5798,7 +5888,8 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } -Animation::Animation() {} +Animation::Animation() { +} Animation::~Animation() { for (int i = 0; i < tracks.size(); i++) { diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 6c1ca3cd05..0ac1279063 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -64,7 +64,6 @@ public: enum UpdateMode { UPDATE_CONTINUOUS, UPDATE_DISCRETE, - UPDATE_TRIGGER, UPDATE_CAPTURE, }; @@ -74,6 +73,12 @@ public: LOOP_PINGPONG, }; + enum LoopedFlag { + LOOPED_FLAG_NONE, + LOOPED_FLAG_END, + LOOPED_FLAG_START, + }; + #ifdef TOOLS_ENABLED enum HandleMode { HANDLE_MODE_FREE, @@ -250,12 +255,11 @@ private: _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward = false) const; template <class T> - _FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const; + _FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices, bool p_is_backward) const; double length = 1.0; real_t step = 0.1; LoopMode loop_mode = LOOP_NONE; - int pingponged = 0; /* Animation compression page format (version 1): * @@ -454,7 +458,7 @@ public: void copy_track(int p_track, Ref<Animation> p_to_animation); - void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const; + void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE) const; void set_length(real_t p_length); real_t get_length() const; @@ -484,6 +488,7 @@ VARIANT_ENUM_CAST(Animation::TrackType); VARIANT_ENUM_CAST(Animation::InterpolationType); VARIANT_ENUM_CAST(Animation::UpdateMode); VARIANT_ENUM_CAST(Animation::LoopMode); +VARIANT_ENUM_CAST(Animation::LoopedFlag); #ifdef TOOLS_ENABLED VARIANT_ENUM_CAST(Animation::HandleMode); VARIANT_ENUM_CAST(Animation::HandleSetMode); diff --git a/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp index 427d418551..b37bfbae62 100644 --- a/scene/resources/animation_library.cpp +++ b/scene/resources/animation_library.cpp @@ -52,11 +52,13 @@ Error AnimationLibrary::add_animation(const StringName &p_name, const Ref<Animat ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER); if (animations.has(p_name)) { + animations.get(p_name)->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed)); animations.erase(p_name); emit_signal(SNAME("animation_removed"), p_name); } animations.insert(p_name, p_animation); + animations.get(p_name)->connect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed).bind(p_name)); emit_signal(SNAME("animation_added"), p_name); notify_property_list_changed(); return OK; @@ -65,6 +67,7 @@ Error AnimationLibrary::add_animation(const StringName &p_name, const Ref<Animat void AnimationLibrary::remove_animation(const StringName &p_name) { ERR_FAIL_COND_MSG(!animations.has(p_name), vformat("Animation not found: %s.", p_name)); + animations.get(p_name)->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed)); animations.erase(p_name); emit_signal(SNAME("animation_removed"), p_name); notify_property_list_changed(); @@ -75,6 +78,8 @@ void AnimationLibrary::rename_animation(const StringName &p_name, const StringNa ERR_FAIL_COND_MSG(!is_valid_animation_name(p_new_name), "Invalid animation name: '" + String(p_new_name) + "'."); ERR_FAIL_COND_MSG(animations.has(p_new_name), vformat("Animation name \"%s\" already exists in library.", p_new_name)); + animations.get(p_name)->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed)); + animations.get(p_name)->connect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed).bind(p_new_name)); animations.insert(p_new_name, animations[p_name]); animations.erase(p_name); emit_signal(SNAME("animation_renamed"), p_name, p_new_name); @@ -100,6 +105,10 @@ TypedArray<StringName> AnimationLibrary::_get_animation_list() const { return ret; } +void AnimationLibrary::_animation_changed(const StringName &p_name) { + emit_signal(SNAME("animation_changed"), p_name); +} + void AnimationLibrary::get_animation_list(List<StringName> *p_animations) const { List<StringName> anims; @@ -115,6 +124,9 @@ void AnimationLibrary::get_animation_list(List<StringName> *p_animations) const } void AnimationLibrary::_set_data(const Dictionary &p_data) { + for (KeyValue<StringName, Ref<Animation>> &K : animations) { + K.value->disconnect(SNAME("changed"), callable_mp(this, &AnimationLibrary::_animation_changed)); + } animations.clear(); List<Variant> keys; p_data.get_key_list(&keys); @@ -146,6 +158,7 @@ void AnimationLibrary::_bind_methods() { ADD_SIGNAL(MethodInfo("animation_added", PropertyInfo(Variant::STRING_NAME, "name"))); ADD_SIGNAL(MethodInfo("animation_removed", PropertyInfo(Variant::STRING_NAME, "name"))); ADD_SIGNAL(MethodInfo("animation_renamed", PropertyInfo(Variant::STRING_NAME, "name"), PropertyInfo(Variant::STRING_NAME, "to_name"))); + ADD_SIGNAL(MethodInfo("animation_changed", PropertyInfo(Variant::STRING_NAME, "name"))); } AnimationLibrary::AnimationLibrary() { } diff --git a/scene/resources/animation_library.h b/scene/resources/animation_library.h index d63807b6d7..54bd641b6d 100644 --- a/scene/resources/animation_library.h +++ b/scene/resources/animation_library.h @@ -42,6 +42,8 @@ class AnimationLibrary : public Resource { TypedArray<StringName> _get_animation_list() const; + void _animation_changed(const StringName &p_name); + friend class AnimationPlayer; //for faster access HashMap<StringName, Ref<Animation>> animations; diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 9289c5da4a..bc2149a8c6 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -341,7 +341,7 @@ real_t Curve::sample_local_nocheck(int p_index, real_t p_local_offset) const { const Point a = _points[p_index]; const Point b = _points[p_index + 1]; - /* Cubic bezier + /* Cubic bézier * * ac-----bc * / \ @@ -774,6 +774,22 @@ void Curve2D::_bake_segment2d(RBMap<real_t, Vector2> &r_bake, real_t p_begin, re } } +void Curve2D::_bake_segment2d_even_length(RBMap<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_length) const { + Vector2 beg = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_begin); + Vector2 end = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_end); + + real_t length = beg.distance_to(end); + + if (length > p_length && p_depth < p_max_depth) { + real_t mp = (p_begin + p_end) * 0.5; + Vector2 mid = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, mp); + r_bake[mp] = mid; + + _bake_segment2d_even_length(r_bake, p_begin, mp, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length); + _bake_segment2d_even_length(r_bake, mp, p_end, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length); + } +} + void Curve2D::_bake() const { if (!baked_cache_dirty) { return; @@ -785,94 +801,62 @@ void Curve2D::_bake() const { if (points.size() == 0) { baked_point_cache.clear(); baked_dist_cache.clear(); + baked_forward_vector_cache.clear(); return; } if (points.size() == 1) { baked_point_cache.resize(1); baked_point_cache.set(0, points[0].position); - baked_dist_cache.resize(1); baked_dist_cache.set(0, 0.0); + baked_forward_vector_cache.resize(1); + baked_forward_vector_cache.set(0, Vector2(0.0, 0.1)); + return; } - Vector2 position = points[0].position; - real_t dist = 0.0; - - List<Vector2> pointlist; - List<real_t> distlist; - - // Start always from origin. - pointlist.push_back(position); - distlist.push_back(0.0); - - for (int i = 0; i < points.size() - 1; i++) { - real_t step = 0.1; // at least 10 substeps ought to be enough? - real_t p = 0.0; - - while (p < 1.0) { - real_t np = p + step; - if (np > 1.0) { - np = 1.0; - } - - Vector2 npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, np); - real_t d = position.distance_to(npp); - - if (d > bake_interval) { - // OK! between P and NP there _has_ to be Something, let's go searching! - - int iterations = 10; //lots of detail! - - real_t low = p; - real_t hi = np; - real_t mid = low + (hi - low) * 0.5; - - for (int j = 0; j < iterations; j++) { - npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, mid); - d = position.distance_to(npp); - - if (bake_interval < d) { - hi = mid; - } else { - low = mid; - } - mid = low + (hi - low) * 0.5; - } - - position = npp; - p = mid; - dist += d; + // Tesselate curve to (almost) even length segments + { + Vector<RBMap<real_t, Vector2>> midpoints = _tessellate_even_length(10, bake_interval); - pointlist.push_back(position); - distlist.push_back(dist); - } else { - p = np; - } + int pc = 1; + for (int i = 0; i < points.size() - 1; i++) { + pc++; + pc += midpoints[i].size(); } - Vector2 npp = points[i + 1].position; - real_t d = position.distance_to(npp); - - position = npp; - dist += d; + baked_point_cache.resize(pc); + baked_dist_cache.resize(pc); + baked_forward_vector_cache.resize(pc); - pointlist.push_back(position); - distlist.push_back(dist); - } + Vector2 *bpw = baked_point_cache.ptrw(); + Vector2 *bfw = baked_forward_vector_cache.ptrw(); - baked_max_ofs = dist; + // Collect positions and sample tilts and tangents for each baked points. + bpw[0] = points[0].position; + bfw[0] = points[0].position.bezier_derivative(points[0].position + points[0].out, points[1].position + points[1].in, points[1].position, 0.0).normalized(); + int pidx = 0; - baked_point_cache.resize(pointlist.size()); - baked_dist_cache.resize(distlist.size()); + for (int i = 0; i < points.size() - 1; i++) { + for (const KeyValue<real_t, Vector2> &E : midpoints[i]) { + pidx++; + bpw[pidx] = E.value; + bfw[pidx] = points[i].position.bezier_derivative(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, E.key).normalized(); + } - Vector2 *w = baked_point_cache.ptrw(); - real_t *wd = baked_dist_cache.ptrw(); + pidx++; + bpw[pidx] = points[i + 1].position; + bfw[pidx] = points[i].position.bezier_derivative(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, 1.0).normalized(); + } - for (int i = 0; i < pointlist.size(); i++) { - w[i] = pointlist[i]; - wd[i] = distlist[i]; + // Recalculate the baked distances. + real_t *bdw = baked_dist_cache.ptrw(); + bdw[0] = 0.0; + for (int i = 0; i < pc - 1; i++) { + bdw[i + 1] = bdw[i] + bpw[i].distance_to(bpw[i + 1]); + } + baked_max_ofs = bdw[pc - 1]; } } @@ -884,27 +868,15 @@ real_t Curve2D::get_baked_length() const { return baked_max_ofs; } -Vector2 Curve2D::sample_baked(real_t p_offset, bool p_cubic) const { - if (baked_cache_dirty) { - _bake(); - } +Curve2D::Interval Curve2D::_find_interval(real_t p_offset) const { + Interval interval = { + -1, + 0.0 + }; + ERR_FAIL_COND_V_MSG(baked_cache_dirty, interval, "Backed cache is dirty"); - // Validate: Curve may not have baked points. int pc = baked_point_cache.size(); - ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D."); - - if (pc == 1) { - return baked_point_cache.get(0); - } - - const Vector2 *r = baked_point_cache.ptr(); - - if (p_offset < 0) { - return r[0]; - } - if (p_offset >= baked_max_ofs) { - return r[pc - 1]; - } + ERR_FAIL_COND_V_MSG(pc < 2, interval, "Less than two points in cache"); int start = 0; int end = pc; @@ -924,9 +896,27 @@ Vector2 Curve2D::sample_baked(real_t p_offset, bool p_cubic) const { real_t offset_end = baked_dist_cache[idx + 1]; real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "Couldn't find baked segment."); + ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, interval, "Offset out of range."); + + interval.idx = idx; + if (idx_interval < FLT_EPSILON) { + interval.frac = 0.5; // For a very short interval, 0.5 is a reasonable choice. + ERR_FAIL_V_MSG(interval, "Zero length interval."); + } + + interval.frac = (p_offset - offset_begin) / idx_interval; + return interval; +} - real_t frac = (p_offset - offset_begin) / idx_interval; +Vector2 Curve2D::_sample_baked(Interval p_interval, bool p_cubic) const { + // Assuming p_interval is valid. + ERR_FAIL_INDEX_V_MSG(p_interval.idx, baked_point_cache.size(), Vector2(), "Invalid interval"); + + int idx = p_interval.idx; + real_t frac = p_interval.frac; + + const Vector2 *r = baked_point_cache.ptr(); + int pc = baked_point_cache.size(); if (p_cubic) { Vector2 pre = idx > 0 ? r[idx - 1] : r[idx]; @@ -937,44 +927,70 @@ Vector2 Curve2D::sample_baked(real_t p_offset, bool p_cubic) const { } } -Transform2D Curve2D::sample_baked_with_rotation(real_t p_offset, bool p_cubic, bool p_loop, real_t p_lookahead) const { - real_t path_length = get_baked_length(); // Ensure baked. - ERR_FAIL_COND_V_MSG(path_length == 0, Transform2D(), "Length of Curve2D is 0."); +Transform2D Curve2D::_sample_posture(Interval p_interval) const { + // Assuming that p_interval is valid. + ERR_FAIL_INDEX_V_MSG(p_interval.idx, baked_point_cache.size(), Transform2D(), "Invalid interval"); - Vector2 pos = sample_baked(p_offset, p_cubic); + int idx = p_interval.idx; + real_t frac = p_interval.frac; - real_t ahead = p_offset + p_lookahead; + Vector2 forward_begin = baked_forward_vector_cache[idx]; + Vector2 forward_end = baked_forward_vector_cache[idx + 1]; - if (p_loop && ahead >= path_length) { - // If our lookahead will loop, we need to check if the path is closed. - int point_count = get_point_count(); - if (point_count > 0) { - Vector2 start_point = get_point_position(0); - Vector2 end_point = get_point_position(point_count - 1); - if (start_point == end_point) { - // Since the path is closed we want to 'smooth off' - // the corner at the start/end. - // So we wrap the lookahead back round. - ahead = Math::fmod(ahead, path_length); - } - } + // Build frames at both ends of the interval, then interpolate. + const Vector2 forward = forward_begin.slerp(forward_end, frac).normalized(); + const Vector2 side = Vector2(-forward.y, forward.x); + + return Transform2D(side, forward, Vector2(0.0, 0.0)); +} + +Vector2 Curve2D::sample_baked(real_t p_offset, bool p_cubic) const { + if (baked_cache_dirty) { + _bake(); } - Vector2 ahead_pos = sample_baked(ahead, p_cubic); + // Validate: Curve may not have baked points. + int pc = baked_point_cache.size(); + ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D."); - Vector2 tangent_to_curve; - if (ahead_pos == pos) { - // This will happen at the end of non-looping or non-closed paths. - // We'll try a look behind instead, in order to get a meaningful angle. - tangent_to_curve = - (pos - sample_baked(p_offset - p_lookahead, p_cubic)).normalized(); - } else { - tangent_to_curve = (ahead_pos - pos).normalized(); + if (pc == 1) { + return baked_point_cache[0]; + } + + p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic. + + Curve2D::Interval interval = _find_interval(p_offset); + return _sample_baked(interval, p_cubic); +} + +Transform2D Curve2D::sample_baked_with_rotation(real_t p_offset, bool p_cubic) const { + if (baked_cache_dirty) { + _bake(); } - Vector2 normal_of_curve = -tangent_to_curve.orthogonal(); + // Validate: Curve may not have baked points. + const int point_count = baked_point_cache.size(); + ERR_FAIL_COND_V_MSG(point_count == 0, Transform2D(), "No points in Curve3D."); + + if (point_count == 1) { + Transform2D t; + t.set_origin(baked_point_cache.get(0)); + ERR_FAIL_V_MSG(t, "Only 1 point in Curve2D."); + } + + p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic. + + // 0. Find interval for all sampling steps. + Curve2D::Interval interval = _find_interval(p_offset); + + // 1. Sample position. + Vector2 pos = _sample_baked(interval, p_cubic); + + // 2. Sample rotation frame. + Transform2D frame = _sample_posture(interval); + frame.set_origin(pos); - return Transform2D(normal_of_curve, tangent_to_curve, pos); + return frame; } PackedVector2Array Curve2D::get_baked_points() const { @@ -1147,6 +1163,50 @@ PackedVector2Array Curve2D::tessellate(int p_max_stages, real_t p_tolerance) con return tess; } +Vector<RBMap<real_t, Vector2>> Curve2D::_tessellate_even_length(int p_max_stages, real_t p_length) const { + Vector<RBMap<real_t, Vector2>> midpoints; + ERR_FAIL_COND_V_MSG(points.size() < 2, midpoints, "Curve must have at least 2 control point"); + + midpoints.resize(points.size() - 1); + + for (int i = 0; i < points.size() - 1; i++) { + _bake_segment2d_even_length(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_length); + } + return midpoints; +} + +PackedVector2Array Curve2D::tessellate_even_length(int p_max_stages, real_t p_length) const { + PackedVector2Array tess; + + Vector<RBMap<real_t, Vector2>> midpoints = _tessellate_even_length(p_max_stages, p_length); + if (midpoints.size() == 0) { + return tess; + } + + int pc = 1; + for (int i = 0; i < points.size() - 1; i++) { + pc++; + pc += midpoints[i].size(); + } + + tess.resize(pc); + Vector2 *bpw = tess.ptrw(); + bpw[0] = points[0].position; + int pidx = 0; + + for (int i = 0; i < points.size() - 1; i++) { + for (const KeyValue<real_t, Vector2> &E : midpoints[i]) { + pidx++; + bpw[pidx] = E.value; + } + + pidx++; + bpw[pidx] = points[i + 1].position; + } + + return tess; +} + bool Curve2D::_set(const StringName &p_name, const Variant &p_value) { Vector<String> components = String(p_name).split("/", true, 2); if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) { @@ -1224,12 +1284,13 @@ void Curve2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bake_interval"), &Curve2D::get_bake_interval); ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve2D::get_baked_length); - ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve2D::sample_baked, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("sample_baked_with_rotation", "offset", "cubic", "loop", "lookahead"), &Curve2D::sample_baked_with_rotation, DEFVAL(false), DEFVAL(true), DEFVAL(4.0)); + ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve2D::sample_baked, DEFVAL(0.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("sample_baked_with_rotation", "offset", "cubic"), &Curve2D::sample_baked_with_rotation, DEFVAL(0.0), DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve2D::get_baked_points); ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve2D::get_closest_point); ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve2D::get_closest_offset); ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve2D::tessellate, DEFVAL(5), DEFVAL(4)); + ClassDB::bind_method(D_METHOD("tessellate_even_length", "max_stages", "tolerance_length"), &Curve2D::tessellate_even_length, DEFVAL(5), DEFVAL(20.0)); ClassDB::bind_method(D_METHOD("_get_data"), &Curve2D::_get_data); ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve2D::_set_data); @@ -1407,15 +1468,15 @@ void Curve3D::_bake_segment3d_even_length(RBMap<real_t, Vector3> &r_bake, real_t Vector3 beg = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_begin); Vector3 end = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_end); - size_t length = beg.distance_to(end); + real_t length = beg.distance_to(end); if (length > p_length && p_depth < p_max_depth) { real_t mp = (p_begin + p_end) * 0.5; Vector3 mid = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, mp); r_bake[mp] = mid; - _bake_segment3d(r_bake, p_begin, mp, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length); - _bake_segment3d(r_bake, mp, p_end, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length); + _bake_segment3d_even_length(r_bake, p_begin, mp, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length); + _bake_segment3d_even_length(r_bake, mp, p_end, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_length); } } @@ -1839,10 +1900,11 @@ Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const { real_t nearest_dist = -1.0f; for (int i = 0; i < pc - 1; i++) { + const real_t interval = baked_dist_cache[i + 1] - baked_dist_cache[i]; Vector3 origin = r[i]; - Vector3 direction = (r[i + 1] - origin) / bake_interval; + Vector3 direction = (r[i + 1] - origin) / interval; - real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval); + real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, interval); Vector3 proj = origin + direction * d; real_t dist = proj.distance_squared_to(p_to_point); @@ -1875,13 +1937,16 @@ real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const { real_t nearest = 0.0f; real_t nearest_dist = -1.0f; - real_t offset = 0.0f; + real_t offset; for (int i = 0; i < pc - 1; i++) { + offset = baked_dist_cache[i]; + + const real_t interval = baked_dist_cache[i + 1] - baked_dist_cache[i]; Vector3 origin = r[i]; - Vector3 direction = (r[i + 1] - origin) / bake_interval; + Vector3 direction = (r[i + 1] - origin) / interval; - real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval); + real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, interval); Vector3 proj = origin + direction * d; real_t dist = proj.distance_squared_to(p_to_point); @@ -1890,8 +1955,6 @@ real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const { nearest = offset + d; nearest_dist = dist; } - - offset += bake_interval; } return nearest; @@ -2131,8 +2194,8 @@ void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_up_vector_enabled"), &Curve3D::is_up_vector_enabled); ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve3D::get_baked_length); - ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve3D::sample_baked, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("sample_baked_with_rotation", "offset", "cubic", "apply_tilt"), &Curve3D::sample_baked_with_rotation, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve3D::sample_baked, DEFVAL(0.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("sample_baked_with_rotation", "offset", "cubic", "apply_tilt"), &Curve3D::sample_baked_with_rotation, DEFVAL(0.0), DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("sample_baked_up_vector", "offset", "apply_tilt"), &Curve3D::sample_baked_up_vector, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve3D::get_baked_points); ClassDB::bind_method(D_METHOD("get_baked_tilts"), &Curve3D::get_baked_tilts); diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 88c3cf3ae6..ea3ceabb14 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -172,6 +172,7 @@ class Curve2D : public Resource { mutable bool baked_cache_dirty = false; mutable PackedVector2Array baked_point_cache; + mutable PackedVector2Array baked_forward_vector_cache; mutable Vector<real_t> baked_dist_cache; mutable real_t baked_max_ofs = 0.0; @@ -181,7 +182,16 @@ class Curve2D : public Resource { real_t bake_interval = 5.0; + struct Interval { + int idx; + real_t frac; + }; + Interval _find_interval(real_t p_offset) const; + Vector2 _sample_baked(Interval p_interval, bool p_cubic) const; + Transform2D _sample_posture(Interval p_interval) const; + void _bake_segment2d(RBMap<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const; + void _bake_segment2d_even_length(RBMap<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_length) const; Dictionary _get_data() const; void _set_data(const Dictionary &p_data); @@ -192,6 +202,8 @@ class Curve2D : public Resource { void _add_point(const Vector2 &p_position, const Vector2 &p_in = Vector2(), const Vector2 &p_out = Vector2(), int p_atpos = -1); void _remove_point(int p_index); + Vector<RBMap<real_t, Vector2>> _tessellate_even_length(int p_max_stages = 5, real_t p_length = 0.2) const; + protected: static void _bind_methods(); @@ -216,12 +228,13 @@ public: real_t get_baked_length() const; Vector2 sample_baked(real_t p_offset, bool p_cubic = false) const; - Transform2D sample_baked_with_rotation(real_t p_offset, bool p_cubic = false, bool p_loop = true, real_t p_lookahead = 4.0) const; + Transform2D sample_baked_with_rotation(real_t p_offset, bool p_cubic = false) const; PackedVector2Array get_baked_points() const; //useful for going through Vector2 get_closest_point(const Vector2 &p_to_point) const; real_t get_closest_offset(const Vector2 &p_to_point) const; PackedVector2Array tessellate(int p_max_stages = 5, real_t p_tolerance = 4) const; //useful for display + PackedVector2Array tessellate_even_length(int p_max_stages = 5, real_t p_length = 20.0) const; // Useful for baking. Curve2D(); }; diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index af51d6539e..584a7e7eac 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -63,6 +63,8 @@ void Font::_bind_methods() { ClassDB::bind_method(D_METHOD("get_font_name"), &Font::get_font_name); ClassDB::bind_method(D_METHOD("get_font_style_name"), &Font::get_font_style_name); ClassDB::bind_method(D_METHOD("get_font_style"), &Font::get_font_style); + ClassDB::bind_method(D_METHOD("get_font_weight"), &Font::get_font_weight); + ClassDB::bind_method(D_METHOD("get_font_stretch"), &Font::get_font_stretch); ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing); ClassDB::bind_method(D_METHOD("get_opentype_features"), &Font::get_opentype_features); @@ -249,6 +251,14 @@ BitField<TextServer::FontStyle> Font::get_font_style() const { return TS->font_get_style(_get_rid()); } +int Font::get_font_weight() const { + return TS->font_get_weight(_get_rid()); +} + +int Font::get_font_stretch() const { + return TS->font_get_stretch(_get_rid()); +} + Dictionary Font::get_opentype_features() const { return Dictionary(); } @@ -590,6 +600,7 @@ _FORCE_INLINE_ void FontFile::_ensure_rid(int p_cache_index) const { TS->font_set_msdf_size(cache[p_cache_index], msdf_size); TS->font_set_fixed_size(cache[p_cache_index], fixed_size); TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter); + TS->font_set_allow_system_fallback(cache[p_cache_index], allow_system_fallback); TS->font_set_hinting(cache[p_cache_index], hinting); TS->font_set_subpixel_positioning(cache[p_cache_index], subpixel_positioning); TS->font_set_oversampling(cache[p_cache_index], oversampling); @@ -881,6 +892,8 @@ void FontFile::_bind_methods() { ClassDB::bind_method(D_METHOD("set_font_name", "name"), &FontFile::set_font_name); ClassDB::bind_method(D_METHOD("set_font_style_name", "name"), &FontFile::set_font_style_name); ClassDB::bind_method(D_METHOD("set_font_style", "style"), &FontFile::set_font_style); + ClassDB::bind_method(D_METHOD("set_font_weight", "weight"), &FontFile::set_font_weight); + ClassDB::bind_method(D_METHOD("set_font_stretch", "stretch"), &FontFile::set_font_stretch); ClassDB::bind_method(D_METHOD("set_antialiasing", "antialiasing"), &FontFile::set_antialiasing); ClassDB::bind_method(D_METHOD("get_antialiasing"), &FontFile::get_antialiasing); @@ -900,6 +913,9 @@ void FontFile::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fixed_size", "fixed_size"), &FontFile::set_fixed_size); ClassDB::bind_method(D_METHOD("get_fixed_size"), &FontFile::get_fixed_size); + ClassDB::bind_method(D_METHOD("set_allow_system_fallback", "allow_system_fallback"), &FontFile::set_allow_system_fallback); + ClassDB::bind_method(D_METHOD("is_allow_system_fallback"), &FontFile::is_allow_system_fallback); + ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &FontFile::set_force_autohinter); ClassDB::bind_method(D_METHOD("is_force_autohinter"), &FontFile::is_force_autohinter); @@ -1007,10 +1023,14 @@ void FontFile::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name"); ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic,Fixed Size", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_weight", PROPERTY_HINT_RANGE, "100,999,25", PROPERTY_USAGE_STORAGE), "set_font_weight", "get_font_weight"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_stretch", PROPERTY_HINT_RANGE, "50,200,25", PROPERTY_USAGE_STORAGE), "set_font_stretch", "get_font_stretch"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range"); ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_system_fallback", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_allow_system_fallback", "is_allow_system_fallback"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_force_autohinter", "is_force_autohinter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE), "set_hinting", "get_hinting"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling"); @@ -1329,6 +1349,7 @@ void FontFile::reset_state() { mipmaps = false; msdf = false; force_autohinter = false; + allow_system_fallback = true; hinting = TextServer::HINTING_LIGHT; subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED; msdf_pixel_range = 14; @@ -1361,6 +1382,7 @@ Error FontFile::load_bitmap_font(const String &p_path) { mipmaps = false; msdf = false; force_autohinter = false; + allow_system_fallback = true; hinting = TextServer::HINTING_NONE; oversampling = 1.0f; @@ -1937,6 +1959,9 @@ Error FontFile::load_bitmap_font(const String &p_path) { set_font_name(font_name); set_font_style(st_flags); + if (st_flags & TextServer::FONT_BOLD) { + set_font_weight(700); + } set_cache_ascent(0, base_size, ascent); set_cache_descent(0, base_size, height - ascent); @@ -1946,7 +1971,7 @@ Error FontFile::load_bitmap_font(const String &p_path) { Error FontFile::load_dynamic_font(const String &p_path) { reset_state(); - Vector<uint8_t> font_data = FileAccess::get_file_as_array(p_path); + Vector<uint8_t> font_data = FileAccess::get_file_as_bytes(p_path); set_data(font_data); return OK; @@ -2000,6 +2025,16 @@ void FontFile::set_font_style(BitField<TextServer::FontStyle> p_style) { TS->font_set_style(cache[0], p_style); } +void FontFile::set_font_weight(int p_weight) { + _ensure_rid(0); + TS->font_set_weight(cache[0], p_weight); +} + +void FontFile::set_font_stretch(int p_stretch) { + _ensure_rid(0); + TS->font_set_stretch(cache[0], p_stretch); +} + void FontFile::set_antialiasing(TextServer::FontAntialiasing p_antialiasing) { if (antialiasing != p_antialiasing) { antialiasing = p_antialiasing; @@ -2090,6 +2125,21 @@ int FontFile::get_fixed_size() const { return fixed_size; } +void FontFile::set_allow_system_fallback(bool p_allow_system_fallback) { + if (allow_system_fallback != p_allow_system_fallback) { + allow_system_fallback = p_allow_system_fallback; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_allow_system_fallback(cache[i], allow_system_fallback); + } + emit_changed(); + } +} + +bool FontFile::is_allow_system_fallback() const { + return allow_system_fallback; +} + void FontFile::set_force_autohinter(bool p_force_autohinter) { if (force_autohinter != p_force_autohinter) { force_autohinter = p_force_autohinter; @@ -2839,6 +2889,9 @@ void SystemFont::_bind_methods() { ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &SystemFont::set_generate_mipmaps); ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &SystemFont::get_generate_mipmaps); + ClassDB::bind_method(D_METHOD("set_allow_system_fallback", "allow_system_fallback"), &SystemFont::set_allow_system_fallback); + ClassDB::bind_method(D_METHOD("is_allow_system_fallback"), &SystemFont::is_allow_system_fallback); + ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &SystemFont::set_force_autohinter); ClassDB::bind_method(D_METHOD("is_force_autohinter"), &SystemFont::is_force_autohinter); @@ -2857,12 +2910,18 @@ void SystemFont::_bind_methods() { ClassDB::bind_method(D_METHOD("get_font_names"), &SystemFont::get_font_names); ClassDB::bind_method(D_METHOD("set_font_names", "names"), &SystemFont::set_font_names); - ClassDB::bind_method(D_METHOD("set_font_style", "style"), &SystemFont::set_font_style); + ClassDB::bind_method(D_METHOD("get_font_italic"), &SystemFont::get_font_italic); + ClassDB::bind_method(D_METHOD("set_font_italic", "italic"), &SystemFont::set_font_italic); + ClassDB::bind_method(D_METHOD("set_font_weight", "weight"), &SystemFont::set_font_weight); + ClassDB::bind_method(D_METHOD("set_font_stretch", "stretch"), &SystemFont::set_font_stretch); ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "font_names"), "set_font_names", "get_font_names"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_FLAGS, "Bold,Italic"), "set_font_style", "get_font_style"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "font_italic"), "set_font_italic", "get_font_italic"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_weight", PROPERTY_HINT_RANGE, "100,999,25"), "set_font_weight", "get_font_weight"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_stretch", PROPERTY_HINT_RANGE, "50,200,25"), "set_font_stretch", "get_font_stretch"); ADD_PROPERTY(PropertyInfo(Variant::INT, "antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD Subpixel", PROPERTY_USAGE_STORAGE), "set_antialiasing", "get_antialiasing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps"), "set_generate_mipmaps", "get_generate_mipmaps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_system_fallback"), "set_allow_system_fallback", "is_allow_system_fallback"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter"), "set_force_autohinter", "is_force_autohinter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), "set_hinting", "get_hinting"); ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), "set_subpixel_positioning", "get_subpixel_positioning"); @@ -2899,13 +2958,14 @@ void SystemFont::_update_base_font() { face_indeces.clear(); ftr_weight = 0; + ftr_stretch = 0; ftr_italic = 0; for (const String &E : names) { if (E.is_empty()) { continue; } - String path = OS::get_singleton()->get_system_font_path(E, style & TextServer::FONT_BOLD, style & TextServer::FONT_ITALIC); + String path = OS::get_singleton()->get_system_font_path(E, weight, stretch, italic); if (path.is_empty()) { continue; } @@ -2917,9 +2977,22 @@ void SystemFont::_update_base_font() { } // If it's a font collection check all faces to match requested style. + int best_score = 0; for (int i = 0; i < file->get_face_count(); i++) { file->set_face_index(0, i); - if (((file->get_font_style() & TextServer::FONT_BOLD) == (style & TextServer::FONT_BOLD)) && ((file->get_font_style() & TextServer::FONT_ITALIC) == (style & TextServer::FONT_ITALIC))) { + BitField<TextServer::FontStyle> style = file->get_font_style(); + int font_weight = file->get_font_weight(); + int font_stretch = file->get_font_stretch(); + int score = (20 - Math::abs(font_weight - weight) / 50); + score += (20 - Math::abs(font_stretch - stretch) / 10); + if (bool(style & TextServer::FONT_ITALIC) == italic) { + score += 30; + } + if (score > best_score) { + face_indeces.clear(); + } + if (score >= best_score) { + best_score = score; face_indeces.push_back(i); } } @@ -2928,19 +3001,25 @@ void SystemFont::_update_base_font() { } file->set_face_index(0, face_indeces[0]); - // If it's a variable font, apply weight and italic coordinates to match requested style. - Dictionary ftr = file->get_supported_variation_list(); - if ((style & TextServer::FONT_BOLD) && ftr.has(TS->name_to_tag("weight"))) { - ftr_weight = 700; - } - if ((style & TextServer::FONT_ITALIC) && ftr.has(TS->name_to_tag("italic"))) { - ftr_italic = 1; + // If it's a variable font, apply weight, stretch and italic coordinates to match requested style. + if (best_score != 50) { + Dictionary ftr = file->get_supported_variation_list(); + if (ftr.has(TS->name_to_tag("width"))) { + ftr_stretch = stretch; + } + if (ftr.has(TS->name_to_tag("weight"))) { + ftr_weight = weight; + } + if (italic && ftr.has(TS->name_to_tag("italic"))) { + ftr_italic = 1; + } } // Apply font rendering settings. file->set_antialiasing(antialiasing); file->set_generate_mipmaps(mipmaps); file->set_force_autohinter(force_autohinter); + file->set_allow_system_fallback(allow_system_fallback); file->set_hinting(hinting); file->set_subpixel_positioning(subpixel_positioning); file->set_multichannel_signed_distance_field(msdf); @@ -2973,11 +3052,15 @@ void SystemFont::reset_state() { names.clear(); face_indeces.clear(); ftr_weight = 0; + ftr_stretch = 0; ftr_italic = 0; - style = 0; + italic = false; + weight = 400; + stretch = 100; antialiasing = TextServer::FONT_ANTIALIASING_GRAY; mipmaps = false; force_autohinter = false; + allow_system_fallback = true; hinting = TextServer::HINTING_LIGHT; subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED; oversampling = 0.f; @@ -3069,6 +3152,20 @@ bool SystemFont::get_generate_mipmaps() const { return mipmaps; } +void SystemFont::set_allow_system_fallback(bool p_allow_system_fallback) { + if (allow_system_fallback != p_allow_system_fallback) { + allow_system_fallback = p_allow_system_fallback; + if (base_font.is_valid()) { + base_font->set_allow_system_fallback(allow_system_fallback); + } + emit_changed(); + } +} + +bool SystemFont::is_allow_system_fallback() const { + return allow_system_fallback; +} + void SystemFont::set_force_autohinter(bool p_force_autohinter) { if (force_autohinter != p_force_autohinter) { force_autohinter = p_force_autohinter; @@ -3150,15 +3247,37 @@ PackedStringArray SystemFont::get_font_names() const { return names; } -void SystemFont::set_font_style(BitField<TextServer::FontStyle> p_style) { - if (style != p_style) { - style = p_style; +void SystemFont::set_font_italic(bool p_italic) { + if (italic != p_italic) { + italic = p_italic; _update_base_font(); } } -BitField<TextServer::FontStyle> SystemFont::get_font_style() const { - return style; +bool SystemFont::get_font_italic() const { + return italic; +} + +void SystemFont::set_font_weight(int p_weight) { + if (weight != p_weight) { + weight = CLAMP(p_weight, 100, 999); + _update_base_font(); + } +} + +int SystemFont::get_font_weight() const { + return weight; +} + +void SystemFont::set_font_stretch(int p_stretch) { + if (stretch != p_stretch) { + stretch = CLAMP(p_stretch, 50, 200); + _update_base_font(); + } +} + +int SystemFont::get_font_stretch() const { + return stretch; } int SystemFont::get_spacing(TextServer::SpacingType p_spacing) const { @@ -3176,6 +3295,9 @@ RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_ if (ftr_weight > 0 && !var.has(TS->name_to_tag("weight"))) { var[TS->name_to_tag("weight")] = ftr_weight; } + if (ftr_stretch > 0 && !var.has(TS->name_to_tag("width"))) { + var[TS->name_to_tag("width")] = ftr_stretch; + } if (ftr_italic > 0 && !var.has(TS->name_to_tag("italic"))) { var[TS->name_to_tag("italic")] = ftr_italic; } @@ -3198,6 +3320,9 @@ RID SystemFont::_get_rid() const { if (ftr_weight > 0) { var[TS->name_to_tag("weight")] = ftr_weight; } + if (ftr_stretch > 0) { + var[TS->name_to_tag("width")] = ftr_stretch; + } if (ftr_italic > 0) { var[TS->name_to_tag("italic")] = ftr_italic; } diff --git a/scene/resources/font.h b/scene/resources/font.h index 5cf596b41d..e9f7507652 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -92,6 +92,8 @@ public: virtual String get_font_name() const; virtual String get_font_style_name() const; virtual BitField<TextServer::FontStyle> get_font_style() const; + virtual int get_font_weight() const; + virtual int get_font_stretch() const; virtual int get_spacing(TextServer::SpacingType p_spacing) const { return 0; }; virtual Dictionary get_opentype_features() const; @@ -148,6 +150,7 @@ class FontFile : public Font { int msdf_size = 48; int fixed_size = 0; bool force_autohinter = false; + bool allow_system_fallback = true; TextServer::Hinting hinting = TextServer::HINTING_LIGHT; TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO; real_t oversampling = 0.f; @@ -191,6 +194,8 @@ public: virtual void set_font_name(const String &p_name); virtual void set_font_style_name(const String &p_name); virtual void set_font_style(BitField<TextServer::FontStyle> p_style); + virtual void set_font_weight(int p_weight); + virtual void set_font_stretch(int p_stretch); virtual void set_antialiasing(TextServer::FontAntialiasing p_antialiasing); virtual TextServer::FontAntialiasing get_antialiasing() const; @@ -210,6 +215,9 @@ public: virtual void set_fixed_size(int p_fixed_size); virtual int get_fixed_size() const; + virtual void set_allow_system_fallback(bool p_allow_system_fallback); + virtual bool is_allow_system_fallback() const; + virtual void set_force_autohinter(bool p_force_autohinter); virtual bool is_force_autohinter() const; @@ -389,18 +397,22 @@ class SystemFont : public Font { GDCLASS(SystemFont, Font); PackedStringArray names; - BitField<TextServer::FontStyle> style = 0; + bool italic = false; + int weight = 400; + int stretch = 100; mutable Ref<Font> theme_font; Ref<FontFile> base_font; Vector<int> face_indeces; int ftr_weight = 0; + int ftr_stretch = 0; int ftr_italic = 0; TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY; bool mipmaps = false; bool force_autohinter = false; + bool allow_system_fallback = true; TextServer::Hinting hinting = TextServer::HINTING_LIGHT; TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO; real_t oversampling = 0.f; @@ -423,6 +435,9 @@ public: virtual void set_generate_mipmaps(bool p_generate_mipmaps); virtual bool get_generate_mipmaps() const; + virtual void set_allow_system_fallback(bool p_allow_system_fallback); + virtual bool is_allow_system_fallback() const; + virtual void set_force_autohinter(bool p_force_autohinter); virtual bool is_force_autohinter() const; @@ -441,8 +456,14 @@ public: virtual void set_font_names(const PackedStringArray &p_names); virtual PackedStringArray get_font_names() const; - virtual void set_font_style(BitField<TextServer::FontStyle> p_style); - virtual BitField<TextServer::FontStyle> get_font_style() const override; + virtual void set_font_italic(bool p_italic); + virtual bool get_font_italic() const; + + virtual void set_font_weight(int p_weight); + virtual int get_font_weight() const override; + + virtual void set_font_stretch(int p_stretch); + virtual int get_font_stretch() const override; virtual int get_spacing(TextServer::SpacingType p_spacing) const override; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 10d193f950..a16d2c2072 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -92,18 +92,13 @@ void Material::inspect_native_shader_code() { RID Material::get_shader_rid() const { RID ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_shader_rid, ret)) { - return ret; - } - return RID(); + GDVIRTUAL_REQUIRED_CALL(_get_shader_rid, ret); + return ret; } Shader::Mode Material::get_shader_mode() const { - Shader::Mode ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_shader_mode, ret)) { - return ret; - } - - return Shader::MODE_MAX; + Shader::Mode ret = Shader::MODE_MAX; + GDVIRTUAL_REQUIRED_CALL(_get_shader_mode, ret); + return ret; } bool Material::_can_do_next_pass() const { @@ -1257,15 +1252,16 @@ void BaseMaterial3D::_update_shader() { } if (distance_fade != DISTANCE_FADE_DISABLED) { + // Use the slightly more expensive circular fade (distance to the object) instead of linear + // (Z distance), so that the fade is always the same regardless of the camera angle. if ((distance_fade == DISTANCE_FADE_OBJECT_DITHER || distance_fade == DISTANCE_FADE_PIXEL_DITHER)) { if (!RenderingServer::get_singleton()->is_low_end()) { code += " {\n"; if (distance_fade == DISTANCE_FADE_OBJECT_DITHER) { - code += " float fade_distance = abs((VIEW_MATRIX * MODEL_MATRIX[3]).z);\n"; - + code += " float fade_distance = length((VIEW_MATRIX * MODEL_MATRIX[3]));\n"; } else { - code += " float fade_distance = -VERTEX.z;\n"; + code += " float fade_distance = length(VERTEX);\n"; } // Use interleaved gradient noise, which is fast but still looks good. code += " const vec3 magic = vec3(0.06711056f, 0.00583715f, 52.9829189f);"; @@ -1279,7 +1275,7 @@ void BaseMaterial3D::_update_shader() { } } else { - code += " ALPHA*=clamp(smoothstep(distance_fade_min,distance_fade_max,-VERTEX.z),0.0,1.0);\n"; + code += " ALPHA *= clamp(smoothstep(distance_fade_min, distance_fade_max, length(VERTEX)), 0.0, 1.0);\n"; } } diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 4f68a6f69b..a5e7602c8b 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -40,119 +40,83 @@ Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr; int Mesh::get_surface_count() const { - int ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_surface_count, ret)) { - return ret; - } - return 0; + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_get_surface_count, ret); + return ret; } int Mesh::surface_get_array_len(int p_idx) const { - int ret; - if (GDVIRTUAL_REQUIRED_CALL(_surface_get_array_len, p_idx, ret)) { - return ret; - } - return 0; + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_surface_get_array_len, p_idx, ret); + return ret; } int Mesh::surface_get_array_index_len(int p_idx) const { - int ret; - if (GDVIRTUAL_REQUIRED_CALL(_surface_get_array_index_len, p_idx, ret)) { - return ret; - } - return 0; + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_surface_get_array_index_len, p_idx, ret); + return ret; } Array Mesh::surface_get_arrays(int p_surface) const { Array ret; - if (GDVIRTUAL_REQUIRED_CALL(_surface_get_arrays, p_surface, ret)) { - return ret; - } - return Array(); + GDVIRTUAL_REQUIRED_CALL(_surface_get_arrays, p_surface, ret); + return ret; } TypedArray<Array> Mesh::surface_get_blend_shape_arrays(int p_surface) const { TypedArray<Array> ret; - if (GDVIRTUAL_REQUIRED_CALL(_surface_get_blend_shape_arrays, p_surface, ret)) { - return ret; - } - - return TypedArray<Array>(); + GDVIRTUAL_REQUIRED_CALL(_surface_get_blend_shape_arrays, p_surface, ret); + return ret; } Dictionary Mesh::surface_get_lods(int p_surface) const { Dictionary ret; - if (GDVIRTUAL_REQUIRED_CALL(_surface_get_lods, p_surface, ret)) { - return ret; - } - - return Dictionary(); + GDVIRTUAL_REQUIRED_CALL(_surface_get_lods, p_surface, ret); + return ret; } uint32_t Mesh::surface_get_format(int p_idx) const { - uint32_t ret; - if (GDVIRTUAL_REQUIRED_CALL(_surface_get_format, p_idx, ret)) { - return ret; - } - - return 0; + uint32_t ret = 0; + GDVIRTUAL_REQUIRED_CALL(_surface_get_format, p_idx, ret); + return ret; } Mesh::PrimitiveType Mesh::surface_get_primitive_type(int p_idx) const { - uint32_t ret; - if (GDVIRTUAL_REQUIRED_CALL(_surface_get_primitive_type, p_idx, ret)) { - return (Mesh::PrimitiveType)ret; - } - - return PRIMITIVE_MAX; + uint32_t ret = PRIMITIVE_MAX; + GDVIRTUAL_REQUIRED_CALL(_surface_get_primitive_type, p_idx, ret); + return (Mesh::PrimitiveType)ret; } void Mesh::surface_set_material(int p_idx, const Ref<Material> &p_material) { - if (GDVIRTUAL_REQUIRED_CALL(_surface_set_material, p_idx, p_material)) { - return; - } + GDVIRTUAL_REQUIRED_CALL(_surface_set_material, p_idx, p_material); } Ref<Material> Mesh::surface_get_material(int p_idx) const { Ref<Material> ret; - if (GDVIRTUAL_REQUIRED_CALL(_surface_get_material, p_idx, ret)) { - return ret; - } - - return Ref<Material>(); + GDVIRTUAL_REQUIRED_CALL(_surface_get_material, p_idx, ret); + return ret; } int Mesh::get_blend_shape_count() const { - int ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_count, ret)) { - return ret; - } - - return 0; + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_count, ret); + return ret; } StringName Mesh::get_blend_shape_name(int p_index) const { StringName ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_name, p_index, ret)) { - return ret; - } - - return StringName(); + GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_name, p_index, ret); + return ret; } void Mesh::set_blend_shape_name(int p_index, const StringName &p_name) { - if (GDVIRTUAL_REQUIRED_CALL(_set_blend_shape_name, p_index, p_name)) { - return; - } + GDVIRTUAL_REQUIRED_CALL(_set_blend_shape_name, p_index, p_name); } AABB Mesh::get_aabb() const { AABB ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_aabb, ret)) { - return ret; - } - - return AABB(); + GDVIRTUAL_REQUIRED_CALL(_get_aabb, ret); + return ret; } Ref<TriangleMesh> Mesh::generate_triangle_mesh() const { diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index f4b7f3d0b2..5316b524ba 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -450,7 +450,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { callable = callable.bindp(argptrs, binds.size()); } - cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags); + cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags | (p_edit_state == GEN_EDIT_STATE_MAIN ? 0 : CONNECT_INHERITED)); } //Node *s = ret_nodes[0]; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 354373ef3c..36e4a8ea37 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -1356,7 +1356,7 @@ Error ResourceLoaderText::save_as_binary(const String &p_path) { wf->seek_end(); - Vector<uint8_t> data = FileAccess::get_file_as_array(temp_file); + Vector<uint8_t> data = FileAccess::get_file_as_bytes(temp_file); wf->store_buffer(data.ptr(), data.size()); { Ref<DirAccess> dar = DirAccess::open(temp_file.get_base_dir()); diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index 3a671edeea..48ec084b02 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -221,7 +221,7 @@ Ref<Resource> ResourceFormatLoaderShader::load(const String &p_path, const Strin Ref<Shader> shader; shader.instantiate(); - Vector<uint8_t> buffer = FileAccess::get_file_as_array(p_path); + Vector<uint8_t> buffer = FileAccess::get_file_as_bytes(p_path); String str; str.parse_utf8((const char *)buffer.ptr(), buffer.size()); diff --git a/scene/resources/shader_include.cpp b/scene/resources/shader_include.cpp index fe628dd323..a680e66a50 100644 --- a/scene/resources/shader_include.cpp +++ b/scene/resources/shader_include.cpp @@ -81,7 +81,7 @@ Ref<Resource> ResourceFormatLoaderShaderInclude::load(const String &p_path, cons Ref<ShaderInclude> shader_inc; shader_inc.instantiate(); - Vector<uint8_t> buffer = FileAccess::get_file_as_array(p_path); + Vector<uint8_t> buffer = FileAccess::get_file_as_bytes(p_path); String str; str.parse_utf8((const char *)buffer.ptr(), buffer.size()); diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp index 413670d23e..87c6c36ee9 100644 --- a/scene/resources/shape_2d.cpp +++ b/scene/resources/shape_2d.cpp @@ -124,7 +124,6 @@ Shape2D::Shape2D(const RID &p_rid) { } Shape2D::~Shape2D() { - if (PhysicsServer2D::get_singleton() != nullptr) { - PhysicsServer2D::get_singleton()->free(shape); - } + ERR_FAIL_NULL(PhysicsServer2D::get_singleton()); + PhysicsServer2D::get_singleton()->free(shape); } diff --git a/scene/resources/shape_3d.cpp b/scene/resources/shape_3d.cpp index 44f21d2a48..7992ba9fd4 100644 --- a/scene/resources/shape_3d.cpp +++ b/scene/resources/shape_3d.cpp @@ -128,7 +128,6 @@ Shape3D::Shape3D(RID p_shape) : shape(p_shape) {} Shape3D::~Shape3D() { - if (PhysicsServer3D::get_singleton() != nullptr) { - PhysicsServer3D::get_singleton()->free(shape); - } + ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); + PhysicsServer3D::get_singleton()->free(shape); } diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index f379f88bed..ea341152e6 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -35,11 +35,9 @@ #include <limits.h> float StyleBox::get_style_margin(Side p_side) const { - float ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_style_margin, p_side, ret)) { - return ret; - } - return 0; + float ret = 0; + GDVIRTUAL_REQUIRED_CALL(_get_style_margin, p_side, ret); + return ret; } bool StyleBox::test_mask(const Point2 &p_point, const Rect2 &p_rect) const { @@ -49,9 +47,7 @@ bool StyleBox::test_mask(const Point2 &p_point, const Rect2 &p_rect) const { } void StyleBox::draw(RID p_canvas_item, const Rect2 &p_rect) const { - if (GDVIRTUAL_REQUIRED_CALL(_draw, p_canvas_item, p_rect)) { - return; - } + GDVIRTUAL_REQUIRED_CALL(_draw, p_canvas_item, p_rect); } void StyleBox::set_default_margin(Side p_side, float p_value) { diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index b5754caa6a..2106619a6b 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -40,19 +40,15 @@ #include "servers/camera/camera_feed.h" int Texture2D::get_width() const { - int ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) { - return ret; - } - return 0; + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_get_width, ret); + return ret; } int Texture2D::get_height() const { - int ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_height, ret)) { - return ret; - } - return 0; + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_get_height, ret); + return ret; } Size2 Texture2D::get_size() const { @@ -1092,57 +1088,44 @@ TypedArray<Image> Texture3D::_get_datai() const { } Image::Format Texture3D::get_format() const { - Image::Format ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_format, ret)) { - return ret; - } - return Image::FORMAT_MAX; + Image::Format ret = Image::FORMAT_MAX; + GDVIRTUAL_REQUIRED_CALL(_get_format, ret); + return ret; } int Texture3D::get_width() const { - int ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) { - return ret; - } - return 0; + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_get_width, ret); + return ret; } int Texture3D::get_height() const { - int ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_height, ret)) { - return ret; - } - return 0; + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_get_height, ret); + return ret; } int Texture3D::get_depth() const { - int ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_depth, ret)) { - return ret; - } - - return 0; + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_get_depth, ret); + return ret; } bool Texture3D::has_mipmaps() const { - bool ret; - if (GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret)) { - return ret; - } - return false; + bool ret = false; + GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret); + return ret; } Vector<Ref<Image>> Texture3D::get_data() const { TypedArray<Image> ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_data, ret)) { - Vector<Ref<Image>> data; - data.resize(ret.size()); - for (int i = 0; i < data.size(); i++) { - data.write[i] = ret[i]; - } - return data; + GDVIRTUAL_REQUIRED_CALL(_get_data, ret); + Vector<Ref<Image>> data; + data.resize(ret.size()); + for (int i = 0; i < data.size(); i++) { + data.write[i] = ret[i]; } - return Vector<Ref<Image>>(); + return data; } void Texture3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_format"), &Texture3D::get_format); @@ -2859,60 +2842,45 @@ AnimatedTexture::~AnimatedTexture() { /////////////////////////////// Image::Format TextureLayered::get_format() const { - Image::Format ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_format, ret)) { - return ret; - } - return Image::FORMAT_MAX; + Image::Format ret = Image::FORMAT_MAX; + GDVIRTUAL_REQUIRED_CALL(_get_format, ret); + return ret; } TextureLayered::LayeredType TextureLayered::get_layered_type() const { - uint32_t ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_layered_type, ret)) { - return (LayeredType)ret; - } - return LAYERED_TYPE_2D_ARRAY; + uint32_t ret = LAYERED_TYPE_2D_ARRAY; + GDVIRTUAL_REQUIRED_CALL(_get_layered_type, ret); + return (LayeredType)ret; } int TextureLayered::get_width() const { - int ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_width, ret)) { - return ret; - } - return 0; + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_get_width, ret); + return ret; } int TextureLayered::get_height() const { - int ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_height, ret)) { - return ret; - } - return 0; + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_get_height, ret); + return ret; } int TextureLayered::get_layers() const { - int ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_layers, ret)) { - return ret; - } - - return 0; + int ret = 0; + GDVIRTUAL_REQUIRED_CALL(_get_layers, ret); + return ret; } bool TextureLayered::has_mipmaps() const { - bool ret; - if (GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret)) { - return ret; - } - return false; + bool ret = false; + GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret); + return ret; } Ref<Image> TextureLayered::get_layer_data(int p_layer) const { Ref<Image> ret; - if (GDVIRTUAL_REQUIRED_CALL(_get_layer_data, p_layer, ret)) { - return ret; - } - return Ref<Image>(); + GDVIRTUAL_REQUIRED_CALL(_get_layer_data, p_layer, ret); + return ret; } void TextureLayered::_bind_methods() { diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index b30ca3e721..461dccfbdd 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -2658,6 +2658,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_world", "NODE_POSITION_WORLD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_position_world", "CAMERA_POSITION_WORLD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_direction_world", "CAMERA_DIRECTION_WORLD" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "camera_visible_layers", "CAMERA_VISIBLE_LAYERS" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_view", "NODE_POSITION_VIEW" }, // Node3D, Fragment @@ -2690,6 +2691,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_world", "NODE_POSITION_WORLD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_position_world", "CAMERA_POSITION_WORLD" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "camera_direction_world", "CAMERA_DIRECTION_WORLD" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR_INT, "camera_visible_layers", "CAMERA_VISIBLE_LAYERS" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "node_position_view", "NODE_POSITION_VIEW" }, // Node3D, Light |