diff options
Diffstat (limited to 'scene/resources')
49 files changed, 2786 insertions, 1528 deletions
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index dfd9c6eb2f..37407edc33 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -888,7 +888,6 @@ int Animation::add_track(TrackType p_type, int p_at_pos) { } } emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); return p_at_pos; } @@ -951,7 +950,6 @@ void Animation::remove_track(int p_track) { memdelete(t); tracks.remove_at(p_track); emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } int Animation::get_track_count() const { @@ -967,7 +965,6 @@ void Animation::track_set_path(int p_track, const NodePath &p_path) { ERR_FAIL_INDEX(p_track, tracks.size()); tracks[p_track]->path = p_path; emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } NodePath Animation::track_get_path(int p_track) const { @@ -1322,7 +1319,7 @@ Error Animation::blend_shape_track_interpolate(int p_track, double p_time, float } void Animation::track_remove_key_at_time(int p_track, double p_time) { - int idx = track_find_key(p_track, p_time, true); + int idx = track_find_key(p_track, p_time, FIND_MODE_APPROX); ERR_FAIL_COND(idx < 0); track_remove_key(p_track, idx); } @@ -1403,7 +1400,7 @@ void Animation::track_remove_key(int p_track, int p_idx) { emit_changed(); } -int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { +int Animation::track_find_key(int p_track, double p_time, FindMode p_find_mode) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), -1); Track *t = tracks[p_track]; @@ -1419,7 +1416,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { uint32_t key_index; bool fetch_compressed_success = _fetch_compressed<3>(tt->compressed_track, p_time, key, time, key_next, time_next, &key_index); ERR_FAIL_COND_V(!fetch_compressed_success, -1); - if (p_exact && time != p_time) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(time, p_time)) || (p_find_mode == FIND_MODE_EXACT && time != p_time)) { return -1; } return key_index; @@ -1429,7 +1426,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= tt->positions.size()) { return -1; } - if (tt->positions[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(tt->positions[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && tt->positions[k].time != p_time)) { return -1; } return k; @@ -1446,7 +1443,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { uint32_t key_index; bool fetch_compressed_success = _fetch_compressed<3>(rt->compressed_track, p_time, key, time, key_next, time_next, &key_index); ERR_FAIL_COND_V(!fetch_compressed_success, -1); - if (p_exact && time != p_time) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(time, p_time)) || (p_find_mode == FIND_MODE_EXACT && time != p_time)) { return -1; } return key_index; @@ -1456,7 +1453,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= rt->rotations.size()) { return -1; } - if (rt->rotations[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(rt->rotations[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && rt->rotations[k].time != p_time)) { return -1; } return k; @@ -1473,7 +1470,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { uint32_t key_index; bool fetch_compressed_success = _fetch_compressed<3>(st->compressed_track, p_time, key, time, key_next, time_next, &key_index); ERR_FAIL_COND_V(!fetch_compressed_success, -1); - if (p_exact && time != p_time) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(time, p_time)) || (p_find_mode == FIND_MODE_EXACT && time != p_time)) { return -1; } return key_index; @@ -1483,7 +1480,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= st->scales.size()) { return -1; } - if (st->scales[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(st->scales[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && st->scales[k].time != p_time)) { return -1; } return k; @@ -1500,7 +1497,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { uint32_t key_index; bool fetch_compressed_success = _fetch_compressed<1>(bst->compressed_track, p_time, key, time, key_next, time_next, &key_index); ERR_FAIL_COND_V(!fetch_compressed_success, -1); - if (p_exact && time != p_time) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(time, p_time)) || (p_find_mode == FIND_MODE_EXACT && time != p_time)) { return -1; } return key_index; @@ -1510,7 +1507,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= bst->blend_shapes.size()) { return -1; } - if (bst->blend_shapes[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(bst->blend_shapes[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && bst->blend_shapes[k].time != p_time)) { return -1; } return k; @@ -1522,7 +1519,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= vt->values.size()) { return -1; } - if (vt->values[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(vt->values[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && vt->values[k].time != p_time)) { return -1; } return k; @@ -1534,7 +1531,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= mt->methods.size()) { return -1; } - if (mt->methods[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(mt->methods[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && mt->methods[k].time != p_time)) { return -1; } return k; @@ -1546,7 +1543,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= bt->values.size()) { return -1; } - if (bt->values[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(bt->values[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && bt->values[k].time != p_time)) { return -1; } return k; @@ -1558,7 +1555,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= at->values.size()) { return -1; } - if (at->values[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(at->values[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && at->values[k].time != p_time)) { return -1; } return k; @@ -1570,7 +1567,7 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const { if (k < 0 || k >= at->values.size()) { return -1; } - if (at->values[k].time != p_time && p_exact) { + if ((p_find_mode == FIND_MODE_APPROX && !Math::is_equal_approx(at->values[k].time, p_time)) || (p_find_mode == FIND_MODE_EXACT && at->values[k].time != p_time)) { return -1; } return k; @@ -2467,7 +2464,6 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol ERR_FAIL_COND_V(idx == -2, T()); - bool result = true; int next = 0; real_t c = 0.0; // prepare for all cases of interpolation @@ -2599,10 +2595,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol } if (p_ok) { - *p_ok = result; - } - if (!result) { - return T(); + *p_ok = true; } real_t tr = p_keys[idx].transition; @@ -2712,111 +2705,11 @@ Variant Animation::value_track_interpolate(int p_track, double p_time) const { return Variant(); } -void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, double from_time, double to_time, List<int> *p_indices) const { - if (from_time != length && to_time == length) { - to_time = length + CMP_EPSILON; //include a little more if at the end - } - int to = _find(vt->values, to_time); - - if (to >= 0 && from_time == to_time && vt->values[to].time == from_time) { - //find exact (0 delta), return if found - p_indices->push_back(to); - return; - } - // 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 && vt->values[to].time >= to_time) { - to--; - } - - if (to < 0) { - return; // not bother - } - - int from = _find(vt->values, from_time); - - // position in the right first event.+ - if (from < 0 || vt->values[from].time < from_time) { - from++; - } - - int max = vt->values.size(); - - for (int i = from; i <= to; i++) { - ERR_CONTINUE(i < 0 || i >= max); // shouldn't happen - p_indices->push_back(i); - } -} - -void Animation::value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const { - ERR_FAIL_INDEX(p_track, tracks.size()); - Track *t = tracks[p_track]; - ERR_FAIL_COND(t->type != TYPE_VALUE); - - ValueTrack *vt = static_cast<ValueTrack *>(t); - - double from_time = p_time - p_delta; - double to_time = p_time; - - if (from_time > to_time) { - SWAP(from_time, to_time); - } - - switch (loop_mode) { - case LOOP_NONE: { - if (from_time < 0) { - from_time = 0; - } - if (from_time > length) { - from_time = length; - } - - if (to_time < 0) { - to_time = 0; - } - if (to_time > length) { - to_time = length; - } - } break; - case LOOP_LINEAR: { - from_time = Math::fposmod(from_time, length); - to_time = Math::fposmod(to_time, length); - - if (from_time > to_time) { - // handle loop by splitting - _value_track_get_key_indices_in_range(vt, from_time, length, p_indices); - _value_track_get_key_indices_in_range(vt, 0, to_time, p_indices); - return; - } - } break; - case LOOP_PINGPONG: { - from_time = Math::pingpong(from_time, length); - to_time = Math::pingpong(to_time, length); - - if (p_pingponged == -1) { - // handle loop by splitting - _value_track_get_key_indices_in_range(vt, 0, from_time, p_indices); - _value_track_get_key_indices_in_range(vt, 0, to_time, p_indices); - return; - } - if (p_pingponged == 1) { - // handle loop by splitting - _value_track_get_key_indices_in_range(vt, from_time, length, p_indices); - _value_track_get_key_indices_in_range(vt, to_time, length, p_indices); - return; - } - } break; - } - - _value_track_get_key_indices_in_range(vt, from_time, to_time, p_indices); -} - 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; @@ -2833,47 +2726,74 @@ 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 (from_time != length && 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. + int from = 0; + int to = len - 1; - if (to >= 0 && p_array[to].time >= to_time) { - to--; + 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; + } + } } - if (to < 0) { - return; // not bother + if (from == to) { + p_indices->push_back(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 (!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); + } } +} - int max = p_array.size(); +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()); - for (int i = from; i <= to; i++) { - ERR_CONTINUE(i < 0 || i >= max); // shouldn't happen - p_indices->push_back(i); + if (p_delta == 0) { + return; // Prevent to get key continuously. } -} -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 { - ERR_FAIL_INDEX(p_track, tracks.size()); const Track *t = tracks[p_track]; 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); } @@ -2902,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); @@ -2910,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: { @@ -2920,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: { @@ -2930,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: { @@ -2940,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, FIND_MODE_EXACT); + 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, FIND_MODE_EXACT); + if (edge >= 0) { + p_indices->push_back(edge); + } + } + } } break; case LOOP_PINGPONG: { if (from_time > length || from_time < 0) { @@ -2981,160 +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 - 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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 - 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); - _track_get_key_indices_in_range(tt->positions, to_time, length, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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 -= 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: { @@ -3142,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: { @@ -3150,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: { @@ -3158,136 +3145,32 @@ 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; } } -void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, double from_time, double to_time, List<int> *p_indices) const { - if (from_time != length && to_time == length) { - to_time = length + CMP_EPSILON; //include a little more if at the end - } - - int to = _find(mt->methods, 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 && mt->methods[to].time >= to_time) { - to--; - } - - if (to < 0) { - return; // not bother - } - - int from = _find(mt->methods, from_time); - - // position in the right first event.+ - if (from < 0 || mt->methods[from].time < from_time) { - from++; - } - - int max = mt->methods.size(); - - for (int i = from; i <= to; i++) { - ERR_CONTINUE(i < 0 || i >= max); // shouldn't happen - p_indices->push_back(i); - } -} - -void Animation::method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const { - ERR_FAIL_INDEX(p_track, tracks.size()); - Track *t = tracks[p_track]; - ERR_FAIL_COND(t->type != TYPE_METHOD); - - MethodTrack *mt = static_cast<MethodTrack *>(t); - - double from_time = p_time - p_delta; - double to_time = p_time; - - if (from_time > to_time) { - SWAP(from_time, to_time); - } - - switch (loop_mode) { - case LOOP_NONE: { - if (from_time < 0) { - from_time = 0; - } - if (from_time > length) { - from_time = length; - } - - if (to_time < 0) { - to_time = 0; - } - if (to_time > length) { - to_time = length; - } - } break; - case LOOP_LINEAR: { - if (from_time > length || from_time < 0) { - from_time = Math::fposmod(from_time, length); - } - if (to_time > length || to_time < 0) { - to_time = Math::fposmod(to_time, length); - } - - if (from_time > to_time) { - // handle loop by splitting - _method_track_get_key_indices_in_range(mt, from_time, length, p_indices); - _method_track_get_key_indices_in_range(mt, 0, to_time, p_indices); - return; - } - } break; - case LOOP_PINGPONG: { - if (from_time > length || from_time < 0) { - from_time = Math::pingpong(from_time, length); - } - if (to_time > length || to_time < 0) { - to_time = Math::pingpong(to_time, length); - } - - if (p_pingponged == -1) { - _method_track_get_key_indices_in_range(mt, 0, from_time, p_indices); - _method_track_get_key_indices_in_range(mt, 0, to_time, p_indices); - return; - } - if (p_pingponged == 1) { - _method_track_get_key_indices_in_range(mt, from_time, length, p_indices); - _method_track_get_key_indices_in_range(mt, to_time, length, p_indices); - return; - } - } break; - default: - break; - } - - _method_track_get_key_indices_in_range(mt, from_time, to_time, p_indices); -} - Vector<Variant> Animation::method_track_get_params(int p_track, int p_key_idx) const { ERR_FAIL_INDEX_V(p_track, tracks.size(), Vector<Variant>()); Track *t = tracks[p_track]; @@ -3834,7 +3717,6 @@ void Animation::track_move_up(int p_track) { } emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } void Animation::track_move_down(int p_track) { @@ -3843,7 +3725,6 @@ void Animation::track_move_down(int p_track) { } emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } void Animation::track_move_to(int p_track, int p_to_index) { @@ -3859,7 +3740,6 @@ void Animation::track_move_to(int p_track, int p_to_index) { tracks.insert(p_to_index > p_track ? p_to_index - 1 : p_to_index, track); emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } void Animation::track_swap(int p_track, int p_with_track) { @@ -3871,7 +3751,6 @@ void Animation::track_swap(int p_track, int p_with_track) { SWAP(tracks.write[p_track], tracks.write[p_with_track]); emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } void Animation::set_step(real_t p_step) { @@ -3939,7 +3818,7 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("track_get_key_count", "track_idx"), &Animation::track_get_key_count); ClassDB::bind_method(D_METHOD("track_get_key_value", "track_idx", "key_idx"), &Animation::track_get_key_value); ClassDB::bind_method(D_METHOD("track_get_key_time", "track_idx", "key_idx"), &Animation::track_get_key_time); - ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "exact"), &Animation::track_find_key, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("track_find_key", "track_idx", "time", "find_mode"), &Animation::track_find_key, DEFVAL(FIND_MODE_NEAREST)); ClassDB::bind_method(D_METHOD("track_set_interpolation_type", "track_idx", "interpolation"), &Animation::track_set_interpolation_type); ClassDB::bind_method(D_METHOD("track_get_interpolation_type", "track_idx"), &Animation::track_get_interpolation_type); @@ -3952,10 +3831,8 @@ void Animation::_bind_methods() { ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode); ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode); - ClassDB::bind_method(D_METHOD("value_track_get_key_indices", "track_idx", "time_sec", "delta"), &Animation::_value_track_get_key_indices); ClassDB::bind_method(D_METHOD("value_track_interpolate", "track_idx", "time_sec"), &Animation::value_track_interpolate); - ClassDB::bind_method(D_METHOD("method_track_get_key_indices", "track_idx", "time_sec", "delta"), &Animation::_method_track_get_key_indices); ClassDB::bind_method(D_METHOD("method_track_get_name", "track_idx", "key_idx"), &Animation::method_track_get_name); ClassDB::bind_method(D_METHOD("method_track_get_params", "track_idx", "key_idx"), &Animation::method_track_get_params); @@ -4001,8 +3878,6 @@ void Animation::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "None,Linear,Ping-Pong"), "set_loop_mode", "get_loop_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001,suffix:s"), "set_step", "get_step"); - ADD_SIGNAL(MethodInfo("tracks_changed")); - BIND_ENUM_CONSTANT(TYPE_VALUE); BIND_ENUM_CONSTANT(TYPE_POSITION_3D); BIND_ENUM_CONSTANT(TYPE_ROTATION_3D); @@ -4021,12 +3896,19 @@ 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); + + BIND_ENUM_CONSTANT(FIND_MODE_NEAREST); + BIND_ENUM_CONSTANT(FIND_MODE_APPROX); + BIND_ENUM_CONSTANT(FIND_MODE_EXACT); } void Animation::clear() { @@ -4041,7 +3923,6 @@ void Animation::clear() { compression.pages.clear(); compression.fps = 120; emit_changed(); - emit_signal(SceneStringNames::get_singleton()->tracks_changed); } bool Animation::_float_track_optimize_key(const TKey<float> t0, const TKey<float> t1, const TKey<float> t2, real_t p_allowed_velocity_err, real_t p_allowed_precision_error) { @@ -4964,7 +4845,7 @@ void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tol if (rollback || best_frame == FRAME_MAX) { // Commit the page if had to rollback or if no track was found - print_animc("\tCommiting page.."); + print_animc("\tCommiting page..."); // The end frame for the page depends entirely on whether its valid or // no more keys were found. @@ -5851,18 +5732,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float 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) { + const Vector<int32_t> arr_a = a; + const Vector<int32_t> arr_b = 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(); + const int32_t *ar = arr_a.ptr(); + const int32_t *br = arr_b.ptr(); Variant va; for (int32_t i = 0; i < sz; i++) { @@ -5874,18 +5755,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } 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) { + const Vector<int64_t> arr_a = a; + const Vector<int64_t> arr_b = 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(); + const int64_t *ar = arr_a.ptr(); + const int64_t *br = arr_b.ptr(); Variant va; for (int64_t i = 0; i < sz; i++) { @@ -5897,18 +5778,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } 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) { + const Vector<float> arr_a = a; + const Vector<float> arr_b = 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(); + const float *ar = arr_a.ptr(); + const float *br = arr_b.ptr(); Variant va; for (int i = 0; i < sz; i++) { @@ -5920,18 +5801,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } 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) { + const Vector<double> arr_a = a; + const Vector<double> arr_b = 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(); + const double *ar = arr_a.ptr(); + const double *br = arr_b.ptr(); Variant va; for (int i = 0; i < sz; i++) { @@ -5943,18 +5824,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } 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) { + const Vector<Vector2> arr_a = a; + const Vector<Vector2> arr_b = 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(); + 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); @@ -5964,18 +5845,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } 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) { + const Vector<Vector3> arr_a = a; + const Vector<Vector3> arr_b = 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(); + 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); @@ -5985,18 +5866,18 @@ Variant Animation::interpolate_variant(const Variant &a, const Variant &b, float } } 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) { + const Vector<Color> arr_a = a; + const Vector<Color> arr_b = 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(); + 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); @@ -6011,7 +5892,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 49c8fa4c22..b7d5a683db 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,18 @@ public: LOOP_PINGPONG, }; + enum LoopedFlag { + LOOPED_FLAG_NONE, + LOOPED_FLAG_END, + LOOPED_FLAG_START, + }; + + enum FindMode { + FIND_MODE_NEAREST, + FIND_MODE_APPROX, + FIND_MODE_EXACT, + }; + #ifdef TOOLS_ENABLED enum HandleMode { HANDLE_MODE_FREE, @@ -250,15 +261,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 _value_track_get_key_indices_in_range(const ValueTrack *vt, double from_time, double to_time, List<int> *p_indices) const; - _FORCE_INLINE_ void _method_track_get_key_indices_in_range(const MethodTrack *mt, 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): * @@ -345,27 +352,6 @@ private: // bind helpers private: - Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const { - List<int> idxs; - value_track_get_key_indices(p_track, p_time, p_delta, &idxs); - Vector<int> idxr; - - for (int &E : idxs) { - idxr.push_back(E); - } - return idxr; - } - Vector<int> _method_track_get_key_indices(int p_track, double p_time, double p_delta) const { - List<int> idxs; - method_track_get_key_indices(p_track, p_time, p_delta, &idxs); - Vector<int> idxr; - - for (int &E : idxs) { - idxr.push_back(E); - } - return idxr; - } - bool _float_track_optimize_key(const TKey<float> t0, const TKey<float> t1, const TKey<float> t2, real_t p_allowed_velocity_err, real_t p_allowed_precision_error); bool _vector2_track_optimize_key(const TKey<Vector2> t0, const TKey<Vector2> t1, const TKey<Vector2> t2, real_t p_alowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error); bool _vector3_track_optimize_key(const TKey<Vector3> t0, const TKey<Vector3> t1, const TKey<Vector3> t2, real_t p_alowed_velocity_err, real_t p_allowed_angular_error, real_t p_allowed_precision_error); @@ -412,7 +398,7 @@ public: void track_set_key_transition(int p_track, int p_key_idx, real_t p_transition); void track_set_key_value(int p_track, int p_key_idx, const Variant &p_value); void track_set_key_time(int p_track, int p_key_idx, double p_time); - int track_find_key(int p_track, double p_time, bool p_exact = false) const; + int track_find_key(int p_track, double p_time, FindMode p_find_mode = FIND_MODE_NEAREST) const; void track_remove_key(int p_track, int p_idx); void track_remove_key_at_time(int p_track, double p_time); int track_get_key_count(int p_track) const; @@ -470,17 +456,15 @@ public: bool track_get_interpolation_loop_wrap(int p_track) const; Variant value_track_interpolate(int p_track, double p_time) const; - void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const; void value_track_set_update_mode(int p_track, UpdateMode p_mode); UpdateMode value_track_get_update_mode(int p_track) const; - void method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const; Vector<Variant> method_track_get_params(int p_track, int p_key_idx) const; StringName method_track_get_name(int p_track, int p_key_idx) const; 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; @@ -510,6 +494,8 @@ 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); +VARIANT_ENUM_CAST(Animation::FindMode); #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/bit_map.cpp b/scene/resources/bit_map.cpp index 1b06e09bb8..86b806bc4f 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -169,7 +169,7 @@ Dictionary BitMap::_get_data() const { return d; } -Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_start) const { +Vector<Vector<Vector2>> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_start) const { int stepx = 0; int stepy = 0; int prevx = 0; @@ -179,9 +179,17 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_sta int curx = startx; int cury = starty; unsigned int count = 0; - HashSet<Point2i> case9s; - HashSet<Point2i> case6s; + + HashMap<Point2i, int> cross_map; + Vector<Vector2> _points; + int points_size = 0; + + Vector<Vector<Vector2>> ret; + + // Add starting entry at start of return. + ret.resize(1); + do { int sv = 0; { // Square value @@ -202,7 +210,7 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_sta sv += (p_rect.has_point(bl) && get_bitv(bl)) ? 4 : 0; Point2i br = Point2i(curx, cury); sv += (p_rect.has_point(br) && get_bitv(br)) ? 8 : 0; - ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector2>()); + ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector<Vector2>>()); } switch (sv) { @@ -266,70 +274,95 @@ Vector<Vector2> BitMap::_march_square(const Rect2i &p_rect, const Point2i &p_sta stepy = 0; break; case 9: - /* + /* Going DOWN if coming from the LEFT, otherwise go UP. + 9 +---+---+ | 1 | | +---+---+ | | 8 | +---+---+ - this should normally go UP, but if we already been here, we go down */ - if (case9s.has(Point2i(curx, cury))) { - //found, so we go down, and delete from case9s; + + if (prevx == 1) { stepx = 0; stepy = 1; - case9s.erase(Point2i(curx, cury)); } else { - //not found, we go up, and add to case9s; stepx = 0; stepy = -1; - case9s.insert(Point2i(curx, cury)); } break; case 6: - /* + /* Going RIGHT if coming from BELOW, otherwise go LEFT. 6 +---+---+ | | 2 | +---+---+ | 4 | | +---+---+ - this normally go RIGHT, but if it's coming from RIGHT, it should go LEFT */ - if (case6s.has(Point2i(curx, cury))) { - //found, so we go left, and delete from case6s; - stepx = -1; + + if (prevy == -1) { + stepx = 1; stepy = 0; - case6s.erase(Point2i(curx, cury)); } else { - //not found, we go right, and add to case6s; - stepx = 1; + stepx = -1; stepy = 0; - case6s.insert(Point2i(curx, cury)); } break; default: ERR_PRINT("this shouldn't happen."); } + + // Handle crossing points. + if (sv == 6 || sv == 9) { + const Point2i cur_pos(curx, cury); + + // Find if this point has occurred before. + if (HashMap<Point2i, int>::Iterator found = cross_map.find(cur_pos)) { + // Add points after the previous crossing to the result. + ret.push_back(_points.slice(found->value + 1, points_size)); + + // Remove points after crossing point. + points_size = found->value + 1; + + // Erase trailing map elements. + while (cross_map.last() != found) { + cross_map.remove(cross_map.last()); + } + + cross_map.erase(cur_pos); + } else { + // Add crossing point to map. + cross_map.insert(cur_pos, points_size - 1); + } + } + // Small optimization: // If the previous direction is same as the current direction, // then we should modify the last vector to current. curx += stepx; cury += stepy; if (stepx == prevx && stepy == prevy) { - _points.write[_points.size() - 1].x = (float)(curx - p_rect.position.x); - _points.write[_points.size() - 1].y = (float)(cury + p_rect.position.y); + _points.set(points_size - 1, Vector2(curx, cury) - p_rect.position); } else { - _points.push_back(Vector2((float)(curx - p_rect.position.x), (float)(cury + p_rect.position.y))); + _points.resize(MAX(points_size + 1, _points.size())); + _points.set(points_size, Vector2(curx, cury) - p_rect.position); + points_size++; } count++; prevx = stepx; prevy = stepy; - ERR_FAIL_COND_V((int)count > width * height, _points); + ERR_FAIL_COND_V((int)count > width * height, Vector<Vector<Vector2>>()); } while (curx != startx || cury != starty); - return _points; + + // Add remaining points to result. + _points.resize(points_size); + + ret.set(0, _points); + + return ret; } static float perpendicular_distance(const Vector2 &i, const Vector2 &start, const Vector2 &end) { @@ -442,7 +475,7 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ for (int j = next_j; j <= pos.y + 1; j++) { if (popped) { // The next loop over j must start normally. - next_j = pos.y; + next_j = pos.y - 1; popped = false; // Skip because an iteration was already executed with current counter values. continue; @@ -486,13 +519,10 @@ static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_ } } } while (reenter || popped); - - print_verbose("BitMap: Max stack size: " + itos(stack.size())); } Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2i &p_rect, float p_epsilon) const { Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect); - print_verbose("BitMap: Rect: " + r); Point2i from; Ref<BitMap> fill; @@ -505,17 +535,16 @@ Vector<Vector<Vector2>> BitMap::clip_opaque_to_polygons(const Rect2i &p_rect, fl if (!fill->get_bit(j, i) && get_bit(j, i)) { fill_bits(this, fill, Point2i(j, i), r); - Vector<Vector2> polygon = _march_square(r, Point2i(j, i)); - print_verbose("BitMap: Pre reduce: " + itos(polygon.size())); - polygon = reduce(polygon, r, p_epsilon); - print_verbose("BitMap: Post reduce: " + itos(polygon.size())); + for (Vector<Vector2> polygon : _march_square(r, Point2i(j, i))) { + polygon = reduce(polygon, r, p_epsilon); - if (polygon.size() < 3) { - print_verbose("Invalid polygon, skipped"); - continue; - } + if (polygon.size() < 3) { + print_verbose("Invalid polygon, skipped"); + continue; + } - polygons.push_back(polygon); + polygons.push_back(polygon); + } } } } diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index 291ed8c4d0..0ec5772fd1 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -46,7 +46,7 @@ class BitMap : public Resource { int width = 0; int height = 0; - Vector<Vector2> _march_square(const Rect2i &p_rect, const Point2i &p_start) const; + Vector<Vector<Vector2>> _march_square(const Rect2i &p_rect, const Point2i &p_start) const; TypedArray<PackedVector2Array> _opaque_to_polygons_bind(const Rect2i &p_rect, float p_epsilon) const; diff --git a/scene/resources/bone_map.cpp b/scene/resources/bone_map.cpp index dfaf82f36a..5698e61004 100644 --- a/scene/resources/bone_map.cpp +++ b/scene/resources/bone_map.cpp @@ -93,7 +93,7 @@ void BoneMap::set_skeleton_bone_name(StringName p_profile_bone_name, const Strin } StringName BoneMap::find_profile_bone_name(StringName p_skeleton_bone_name) const { - StringName profile_bone_name = StringName(); + StringName profile_bone_name; HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); while (E) { if (E->value == p_skeleton_bone_name) { diff --git a/scene/resources/camera_attributes.cpp b/scene/resources/camera_attributes.cpp index 3c322f32b6..8e4876e01f 100644 --- a/scene/resources/camera_attributes.cpp +++ b/scene/resources/camera_attributes.cpp @@ -120,7 +120,7 @@ void CameraAttributes::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_exposure_scale", "exposure_grey"), &CameraAttributes::set_auto_exposure_scale); ClassDB::bind_method(D_METHOD("get_auto_exposure_scale"), &CameraAttributes::get_auto_exposure_scale); - ADD_GROUP("Exposure", "exposure"); + ADD_GROUP("Exposure", "exposure_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_sensitivity", PROPERTY_HINT_RANGE, "0.1,32000.0,0.1,suffix:ISO"), "set_exposure_sensitivity", "get_exposure_sensitivity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_multiplier", PROPERTY_HINT_RANGE, "0.0,2048.0,0.001"), "set_exposure_multiplier", "get_exposure_multiplier"); @@ -472,7 +472,7 @@ void CameraAttributesPhysical::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp,suffix:m"), "set_near", "get_near"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frustum_far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp,suffix:m"), "set_far", "get_far"); - ADD_GROUP("Exposure", "exposure"); + ADD_GROUP("Exposure", "exposure_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_aperture", PROPERTY_HINT_RANGE, "0.5,64.0,0.01,exp,suffix:f-stop"), "set_aperture", "get_aperture"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure_shutter_speed", PROPERTY_HINT_RANGE, "0.1,8000.0,0.001,suffix:1/s"), "set_shutter_speed", "get_shutter_speed"); diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index eda9af9dde..be9c0dc725 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -31,6 +31,7 @@ #include "curve.h" #include "core/core_string_names.h" +#include "core/math/math_funcs.h" const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed"; @@ -340,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 * / \ @@ -773,6 +774,35 @@ 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); + } +} + +Vector2 Curve2D::_calculate_tangent(const Vector2 &p_begin, const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) { + // Handle corner cases. + if (Math::is_zero_approx(p_t - 0.0f) && p_control_1.is_equal_approx(p_begin)) { + return (p_end - p_begin).normalized(); + } + + if (Math::is_zero_approx(p_t - 1.0f) && p_control_2.is_equal_approx(p_end)) { + return (p_end - p_begin).normalized(); + } + + return p_begin.bezier_derivative(p_control_1, p_control_2, p_end, p_t).normalized(); +} + void Curve2D::_bake() const { if (!baked_cache_dirty) { return; @@ -784,94 +814,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; + // Tessellate curve to (almost) even length segments + { + Vector<RBMap<real_t, Vector2>> midpoints = _tessellate_even_length(10, bake_interval); - 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; - - 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] = _calculate_tangent(points[0].position, points[0].position + points[0].out, points[1].position + points[1].in, points[1].position, 0.0); + 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] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, E.key); + } - Vector2 *w = baked_point_cache.ptrw(); - real_t *wd = baked_dist_cache.ptrw(); + pidx++; + bpw[pidx] = points[i + 1].position; + bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, 1.0); + } - 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]; } } @@ -883,27 +881,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; @@ -923,9 +909,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]; @@ -936,44 +940,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]; } - Vector2 normal_of_curve = -tangent_to_curve.orthogonal(); + p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic. - return Transform2D(normal_of_curve, tangent_to_curve, pos); + 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(); + } + + // 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 frame; } PackedVector2Array Curve2D::get_baked_points() const { @@ -1014,10 +1044,11 @@ Vector2 Curve2D::get_closest_point(const Vector2 &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]; Vector2 origin = r[i]; - Vector2 direction = (r[i + 1] - origin) / bake_interval; + Vector2 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); Vector2 proj = origin + direction * d; real_t dist = proj.distance_squared_to(p_to_point); @@ -1053,10 +1084,13 @@ real_t Curve2D::get_closest_offset(const Vector2 &p_to_point) const { real_t offset = 0.0f; 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]; Vector2 origin = r[i]; - Vector2 direction = (r[i + 1] - origin) / bake_interval; + Vector2 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); Vector2 proj = origin + direction * d; real_t dist = proj.distance_squared_to(p_to_point); @@ -1065,8 +1099,6 @@ real_t Curve2D::get_closest_offset(const Vector2 &p_to_point) const { nearest = offset + d; nearest_dist = dist; } - - offset += bake_interval; } return nearest; @@ -1146,6 +1178,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()) { @@ -1223,12 +1299,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); @@ -1402,6 +1479,35 @@ void Curve3D::_bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, re } } +void Curve3D::_bake_segment3d_even_length(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_length) const { + 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); + + 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_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); + } +} + +Vector3 Curve3D::_calculate_tangent(const Vector3 &p_begin, const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) { + // Handle corner cases. + if (Math::is_zero_approx(p_t - 0.0f) && p_control_1.is_equal_approx(p_begin)) { + return (p_end - p_begin).normalized(); + } + + if (Math::is_zero_approx(p_t - 1.0f) && p_control_2.is_equal_approx(p_end)) { + return (p_end - p_begin).normalized(); + } + + return p_begin.bezier_derivative(p_control_1, p_control_2, p_end, p_t).normalized(); +} + void Curve3D::_bake() const { if (!baked_cache_dirty) { return; @@ -1413,8 +1519,10 @@ void Curve3D::_bake() const { if (points.size() == 0) { baked_point_cache.clear(); baked_tilt_cache.clear(); - baked_up_vector_cache.clear(); baked_dist_cache.clear(); + + baked_forward_vector_cache.clear(); + baked_up_vector_cache.clear(); return; } @@ -1425,10 +1533,12 @@ void Curve3D::_bake() const { baked_tilt_cache.set(0, points[0].tilt); baked_dist_cache.resize(1); baked_dist_cache.set(0, 0.0); + baked_forward_vector_cache.resize(1); + baked_forward_vector_cache.set(0, Vector3(0.0, 0.0, 1.0)); if (up_vector_enabled) { baked_up_vector_cache.resize(1); - baked_up_vector_cache.set(0, Vector3(0, 1, 0)); + baked_up_vector_cache.set(0, Vector3(0.0, 1.0, 0.0)); } else { baked_up_vector_cache.clear(); } @@ -1436,136 +1546,135 @@ void Curve3D::_bake() const { return; } - Vector3 position = points[0].position; - real_t dist = 0.0; - List<Plane> pointlist; - List<real_t> distlist; - - // Start always from origin. - pointlist.push_back(Plane(position, points[0].tilt)); - distlist.push_back(0.0); + // Step 1: Tessellate curve to (almost) even length segments + { + Vector<RBMap<real_t, Vector3>> midpoints = _tessellate_even_length(10, bake_interval); - 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; + int pc = 1; + for (int i = 0; i < points.size() - 1; i++) { + pc++; + pc += midpoints[i].size(); + } - while (p < 1.0) { - real_t np = p + step; - if (np > 1.0) { - np = 1.0; + baked_point_cache.resize(pc); + baked_tilt_cache.resize(pc); + baked_dist_cache.resize(pc); + baked_forward_vector_cache.resize(pc); + + Vector3 *bpw = baked_point_cache.ptrw(); + real_t *btw = baked_tilt_cache.ptrw(); + Vector3 *bfw = baked_forward_vector_cache.ptrw(); + + // Collect positions and sample tilts and tangents for each baked points. + bpw[0] = points[0].position; + bfw[0] = _calculate_tangent(points[0].position, points[0].position + points[0].out, points[1].position + points[1].in, points[1].position, 0.0); + btw[0] = points[0].tilt; + int pidx = 0; + + for (int i = 0; i < points.size() - 1; i++) { + for (const KeyValue<real_t, Vector3> &E : midpoints[i]) { + pidx++; + bpw[pidx] = E.value; + bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, E.key); + btw[pidx] = Math::lerp(points[i].tilt, points[i + 1].tilt, E.key); } - Vector3 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; - Plane post; - post.normal = position; - post.d = Math::lerp(points[i].tilt, points[i + 1].tilt, mid); - dist += d; - - pointlist.push_back(post); - distlist.push_back(dist); - } else { - p = np; - } + pidx++; + bpw[pidx] = points[i + 1].position; + bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, 1.0); + btw[pidx] = points[i + 1].tilt; } - Vector3 npp = points[i + 1].position; - real_t d = position.distance_to(npp); - - position = npp; - Plane post; - post.normal = position; - post.d = points[i + 1].tilt; - - dist += d; - - pointlist.push_back(post); - distlist.push_back(dist); + // 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]; } - baked_max_ofs = dist; - - baked_point_cache.resize(pointlist.size()); - Vector3 *w = baked_point_cache.ptrw(); - int idx = 0; + if (!up_vector_enabled) { + baked_up_vector_cache.resize(0); + return; + } - baked_tilt_cache.resize(pointlist.size()); - real_t *wt = baked_tilt_cache.ptrw(); + // Step 2: Calculate the up vectors and the whole local reference frame + // + // See Dougan, Carl. "The parallel transport frame." Game Programming Gems 2 (2001): 215-219. + // for an example discussing about why not the Frenet frame. + { + int point_count = baked_point_cache.size(); - baked_up_vector_cache.resize(up_vector_enabled ? pointlist.size() : 0); - Vector3 *up_write = baked_up_vector_cache.ptrw(); + baked_up_vector_cache.resize(point_count); + Vector3 *up_write = baked_up_vector_cache.ptrw(); - baked_dist_cache.resize(pointlist.size()); - real_t *wd = baked_dist_cache.ptrw(); + const Vector3 *forward_ptr = baked_forward_vector_cache.ptr(); + const Vector3 *points_ptr = baked_point_cache.ptr(); - Vector3 sideways; - Vector3 up; - Vector3 forward; + Basis frame; // X-right, Y-up, Z-forward. + Basis frame_prev; - Vector3 prev_sideways = Vector3(1, 0, 0); - Vector3 prev_up = Vector3(0, 1, 0); - Vector3 prev_forward = Vector3(0, 0, 1); + // Set the initial frame based on Y-up rule. + { + Vector3 forward = forward_ptr[0]; - for (const Plane &E : pointlist) { - w[idx] = E.normal; - wt[idx] = E.d; - wd[idx] = distlist[idx]; + if (abs(forward.dot(Vector3(0, 1, 0))) > 1.0 - UNIT_EPSILON) { + frame_prev = Basis::looking_at(-forward, Vector3(1, 0, 0)); + } else { + frame_prev = Basis::looking_at(-forward, Vector3(0, 1, 0)); + } - if (!up_vector_enabled) { - idx++; - continue; + up_write[0] = frame_prev.get_column(1); } - forward = idx > 0 ? (w[idx] - w[idx - 1]).normalized() : prev_forward; + // Calculate the Parallel Transport Frame. + for (int idx = 1; idx < point_count; idx++) { + Vector3 forward = forward_ptr[idx]; - real_t y_dot = prev_up.dot(forward); + Basis rotate; + rotate.rotate_to_align(frame_prev.get_column(2), forward); + frame = rotate * frame_prev; + frame.orthonormalize(); // guard against float error accumulation - if (y_dot > (1.0f - CMP_EPSILON)) { - sideways = prev_sideways; - up = -prev_forward; - } else if (y_dot < -(1.0f - CMP_EPSILON)) { - sideways = prev_sideways; - up = prev_forward; - } else { - sideways = prev_up.cross(forward).normalized(); - up = forward.cross(sideways).normalized(); + up_write[idx] = frame.get_column(1); + frame_prev = frame; } - if (idx == 1) { - up_write[0] = up; + bool is_loop = true; + // Loop smoothing only applies when the curve is a loop, which means two ends meet, and share forward directions. + { + if (!points_ptr[0].is_equal_approx(points_ptr[point_count - 1])) { + is_loop = false; + } + + real_t dot = forward_ptr[0].dot(forward_ptr[point_count - 1]); + if (dot < 1.0 - UNIT_EPSILON) { // Alignment should not be too tight, or it doesn't work for coarse bake interval. + is_loop = false; + } } - up_write[idx] = up; + // Twist up vectors, so that they align at two ends of the curve. + if (is_loop) { + const Vector3 up_start = up_write[0]; + const Vector3 up_end = up_write[point_count - 1]; - prev_sideways = sideways; - prev_up = up; - prev_forward = forward; + real_t sign = SIGN(up_end.cross(up_start).dot(forward_ptr[0])); + real_t full_angle = Quaternion(up_end, up_start).get_angle(); - idx++; + if (abs(full_angle) < CMP_EPSILON) { + return; + } else { + const real_t *dists = baked_dist_cache.ptr(); + for (int idx = 1; idx < point_count; idx++) { + const real_t frac = dists[idx] / baked_max_ofs; + const real_t angle = Math::lerp((real_t)0.0, full_angle, frac); + Basis twist(forward_ptr[idx] * sign, angle); + + up_write[idx] = twist.xform(up_write[idx]); + } + } + } } } @@ -1577,27 +1686,15 @@ real_t Curve3D::get_baked_length() const { return baked_max_ofs; } -Vector3 Curve3D::sample_baked(real_t p_offset, bool p_cubic) const { - if (baked_cache_dirty) { - _bake(); - } +Curve3D::Interval Curve3D::_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, Vector3(), "No points in Curve3D."); - - if (pc == 1) { - return baked_point_cache.get(0); - } - - const Vector3 *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; @@ -1617,9 +1714,27 @@ Vector3 Curve3D::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, Vector3(), "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; +} + +Vector3 Curve3D::_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(), Vector3(), "Invalid interval"); + + int idx = p_interval.idx; + real_t frac = p_interval.frac; - real_t frac = (p_offset - offset_begin) / idx_interval; + const Vector3 *r = baked_point_cache.ptr(); + int pc = baked_point_cache.size(); if (p_cubic) { Vector3 pre = idx > 0 ? r[idx - 1] : r[idx]; @@ -1630,114 +1745,142 @@ Vector3 Curve3D::sample_baked(real_t p_offset, bool p_cubic) const { } } -real_t Curve3D::sample_baked_tilt(real_t p_offset) const { - if (baked_cache_dirty) { - _bake(); - } +real_t Curve3D::_sample_baked_tilt(Interval p_interval) const { + // Assuming that p_interval is valid. + ERR_FAIL_INDEX_V_MSG(p_interval.idx, baked_tilt_cache.size(), 0.0, "Invalid interval"); - // Validate: Curve may not have baked tilts. - int pc = baked_tilt_cache.size(); - ERR_FAIL_COND_V_MSG(pc == 0, 0, "No tilts in Curve3D."); + int idx = p_interval.idx; + real_t frac = p_interval.frac; - if (pc == 1) { - return baked_tilt_cache.get(0); + const real_t *r = baked_tilt_cache.ptr(); + + return Math::lerp(r[idx], r[idx + 1], frac); +} + +Basis Curve3D::_sample_posture(Interval p_interval, bool p_apply_tilt) const { + // Assuming that p_interval is valid. + ERR_FAIL_INDEX_V_MSG(p_interval.idx, baked_point_cache.size(), Basis(), "Invalid interval"); + if (up_vector_enabled) { + ERR_FAIL_INDEX_V_MSG(p_interval.idx, baked_up_vector_cache.size(), Basis(), "Invalid interval"); } - const real_t *r = baked_tilt_cache.ptr(); + int idx = p_interval.idx; + real_t frac = p_interval.frac; - if (p_offset < 0) { - return r[0]; + Vector3 forward_begin = baked_forward_vector_cache[idx]; + Vector3 forward_end = baked_forward_vector_cache[idx + 1]; + + Vector3 up_begin; + Vector3 up_end; + if (up_vector_enabled) { + up_begin = baked_up_vector_cache[idx]; + up_end = baked_up_vector_cache[idx + 1]; + } else { + up_begin = Vector3(0.0, 1.0, 0.0); + up_end = Vector3(0.0, 1.0, 0.0); } - if (p_offset >= baked_max_ofs) { - return r[pc - 1]; + + // Build frames at both ends of the interval, then interpolate. + const Basis frame_begin = Basis::looking_at(-forward_begin, up_begin); + const Basis frame_end = Basis::looking_at(-forward_end, up_end); + const Basis frame = frame_begin.slerp(frame_end, frac).orthonormalized(); + + if (!p_apply_tilt) { + return frame; } - int start = 0; - int end = pc; - int idx = (end + start) / 2; - // Binary search to find baked points. - while (start < idx) { - real_t offset = baked_dist_cache[idx]; - if (p_offset <= offset) { - end = idx; - } else { - start = idx; - } - idx = (end + start) / 2; + // Applying tilt. + const real_t tilt = _sample_baked_tilt(p_interval); + Vector3 forward = frame.get_column(2); + + const Basis twist(forward, tilt); + return twist * frame; +} + +Vector3 Curve3D::sample_baked(real_t p_offset, bool p_cubic) const { + if (baked_cache_dirty) { + _bake(); } - real_t offset_begin = baked_dist_cache[idx]; - real_t offset_end = baked_dist_cache[idx + 1]; + // Validate: Curve may not have baked points. + int pc = baked_point_cache.size(); + ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D."); - real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, 0, "Couldn't find baked segment."); + if (pc == 1) { + return baked_point_cache[0]; + } - real_t frac = (p_offset - offset_begin) / idx_interval; + p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic. - return Math::lerp(r[idx], r[idx + 1], (real_t)frac); + Curve3D::Interval interval = _find_interval(p_offset); + return _sample_baked(interval, p_cubic); } -Vector3 Curve3D::sample_baked_up_vector(real_t p_offset, bool p_apply_tilt) const { +Transform3D Curve3D::sample_baked_with_rotation(real_t p_offset, bool p_cubic, bool p_apply_tilt) const { if (baked_cache_dirty) { _bake(); } - // Validate: Curve may not have baked up vectors. - int count = baked_up_vector_cache.size(); - ERR_FAIL_COND_V_MSG(count == 0, Vector3(0, 1, 0), "No up vectors in Curve3D."); + // Validate: Curve may not have baked points. + const int point_count = baked_point_cache.size(); + ERR_FAIL_COND_V_MSG(point_count == 0, Transform3D(), "No points in Curve3D."); - if (count == 1) { - return baked_up_vector_cache.get(0); + if (point_count == 1) { + Transform3D t; + t.origin = baked_point_cache.get(0); + ERR_FAIL_V_MSG(t, "Only 1 point in Curve3D."); } - const Vector3 *r = baked_up_vector_cache.ptr(); - const Vector3 *rp = baked_point_cache.ptr(); - const real_t *rt = baked_tilt_cache.ptr(); + p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic. - int start = 0; - int end = count; - int idx = (end + start) / 2; - // Binary search to find baked points. - while (start < idx) { - real_t offset = baked_dist_cache[idx]; - if (p_offset <= offset) { - end = idx; - } else { - start = idx; - } - idx = (end + start) / 2; - } + // 0. Find interval for all sampling steps. + Curve3D::Interval interval = _find_interval(p_offset); - if (idx == count - 1) { - return p_apply_tilt ? r[idx].rotated((rp[idx] - rp[idx - 1]).normalized(), rt[idx]) : r[idx]; + // 1. Sample position. + Vector3 pos = _sample_baked(interval, p_cubic); + + // 2. Sample rotation frame. + Basis frame = _sample_posture(interval, p_apply_tilt); + + return Transform3D(frame, pos); +} + +real_t Curve3D::sample_baked_tilt(real_t p_offset) const { + if (baked_cache_dirty) { + _bake(); } - real_t offset_begin = baked_dist_cache[idx]; - real_t offset_end = baked_dist_cache[idx + 1]; + // Validate: Curve may not have baked tilts. + int pc = baked_tilt_cache.size(); + ERR_FAIL_COND_V_MSG(pc == 0, 0, "No tilts in Curve3D."); - real_t idx_interval = offset_end - offset_begin; - ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(0, 1, 0), "Couldn't find baked segment."); + if (pc == 1) { + return baked_tilt_cache.get(0); + } - real_t frac = (p_offset - offset_begin) / idx_interval; + p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic - Vector3 forward = (rp[idx + 1] - rp[idx]).normalized(); - Vector3 up = r[idx]; - Vector3 up1 = r[idx + 1]; + Curve3D::Interval interval = _find_interval(p_offset); + return _sample_baked_tilt(interval); +} - if (p_apply_tilt) { - up.rotate(forward, rt[idx]); - up1.rotate(idx + 2 >= count ? forward : (rp[idx + 2] - rp[idx + 1]).normalized(), rt[idx + 1]); +Vector3 Curve3D::sample_baked_up_vector(real_t p_offset, bool p_apply_tilt) const { + if (baked_cache_dirty) { + _bake(); } - Vector3 axis = up.cross(up1); + // Validate: Curve may not have baked up vectors. + ERR_FAIL_COND_V_MSG(!up_vector_enabled, Vector3(0, 1, 0), "No up vectors in Curve3D."); - if (axis.length_squared() < CMP_EPSILON2) { - axis = forward; - } else { - axis.normalize(); + int count = baked_up_vector_cache.size(); + if (count == 1) { + return baked_up_vector_cache.get(0); } - return up.rotated(axis, up.angle_to(up1) * frac); + p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic. + + Curve3D::Interval interval = _find_interval(p_offset); + return _sample_posture(interval, p_apply_tilt).get_column(1); } PackedVector3Array Curve3D::get_baked_points() const { @@ -1785,10 +1928,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); @@ -1821,13 +1965,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); @@ -1836,8 +1983,6 @@ real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const { nearest = offset + d; nearest_dist = dist; } - - offset += bake_interval; } return nearest; @@ -1942,6 +2087,50 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) con return tess; } +Vector<RBMap<real_t, Vector3>> Curve3D::_tessellate_even_length(int p_max_stages, real_t p_length) const { + Vector<RBMap<real_t, Vector3>> 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_segment3d_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; +} + +PackedVector3Array Curve3D::tessellate_even_length(int p_max_stages, real_t p_length) const { + PackedVector3Array tess; + + Vector<RBMap<real_t, Vector3>> 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); + Vector3 *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, Vector3> &E : midpoints[i]) { + pidx++; + bpw[pidx] = E.value; + } + + pidx++; + bpw[pidx] = points[i + 1].position; + } + + return tess; +} + bool Curve3D::_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()) { @@ -2033,7 +2222,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", "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); @@ -2041,6 +2231,7 @@ void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve3D::get_closest_point); ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve3D::get_closest_offset); ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve3D::tessellate, DEFVAL(5), DEFVAL(4)); + ClassDB::bind_method(D_METHOD("tessellate_even_length", "max_stages", "tolerance_length"), &Curve3D::tessellate_even_length, DEFVAL(5), DEFVAL(0.2)); ClassDB::bind_method(D_METHOD("_get_data"), &Curve3D::_get_data); ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve3D::_set_data); diff --git a/scene/resources/curve.h b/scene/resources/curve.h index fa1d35aab1..26608c47cd 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -172,16 +172,27 @@ 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; void mark_dirty(); + static Vector2 _calculate_tangent(const Vector2 &p_begin, const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t); void _bake() const; 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 +203,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 +229,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(); }; @@ -238,26 +252,33 @@ class Curve3D : public Resource { Vector<Point> points; - struct BakedPoint { - real_t ofs = 0.0; - Vector3 point; - }; - mutable bool baked_cache_dirty = false; mutable PackedVector3Array baked_point_cache; mutable Vector<real_t> baked_tilt_cache; mutable PackedVector3Array baked_up_vector_cache; + mutable PackedVector3Array baked_forward_vector_cache; mutable Vector<real_t> baked_dist_cache; mutable real_t baked_max_ofs = 0.0; void mark_dirty(); + static Vector3 _calculate_tangent(const Vector3 &p_begin, const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t); void _bake() const; + struct Interval { + int idx; + real_t frac; + }; + Interval _find_interval(real_t p_offset) const; + Vector3 _sample_baked(Interval p_interval, bool p_cubic) const; + real_t _sample_baked_tilt(Interval p_interval) const; + Basis _sample_posture(Interval p_interval, bool p_apply_tilt = false) const; + real_t bake_interval = 0.2; bool up_vector_enabled = true; void _bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const; + void _bake_segment3d_even_length(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &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); @@ -268,6 +289,8 @@ class Curve3D : public Resource { void _add_point(const Vector3 &p_position, const Vector3 &p_in = Vector3(), const Vector3 &p_out = Vector3(), int p_atpos = -1); void _remove_point(int p_index); + Vector<RBMap<real_t, Vector3>> _tessellate_even_length(int p_max_stages = 5, real_t p_length = 0.2) const; + protected: static void _bind_methods(); @@ -296,15 +319,17 @@ public: real_t get_baked_length() const; Vector3 sample_baked(real_t p_offset, bool p_cubic = false) const; + Transform3D sample_baked_with_rotation(real_t p_offset, bool p_cubic = false, bool p_apply_tilt = false) const; real_t sample_baked_tilt(real_t p_offset) const; Vector3 sample_baked_up_vector(real_t p_offset, bool p_apply_tilt = false) const; - PackedVector3Array get_baked_points() const; //useful for going through + PackedVector3Array get_baked_points() const; // Useful for going through. Vector<real_t> get_baked_tilts() const; //useful for going through PackedVector3Array get_baked_up_vectors() const; Vector3 get_closest_point(const Vector3 &p_to_point) const; real_t get_closest_offset(const Vector3 &p_to_point) const; - PackedVector3Array tessellate(int p_max_stages = 5, real_t p_tolerance = 4) const; //useful for display + PackedVector3Array tessellate(int p_max_stages = 5, real_t p_tolerance = 4) const; // Useful for display. + PackedVector3Array tessellate_even_length(int p_max_stages = 5, real_t p_length = 0.2) const; // Useful for baking. Curve3D(); }; diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 894936acd7..571ddee763 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -84,7 +84,8 @@ static Ref<ImageTexture> generate_icon(int p_index) { // with integer scales. const bool upsample = !Math::is_equal_approx(Math::round(scale), scale); ImageLoaderSVG img_loader; - img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap<Color, Color>()); + Error err = img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap<Color, Color>()); + ERR_FAIL_COND_V_MSG(err != OK, Ref<ImageTexture>(), "Failed generating icon, unsupported or invalid SVG data in default theme."); #endif return ImageTexture::create_from_image(img); @@ -99,6 +100,11 @@ static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margi void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const Ref<Font> &bold_font, const Ref<Font> &bold_italics_font, const Ref<Font> &italics_font, Ref<Texture2D> &default_icon, Ref<StyleBox> &default_style, float p_scale) { scale = p_scale; + // Default theme properties. + theme->set_default_font(default_font); + theme->set_default_font_size(default_font_size * scale); + theme->set_default_base_scale(scale); + // Font colors const Color control_font_color = Color(0.875, 0.875, 0.875); const Color control_font_low_color = Color(0.7, 0.7, 0.7); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 185b6f4bdc..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,16 +1023,20 @@ 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"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font"), PROPERTY_USAGE_STORAGE), "set_fallbacks", "get_fallbacks"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font"), PROPERTY_USAGE_STORAGE), "set_fallbacks", "get_fallbacks"); } bool FontFile::_set(const StringName &p_name, const Variant &p_value) { @@ -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; @@ -1396,6 +1418,9 @@ Error FontFile::load_bitmap_font(const String &p_path) { case 1: /* info */ { ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, RTR("Invalid BMFont info block size.")); base_size = f->get_16(); + if (base_size <= 0) { + base_size = 16; + } uint8_t flags = f->get_8(); if (flags & (1 << 3)) { st_flags.set_flag(TextServer::FONT_BOLD); @@ -1681,7 +1706,6 @@ Error FontFile::load_bitmap_font(const String &p_path) { if (type == "info") { if (keys.has("size")) { base_size = keys["size"].to_int(); - set_fixed_size(base_size); } if (keys.has("outline")) { outline = keys["outline"].to_int(); @@ -1730,6 +1754,7 @@ Error FontFile::load_bitmap_font(const String &p_path) { encoding = 2; } } + set_fixed_size(base_size); } else if (type == "common") { if (keys.has("lineHeight")) { height = keys["lineHeight"].to_int(); @@ -1788,7 +1813,10 @@ Error FontFile::load_bitmap_font(const String &p_path) { ERR_FAIL_V_MSG(ERR_CANT_CREATE, RTR("Unsupported BMFont texture format.")); } } else { - if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline + if ((ch[3] == 0) && (ch[0] == 4) && (ch[1] == 4) && (ch[2] == 4) && img->get_format() == Image::FORMAT_RGBA8) { // might be RGBA8 color, no outline (color part of the image should be sold white, but some apps designed for Godot 3 generate color fonts with this config) + outline = 0; + set_texture_image(0, Vector2i(base_size, 0), page, img); + } else if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline outline = 0; ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, RTR("Unsupported BMFont texture format.")); set_texture_image(0, Vector2i(base_size, 0), page, img); @@ -1931,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); @@ -1940,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; @@ -1994,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; @@ -2084,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; @@ -2597,18 +2653,18 @@ void FontVariation::_bind_methods() { ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &FontVariation::set_spacing); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_base_font", "get_base_font"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks"); - ADD_GROUP("Variation", "variation"); + ADD_GROUP("Variation", "variation_"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_opentype"), "set_variation_opentype", "get_variation_opentype"); ADD_PROPERTY(PropertyInfo(Variant::INT, "variation_face_index"), "set_variation_face_index", "get_variation_face_index"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "variation_embolden", PROPERTY_HINT_RANGE, "-2,2,0.01"), "set_variation_embolden", "get_variation_embolden"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "variation_transform", PROPERTY_HINT_NONE, "suffix:px"), "set_variation_transform", "get_variation_transform"); - ADD_GROUP("OpenType Features", "opentype"); + ADD_GROUP("OpenType Features", "opentype_"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_features"), "set_opentype_features", "get_opentype_features"); - ADD_GROUP("Extra Spacing", "spacing"); + ADD_GROUP("Extra Spacing", "spacing_"); ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_glyph", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_GLYPH); ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_space", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_SPACE); ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_TOP); @@ -2833,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); @@ -2851,18 +2910,24 @@ 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"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field"), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), "set_oversampling", "get_oversampling"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("Font")), "set_fallbacks", "get_fallbacks"); } void SystemFont::_update_rids() const { @@ -2893,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; } @@ -2911,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); } } @@ -2922,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); @@ -2967,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; @@ -3063,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; @@ -3144,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 { @@ -3170,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; } @@ -3192,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/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index b728c24e0d..d1278f9340 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -255,7 +255,7 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma } #define VERTEX_SKIN_FUNC(bone_count, vert_idx, read_array, write_array, transform_array, bone_array, weight_array) \ - Vector3 transformed_vert = Vector3(); \ + Vector3 transformed_vert; \ for (unsigned int weight_idx = 0; weight_idx < bone_count; weight_idx++) { \ int bone_idx = bone_array[vert_idx * bone_count + weight_idx]; \ float w = weight_array[vert_idx * bone_count + weight_idx]; \ @@ -971,10 +971,10 @@ Vector<Ref<Shape3D>> ImporterMesh::convex_decompose(const Mesh::ConvexDecomposit return ret; } -Ref<Shape3D> ImporterMesh::create_trimesh_shape() const { +Ref<ConcavePolygonShape3D> ImporterMesh::create_trimesh_shape() const { Vector<Face3> faces = get_faces(); if (faces.size() == 0) { - return Ref<Shape3D>(); + return Ref<ConcavePolygonShape3D>(); } Vector<Vector3> face_points; diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h index 088a77edd1..bbd6498fcf 100644 --- a/scene/resources/importer_mesh.h +++ b/scene/resources/importer_mesh.h @@ -119,7 +119,7 @@ public: Vector<Face3> get_faces() const; Vector<Ref<Shape3D>> convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const; - Ref<Shape3D> create_trimesh_shape() const; + Ref<ConcavePolygonShape3D> create_trimesh_shape() const; Ref<NavigationMesh> create_navigation_mesh(); Error lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache); diff --git a/scene/resources/label_settings.cpp b/scene/resources/label_settings.cpp index ef380a68f9..c49620ce27 100644 --- a/scene/resources/label_settings.cpp +++ b/scene/resources/label_settings.cpp @@ -66,16 +66,16 @@ void LabelSettings::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing"); - ADD_GROUP("Font", "font"); + ADD_GROUP("Font", "font_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font"); ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,1024,1,or_greater,suffix:px"), "set_font_size", "get_font_size"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "font_color"), "set_font_color", "get_font_color"); - ADD_GROUP("Outline", "outline"); + ADD_GROUP("Outline", "outline_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,127,1,or_greater,suffix:px"), "set_outline_size", "get_outline_size"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_color"), "set_outline_color", "get_outline_color"); - ADD_GROUP("Shadow", "shadow"); + ADD_GROUP("Shadow", "shadow_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,127,1,or_greater,suffix:px"), "set_shadow_size", "get_shadow_size"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_shadow_offset", "get_shadow_offset"); 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 706db3af16..af770ddede 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 { @@ -388,7 +352,7 @@ Vector<Face3> Mesh::get_surface_faces(int p_surface) const { return Vector<Face3>(); } -Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const { +Ref<ConvexPolygonShape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const { if (p_simplify) { ConvexDecompositionSettings settings; settings.max_convex_hulls = 1; @@ -425,10 +389,10 @@ Ref<Shape3D> Mesh::create_convex_shape(bool p_clean, bool p_simplify) const { return shape; } -Ref<Shape3D> Mesh::create_trimesh_shape() const { +Ref<ConcavePolygonShape3D> Mesh::create_trimesh_shape() const { Vector<Face3> faces = get_faces(); if (faces.size() == 0) { - return Ref<Shape3D>(); + return Ref<ConcavePolygonShape3D>(); } Vector<Vector3> face_points; @@ -940,7 +904,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma dst[0] = (int16_t)CLAMP(src[0] / 127.0f * 32767, -32768, 32767); dst[1] = (int16_t)CLAMP(src[1] / 127.0f * 32767, -32768, 32767); } - src_offset += sizeof(int16_t) * 2; + src_offset += sizeof(int8_t) * 2; } else { for (uint32_t i = 0; i < p_elements; i++) { const int16_t *src = (const int16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; @@ -962,7 +926,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535); dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535); } - src_offset += sizeof(uint16_t) * 2; + src_offset += sizeof(uint8_t) * 4; // 1 byte padding } else { for (uint32_t i = 0; i < p_elements; i++) { const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; @@ -973,7 +937,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535); dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535); } - src_offset += sizeof(uint16_t) * 2; + src_offset += sizeof(float) * 3; } } @@ -988,7 +952,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma dst[0] = (uint16_t)CLAMP((src[0] / 127.0f * .5f + .5f) * 65535, 0, 65535); dst[1] = (uint16_t)CLAMP((src[1] / 127.0f * .5f + .5f) * 65535, 0, 65535); } - src_offset += sizeof(uint16_t) * 2; + src_offset += sizeof(uint8_t) * 2; } else { // int16 SNORM -> uint16 UNORM for (uint32_t i = 0; i < p_elements; i++) { const int16_t *src = (const int16_t *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; @@ -1010,7 +974,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535); dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535); } - src_offset += sizeof(uint16_t) * 2; + src_offset += sizeof(uint8_t) * 4; } else { for (uint32_t i = 0; i < p_elements; i++) { const float *src = (const float *)&src_vertex_ptr[i * src_vertex_stride + src_offset]; @@ -1021,7 +985,7 @@ void _fix_array_compatibility(const Vector<uint8_t> &p_src, uint32_t p_old_forma dst[0] = (uint16_t)CLAMP(res.x * 65535, 0, 65535); dst[1] = (uint16_t)CLAMP(res.y * 65535, 0, 65535); } - src_offset += sizeof(uint16_t) * 2; + src_offset += sizeof(float) * 4; } } } break; @@ -1154,7 +1118,19 @@ bool ArrayMesh::_set(const StringName &p_name, const Variant &p_value) { if (sl == -1) { return false; } - int idx = sname.substr(8, sl - 8).to_int() - 1; + int idx = sname.substr(8, sl - 8).to_int(); + + // This is a bit of a hack to ensure compatibility with older material + // overrides that start indexing at 1. + // We assume that idx 0 is always read first, if its not, this won't work. + if (idx == 0) { + surface_index_0 = true; + } + if (!surface_index_0) { + // This means the file was created when the indexing started at 1, so decrease by one. + idx--; + } + String what = sname.get_slicec('/', 1); if (what == "material") { surface_set_material(idx, p_value); @@ -1527,7 +1503,7 @@ bool ArrayMesh::_get(const StringName &p_name, Variant &r_ret) const { if (sl == -1) { return false; } - int idx = sname.substr(8, sl - 8).to_int() - 1; + int idx = sname.substr(8, sl - 8).to_int(); String what = sname.get_slicec('/', 1); if (what == "material") { r_ret = surface_get_material(idx); @@ -1555,11 +1531,11 @@ void ArrayMesh::_get_property_list(List<PropertyInfo> *p_list) const { } for (int i = 0; i < surfaces.size(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i + 1) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::STRING, "surface_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); if (surfaces[i].is_2d) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial", PROPERTY_USAGE_EDITOR)); } else { - p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i + 1) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_EDITOR)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_" + itos(i) + "/material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_EDITOR)); } } } diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index 5ed4164117..fabc09a42c 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -35,9 +35,12 @@ #include "core/math/face3.h" #include "core/math/triangle_mesh.h" #include "scene/resources/material.h" -#include "scene/resources/shape_3d.h" #include "servers/rendering_server.h" +class ConcavePolygonShape3D; +class ConvexPolygonShape3D; +class Shape3D; + class Mesh : public Resource { GDCLASS(Mesh, Resource); @@ -211,8 +214,8 @@ public: static ConvexDecompositionFunc convex_decomposition_function; Vector<Ref<Shape3D>> convex_decompose(const ConvexDecompositionSettings &p_settings) const; - Ref<Shape3D> create_convex_shape(bool p_clean = true, bool p_simplify = false) const; - Ref<Shape3D> create_trimesh_shape() const; + Ref<ConvexPolygonShape3D> create_convex_shape(bool p_clean = true, bool p_simplify = false) const; + Ref<ConcavePolygonShape3D> create_trimesh_shape() const; virtual int get_builtin_bind_pose_count() const; virtual Transform3D get_builtin_bind_pose(int p_index) const; @@ -259,6 +262,7 @@ protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + bool surface_index_0 = false; virtual void reset_state() override; diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp index 7c78b757c7..858146da96 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -57,10 +57,18 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) { _set_item_shapes(idx, p_value); } else if (what == "preview") { set_item_preview(idx, p_value); - } else if (what == "navmesh") { - set_item_navmesh(idx, p_value); - } else if (what == "navmesh_transform") { - set_item_navmesh_transform(idx, p_value); + } else if (what == "navigation_mesh") { + set_item_navigation_mesh(idx, p_value); + } else if (what == "navigation_mesh_transform") { + set_item_navigation_mesh_transform(idx, p_value); +#ifndef DISABLE_DEPRECATED + } else if (what == "navmesh") { // Renamed in 4.0 beta 9. + set_item_navigation_mesh(idx, p_value); + } else if (what == "navmesh_transform") { // Renamed in 4.0 beta 9. + set_item_navigation_mesh_transform(idx, p_value); +#endif // DISABLE_DEPRECATED + } else if (what == "navigation_layers") { + set_item_navigation_layers(idx, p_value); } else { return false; } @@ -85,10 +93,18 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_item_mesh_transform(idx); } else if (what == "shapes") { r_ret = _get_item_shapes(idx); - } else if (what == "navmesh") { - r_ret = get_item_navmesh(idx); - } else if (what == "navmesh_transform") { - r_ret = get_item_navmesh_transform(idx); + } else if (what == "navigation_mesh") { + r_ret = get_item_navigation_mesh(idx); + } else if (what == "navigation_mesh_transform") { + r_ret = get_item_navigation_mesh_transform(idx); +#ifndef DISABLE_DEPRECATED + } else if (what == "navmesh") { // Renamed in 4.0 beta 9. + r_ret = get_item_navigation_mesh(idx); + } else if (what == "navmesh_transform") { // Renamed in 4.0 beta 9. + r_ret = get_item_navigation_mesh_transform(idx); +#endif // DISABLE_DEPRECATED + } else if (what == "navigation_layers") { + r_ret = get_item_navigation_layers(idx); } else if (what == "preview") { r_ret = get_item_preview(idx); } else { @@ -105,8 +121,9 @@ void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("mesh"), PROPERTY_HINT_RESOURCE_TYPE, "Mesh")); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("mesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); p_list->push_back(PropertyInfo(Variant::ARRAY, prop_name + PNAME("shapes"))); - p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("navmesh"), PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh")); - p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("navmesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); + p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("navigation_mesh"), PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("navigation_mesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); + p_list->push_back(PropertyInfo(Variant::INT, prop_name + PNAME("navigation_layers"), PROPERTY_HINT_LAYERS_3D_NAVIGATION)); p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("preview"), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT)); } } @@ -150,18 +167,27 @@ void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) notify_property_list_changed(); } -void MeshLibrary::set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh) { +void MeshLibrary::set_item_navigation_mesh(int p_item, const Ref<NavigationMesh> &p_navigation_mesh) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); - item_map[p_item].navmesh = p_navmesh; + item_map[p_item].navigation_mesh = p_navigation_mesh; notify_property_list_changed(); notify_change_to_owners(); emit_changed(); notify_property_list_changed(); } -void MeshLibrary::set_item_navmesh_transform(int p_item, const Transform3D &p_transform) { +void MeshLibrary::set_item_navigation_mesh_transform(int p_item, const Transform3D &p_transform) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); - item_map[p_item].navmesh_transform = p_transform; + item_map[p_item].navigation_mesh_transform = p_transform; + notify_change_to_owners(); + emit_changed(); + notify_property_list_changed(); +} + +void MeshLibrary::set_item_navigation_layers(int p_item, uint32_t p_navigation_layers) { + ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); + item_map[p_item].navigation_layers = p_navigation_layers; + notify_property_list_changed(); notify_change_to_owners(); emit_changed(); notify_property_list_changed(); @@ -194,14 +220,19 @@ Vector<MeshLibrary::ShapeData> MeshLibrary::get_item_shapes(int p_item) const { return item_map[p_item].shapes; } -Ref<NavigationMesh> MeshLibrary::get_item_navmesh(int p_item) const { +Ref<NavigationMesh> MeshLibrary::get_item_navigation_mesh(int p_item) const { ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Ref<NavigationMesh>(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); - return item_map[p_item].navmesh; + return item_map[p_item].navigation_mesh; } -Transform3D MeshLibrary::get_item_navmesh_transform(int p_item) const { +Transform3D MeshLibrary::get_item_navigation_mesh_transform(int p_item) const { ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Transform3D(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); - return item_map[p_item].navmesh_transform; + return item_map[p_item].navigation_mesh_transform; +} + +uint32_t MeshLibrary::get_item_navigation_layers(int p_item) const { + ERR_FAIL_COND_V_MSG(!item_map.has(p_item), 0, "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); + return item_map[p_item].navigation_layers; } Ref<Texture2D> MeshLibrary::get_item_preview(int p_item) const { @@ -314,15 +345,17 @@ void MeshLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name); ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh); ClassDB::bind_method(D_METHOD("set_item_mesh_transform", "id", "mesh_transform"), &MeshLibrary::set_item_mesh_transform); - ClassDB::bind_method(D_METHOD("set_item_navmesh", "id", "navmesh"), &MeshLibrary::set_item_navmesh); - ClassDB::bind_method(D_METHOD("set_item_navmesh_transform", "id", "navmesh"), &MeshLibrary::set_item_navmesh_transform); + ClassDB::bind_method(D_METHOD("set_item_navigation_mesh", "id", "navigation_mesh"), &MeshLibrary::set_item_navigation_mesh); + ClassDB::bind_method(D_METHOD("set_item_navigation_mesh_transform", "id", "navigation_mesh"), &MeshLibrary::set_item_navigation_mesh_transform); + ClassDB::bind_method(D_METHOD("set_item_navigation_layers", "id", "navigation_layers"), &MeshLibrary::set_item_navigation_layers); ClassDB::bind_method(D_METHOD("set_item_shapes", "id", "shapes"), &MeshLibrary::_set_item_shapes); ClassDB::bind_method(D_METHOD("set_item_preview", "id", "texture"), &MeshLibrary::set_item_preview); ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name); ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh); ClassDB::bind_method(D_METHOD("get_item_mesh_transform", "id"), &MeshLibrary::get_item_mesh_transform); - ClassDB::bind_method(D_METHOD("get_item_navmesh", "id"), &MeshLibrary::get_item_navmesh); - ClassDB::bind_method(D_METHOD("get_item_navmesh_transform", "id"), &MeshLibrary::get_item_navmesh_transform); + ClassDB::bind_method(D_METHOD("get_item_navigation_mesh", "id"), &MeshLibrary::get_item_navigation_mesh); + ClassDB::bind_method(D_METHOD("get_item_navigation_mesh_transform", "id"), &MeshLibrary::get_item_navigation_mesh_transform); + ClassDB::bind_method(D_METHOD("get_item_navigation_layers", "id"), &MeshLibrary::get_item_navigation_layers); ClassDB::bind_method(D_METHOD("get_item_shapes", "id"), &MeshLibrary::_get_item_shapes); ClassDB::bind_method(D_METHOD("get_item_preview", "id"), &MeshLibrary::get_item_preview); ClassDB::bind_method(D_METHOD("remove_item", "id"), &MeshLibrary::remove_item); diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h index 79acb41c4e..1d5af9e176 100644 --- a/scene/resources/mesh_library.h +++ b/scene/resources/mesh_library.h @@ -49,11 +49,12 @@ public: struct Item { String name; Ref<Mesh> mesh; + Transform3D mesh_transform; Vector<ShapeData> shapes; Ref<Texture2D> preview; - Transform3D navmesh_transform; - Transform3D mesh_transform; - Ref<NavigationMesh> navmesh; + Ref<NavigationMesh> navigation_mesh; + Transform3D navigation_mesh_transform; + uint32_t navigation_layers = 1; }; RBMap<int, Item> item_map; @@ -74,15 +75,17 @@ public: void set_item_name(int p_item, const String &p_name); void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh); void set_item_mesh_transform(int p_item, const Transform3D &p_transform); - void set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh); - void set_item_navmesh_transform(int p_item, const Transform3D &p_transform); + void set_item_navigation_mesh(int p_item, const Ref<NavigationMesh> &p_navigation_mesh); + void set_item_navigation_mesh_transform(int p_item, const Transform3D &p_transform); + void set_item_navigation_layers(int p_item, uint32_t p_navigation_layers); void set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes); void set_item_preview(int p_item, const Ref<Texture2D> &p_preview); String get_item_name(int p_item) const; Ref<Mesh> get_item_mesh(int p_item) const; Transform3D get_item_mesh_transform(int p_item) const; - Ref<NavigationMesh> get_item_navmesh(int p_item) const; - Transform3D get_item_navmesh_transform(int p_item) const; + Ref<NavigationMesh> get_item_navigation_mesh(int p_item) const; + Transform3D get_item_navigation_mesh_transform(int p_item) const; + uint32_t get_item_navigation_layers(int p_item) const; Vector<ShapeData> get_item_shapes(int p_item) const; Ref<Texture2D> get_item_preview(int p_item) const; diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index ff4a7a4560..8afb0563b2 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -242,6 +242,7 @@ void MultiMesh::set_instance_transform(int p_instance, const Transform3D &p_tran void MultiMesh::set_instance_transform_2d(int p_instance, const Transform2D &p_transform) { RenderingServer::get_singleton()->multimesh_instance_set_transform_2d(multimesh, p_instance, p_transform); + emit_changed(); } Transform3D MultiMesh::get_instance_transform(int p_instance) const { diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index de97498674..bf4291869f 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -225,13 +225,13 @@ float NavigationMesh::get_edge_max_error() const { return edge_max_error; } -void NavigationMesh::set_verts_per_poly(float p_value) { +void NavigationMesh::set_vertices_per_polygon(float p_value) { ERR_FAIL_COND(p_value < 3); - verts_per_poly = p_value; + vertices_per_polygon = p_value; } -float NavigationMesh::get_verts_per_poly() const { - return verts_per_poly; +float NavigationMesh::get_vertices_per_polygon() const { + return vertices_per_polygon; } void NavigationMesh::set_detail_sample_distance(float p_value) { @@ -483,8 +483,8 @@ void NavigationMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_edge_max_error", "edge_max_error"), &NavigationMesh::set_edge_max_error); ClassDB::bind_method(D_METHOD("get_edge_max_error"), &NavigationMesh::get_edge_max_error); - ClassDB::bind_method(D_METHOD("set_verts_per_poly", "verts_per_poly"), &NavigationMesh::set_verts_per_poly); - ClassDB::bind_method(D_METHOD("get_verts_per_poly"), &NavigationMesh::get_verts_per_poly); + ClassDB::bind_method(D_METHOD("set_vertices_per_polygon", "vertices_per_polygon"), &NavigationMesh::set_vertices_per_polygon); + ClassDB::bind_method(D_METHOD("get_vertices_per_polygon"), &NavigationMesh::get_vertices_per_polygon); ClassDB::bind_method(D_METHOD("set_detail_sample_distance", "detail_sample_dist"), &NavigationMesh::set_detail_sample_distance); ClassDB::bind_method(D_METHOD("get_detail_sample_distance"), &NavigationMesh::get_detail_sample_distance); @@ -527,9 +527,9 @@ void NavigationMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Both"), "set_parsed_geometry_type", "get_parsed_geometry_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_PROPERTY_DEFAULT("geometry_collision_mask", 0xFFFFFFFF); - ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_source_geometry_mode", PROPERTY_HINT_ENUM, "NavMesh Children,Group With Children,Group Explicit"), "set_source_geometry_mode", "get_source_geometry_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "geometry_source_geometry_mode", PROPERTY_HINT_ENUM, "Root Node Children,Group With Children,Group Explicit"), "set_source_geometry_mode", "get_source_geometry_mode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "geometry_source_group_name"), "set_source_group_name", "get_source_group_name"); - ADD_PROPERTY_DEFAULT("geometry_source_group_name", StringName("navmesh")); + ADD_PROPERTY_DEFAULT("geometry_source_group_name", StringName("navigation_mesh_source_group")); ADD_GROUP("Cells", "cell_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size", PROPERTY_HINT_RANGE, "0.01,500.0,0.01,or_greater,suffix:m"), "set_cell_size", "get_cell_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_height", PROPERTY_HINT_RANGE, "0.01,500.0,0.01,or_greater,suffix:m"), "set_cell_height", "get_cell_height"); @@ -544,8 +544,8 @@ void NavigationMesh::_bind_methods() { ADD_GROUP("Edges", "edge_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge_max_length", PROPERTY_HINT_RANGE, "0.0,50.0,0.01,or_greater,suffix:m"), "set_edge_max_length", "get_edge_max_length"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge_max_error", PROPERTY_HINT_RANGE, "0.1,3.0,0.01,or_greater,suffix:m"), "set_edge_max_error", "get_edge_max_error"); - ADD_GROUP("Polygons", "polygon_"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "polygon_verts_per_poly", PROPERTY_HINT_RANGE, "3.0,12.0,1.0,or_greater"), "set_verts_per_poly", "get_verts_per_poly"); + ADD_GROUP("Polygons", ""); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "vertices_per_polygon", PROPERTY_HINT_RANGE, "3.0,12.0,1.0,or_greater"), "set_vertices_per_polygon", "get_vertices_per_polygon"); ADD_GROUP("Details", "detail_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail_sample_distance", PROPERTY_HINT_RANGE, "0.1,16.0,0.01,or_greater,suffix:m"), "set_detail_sample_distance", "get_detail_sample_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "detail_sample_max_error", PROPERTY_HINT_RANGE, "0.0,16.0,0.01,or_greater,suffix:m"), "set_detail_sample_max_error", "get_detail_sample_max_error"); @@ -566,7 +566,7 @@ void NavigationMesh::_bind_methods() { BIND_ENUM_CONSTANT(PARSED_GEOMETRY_BOTH); BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MAX); - BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_NAVMESH_CHILDREN); + BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_ROOT_NODE_CHILDREN); BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN); BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_GROUPS_EXPLICIT); BIND_ENUM_CONSTANT(SOURCE_GEOMETRY_MAX); @@ -581,7 +581,7 @@ void NavigationMesh::_validate_property(PropertyInfo &p_property) const { } if (p_property.name == "geometry_source_group_name") { - if (source_geometry_mode == SOURCE_GEOMETRY_NAVMESH_CHILDREN) { + if (source_geometry_mode == SOURCE_GEOMETRY_ROOT_NODE_CHILDREN) { p_property.usage = PROPERTY_USAGE_NONE; return; } @@ -590,35 +590,16 @@ void NavigationMesh::_validate_property(PropertyInfo &p_property) const { #ifndef DISABLE_DEPRECATED bool NavigationMesh::_set(const StringName &p_name, const Variant &p_value) { - String prop_name = p_name; - if (prop_name.find("/") != -1) { - // Compatibility with pre-3.5 "category/path" property names. - prop_name = prop_name.replace("/", "_"); - if (prop_name == "sample_partition_type_sample_partition_type") { - set("sample_partition_type", p_value); - } else if (prop_name == "filter_filter_walkable_low_height_spans") { - set("filter_walkable_low_height_spans", p_value); - } else { - set(prop_name, p_value); - } - + if (p_name == "polygon_verts_per_poly") { // Renamed in 4.0 beta 9. + set_vertices_per_polygon(p_value); return true; } return false; } bool NavigationMesh::_get(const StringName &p_name, Variant &r_ret) const { - String prop_name = p_name; - if (prop_name.find("/") != -1) { - // Compatibility with pre-3.5 "category/path" property names. - prop_name = prop_name.replace("/", "_"); - if (prop_name == "sample_partition_type_sample_partition_type") { - r_ret = get("sample_partition_type"); - } else if (prop_name == "filter_filter_walkable_low_height_spans") { - r_ret = get("filter_walkable_low_height_spans"); - } else { - r_ret = get(prop_name); - } + if (p_name == "polygon_verts_per_poly") { // Renamed in 4.0 beta 9. + r_ret = get_vertices_per_polygon(); return true; } return false; diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h index 5ddbd75dcb..3d072423db 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -84,7 +84,7 @@ public: }; enum SourceGeometryMode { - SOURCE_GEOMETRY_NAVMESH_CHILDREN = 0, + SOURCE_GEOMETRY_ROOT_NODE_CHILDREN = 0, SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN, SOURCE_GEOMETRY_GROUPS_EXPLICIT, SOURCE_GEOMETRY_MAX @@ -101,7 +101,7 @@ protected: float region_merge_size = 20.0f; float edge_max_length = 12.0f; float edge_max_error = 1.3f; - float verts_per_poly = 6.0f; + float vertices_per_polygon = 6.0f; float detail_sample_distance = 6.0f; float detail_sample_max_error = 1.0f; @@ -109,8 +109,8 @@ protected: ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES; uint32_t collision_mask = 0xFFFFFFFF; - SourceGeometryMode source_geometry_mode = SOURCE_GEOMETRY_NAVMESH_CHILDREN; - StringName source_group_name = "navmesh"; + SourceGeometryMode source_geometry_mode = SOURCE_GEOMETRY_ROOT_NODE_CHILDREN; + StringName source_group_name = "navigation_mesh_source_group"; bool filter_low_hanging_obstacles = false; bool filter_ledge_spans = false; @@ -168,8 +168,8 @@ public: void set_edge_max_error(float p_value); float get_edge_max_error() const; - void set_verts_per_poly(float p_value); - float get_verts_per_poly() const; + void set_vertices_per_polygon(float p_value); + float get_vertices_per_polygon() const; void set_detail_sample_distance(float p_value); float get_detail_sample_distance() const; diff --git a/scene/resources/navigation_polygon.cpp b/scene/resources/navigation_polygon.cpp new file mode 100644 index 0000000000..04077e95a7 --- /dev/null +++ b/scene/resources/navigation_polygon.cpp @@ -0,0 +1,354 @@ +/*************************************************************************/ +/* navigation_polygon.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "navigation_polygon.h" + +#include "core/core_string_names.h" +#include "core/math/geometry_2d.h" +#include "core/os/mutex.h" + +#include "thirdparty/misc/polypartition.h" + +#ifdef TOOLS_ENABLED +Rect2 NavigationPolygon::_edit_get_rect() const { + if (rect_cache_dirty) { + item_rect = Rect2(); + bool first = true; + + for (int i = 0; i < outlines.size(); i++) { + const Vector<Vector2> &outline = outlines[i]; + const int outline_size = outline.size(); + if (outline_size < 3) { + continue; + } + const Vector2 *p = outline.ptr(); + for (int j = 0; j < outline_size; j++) { + if (first) { + item_rect = Rect2(p[j], Vector2(0, 0)); + first = false; + } else { + item_rect.expand_to(p[j]); + } + } + } + + rect_cache_dirty = false; + } + return item_rect; +} + +bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { + for (int i = 0; i < outlines.size(); i++) { + const Vector<Vector2> &outline = outlines[i]; + const int outline_size = outline.size(); + if (outline_size < 3) { + continue; + } + if (Geometry2D::is_point_in_polygon(p_point, Variant(outline))) { + return true; + } + } + return false; +} +#endif + +void NavigationPolygon::set_vertices(const Vector<Vector2> &p_vertices) { + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } + vertices = p_vertices; + rect_cache_dirty = true; +} + +Vector<Vector2> NavigationPolygon::get_vertices() const { + return vertices; +} + +void NavigationPolygon::_set_polygons(const TypedArray<Vector<int32_t>> &p_array) { + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } + polygons.resize(p_array.size()); + for (int i = 0; i < p_array.size(); i++) { + polygons.write[i].indices = p_array[i]; + } +} + +TypedArray<Vector<int32_t>> NavigationPolygon::_get_polygons() const { + TypedArray<Vector<int32_t>> ret; + ret.resize(polygons.size()); + for (int i = 0; i < ret.size(); i++) { + ret[i] = polygons[i].indices; + } + + return ret; +} + +void NavigationPolygon::_set_outlines(const TypedArray<Vector<Vector2>> &p_array) { + outlines.resize(p_array.size()); + for (int i = 0; i < p_array.size(); i++) { + outlines.write[i] = p_array[i]; + } + rect_cache_dirty = true; +} + +TypedArray<Vector<Vector2>> NavigationPolygon::_get_outlines() const { + TypedArray<Vector<Vector2>> ret; + ret.resize(outlines.size()); + for (int i = 0; i < ret.size(); i++) { + ret[i] = outlines[i]; + } + + return ret; +} + +void NavigationPolygon::add_polygon(const Vector<int> &p_polygon) { + Polygon polygon; + polygon.indices = p_polygon; + polygons.push_back(polygon); + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } +} + +void NavigationPolygon::add_outline_at_index(const Vector<Vector2> &p_outline, int p_index) { + outlines.insert(p_index, p_outline); + rect_cache_dirty = true; +} + +int NavigationPolygon::get_polygon_count() const { + return polygons.size(); +} + +Vector<int> NavigationPolygon::get_polygon(int p_idx) { + ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>()); + return polygons[p_idx].indices; +} + +void NavigationPolygon::clear_polygons() { + polygons.clear(); + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } +} + +Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() { + MutexLock lock(navigation_mesh_generation); + + if (navigation_mesh.is_null()) { + navigation_mesh.instantiate(); + Vector<Vector3> verts; + { + verts.resize(get_vertices().size()); + Vector3 *w = verts.ptrw(); + + const Vector2 *r = get_vertices().ptr(); + + for (int i(0); i < get_vertices().size(); i++) { + w[i] = Vector3(r[i].x, 0.0, r[i].y); + } + } + navigation_mesh->set_vertices(verts); + + for (int i(0); i < get_polygon_count(); i++) { + navigation_mesh->add_polygon(get_polygon(i)); + } + } + + return navigation_mesh; +} + +void NavigationPolygon::add_outline(const Vector<Vector2> &p_outline) { + outlines.push_back(p_outline); + rect_cache_dirty = true; +} + +int NavigationPolygon::get_outline_count() const { + return outlines.size(); +} + +void NavigationPolygon::set_outline(int p_idx, const Vector<Vector2> &p_outline) { + ERR_FAIL_INDEX(p_idx, outlines.size()); + outlines.write[p_idx] = p_outline; + rect_cache_dirty = true; +} + +void NavigationPolygon::remove_outline(int p_idx) { + ERR_FAIL_INDEX(p_idx, outlines.size()); + outlines.remove_at(p_idx); + rect_cache_dirty = true; +} + +Vector<Vector2> NavigationPolygon::get_outline(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, outlines.size(), Vector<Vector2>()); + return outlines[p_idx]; +} + +void NavigationPolygon::clear_outlines() { + outlines.clear(); + rect_cache_dirty = true; +} + +void NavigationPolygon::make_polygons_from_outlines() { + { + MutexLock lock(navigation_mesh_generation); + navigation_mesh.unref(); + } + List<TPPLPoly> in_poly, out_poly; + + Vector2 outside_point(-1e10, -1e10); + + for (int i = 0; i < outlines.size(); i++) { + Vector<Vector2> ol = outlines[i]; + int olsize = ol.size(); + if (olsize < 3) { + continue; + } + const Vector2 *r = ol.ptr(); + for (int j = 0; j < olsize; j++) { + outside_point.x = MAX(r[j].x, outside_point.x); + outside_point.y = MAX(r[j].y, outside_point.y); + } + } + + outside_point += Vector2(0.7239784, 0.819238); //avoid precision issues + + for (int i = 0; i < outlines.size(); i++) { + Vector<Vector2> ol = outlines[i]; + int olsize = ol.size(); + if (olsize < 3) { + continue; + } + const Vector2 *r = ol.ptr(); + + int interscount = 0; + //test if this is an outer outline + for (int k = 0; k < outlines.size(); k++) { + if (i == k) { + continue; //no self intersect + } + + Vector<Vector2> ol2 = outlines[k]; + int olsize2 = ol2.size(); + if (olsize2 < 3) { + continue; + } + const Vector2 *r2 = ol2.ptr(); + + for (int l = 0; l < olsize2; l++) { + if (Geometry2D::segment_intersects_segment(r[0], outside_point, r2[l], r2[(l + 1) % olsize2], nullptr)) { + interscount++; + } + } + } + + bool outer = (interscount % 2) == 0; + + TPPLPoly tp; + tp.Init(olsize); + for (int j = 0; j < olsize; j++) { + tp[j] = r[j]; + } + + if (outer) { + tp.SetOrientation(TPPL_ORIENTATION_CCW); + } else { + tp.SetOrientation(TPPL_ORIENTATION_CW); + tp.SetHole(true); + } + + in_poly.push_back(tp); + } + + TPPLPartition tpart; + if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed! + ERR_PRINT("NavigationPolygon: Convex partition failed! Failed to convert outlines to a valid NavigationMesh." + "\nNavigationPolygon outlines can not overlap vertices or edges inside same outline or with other outlines or have any intersections." + "\nAdd the outmost and largest outline first. To add holes inside this outline add the smaller outlines with opposite winding order."); + return; + } + + polygons.clear(); + vertices.clear(); + + HashMap<Vector2, int> points; + for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) { + TPPLPoly &tp = I->get(); + + struct Polygon p; + + for (int64_t i = 0; i < tp.GetNumPoints(); i++) { + HashMap<Vector2, int>::Iterator E = points.find(tp[i]); + if (!E) { + E = points.insert(tp[i], vertices.size()); + vertices.push_back(tp[i]); + } + p.indices.push_back(E->value); + } + + polygons.push_back(p); + } + + emit_signal(CoreStringNames::get_singleton()->changed); +} + +void NavigationPolygon::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationPolygon::set_vertices); + ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationPolygon::get_vertices); + + ClassDB::bind_method(D_METHOD("add_polygon", "polygon"), &NavigationPolygon::add_polygon); + ClassDB::bind_method(D_METHOD("get_polygon_count"), &NavigationPolygon::get_polygon_count); + ClassDB::bind_method(D_METHOD("get_polygon", "idx"), &NavigationPolygon::get_polygon); + ClassDB::bind_method(D_METHOD("clear_polygons"), &NavigationPolygon::clear_polygons); + ClassDB::bind_method(D_METHOD("get_navigation_mesh"), &NavigationPolygon::get_navigation_mesh); + + ClassDB::bind_method(D_METHOD("add_outline", "outline"), &NavigationPolygon::add_outline); + ClassDB::bind_method(D_METHOD("add_outline_at_index", "outline", "index"), &NavigationPolygon::add_outline_at_index); + ClassDB::bind_method(D_METHOD("get_outline_count"), &NavigationPolygon::get_outline_count); + ClassDB::bind_method(D_METHOD("set_outline", "idx", "outline"), &NavigationPolygon::set_outline); + ClassDB::bind_method(D_METHOD("get_outline", "idx"), &NavigationPolygon::get_outline); + ClassDB::bind_method(D_METHOD("remove_outline", "idx"), &NavigationPolygon::remove_outline); + ClassDB::bind_method(D_METHOD("clear_outlines"), &NavigationPolygon::clear_outlines); + ClassDB::bind_method(D_METHOD("make_polygons_from_outlines"), &NavigationPolygon::make_polygons_from_outlines); + + ClassDB::bind_method(D_METHOD("_set_polygons", "polygons"), &NavigationPolygon::_set_polygons); + ClassDB::bind_method(D_METHOD("_get_polygons"), &NavigationPolygon::_get_polygons); + + ClassDB::bind_method(D_METHOD("_set_outlines", "outlines"), &NavigationPolygon::_set_outlines); + ClassDB::bind_method(D_METHOD("_get_outlines"), &NavigationPolygon::_get_outlines); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines"); +} diff --git a/scene/resources/navigation_polygon.h b/scene/resources/navigation_polygon.h new file mode 100644 index 0000000000..e943cff04e --- /dev/null +++ b/scene/resources/navigation_polygon.h @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* navigation_polygon.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NAVIGATION_POLYGON_H +#define NAVIGATION_POLYGON_H + +#include "scene/2d/node_2d.h" +#include "scene/resources/navigation_mesh.h" + +class NavigationPolygon : public Resource { + GDCLASS(NavigationPolygon, Resource); + + Vector<Vector2> vertices; + struct Polygon { + Vector<int> indices; + }; + Vector<Polygon> polygons; + Vector<Vector<Vector2>> outlines; + + mutable Rect2 item_rect; + mutable bool rect_cache_dirty = true; + + Mutex navigation_mesh_generation; + // Navigation mesh + Ref<NavigationMesh> navigation_mesh; + +protected: + static void _bind_methods(); + + void _set_polygons(const TypedArray<Vector<int32_t>> &p_array); + TypedArray<Vector<int32_t>> _get_polygons() const; + + void _set_outlines(const TypedArray<Vector<Vector2>> &p_array); + TypedArray<Vector<Vector2>> _get_outlines() const; + +public: +#ifdef TOOLS_ENABLED + Rect2 _edit_get_rect() const; + bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const; +#endif + + void set_vertices(const Vector<Vector2> &p_vertices); + Vector<Vector2> get_vertices() const; + + void add_polygon(const Vector<int> &p_polygon); + int get_polygon_count() const; + + void add_outline(const Vector<Vector2> &p_outline); + void add_outline_at_index(const Vector<Vector2> &p_outline, int p_index); + void set_outline(int p_idx, const Vector<Vector2> &p_outline); + Vector<Vector2> get_outline(int p_idx) const; + void remove_outline(int p_idx); + int get_outline_count() const; + + void clear_outlines(); + void make_polygons_from_outlines(); + + Vector<int> get_polygon(int p_idx); + void clear_polygons(); + + Ref<NavigationMesh> get_navigation_mesh(); + + NavigationPolygon() {} + ~NavigationPolygon() {} +}; + +#endif // NAVIGATION_POLYGON_H diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 1c99fa5554..5316b524ba 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -71,7 +71,7 @@ static Array _sanitize_node_pinned_properties(Node *p_node) { } Node *SceneState::instantiate(GenEditState p_edit_state) const { - // nodes where instancing failed (because something is missing) + // Nodes where instantiation failed (because something is missing.) List<Node *> stray_instances; #define NODE_FROM_ID(p_name, p_id) \ @@ -122,7 +122,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { NODE_FROM_ID(nparent, n.parent); #ifdef DEBUG_ENABLED if (!nparent && (n.parent & FLAG_ID_IS_PATH)) { - WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instancing: '" + get_path() + "'.").ascii().get_data()); + WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instantiating: '" + get_path() + "'.").ascii().get_data()); old_parent_path = String(node_paths[n.parent & FLAG_MASK]).trim_prefix("./").replace("/", "@"); nparent = ret_nodes[0]; } @@ -131,7 +131,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } else { // i == 0 is root node. ERR_FAIL_COND_V_MSG(n.parent != -1, nullptr, vformat("Invalid scene: root node %s cannot specify a parent node.", snames[n.name])); - ERR_FAIL_COND_V_MSG(n.type == TYPE_INSTANCED && base_scene_idx < 0, nullptr, vformat("Invalid scene: root node %s in an instance, but there's no base scene.", snames[n.name])); + ERR_FAIL_COND_V_MSG(n.type == TYPE_INSTANTIATED && base_scene_idx < 0, nullptr, vformat("Invalid scene: root node %s in an instance, but there's no base scene.", snames[n.name])); } Node *node = nullptr; @@ -169,7 +169,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { ERR_FAIL_COND_V(!node, nullptr); } - } else if (n.type == TYPE_INSTANCED) { + } else if (n.type == TYPE_INSTANTIATED) { //get the node from somewhere, it likely already exists from another instance if (parent) { node = parent->_get_child_by_name(snames[n.name]); @@ -345,7 +345,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { node->add_to_group(snames[n.groups[j]], true); } - if (n.instance >= 0 || n.type != TYPE_INSTANCED || i == 0) { + if (n.instance >= 0 || n.type != TYPE_INSTANTIATED || i == 0) { //if node was not part of instance, must set its name, parenthood and ownership if (i > 0) { if (parent) { @@ -382,7 +382,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } - // we only want to deal with pinned flag if instancing as pure main (no instance, no inheriting) + // We only want to deal with pinned flag if instantiating as pure main (no instance, no inheriting.) if (p_edit_state == GEN_EDIT_STATE_MAIN) { _sanitize_node_pinned_properties(node); } else { @@ -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]; @@ -665,7 +665,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has // Save the right type. If this node was created by an instance // then flag that the node should not be created but reused if (states_stack.is_empty() && !is_editable_instance) { - //this node is not part of an instancing process, so save the type + //This node is not part of an instantiation process, so save the type. if (missing_node != nullptr) { // It's a missing node (type non existent on load). nd.type = _nm_get_string(missing_node->get_original_class(), name_map); @@ -675,7 +675,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has } else { // this node is part of an instantiated process, so do not save the type. // instead, save that it was instantiated - nd.type = TYPE_INSTANCED; + nd.type = TYPE_INSTANTIATED; } // determine whether to save this node or not @@ -1005,6 +1005,37 @@ void SceneState::clear() { base_scene_idx = -1; } +Error SceneState::copy_from(const Ref<SceneState> &p_scene_state) { + ERR_FAIL_COND_V(p_scene_state.is_null(), ERR_INVALID_PARAMETER); + + clear(); + + for (const StringName &E : p_scene_state->names) { + names.append(E); + } + for (const Variant &E : p_scene_state->variants) { + variants.append(E); + } + for (const SceneState::NodeData &E : p_scene_state->nodes) { + nodes.append(E); + } + for (const SceneState::ConnectionData &E : p_scene_state->connections) { + connections.append(E); + } + for (KeyValue<NodePath, int> &E : p_scene_state->node_path_cache) { + node_path_cache.insert(E.key, E.value); + } + for (const NodePath &E : p_scene_state->node_paths) { + node_paths.append(E); + } + for (const NodePath &E : p_scene_state->editable_instances) { + editable_instances.append(E); + } + base_scene_idx = p_scene_state->base_scene_idx; + + return OK; +} + Ref<SceneState> SceneState::get_base_scene_state() const { if (base_scene_idx >= 0) { Ref<PackedScene> ps = variants[base_scene_idx]; @@ -1351,7 +1382,7 @@ int SceneState::get_node_count() const { StringName SceneState::get_node_type(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, nodes.size(), StringName()); - if (nodes[p_idx].type == TYPE_INSTANCED) { + if (nodes[p_idx].type == TYPE_INSTANTIATED) { return StringName(); } return names[nodes[p_idx].type]; @@ -1737,6 +1768,28 @@ void PackedScene::clear() { state->clear(); } +void PackedScene::reload_from_file() { + String path = get_path(); + if (!path.is_resource_file()) { + return; + } + + Ref<PackedScene> s = ResourceLoader::load(ResourceLoader::path_remap(path), get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE); + if (!s.is_valid()) { + return; + } + + // Backup the loaded_state + Ref<SceneState> loaded_state = s->get_state(); + // This assigns a new state to s->state + // We do this because of the next step + s->recreate_state(); + // This has a side-effect to clear s->state + copy_from(s); + // Then, we copy the backed-up loaded_state to state + state->copy_from(loaded_state); +} + bool PackedScene::can_instantiate() const { return state->can_instantiate(); } diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index c6f82ddd5e..ad1f50cd39 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -108,7 +108,7 @@ protected: public: enum { FLAG_ID_IS_PATH = (1 << 30), - TYPE_INSTANCED = 0x7FFFFFFF, + TYPE_INSTANTIATED = 0x7FFFFFFF, FLAG_INSTANCE_IS_PLACEHOLDER = (1 << 30), FLAG_PATH_PROPERTY_IS_NODE = (1 << 30), FLAG_PROP_NAME_MASK = FLAG_PATH_PROPERTY_IS_NODE - 1, @@ -143,6 +143,7 @@ public: String get_path() const; void clear(); + Error copy_from(const Ref<SceneState> &p_scene_state); bool can_instantiate() const; Node *instantiate(GenEditState p_edit_state) const; @@ -235,6 +236,8 @@ public: void recreate_state(); void replace_state(Ref<SceneState> p_by); + virtual void reload_from_file() override; + virtual void set_path(const String &p_path, bool p_take_over = false) override; #ifdef TOOLS_ENABLED virtual void set_last_modified_time(uint64_t p_time) override { diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index eb83a37c7b..54d3676c15 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -30,6 +30,7 @@ #include "primitive_meshes.h" +#include "core/config/project_settings.h" #include "core/core_string_names.h" #include "scene/resources/theme.h" #include "scene/theme/theme_db.h" @@ -37,6 +38,8 @@ #include "thirdparty/misc/clipper.hpp" #include "thirdparty/misc/polypartition.h" +#define PADDING_REF_SIZE 1024.0 + /** PrimitiveMesh */ @@ -94,6 +97,26 @@ void PrimitiveMesh::_update() const { } } + if (add_uv2) { + // _create_mesh_array should populate our UV2, this is a fallback in case it doesn't. + // As we don't know anything about the geometry we only pad the right and bottom edge + // of our texture. + Vector<Vector2> uv = arr[RS::ARRAY_TEX_UV]; + Vector<Vector2> uv2 = arr[RS::ARRAY_TEX_UV2]; + + if (uv.size() > 0 && uv2.size() == 0) { + Vector2 uv2_scale = get_uv2_scale(); + uv2.resize(uv.size()); + + Vector2 *uv2w = uv2.ptrw(); + for (int i = 0; i < uv.size(); i++) { + uv2w[i] = uv[i] * uv2_scale; + } + } + + arr[RS::ARRAY_TEX_UV2] = uv2; + } + array_len = pc; index_array_len = indices.size(); // in with the new @@ -160,7 +183,12 @@ TypedArray<Array> PrimitiveMesh::surface_get_blend_shape_arrays(int p_surface) c uint32_t PrimitiveMesh::surface_get_format(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, 1, 0); - return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX; + uint32_t mesh_format = RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV | RS::ARRAY_FORMAT_INDEX; + if (add_uv2) { + mesh_format |= RS::ARRAY_FORMAT_TEX_UV2; + } + + return mesh_format; } Mesh::PrimitiveType PrimitiveMesh::surface_get_primitive_type(int p_idx) const { @@ -219,9 +247,17 @@ void PrimitiveMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces); ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces); + ClassDB::bind_method(D_METHOD("set_add_uv2", "add_uv2"), &PrimitiveMesh::set_add_uv2); + ClassDB::bind_method(D_METHOD("get_add_uv2"), &PrimitiveMesh::get_add_uv2); + + ClassDB::bind_method(D_METHOD("set_uv2_padding", "uv2_padding"), &PrimitiveMesh::set_uv2_padding); + ClassDB::bind_method(D_METHOD("get_uv2_padding"), &PrimitiveMesh::get_uv2_padding); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material"); ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "add_uv2"), "set_add_uv2", "get_add_uv2"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "uv2_padding"), "set_uv2_padding", "get_uv2_padding"); GDVIRTUAL_BIND(_create_mesh_array); } @@ -233,7 +269,7 @@ void PrimitiveMesh::set_material(const Ref<Material> &p_material) { RenderingServer::get_singleton()->mesh_surface_set_material(mesh, 0, material.is_null() ? RID() : material->get_rid()); notify_property_list_changed(); emit_changed(); - }; + } } Ref<Material> PrimitiveMesh::get_material() const { @@ -263,6 +299,42 @@ bool PrimitiveMesh::get_flip_faces() const { return flip_faces; } +void PrimitiveMesh::set_add_uv2(bool p_enable) { + add_uv2 = p_enable; + _update_lightmap_size(); + _request_update(); +} + +void PrimitiveMesh::set_uv2_padding(float p_padding) { + uv2_padding = p_padding; + _update_lightmap_size(); + _request_update(); +} + +Vector2 PrimitiveMesh::get_uv2_scale(Vector2 p_margin_scale) const { + Vector2 uv2_scale; + Vector2 lightmap_size = get_lightmap_size_hint(); + + // Calculate it as a margin, if no lightmap size hint is given we assume "PADDING_REF_SIZE" as our texture size. + uv2_scale.x = p_margin_scale.x * uv2_padding / (lightmap_size.x == 0.0 ? PADDING_REF_SIZE : lightmap_size.x); + uv2_scale.y = p_margin_scale.y * uv2_padding / (lightmap_size.y == 0.0 ? PADDING_REF_SIZE : lightmap_size.y); + + // Inverse it to turn our margin into a scale + uv2_scale = Vector2(1.0, 1.0) - uv2_scale; + + return uv2_scale; +} + +float PrimitiveMesh::get_lightmap_texel_size() const { + float texel_size = GLOBAL_GET("rendering/lightmapping/primitive_meshes/texel_size"); + + if (texel_size <= 0.0) { + texel_size = 0.2; + } + + return texel_size; +} + PrimitiveMesh::PrimitiveMesh() { mesh = RenderingServer::get_singleton()->mesh_create(); } @@ -275,22 +347,52 @@ PrimitiveMesh::~PrimitiveMesh() { CapsuleMesh */ +void CapsuleMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend + float vertical_length = radial_length * 2 + (height - 2.0 * radius); // total vertical length + + _lightmap_size_hint.x = MAX(1.0, 4.0 * radial_length / texel_size) + padding; + _lightmap_size_hint.y = MAX(1.0, vertical_length / texel_size) + padding; + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void CapsuleMesh::_create_mesh_array(Array &p_arr) const { - create_mesh_array(p_arr, radius, height, radial_segments, rings); + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + create_mesh_array(p_arr, radius, height, radial_segments, rings, _add_uv2, _uv2_padding); } -void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const float height, const int radial_segments, const int rings) { +void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const float height, const int radial_segments, const int rings, bool p_add_uv2, const float p_uv2_padding) { int i, j, prevrow, thisrow, point; float x, y, z, u, v, w; float onethird = 1.0 / 3.0; float twothirds = 2.0 / 3.0; + // Only used if we calculate UV2 + float radial_width = 2.0 * radius * Math_PI; + float radial_h = radial_width / (radial_width + p_uv2_padding); + float radial_length = radius * Math_PI * 0.5; // circumference of 90 degree bend + float vertical_length = radial_length * 2 + (height - 2.0 * radius) + p_uv2_padding; // total vertical length + float radial_v = radial_length / vertical_length; // v size of top and bottom section + float height_v = (height - 2.0 * radius) / vertical_length; // v size of height section + // note, this has been aligned with our collision shape but I've left the descriptions as top/middle/bottom Vector<Vector3> points; Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -322,6 +424,9 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa normals.push_back(p.normalized()); ADD_TANGENT(-z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, v * onethird)); + if (p_add_uv2) { + uv2s.push_back(Vector2(u * radial_h, v * radial_v)); + } point++; if (i > 0 && j > 0) { @@ -332,12 +437,12 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } /* cylinder */ thisrow = point; @@ -361,6 +466,9 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa normals.push_back(Vector3(x, 0.0, -z)); ADD_TANGENT(-z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, onethird + (v * onethird))); + if (p_add_uv2) { + uv2s.push_back(Vector2(u * radial_h, radial_v + (v * height_v))); + } point++; if (i > 0 && j > 0) { @@ -371,12 +479,12 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } /* bottom hemisphere */ thisrow = point; @@ -390,17 +498,20 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa y = radius * cos(0.5 * Math_PI * v); for (i = 0; i <= radial_segments; i++) { - float u2 = i; - u2 /= radial_segments; + u = i; + u /= radial_segments; - x = -sin(u2 * Math_TAU); - z = cos(u2 * Math_TAU); + x = -sin(u * Math_TAU); + z = cos(u * Math_TAU); Vector3 p = Vector3(x * radius * w, y, -z * radius * w); points.push_back(p + Vector3(0.0, -0.5 * height + radius, 0.0)); normals.push_back(p.normalized()); ADD_TANGENT(-z, 0.0, -x, 1.0) - uvs.push_back(Vector2(u2, twothirds + ((v - 1.0) * onethird))); + uvs.push_back(Vector2(u, twothirds + ((v - 1.0) * onethird))); + if (p_add_uv2) { + uv2s.push_back(Vector2(u * radial_h, radial_v + height_v + ((v - 1.0) * radial_v))); + } point++; if (i > 0 && j > 0) { @@ -411,17 +522,20 @@ void CapsuleMesh::create_mesh_array(Array &p_arr, const float radius, const floa indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (p_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -450,6 +564,7 @@ void CapsuleMesh::set_radius(const float p_radius) { if (radius > height * 0.5) { height = radius * 2.0; } + _update_lightmap_size(); _request_update(); } @@ -462,6 +577,7 @@ void CapsuleMesh::set_height(const float p_height) { if (radius > height * 0.5) { radius = height * 0.5; } + _update_lightmap_size(); _request_update(); } @@ -493,16 +609,53 @@ CapsuleMesh::CapsuleMesh() {} BoxMesh */ +void BoxMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float width = (size.x + size.z) / texel_size; + float length = (size.y + size.y + MAX(size.x, size.z)) / texel_size; + + _lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding; + _lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding; + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void BoxMesh::_create_mesh_array(Array &p_arr) const { - BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d); + // Note about padding, with our box each face of the box faces a different direction so we want a seam + // around every face. We thus add our padding to the right and bottom of each face. + // With 3 faces along the width and 2 along the height of the texture we need to adjust our scale + // accordingly. + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + BoxMesh::create_mesh_array(p_arr, size, subdivide_w, subdivide_h, subdivide_d, _add_uv2, _uv2_padding); } -void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int subdivide_h, int subdivide_d) { +void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int subdivide_h, int subdivide_d, bool p_add_uv2, const float p_uv2_padding) { int i, j, prevrow, thisrow, point; float x, y, z; float onethird = 1.0 / 3.0; float twothirds = 2.0 / 3.0; + // Only used if we calculate UV2 + // TODO this could be improved by changing the order depending on which side is the longest (basically the below works best if size.y is the longest) + float total_h = (size.x + size.z + (2.0 * p_uv2_padding)); + float padding_h = p_uv2_padding / total_h; + float width_h = size.x / total_h; + float depth_h = size.z / total_h; + float total_v = (size.y + size.y + MAX(size.x, size.z) + (3.0 * p_uv2_padding)); + float padding_v = p_uv2_padding / total_v; + float width_v = size.x / total_v; + float height_v = size.y / total_v; + float depth_v = size.z / total_v; + Vector3 start_pos = size * -0.5; // set our bounding box @@ -511,6 +664,7 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -525,18 +679,24 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int thisrow = point; prevrow = 0; for (j = 0; j <= subdivide_h + 1; j++) { + float v = j; + float v2 = v / (subdivide_w + 1.0); + v /= (2.0 * (subdivide_h + 1.0)); + x = start_pos.x; for (i = 0; i <= subdivide_w + 1; i++) { float u = i; - float v = j; + float u2 = u / (subdivide_w + 1.0); u /= (3.0 * (subdivide_w + 1.0)); - v /= (2.0 * (subdivide_h + 1.0)); // front points.push_back(Vector3(x, -y, -start_pos.z)); // double negative on the Z! normals.push_back(Vector3(0.0, 0.0, 1.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(u2 * width_h, v2 * height_v)); + } point++; // back @@ -544,6 +704,9 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int normals.push_back(Vector3(0.0, 0.0, -1.0)); ADD_TANGENT(-1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(twothirds + u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(u2 * width_h, height_v + padding_v + (v2 * height_v))); + } point++; if (i > 0 && j > 0) { @@ -564,33 +727,39 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } x += size.x / (subdivide_w + 1.0); - }; + } y += size.y / (subdivide_h + 1.0); prevrow = thisrow; thisrow = point; - }; + } // left + right y = start_pos.y; thisrow = point; prevrow = 0; for (j = 0; j <= (subdivide_h + 1); j++) { + float v = j; + float v2 = v / (subdivide_h + 1.0); + v /= (2.0 * (subdivide_h + 1.0)); + z = start_pos.z; for (i = 0; i <= (subdivide_d + 1); i++) { float u = i; - float v = j; + float u2 = u / (subdivide_d + 1.0); u /= (3.0 * (subdivide_d + 1.0)); - v /= (2.0 * (subdivide_h + 1.0)); // right points.push_back(Vector3(-start_pos.x, -y, -z)); normals.push_back(Vector3(1.0, 0.0, 0.0)); ADD_TANGENT(0.0, 0.0, -1.0, 1.0); uvs.push_back(Vector2(onethird + u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), v2 * height_v)); + } point++; // left @@ -598,6 +767,9 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int normals.push_back(Vector3(-1.0, 0.0, 0.0)); ADD_TANGENT(0.0, 0.0, 1.0, 1.0); uvs.push_back(Vector2(u, 0.5 + v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), height_v + padding_v + (v2 * height_v))); + } point++; if (i > 0 && j > 0) { @@ -618,33 +790,39 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } z += size.z / (subdivide_d + 1.0); - }; + } y += size.y / (subdivide_h + 1.0); prevrow = thisrow; thisrow = point; - }; + } // top + bottom z = start_pos.z; thisrow = point; prevrow = 0; for (j = 0; j <= (subdivide_d + 1); j++) { + float v = j; + float v2 = v / (subdivide_d + 1.0); + v /= (2.0 * (subdivide_d + 1.0)); + x = start_pos.x; for (i = 0; i <= (subdivide_w + 1); i++) { float u = i; - float v = j; + float u2 = u / (subdivide_w + 1.0); u /= (3.0 * (subdivide_w + 1.0)); - v /= (2.0 * (subdivide_d + 1.0)); // top points.push_back(Vector3(-x, -start_pos.y, -z)); normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(-1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(onethird + u, 0.5 + v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(u2 * width_h, ((height_v + padding_v) * 2.0) + (v2 * depth_v))); + } point++; // bottom @@ -652,6 +830,9 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(twothirds + u, 0.5 + v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + (u2 * depth_h), ((height_v + padding_v) * 2.0) + (v2 * width_v))); + } point++; if (i > 0 && j > 0) { @@ -672,20 +853,23 @@ void BoxMesh::create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w, int indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } x += size.x / (subdivide_w + 1.0); - }; + } z += size.z / (subdivide_d + 1.0); prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (p_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -708,6 +892,7 @@ void BoxMesh::_bind_methods() { void BoxMesh::set_size(const Vector3 &p_size) { size = p_size; + _update_lightmap_size(); _request_update(); } @@ -748,18 +933,58 @@ BoxMesh::BoxMesh() {} CylinderMesh */ +void CylinderMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float top_circumference = top_radius * Math_PI * 2.0; + float bottom_circumference = bottom_radius * Math_PI * 2.0; + + float _width = MAX(top_circumference, bottom_circumference) / texel_size + padding; + _width = MAX(_width, (((top_radius + bottom_radius) / texel_size) + padding) * 2.0); // this is extremely unlikely to be larger, will only happen if padding is larger then our diameter. + _lightmap_size_hint.x = MAX(1.0, _width); + + float _height = ((height + (MAX(top_radius, bottom_radius) * 2.0)) / texel_size) + (2.0 * padding); + + _lightmap_size_hint.y = MAX(1.0, _height); + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void CylinderMesh::_create_mesh_array(Array &p_arr) const { - create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom); + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + create_mesh_array(p_arr, top_radius, bottom_radius, height, radial_segments, rings, cap_top, cap_bottom, _add_uv2, _uv2_padding); } -void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments, int rings, bool cap_top, bool cap_bottom) { +void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments, int rings, bool cap_top, bool cap_bottom, bool p_add_uv2, const float p_uv2_padding) { int i, j, prevrow, thisrow, point; - float x, y, z, u, v, radius; + float x, y, z, u, v, radius, radius_h; + + // Only used if we calculate UV2 + float top_circumference = top_radius * Math_PI * 2.0; + float bottom_circumference = bottom_radius * Math_PI * 2.0; + float vertical_length = height + MAX(2.0 * top_radius, 2.0 * bottom_radius) + (2.0 * p_uv2_padding); + float height_v = height / vertical_length; + float padding_v = p_uv2_padding / vertical_length; + + float horizonal_length = MAX(MAX(2.0 * (top_radius + bottom_radius + p_uv2_padding), top_circumference + p_uv2_padding), bottom_circumference + p_uv2_padding); + float center_h = 0.5 * (horizonal_length - p_uv2_padding) / horizonal_length; + float top_h = top_circumference / horizonal_length; + float bottom_h = bottom_circumference / horizonal_length; + float padding_h = p_uv2_padding / horizonal_length; Vector<Vector3> points; Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -777,6 +1002,7 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto v /= (rings + 1); radius = top_radius + ((bottom_radius - top_radius) * v); + radius_h = top_h + ((bottom_h - top_h) * v); y = height * v; y = (height * 0.5) - y; @@ -793,6 +1019,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(x, side_normal_y, z).normalized()); ADD_TANGENT(z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, v * 0.5)); + if (p_add_uv2) { + uv2s.push_back(Vector2(center_h + (u - 0.5) * radius_h, v * height_v)); + } point++; if (i > 0 && j > 0) { @@ -803,14 +1032,20 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } + + // Adjust for bottom section, only used if we calculate UV2s. + top_h = top_radius / horizonal_length; + float top_v = top_radius / vertical_length; + bottom_h = bottom_radius / horizonal_length; + float bottom_v = bottom_radius / vertical_length; - // add top + // Add top. if (cap_top && top_radius > 0.0) { y = height * 0.5; @@ -819,6 +1054,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) uvs.push_back(Vector2(0.25, 0.75)); + if (p_add_uv2) { + uv2s.push_back(Vector2(top_h, height_v + padding_v + MAX(top_v, bottom_v))); + } point++; for (i = 0; i <= radial_segments; i++) { @@ -836,17 +1074,20 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) uvs.push_back(Vector2(u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(top_h + (x * top_h), height_v + padding_v + MAX(top_v, bottom_v) + (z * top_v))); + } point++; if (i > 0) { indices.push_back(thisrow); indices.push_back(point - 1); indices.push_back(point - 2); - }; - }; - }; + } + } + } - // add bottom + // Add bottom. if (cap_bottom && bottom_radius > 0.0) { y = height * -0.5; @@ -855,6 +1096,9 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) uvs.push_back(Vector2(0.75, 0.75)); + if (p_add_uv2) { + uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h, height_v + padding_v + MAX(top_v, bottom_v))); + } point++; for (i = 0; i <= radial_segments; i++) { @@ -872,20 +1116,26 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) uvs.push_back(Vector2(u, v)); + if (p_add_uv2) { + uv2s.push_back(Vector2(top_h + top_h + padding_h + bottom_h + (x * bottom_h), height_v + padding_v + MAX(top_v, bottom_v) - (z * bottom_v))); + } point++; if (i > 0) { indices.push_back(thisrow); indices.push_back(point - 2); indices.push_back(point - 1); - }; - }; - }; + } + } + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (p_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -919,6 +1169,7 @@ void CylinderMesh::_bind_methods() { void CylinderMesh::set_top_radius(const float p_radius) { top_radius = p_radius; + _update_lightmap_size(); _request_update(); } @@ -928,6 +1179,7 @@ float CylinderMesh::get_top_radius() const { void CylinderMesh::set_bottom_radius(const float p_radius) { bottom_radius = p_radius; + _update_lightmap_size(); _request_update(); } @@ -937,6 +1189,7 @@ float CylinderMesh::get_bottom_radius() const { void CylinderMesh::set_height(const float p_height) { height = p_height; + _update_lightmap_size(); _request_update(); } @@ -986,10 +1239,26 @@ CylinderMesh::CylinderMesh() {} PlaneMesh */ +void PlaneMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + _lightmap_size_hint.x = MAX(1.0, (size.x / texel_size) + padding); + _lightmap_size_hint.y = MAX(1.0, (size.y / texel_size) + padding); + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void PlaneMesh::_create_mesh_array(Array &p_arr) const { int i, j, prevrow, thisrow, point; float x, z; + // Plane mesh can use default UV2 calculation as implemented in Primitive Mesh + Size2 start_pos = size * -0.5; Vector3 normal = Vector3(0.0, 1.0, 0.0); @@ -1043,15 +1312,15 @@ void PlaneMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; + } x += size.x / (subdivide_w + 1.0); - }; + } z += size.y / (subdivide_d + 1.0); prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; @@ -1088,6 +1357,7 @@ void PlaneMesh::_bind_methods() { void PlaneMesh::set_size(const Size2 &p_size) { size = p_size; + _update_lightmap_size(); _request_update(); } @@ -1137,12 +1407,49 @@ PlaneMesh::PlaneMesh() {} PrismMesh */ +void PrismMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + // left_to_right does not effect the surface area of the prism so we ignore that. + // TODO we could combine the two triangles and save some space but we need to re-align the uv1 and adjust the tangent. + + float width = (size.x + size.z) / texel_size; + float length = (size.y + size.y + size.z) / texel_size; + + _lightmap_size_hint.x = MAX(1.0, width) + 2.0 * padding; + _lightmap_size_hint.y = MAX(1.0, length) + 3.0 * padding; + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void PrismMesh::_create_mesh_array(Array &p_arr) const { int i, j, prevrow, thisrow, point; float x, y, z; float onethird = 1.0 / 3.0; float twothirds = 2.0 / 3.0; + // Only used if we calculate UV2 + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + float horizontal_total = size.x + size.z + 2.0 * _uv2_padding; + float width_h = size.x / horizontal_total; + float depth_h = size.z / horizontal_total; + float padding_h = _uv2_padding / horizontal_total; + + float vertical_total = (size.y + size.y + size.z) + (3.0 * _uv2_padding); + float height_v = size.y / vertical_total; + float depth_v = size.z / vertical_total; + float padding_v = _uv2_padding / vertical_total; + + // and start building + Vector3 start_pos = size * -0.5; // set our bounding box @@ -1151,6 +1458,7 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -1171,12 +1479,15 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { float offset_front = (1.0 - scale) * onethird * left_to_right; float offset_back = (1.0 - scale) * onethird * (1.0 - left_to_right); + float v = j; + float v2 = j / (subdivide_h + 1.0); + v /= (2.0 * (subdivide_h + 1.0)); + x = 0.0; for (i = 0; i <= (subdivide_w + 1); i++) { float u = i; - float v = j; + float u2 = i / (subdivide_w + 1.0); u /= (3.0 * (subdivide_w + 1.0)); - v /= (2.0 * (subdivide_h + 1.0)); u *= scale; @@ -1185,6 +1496,9 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { normals.push_back(Vector3(0.0, 0.0, 1.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(offset_front + u, v)); + if (_add_uv2) { + uv2s.push_back(Vector2(u2 * scale * width_h, v2 * height_v)); + } point++; /* back */ @@ -1192,6 +1506,9 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { normals.push_back(Vector3(0.0, 0.0, -1.0)); ADD_TANGENT(-1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(twothirds + offset_back + u, v)); + if (_add_uv2) { + uv2s.push_back(Vector2(u2 * scale * width_h, height_v + padding_v + v2 * height_v)); + } point++; if (i > 0 && j == 1) { @@ -1224,15 +1541,15 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } x += scale * size.x / (subdivide_w + 1.0); - }; + } y += size.y / (subdivide_h + 1.0); prevrow = thisrow; thisrow = point; - }; + } /* left + right */ Vector3 normal_left, normal_right; @@ -1246,6 +1563,10 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { thisrow = point; prevrow = 0; for (j = 0; j <= (subdivide_h + 1); j++) { + float v = j; + float v2 = j / (subdivide_h + 1.0); + v /= (2.0 * (subdivide_h + 1.0)); + float left, right; float scale = (y - start_pos.y) / size.y; @@ -1255,15 +1576,17 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { z = start_pos.z; for (i = 0; i <= (subdivide_d + 1); i++) { float u = i; - float v = j; + float u2 = u / (subdivide_d + 1.0); u /= (3.0 * (subdivide_d + 1.0)); - v /= (2.0 * (subdivide_h + 1.0)); /* right */ points.push_back(Vector3(right, -y, -z)); normals.push_back(normal_right); ADD_TANGENT(0.0, 0.0, -1.0, 1.0); uvs.push_back(Vector2(onethird + u, v)); + if (_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, v2 * height_v)); + } point++; /* left */ @@ -1271,6 +1594,9 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { normals.push_back(normal_left); ADD_TANGENT(0.0, 0.0, 1.0, 1.0); uvs.push_back(Vector2(u, 0.5 + v)); + if (_add_uv2) { + uv2s.push_back(Vector2(width_h + padding_h + u2 * depth_h, height_v + padding_v + v2 * height_v)); + } point++; if (i > 0 && j > 0) { @@ -1291,33 +1617,39 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(prevrow + i2 + 1); indices.push_back(thisrow + i2 + 1); indices.push_back(thisrow + i2 - 1); - }; + } z += size.z / (subdivide_d + 1.0); - }; + } y += size.y / (subdivide_h + 1.0); prevrow = thisrow; thisrow = point; - }; + } /* bottom */ z = start_pos.z; thisrow = point; prevrow = 0; for (j = 0; j <= (subdivide_d + 1); j++) { + float v = j; + float v2 = v / (subdivide_d + 1.0); + v /= (2.0 * (subdivide_d + 1.0)); + x = start_pos.x; for (i = 0; i <= (subdivide_w + 1); i++) { float u = i; - float v = j; + float u2 = u / (subdivide_w + 1.0); u /= (3.0 * (subdivide_w + 1.0)); - v /= (2.0 * (subdivide_d + 1.0)); /* bottom */ points.push_back(Vector3(x, start_pos.y, -z)); normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0); uvs.push_back(Vector2(twothirds + u, 0.5 + v)); + if (_add_uv2) { + uv2s.push_back(Vector2(u2 * width_h, 2.0 * (height_v + padding_v) + v2 * depth_v)); + } point++; if (i > 0 && j > 0) { @@ -1328,20 +1660,23 @@ void PrismMesh::_create_mesh_array(Array &p_arr) const { indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; + } x += size.x / (subdivide_w + 1.0); - }; + } z += size.z / (subdivide_d + 1.0); prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -1377,6 +1712,7 @@ float PrismMesh::get_left_to_right() const { void PrismMesh::set_size(const Vector3 &p_size) { size = p_size; + _update_lightmap_size(); _request_update(); } @@ -1417,22 +1753,50 @@ PrismMesh::PrismMesh() {} SphereMesh */ +void SphereMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float _width = radius * Math_TAU; + _lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding); + float _height = (is_hemisphere ? 1.0 : 0.5) * height * Math_PI; // note, with hemisphere height is our radius, while with a full sphere it is the diameter.. + _lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding); + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void SphereMesh::_create_mesh_array(Array &p_arr) const { - create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere); + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + create_mesh_array(p_arr, radius, height, radial_segments, rings, is_hemisphere, _add_uv2, _uv2_padding); } -void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere) { +void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int radial_segments, int rings, bool is_hemisphere, bool p_add_uv2, const float p_uv2_padding) { int i, j, prevrow, thisrow, point; float x, y, z; float scale = height * (is_hemisphere ? 1.0 : 0.5); + // Only used if we calculate UV2 + float circumference = radius * Math_TAU; + float horizontal_length = circumference + p_uv2_padding; + float center_h = 0.5 * circumference / horizontal_length; + + float height_v = scale * Math_PI / ((scale * Math_PI) + p_uv2_padding); + // set our bounding box Vector<Vector3> points; Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; point = 0; @@ -1467,9 +1831,13 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int points.push_back(p); Vector3 normal = Vector3(x * w * scale, radius * (y / scale), z * w * scale); normals.push_back(normal.normalized()); - }; + } ADD_TANGENT(z, 0.0, -x, 1.0) uvs.push_back(Vector2(u, v)); + if (p_add_uv2) { + float w_h = w * 2.0 * center_h; + uv2s.push_back(Vector2(center_h + ((u - 0.5) * w_h), v * height_v)); + } point++; if (i > 0 && j > 0) { @@ -1480,17 +1848,20 @@ void SphereMesh::create_mesh_array(Array &p_arr, float radius, float height, int indices.push_back(prevrow + i); indices.push_back(thisrow + i); indices.push_back(thisrow + i - 1); - }; - }; + } + } prevrow = thisrow; thisrow = point; - }; + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (p_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -1517,6 +1888,7 @@ void SphereMesh::_bind_methods() { void SphereMesh::set_radius(const float p_radius) { radius = p_radius; + _update_lightmap_size(); _request_update(); } @@ -1526,6 +1898,7 @@ float SphereMesh::get_radius() const { void SphereMesh::set_height(const float p_height) { height = p_height; + _update_lightmap_size(); _request_update(); } @@ -1553,6 +1926,7 @@ int SphereMesh::get_rings() const { void SphereMesh::set_is_hemisphere(const bool p_is_hemisphere) { is_hemisphere = p_is_hemisphere; + _update_lightmap_size(); _request_update(); } @@ -1566,6 +1940,31 @@ SphereMesh::SphereMesh() {} TorusMesh */ +void TorusMesh::_update_lightmap_size() { + if (get_add_uv2()) { + // size must have changed, update lightmap size hint + Size2i _lightmap_size_hint; + float texel_size = get_lightmap_texel_size(); + float padding = get_uv2_padding(); + + float min_radius = inner_radius; + float max_radius = outer_radius; + + if (min_radius > max_radius) { + SWAP(min_radius, max_radius); + } + + float radius = (max_radius - min_radius) * 0.5; + + float _width = max_radius * Math_TAU; + _lightmap_size_hint.x = MAX(1.0, (_width / texel_size) + padding); + float _height = radius * Math_TAU; + _lightmap_size_hint.y = MAX(1.0, (_height / texel_size) + padding); + + set_lightmap_size_hint(_lightmap_size_hint); + } +} + void TorusMesh::_create_mesh_array(Array &p_arr) const { // set our bounding box @@ -1573,6 +1972,7 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { Vector<Vector3> normals; Vector<float> tangents; Vector<Vector2> uvs; + Vector<Vector2> uv2s; Vector<int> indices; #define ADD_TANGENT(m_x, m_y, m_z, m_d) \ @@ -1592,6 +1992,17 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { float radius = (max_radius - min_radius) * 0.5; + // Only used if we calculate UV2 + bool _add_uv2 = get_add_uv2(); + float texel_size = get_lightmap_texel_size(); + float _uv2_padding = get_uv2_padding() * texel_size; + + float horizontal_total = max_radius * Math_TAU + _uv2_padding; + float max_h = max_radius * Math_TAU / horizontal_total; + float delta_h = (max_radius - min_radius) * Math_TAU / horizontal_total; + + float height_v = radius * Math_TAU / (radius * Math_TAU + _uv2_padding); + for (int i = 0; i <= rings; i++) { int prevrow = (i - 1) * (ring_segments + 1); int thisrow = i * (ring_segments + 1); @@ -1607,10 +2018,17 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { Vector2 normalj = Vector2(-Math::cos(angj), Math::sin(angj)); Vector2 normalk = normalj * radius + Vector2(min_radius + radius, 0); + float offset_h = 0.5 * (1.0 - normalj.x) * delta_h; + float adj_h = max_h - offset_h; + offset_h *= 0.5; + points.push_back(Vector3(normali.x * normalk.x, normalk.y, normali.y * normalk.x)); normals.push_back(Vector3(normali.x * normalj.x, normalj.y, normali.y * normalj.x)); ADD_TANGENT(-Math::cos(angi), 0.0, Math::sin(angi), 1.0); uvs.push_back(Vector2(inci, incj)); + if (_add_uv2) { + uv2s.push_back(Vector2(offset_h + inci * adj_h, incj * height_v)); + } if (i > 0 && j > 0) { indices.push_back(thisrow + j - 1); @@ -1628,6 +2046,9 @@ void TorusMesh::_create_mesh_array(Array &p_arr) const { p_arr[RS::ARRAY_NORMAL] = normals; p_arr[RS::ARRAY_TANGENT] = tangents; p_arr[RS::ARRAY_TEX_UV] = uvs; + if (_add_uv2) { + p_arr[RS::ARRAY_TEX_UV2] = uv2s; + } p_arr[RS::ARRAY_INDEX] = indices; } @@ -1750,6 +2171,24 @@ int TubeTrailMesh::get_section_rings() const { return section_rings; } +void TubeTrailMesh::set_cap_top(bool p_cap_top) { + cap_top = p_cap_top; + _request_update(); +} + +bool TubeTrailMesh::is_cap_top() const { + return cap_top; +} + +void TubeTrailMesh::set_cap_bottom(bool p_cap_bottom) { + cap_bottom = p_cap_bottom; + _request_update(); +} + +bool TubeTrailMesh::is_cap_bottom() const { + return cap_bottom; +} + void TubeTrailMesh::set_curve(const Ref<Curve> &p_curve) { if (curve == p_curve) { return; @@ -1785,6 +2224,8 @@ Transform3D TubeTrailMesh::get_builtin_bind_pose(int p_index) const { } void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { + // Seeing use case for TubeTrailMesh, no need to do anything more then default UV2 calculation + PackedVector3Array points; PackedVector3Array normals; PackedFloat32Array tangents; @@ -1861,49 +2302,21 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { thisrow = point; } - // add top - float scale_pos = 1.0; - if (curve.is_valid() && curve->get_point_count() > 0) { - scale_pos = curve->sample_baked(0); - } - - if (scale_pos > CMP_EPSILON) { - float y = depth * 0.5; - - thisrow = point; - points.push_back(Vector3(0.0, y, 0)); - normals.push_back(Vector3(0.0, 1.0, 0.0)); - ADD_TANGENT(1.0, 0.0, 0.0, 1.0) - uvs.push_back(Vector2(0.25, 0.75)); - point++; - - bone_indices.push_back(0); - bone_indices.push_back(0); - bone_indices.push_back(0); - bone_indices.push_back(0); - - bone_weights.push_back(1.0); - bone_weights.push_back(0); - bone_weights.push_back(0); - bone_weights.push_back(0); - - float rm = radius * scale_pos; - - for (int i = 0; i <= radial_steps; i++) { - float r = i; - r /= radial_steps; - - float x = sin(r * Math_TAU); - float z = cos(r * Math_TAU); + if (cap_top) { + // add top + float scale_pos = 1.0; + if (curve.is_valid() && curve->get_point_count() > 0) { + scale_pos = curve->sample_baked(0); + } - float u = ((x + 1.0) * 0.25); - float v = 0.5 + ((z + 1.0) * 0.25); + if (scale_pos > CMP_EPSILON) { + float y = depth * 0.5; - Vector3 p = Vector3(x * rm, y, z * rm); - points.push_back(p); + thisrow = point; + points.push_back(Vector3(0.0, y, 0)); normals.push_back(Vector3(0.0, 1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) - uvs.push_back(Vector2(u, v)); + uvs.push_back(Vector2(0.25, 0.75)); point++; bone_indices.push_back(0); @@ -1916,57 +2329,59 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { bone_weights.push_back(0); bone_weights.push_back(0); - if (i > 0) { - indices.push_back(thisrow); - indices.push_back(point - 1); - indices.push_back(point - 2); - }; - }; - }; + float rm = radius * scale_pos; - float scale_neg = 1.0; - if (curve.is_valid() && curve->get_point_count() > 0) { - scale_neg = curve->sample_baked(1.0); - } + for (int i = 0; i <= radial_steps; i++) { + float r = i; + r /= radial_steps; - // add bottom - if (scale_neg > CMP_EPSILON) { - float y = depth * -0.5; + float x = sin(r * Math_TAU); + float z = cos(r * Math_TAU); - thisrow = point; - points.push_back(Vector3(0.0, y, 0.0)); - normals.push_back(Vector3(0.0, -1.0, 0.0)); - ADD_TANGENT(1.0, 0.0, 0.0, 1.0) - uvs.push_back(Vector2(0.75, 0.75)); - point++; + float u = ((x + 1.0) * 0.25); + float v = 0.5 + ((z + 1.0) * 0.25); - bone_indices.push_back(sections); - bone_indices.push_back(0); - bone_indices.push_back(0); - bone_indices.push_back(0); + Vector3 p = Vector3(x * rm, y, z * rm); + points.push_back(p); + normals.push_back(Vector3(0.0, 1.0, 0.0)); + ADD_TANGENT(1.0, 0.0, 0.0, 1.0) + uvs.push_back(Vector2(u, v)); + point++; - bone_weights.push_back(1.0); - bone_weights.push_back(0); - bone_weights.push_back(0); - bone_weights.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); - float rm = radius * scale_neg; + bone_weights.push_back(1.0); + bone_weights.push_back(0); + bone_weights.push_back(0); + bone_weights.push_back(0); - for (int i = 0; i <= radial_steps; i++) { - float r = i; - r /= radial_steps; + if (i > 0) { + indices.push_back(thisrow); + indices.push_back(point - 1); + indices.push_back(point - 2); + } + } + } + } - float x = sin(r * Math_TAU); - float z = cos(r * Math_TAU); + if (cap_bottom) { + float scale_neg = 1.0; + if (curve.is_valid() && curve->get_point_count() > 0) { + scale_neg = curve->sample_baked(1.0); + } - float u = 0.5 + ((x + 1.0) * 0.25); - float v = 1.0 - ((z + 1.0) * 0.25); + if (scale_neg > CMP_EPSILON) { + // add bottom + float y = depth * -0.5; - Vector3 p = Vector3(x * rm, y, z * rm); - points.push_back(p); + thisrow = point; + points.push_back(Vector3(0.0, y, 0.0)); normals.push_back(Vector3(0.0, -1.0, 0.0)); ADD_TANGENT(1.0, 0.0, 0.0, 1.0) - uvs.push_back(Vector2(u, v)); + uvs.push_back(Vector2(0.75, 0.75)); point++; bone_indices.push_back(sections); @@ -1979,13 +2394,43 @@ void TubeTrailMesh::_create_mesh_array(Array &p_arr) const { bone_weights.push_back(0); bone_weights.push_back(0); - if (i > 0) { - indices.push_back(thisrow); - indices.push_back(point - 2); - indices.push_back(point - 1); - }; - }; - }; + float rm = radius * scale_neg; + + for (int i = 0; i <= radial_steps; i++) { + float r = i; + r /= radial_steps; + + float x = sin(r * Math_TAU); + float z = cos(r * Math_TAU); + + float u = 0.5 + ((x + 1.0) * 0.25); + float v = 1.0 - ((z + 1.0) * 0.25); + + Vector3 p = Vector3(x * rm, y, z * rm); + points.push_back(p); + normals.push_back(Vector3(0.0, -1.0, 0.0)); + ADD_TANGENT(1.0, 0.0, 0.0, 1.0) + uvs.push_back(Vector2(u, v)); + point++; + + bone_indices.push_back(sections); + bone_indices.push_back(0); + bone_indices.push_back(0); + bone_indices.push_back(0); + + bone_weights.push_back(1.0); + bone_weights.push_back(0); + bone_weights.push_back(0); + bone_weights.push_back(0); + + if (i > 0) { + indices.push_back(thisrow); + indices.push_back(point - 2); + indices.push_back(point - 1); + } + } + } + } p_arr[RS::ARRAY_VERTEX] = points; p_arr[RS::ARRAY_NORMAL] = normals; @@ -2012,6 +2457,12 @@ void TubeTrailMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_section_rings", "section_rings"), &TubeTrailMesh::set_section_rings); ClassDB::bind_method(D_METHOD("get_section_rings"), &TubeTrailMesh::get_section_rings); + ClassDB::bind_method(D_METHOD("set_cap_top", "cap_top"), &TubeTrailMesh::set_cap_top); + ClassDB::bind_method(D_METHOD("is_cap_top"), &TubeTrailMesh::is_cap_top); + + ClassDB::bind_method(D_METHOD("set_cap_bottom", "cap_bottom"), &TubeTrailMesh::set_cap_bottom); + ClassDB::bind_method(D_METHOD("is_cap_bottom"), &TubeTrailMesh::is_cap_bottom); + ClassDB::bind_method(D_METHOD("set_curve", "curve"), &TubeTrailMesh::set_curve); ClassDB::bind_method(D_METHOD("get_curve"), &TubeTrailMesh::get_curve); @@ -2024,13 +2475,16 @@ void TubeTrailMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "section_rings", PROPERTY_HINT_RANGE, "1,128,1"), "set_section_rings", "get_section_rings"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_top"), "set_cap_top", "is_cap_top"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cap_bottom"), "set_cap_bottom", "is_cap_bottom"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); } TubeTrailMesh::TubeTrailMesh() { } -// TUBE TRAIL +// RIBBON TRAIL void RibbonTrailMesh::set_shape(Shape p_shape) { shape = p_shape; @@ -2109,6 +2563,8 @@ Transform3D RibbonTrailMesh::get_builtin_bind_pose(int p_index) const { } void RibbonTrailMesh::_create_mesh_array(Array &p_arr) const { + // Seeing use case of ribbon trail mesh, no need to implement special UV2 calculation + PackedVector3Array points; PackedVector3Array normals; PackedFloat32Array tangents; @@ -2354,13 +2810,7 @@ void TextMesh::_generate_glyph_mesh_data(const GlyphMeshKey &p_key, const Glyph real_t step = CLAMP(curve_step / (p0 - p3).length(), 0.01, 0.5); real_t t = step; while (t < 1.0) { - real_t omt = (1.0 - t); - real_t omt2 = omt * omt; - real_t omt3 = omt2 * omt; - real_t t2 = t * t; - real_t t3 = t2 * t; - - Vector2 point = p0 * omt3 + p1 * omt2 * t * 3.0 + p2 * omt * t2 * 3.0 + p3 * t3; + Vector2 point = p0.bezier_interpolate(p1, p2, p3, t); Vector2 p = point * pixel_size + origin; polygon.push_back(ContourPoint(p, false)); t += step; diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index ee61f0ac55..5cef042a18 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -56,6 +56,9 @@ private: Ref<Material> material; bool flip_faces = false; + bool add_uv2 = false; + float uv2_padding = 2.0; + // make sure we do an update after we've finished constructing our object mutable bool pending_request = true; void _update() const; @@ -70,6 +73,10 @@ protected: void _request_update(); GDVIRTUAL0RC(Array, _create_mesh_array) + Vector2 get_uv2_scale(Vector2 p_margin_scale = Vector2(1.0, 1.0)) const; + float get_lightmap_texel_size() const; + virtual void _update_lightmap_size(){}; + public: virtual int get_surface_count() const override; virtual int surface_get_array_len(int p_idx) const override; @@ -98,6 +105,12 @@ public: void set_flip_faces(bool p_enable); bool get_flip_faces() const; + void set_add_uv2(bool p_enable); + bool get_add_uv2() const { return add_uv2; } + + void set_uv2_padding(float p_padding); + float get_uv2_padding() const { return uv2_padding; } + PrimitiveMesh(); ~PrimitiveMesh(); }; @@ -118,8 +131,10 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: - static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 8); + static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 8, bool p_add_uv2 = false, const float p_uv2_padding = 1.0); void set_radius(const float p_radius); float get_radius() const; @@ -152,8 +167,10 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: - static void create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w = 0, int subdivide_h = 0, int subdivide_d = 0); + static void create_mesh_array(Array &p_arr, Vector3 size, int subdivide_w = 0, int subdivide_h = 0, int subdivide_d = 0, bool p_add_uv2 = false, const float p_uv2_padding = 1.0); void set_size(const Vector3 &p_size); Vector3 get_size() const; @@ -190,8 +207,10 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: - static void create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments = 64, int rings = 4, bool cap_top = true, bool cap_bottom = true); + static void create_mesh_array(Array &p_arr, float top_radius, float bottom_radius, float height, int radial_segments = 64, int rings = 4, bool cap_top = true, bool cap_bottom = true, bool p_add_uv2 = false, const float p_uv2_padding = 1.0); void set_top_radius(const float p_radius); float get_top_radius() const; @@ -241,6 +260,8 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: void set_size(const Size2 &p_size); Size2 get_size() const; @@ -292,6 +313,8 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: void set_left_to_right(const float p_left_to_right); float get_left_to_right() const; @@ -328,8 +351,10 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: - static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 32, bool is_hemisphere = false); + static void create_mesh_array(Array &p_arr, float radius, float height, int radial_segments = 64, int rings = 32, bool is_hemisphere = false, bool p_add_uv2 = false, const float p_uv2_padding = 1.0); void set_radius(const float p_radius); float get_radius() const; @@ -365,6 +390,8 @@ protected: static void _bind_methods(); virtual void _create_mesh_array(Array &p_arr) const override; + virtual void _update_lightmap_size() override; + public: void set_inner_radius(const float p_inner_radius); float get_inner_radius() const; @@ -404,6 +431,8 @@ private: int sections = 5; float section_length = 0.2; int section_rings = 3; + bool cap_top = true; + bool cap_bottom = true; Ref<Curve> curve; @@ -429,6 +458,12 @@ public: void set_section_rings(const int p_section_rings); int get_section_rings() const; + void set_cap_top(bool p_cap_top); + bool is_cap_top() const; + + void set_cap_bottom(bool p_cap_bottom); + bool is_cap_bottom() const; + void set_curve(const Ref<Curve> &p_curve); Ref<Curve> get_curve() const; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 85b538b1d9..092f672cba 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -144,6 +144,7 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R } String id = token.value; + Error err = OK; if (!ignore_resource_parsing) { if (!ext_resources.has(id)) { @@ -163,7 +164,7 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R error = ERR_FILE_MISSING_DEPENDENCIES; error_text = "[ext_resource] referenced nonexistent resource at: " + path; _printerr(); - return error; + err = error; } else { ResourceLoader::notify_dependency_error(local_path, path, type); } @@ -175,7 +176,7 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R error = ERR_FILE_MISSING_DEPENDENCIES; error_text = "[ext_resource] referenced non-loaded resource at: " + path; _printerr(); - return error; + err = error; } } else { r_res = Ref<Resource>(); @@ -187,7 +188,7 @@ Error ResourceLoaderText::_parse_ext_resource(VariantParser::Stream *p_stream, R return ERR_PARSE_ERROR; } - return OK; + return err; } Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourceParser &parser) { @@ -217,7 +218,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars if (next_tag.fields.has("type")) { type = packed_scene->get_state()->add_name(next_tag.fields["type"]); } else { - type = SceneState::TYPE_INSTANCED; //no type? assume this was instantiated + type = SceneState::TYPE_INSTANTIATED; //no type? assume this was instantiated } HashSet<StringName> path_properties; @@ -256,7 +257,7 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars if (next_tag.fields.has("owner")) { owner = packed_scene->get_state()->add_node_path(next_tag.fields["owner"]); } else { - if (parent != -1 && !(type == SceneState::TYPE_INSTANCED && instance == -1)) { + if (parent != -1 && !(type == SceneState::TYPE_INSTANTIATED && instance == -1)) { owner = 0; //if no owner, owner is root } } @@ -511,6 +512,7 @@ Error ResourceLoaderText::load() { if (error) { _printerr(); + return error; } resource_current++; @@ -600,9 +602,13 @@ Error ResourceLoaderText::load() { resource_current++; int_resources[id] = res; //always assign int resources - if (do_assign && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { - res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); - res->set_scene_unique_id(id); + if (do_assign) { + if (cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE) { + res->set_path(path); + } else { + res->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); + res->set_scene_unique_id(id); + } } Dictionary missing_resource_properties; @@ -830,7 +836,8 @@ void ResourceLoaderText::set_translation_remapped(bool p_remapped) { translation_remapped = p_remapped; } -ResourceLoaderText::ResourceLoaderText() {} +ResourceLoaderText::ResourceLoaderText() : + stream(false) {} void ResourceLoaderText::get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types) { open(p_f); @@ -884,6 +891,7 @@ void ResourceLoaderText::get_dependencies(Ref<FileAccess> p_f, List<String> *p_d error_text = "Unexpected end of file"; _printerr(); error = ERR_FILE_CORRUPT; + return; } } } @@ -974,15 +982,26 @@ Error ResourceLoaderText::rename_dependencies(Ref<FileAccess> p_f, const String f->seek(tag_end); - uint8_t c = f->get_8(); - if (c == '\n' && !f->eof_reached()) { - // Skip first newline character since we added one - c = f->get_8(); + const uint32_t buffer_size = 2048; + uint8_t *buffer = (uint8_t *)alloca(buffer_size); + uint32_t num_read; + + num_read = f->get_buffer(buffer, buffer_size); + ERR_FAIL_COND_V_MSG(num_read == UINT32_MAX, ERR_CANT_CREATE, "Failed to allocate memory for buffer."); + ERR_FAIL_COND_V(num_read == 0, ERR_FILE_CORRUPT); + + if (*buffer == '\n') { + // Skip first newline character since we added one. + if (num_read > 1) { + fw->store_buffer(buffer + 1, num_read - 1); + } + } else { + fw->store_buffer(buffer, num_read); } while (!f->eof_reached()) { - fw->store_8(c); - c = f->get_8(); + num_read = f->get_buffer(buffer, buffer_size); + fw->store_buffer(buffer, num_read); } bool all_ok = fw->get_error() == OK; @@ -1349,7 +1368,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 4d566178a5..48ec084b02 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -177,7 +177,7 @@ bool Shader::is_text_shader() const { } bool Shader::has_parameter(const StringName &p_name) const { - return params_cache.has("shader_parameter/" + p_name); + return params_cache.has(p_name); } void Shader::_update_shader() const { @@ -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 af84144591..87c6c36ee9 100644 --- a/scene/resources/shape_2d.cpp +++ b/scene/resources/shape_2d.cpp @@ -124,5 +124,6 @@ Shape2D::Shape2D(const RID &p_rid) { } Shape2D::~Shape2D() { + 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 4423c1d7bb..7992ba9fd4 100644 --- a/scene/resources/shape_3d.cpp +++ b/scene/resources/shape_3d.cpp @@ -128,5 +128,6 @@ Shape3D::Shape3D(RID p_shape) : shape(p_shape) {} Shape3D::~Shape3D() { + ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); PhysicsServer3D::get_singleton()->free(shape); } diff --git a/scene/resources/skeleton_modification_2d_fabrik.h b/scene/resources/skeleton_modification_2d_fabrik.h index 4a875d039f..0ca6582965 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.h +++ b/scene/resources/skeleton_modification_2d_fabrik.h @@ -68,8 +68,8 @@ private: float chain_tolarance = 0.01; int chain_max_iterations = 10; int chain_iterations = 0; - Transform2D target_global_pose = Transform2D(); - Transform2D origin_global_pose = Transform2D(); + Transform2D target_global_pose; + Transform2D origin_global_pose; void fabrik_joint_update_bone2d_cache(int p_joint_idx); void chain_backwards(); diff --git a/scene/resources/skeleton_modification_3d_fabrik.h b/scene/resources/skeleton_modification_3d_fabrik.h index e2e490d636..3e3aa5e587 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.h +++ b/scene/resources/skeleton_modification_3d_fabrik.h @@ -47,7 +47,7 @@ private: bool auto_calculate_length = true; bool use_tip_node = false; - NodePath tip_node = NodePath(); + NodePath tip_node; ObjectID tip_node_cache; bool use_target_basis = false; @@ -68,8 +68,8 @@ private: void update_joint_tip_cache(int p_joint_idx); int final_joint_idx = 0; - Transform3D target_global_pose = Transform3D(); - Transform3D origin_global_pose = Transform3D(); + Transform3D target_global_pose; + Transform3D origin_global_pose; void chain_backwards(); void chain_forwards(); 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/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp index f1eddd8ffc..cb5cb4ef96 100644 --- a/scene/resources/syntax_highlighter.cpp +++ b/scene/resources/syntax_highlighter.cpp @@ -336,7 +336,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) { } String word = str.substr(j, to - j); - Color col = Color(); + Color col; if (keywords.has(word)) { col = keywords[word]; } else if (member_keywords.has(word)) { diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index afab44834d..d094563e6a 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -56,8 +56,8 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::set_bidi_override); ClassDB::bind_method(D_METHOD("add_string", "text", "font", "font_size", "language", "meta"), &TextLine::add_string, DEFVAL(""), DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); + ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length", "baseline"), &TextLine::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align", "baseline"), &TextLine::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("set_width", "width"), &TextLine::set_width); ClassDB::bind_method(D_METHOD("get_width"), &TextLine::get_width); @@ -202,15 +202,15 @@ bool TextLine::add_string(const String &p_text, const Ref<Font> &p_font, int p_f return res; } -bool TextLine::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) { - bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length); +bool TextLine::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length, float p_baseline) { + bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length, p_baseline); dirty = true; return res; } -bool TextLine::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) { +bool TextLine::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, float p_baseline) { const_cast<TextLine *>(this)->_shape(); - return TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align); + return TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align, p_baseline); } Array TextLine::get_objects() const { diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h index e70e82cf2b..801a8a8c11 100644 --- a/scene/resources/text_line.h +++ b/scene/resources/text_line.h @@ -76,8 +76,8 @@ public: bool get_preserve_control() const; bool add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", const Variant &p_meta = Variant()); - bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1); - bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER); + bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1, float p_baseline = 0.0); + bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, float p_baseline = 0.0); void set_horizontal_alignment(HorizontalAlignment p_alignment); HorizontalAlignment get_horizontal_alignment() const; diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 59bb24c8b8..d2e85d28e6 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -64,8 +64,8 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap); ClassDB::bind_method(D_METHOD("add_string", "text", "font", "font_size", "language", "meta"), &TextParagraph::add_string, DEFVAL(""), DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); + ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length", "baseline"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align", "baseline"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &TextParagraph::set_alignment); ClassDB::bind_method(D_METHOD("get_alignment"), &TextParagraph::get_alignment); @@ -385,18 +385,18 @@ void TextParagraph::set_bidi_override(const Array &p_override) { lines_dirty = true; } -bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) { +bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length, float p_baseline) { _THREAD_SAFE_METHOD_ - bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length); + bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length, p_baseline); lines_dirty = true; return res; } -bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) { +bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, float p_baseline) { _THREAD_SAFE_METHOD_ - bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align); + bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align, p_baseline); lines_dirty = true; return res; } diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index 0fe82b4364..dced278db4 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -95,8 +95,8 @@ public: void clear_dropcap(); bool add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", const Variant &p_meta = Variant()); - bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1); - bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER); + bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1, float p_baseline = 0.0); + bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, float p_baseline = 0.0); void set_alignment(HorizontalAlignment p_alignment); HorizontalAlignment get_alignment() const; diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index a6fb359051..18915e294e 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 { @@ -64,6 +60,7 @@ bool Texture2D::is_pixel_opaque(int p_x, int p_y) const { GDVIRTUAL_CALL(_is_pixel_opaque, p_x, p_y, ret); return ret; } + bool Texture2D::has_alpha() const { bool ret = true; GDVIRTUAL_CALL(_has_alpha, ret); @@ -1091,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); @@ -1607,35 +1591,28 @@ bool AtlasTexture::get_rect_region(const Rect2 &p_rect, const Rect2 &p_src_rect, return false; } - Rect2 rc = region; - Rect2 src = p_src_rect; if (src.size == Size2()) { - src.size = rc.size; + src.size = region.size; } Vector2 scale = p_rect.size / src.size; - src.position += (rc.position - margin.position); - Rect2 src_c = rc.intersection(src); - if (src_c.size == Size2()) { + src.position += (region.position - margin.position); + Rect2 src_clipped = region.intersection(src); + if (src_clipped.size == Size2()) { return false; } - Vector2 ofs = (src_c.position - src.position); + Vector2 ofs = (src_clipped.position - src.position); if (scale.x < 0) { - float mx = (margin.size.width - margin.position.x); - mx -= margin.position.x; - ofs.x = -(ofs.x + mx); + ofs.x += (src_clipped.size.x - src.size.x); } if (scale.y < 0) { - float my = margin.size.height - margin.position.y; - my -= margin.position.y; - ofs.y = -(ofs.y + my); + ofs.y += (src_clipped.size.y - src.size.y); } - Rect2 dr(p_rect.position + ofs * scale, src_c.size * scale); - r_rect = dr; - r_src_rect = src_c; + r_rect = Rect2(p_rect.position + ofs * scale, src_clipped.size * scale); + r_src_rect = src_clipped; return true; } @@ -2858,60 +2835,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/tile_set.cpp b/scene/resources/tile_set.cpp index 3caf6484d9..0b0461432b 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -35,7 +35,6 @@ #include "core/math/geometry_2d.h" #include "core/templates/local_vector.h" #include "core/templates/rb_set.h" -#include "scene/2d/navigation_region_2d.h" #include "scene/gui/control.h" #include "scene/resources/convex_polygon_shape_2d.h" #include "servers/navigation_server_2d.h" @@ -477,7 +476,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source int new_source_id = p_atlas_source_id_override >= 0 ? p_atlas_source_id_override : next_source_id; sources[new_source_id] = p_tile_set_source; - source_ids.append(new_source_id); + source_ids.push_back(new_source_id); source_ids.sort(); p_tile_set_source->set_tile_set(this); _compute_next_source_id(); @@ -517,7 +516,7 @@ void TileSet::set_source_id(int p_source_id, int p_new_source_id) { sources.erase(p_source_id); source_ids.erase(p_source_id); - source_ids.append(p_new_source_id); + source_ids.push_back(p_new_source_id); source_ids.sort(); _compute_next_source_id(); @@ -1297,7 +1296,7 @@ void TileSet::cleanup_invalid_tile_proxies() { Vector<int> source_to_remove; for (const KeyValue<int, int> &E : source_level_proxies) { if (has_source(E.key)) { - source_to_remove.append(E.key); + source_to_remove.push_back(E.key); } } for (int i = 0; i < source_to_remove.size(); i++) { @@ -1309,7 +1308,7 @@ void TileSet::cleanup_invalid_tile_proxies() { for (const KeyValue<Array, Array> &E : coords_level_proxies) { Array a = E.key; if (has_source(a[0]) && get_source(a[0])->has_tile(a[1])) { - coords_to_remove.append(a); + coords_to_remove.push_back(a); } } for (int i = 0; i < coords_to_remove.size(); i++) { @@ -1322,7 +1321,7 @@ void TileSet::cleanup_invalid_tile_proxies() { for (const KeyValue<Array, Array> &E : alternative_level_proxies) { Array a = E.key; if (has_source(a[0]) && get_source(a[0])->has_tile(a[1]) && get_source(a[0])->has_alternative_tile(a[1], a[2])) { - alternative_to_remove.append(a); + alternative_to_remove.push_back(a); } } for (int i = 0; i < alternative_to_remove.size(); i++) { @@ -1439,16 +1438,18 @@ TileMapCell TileSet::get_random_tile_from_terrains_pattern(int p_terrain_set, Ti Vector<Vector2> TileSet::get_tile_shape_polygon() { Vector<Vector2> points; if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { - points.append(Vector2(-0.5, -0.5)); - points.append(Vector2(0.5, -0.5)); - points.append(Vector2(0.5, 0.5)); - points.append(Vector2(-0.5, 0.5)); + points.push_back(Vector2(-0.5, -0.5)); + points.push_back(Vector2(0.5, -0.5)); + points.push_back(Vector2(0.5, 0.5)); + points.push_back(Vector2(-0.5, 0.5)); + } else if (tile_shape == TileSet::TILE_SHAPE_ISOMETRIC) { + points.push_back(Vector2(0.0, -0.5)); + points.push_back(Vector2(-0.5, 0.0)); + points.push_back(Vector2(0.0, 0.5)); + points.push_back(Vector2(0.5, 0.0)); } else { float overlap = 0.0; switch (tile_shape) { - case TileSet::TILE_SHAPE_ISOMETRIC: - overlap = 0.5; - break; case TileSet::TILE_SHAPE_HEXAGON: overlap = 0.25; break; @@ -1459,12 +1460,13 @@ Vector<Vector2> TileSet::get_tile_shape_polygon() { break; } - points.append(Vector2(0.0, -0.5)); - points.append(Vector2(-0.5, overlap - 0.5)); - points.append(Vector2(-0.5, 0.5 - overlap)); - points.append(Vector2(0.0, 0.5)); - points.append(Vector2(0.5, 0.5 - overlap)); - points.append(Vector2(0.5, overlap - 0.5)); + points.push_back(Vector2(0.0, -0.5)); + points.push_back(Vector2(-0.5, overlap - 0.5)); + points.push_back(Vector2(-0.5, 0.5 - overlap)); + points.push_back(Vector2(0.0, 0.5)); + points.push_back(Vector2(0.5, 0.5 - overlap)); + points.push_back(Vector2(0.5, overlap - 0.5)); + if (get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL) { for (int i = 0; i < points.size(); i++) { points.write[i] = Vector2(points[i].y, points[i].x); @@ -3789,7 +3791,7 @@ Vector2i TileSetAtlasSource::get_atlas_grid_size() const { Size2i valid_area = txt->get_size() - margins; // Compute the number of valid tiles in the tiles atlas - Size2i grid_size = Size2i(); + Size2i grid_size; if (valid_area.x >= texture_region_size.x && valid_area.y >= texture_region_size.y) { valid_area -= texture_region_size; grid_size = Size2i(1, 1) + valid_area / (texture_region_size + separation); @@ -3854,7 +3856,7 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) tiles[coords].alternatives[alternative_id] = memnew(TileData); tiles[coords].alternatives[alternative_id]->set_tile_set(tile_set); tiles[coords].alternatives[alternative_id]->set_allow_transform(alternative_id > 0); - tiles[coords].alternatives_ids.append(alternative_id); + tiles[coords].alternatives_ids.push_back(alternative_id); } if (components.size() >= 3) { bool valid; @@ -4028,11 +4030,11 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector tad.alternatives[0]->set_allow_transform(false); tad.alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); tad.alternatives[0]->notify_property_list_changed(); - tad.alternatives_ids.append(0); + tad.alternatives_ids.push_back(0); // Create and resize the tile. tiles.insert(p_atlas_coords, tad); - tiles_ids.append(p_atlas_coords); + tiles_ids.push_back(p_atlas_coords); tiles_ids.sort(); _create_coords_mapping_cache(p_atlas_coords); @@ -4343,7 +4345,7 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_ tiles.erase(p_atlas_coords); tiles_ids.erase(p_atlas_coords); - tiles_ids.append(new_atlas_coords); + tiles_ids.push_back(new_atlas_coords); tiles_ids.sort(); } tiles[new_atlas_coords].size_in_atlas = new_size; @@ -4363,8 +4365,9 @@ int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, i tiles[p_atlas_coords].alternatives[new_alternative_id] = memnew(TileData); tiles[p_atlas_coords].alternatives[new_alternative_id]->set_tile_set(tile_set); tiles[p_atlas_coords].alternatives[new_alternative_id]->set_allow_transform(true); + tiles[p_atlas_coords].alternatives[new_alternative_id]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); tiles[p_atlas_coords].alternatives[new_alternative_id]->notify_property_list_changed(); - tiles[p_atlas_coords].alternatives_ids.append(new_alternative_id); + tiles[p_atlas_coords].alternatives_ids.push_back(new_alternative_id); tiles[p_atlas_coords].alternatives_ids.sort(); _compute_next_alternative_id(p_atlas_coords); @@ -4394,7 +4397,7 @@ void TileSetAtlasSource::set_alternative_tile_id(const Vector2i p_atlas_coords, ERR_FAIL_COND_MSG(tiles[p_atlas_coords].alternatives.has(p_new_id), vformat("TileSetAtlasSource has already an alternative with id %d at %s.", p_new_id, String(p_atlas_coords))); tiles[p_atlas_coords].alternatives[p_new_id] = tiles[p_atlas_coords].alternatives[p_alternative_tile]; - tiles[p_atlas_coords].alternatives_ids.append(p_new_id); + tiles[p_atlas_coords].alternatives_ids.push_back(p_new_id); tiles[p_atlas_coords].alternatives.erase(p_alternative_tile); tiles[p_atlas_coords].alternatives_ids.erase(p_alternative_tile); @@ -4682,7 +4685,7 @@ int TileSetScenesCollectionSource::create_scene_tile(Ref<PackedScene> p_packed_s int new_scene_id = p_id_override >= 0 ? p_id_override : next_scene_id; scenes[new_scene_id] = SceneData(); - scenes_ids.append(new_scene_id); + scenes_ids.push_back(new_scene_id); scenes_ids.sort(); set_scene_tile_scene(new_scene_id, p_packed_scene); _compute_next_alternative_id(); @@ -4699,7 +4702,7 @@ void TileSetScenesCollectionSource::set_scene_tile_id(int p_id, int p_new_id) { scenes[p_new_id] = SceneData(); scenes[p_new_id] = scenes[p_id]; - scenes_ids.append(p_new_id); + scenes_ids.push_back(p_new_id); scenes_ids.sort(); _compute_next_alternative_id(); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 8f175e99a6..88bbbd157a 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -36,10 +36,10 @@ #include "core/templates/local_vector.h" #include "core/templates/rb_set.h" #include "scene/2d/light_occluder_2d.h" -#include "scene/2d/navigation_region_2d.h" #include "scene/main/canvas_item.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" +#include "scene/resources/navigation_polygon.h" #include "scene/resources/packed_scene.h" #include "scene/resources/physics_material.h" #include "scene/resources/shape_2d.h" @@ -785,7 +785,7 @@ private: bool flip_h = false; bool flip_v = false; bool transpose = false; - Vector2i tex_offset = Vector2i(); + Vector2i tex_offset; Ref<Material> material = Ref<Material>(); Color modulate = Color(1.0, 1.0, 1.0, 1.0); int z_index = 0; diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index b30ca3e721..6b8f8097a8 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -2066,10 +2066,9 @@ Error VisualShader::_write_node(Type type, StringBuilder *p_global_code, StringB } if (!node_code.is_empty()) { - r_code += "\n"; + r_code += "\n\n"; } - r_code += "\n"; // r_processed.insert(p_node); return OK; @@ -2366,71 +2365,62 @@ void VisualShader::_update_shader() const { String global_compute_code; if (shader_mode == Shader::MODE_PARTICLES) { - bool has_start = !code_map[TYPE_START].is_empty(); bool has_start_custom = !code_map[TYPE_START_CUSTOM].is_empty(); bool has_process = !code_map[TYPE_PROCESS].is_empty(); bool has_process_custom = !code_map[TYPE_PROCESS_CUSTOM].is_empty(); bool has_collide = !code_map[TYPE_COLLIDE].is_empty(); shader_code += "void start() {\n"; - if (has_start || has_start_custom) { - shader_code += " uint __seed = __hash(NUMBER + uint(1) + RANDOM_SEED);\n"; - shader_code += " vec3 __diff = TRANSFORM[3].xyz - EMISSION_TRANSFORM[3].xyz;\n"; - shader_code += " float __radians;\n"; - shader_code += " vec3 __vec3_buff1;\n"; - shader_code += " vec3 __vec3_buff2;\n"; - shader_code += " float __scalar_buff1;\n"; - shader_code += " float __scalar_buff2;\n"; - shader_code += " int __scalar_ibuff;\n"; - shader_code += " vec4 __vec4_buff;\n"; - shader_code += " vec3 __ndiff = normalize(__diff);\n\n"; - } - if (has_start) { - shader_code += " {\n"; - shader_code += code_map[TYPE_START].replace("\n ", "\n "); - shader_code += " }\n"; - if (has_start_custom) { - shader_code += " \n"; - } - } + shader_code += " uint __seed = __hash(NUMBER + uint(1) + RANDOM_SEED);\n"; + shader_code += "\n"; + shader_code += " {\n"; + shader_code += code_map[TYPE_START].replace("\n ", "\n "); + shader_code += " }\n"; if (has_start_custom) { + shader_code += " \n"; shader_code += " {\n"; shader_code += code_map[TYPE_START_CUSTOM].replace("\n ", "\n "); shader_code += " }\n"; } shader_code += "}\n\n"; - shader_code += "void process() {\n"; + if (has_process || has_process_custom || has_collide) { + shader_code += "void process() {\n"; shader_code += " uint __seed = __hash(NUMBER + uint(1) + RANDOM_SEED);\n"; - shader_code += " vec3 __vec3_buff1;\n"; - shader_code += " vec3 __diff = TRANSFORM[3].xyz - EMISSION_TRANSFORM[3].xyz;\n"; - shader_code += " vec3 __ndiff = normalize(__diff);\n\n"; - } - shader_code += " {\n"; - String tab = " "; - if (has_collide) { - shader_code += " if (COLLIDED) {\n\n"; - shader_code += code_map[TYPE_COLLIDE].replace("\n ", "\n "); + shader_code += "\n"; + if (has_process || has_collide) { + shader_code += " {\n"; + } + String tab = " "; + if (has_collide) { + shader_code += " if (COLLIDED) {\n\n"; + shader_code += code_map[TYPE_COLLIDE].replace("\n ", "\n "); + if (has_process) { + shader_code += " } else {\n\n"; + tab += " "; + } + } if (has_process) { - shader_code += " } else {\n\n"; - tab += " "; + shader_code += code_map[TYPE_PROCESS].replace("\n ", "\n " + tab); + } + if (has_collide) { + shader_code += " }\n"; + } + if (has_process || has_collide) { + shader_code += " }\n"; } - } - if (has_process) { - shader_code += code_map[TYPE_PROCESS].replace("\n ", "\n " + tab); - } - if (has_collide) { - shader_code += " }\n"; - } - shader_code += " }\n"; - if (has_process_custom) { - shader_code += " {\n\n"; - shader_code += code_map[TYPE_PROCESS_CUSTOM].replace("\n ", "\n "); - shader_code += " }\n"; - } + if (has_process_custom) { + if (has_process || has_collide) { + shader_code += " \n"; + } + shader_code += " {\n"; + shader_code += code_map[TYPE_PROCESS_CUSTOM].replace("\n ", "\n "); + shader_code += " }\n"; + } - shader_code += "}\n\n"; + shader_code += "}\n\n"; + } global_compute_code += "float __rand_from_seed(inout uint seed) {\n"; global_compute_code += " int k;\n"; @@ -2658,6 +2648,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 +2681,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 diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 04637983b5..03abac1b3e 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -3108,9 +3108,9 @@ void VisualShaderNodeUVFunc::set_function(VisualShaderNodeUVFunc::Function p_fun return; } if (p_func == FUNC_PANNING) { - set_input_port_default_value(2, Vector2()); // offset + set_input_port_default_value(2, Vector2(), get_input_port_default_value(2)); // offset } else { // FUNC_SCALING - set_input_port_default_value(2, Vector2(0.5, 0.5)); // pivot + set_input_port_default_value(2, Vector2(0.5, 0.5), get_input_port_default_value(2)); // pivot } func = p_func; emit_changed(); diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp index f125b05a26..d61a343688 100644 --- a/scene/resources/visual_shader_particle_nodes.cpp +++ b/scene/resources/visual_shader_particle_nodes.cpp @@ -374,13 +374,13 @@ String VisualShaderNodeParticleMeshEmitter::_generate_code(VisualShader::Type p_ if (is_output_port_connected(p_index)) { switch (p_port_type) { case PORT_TYPE_VECTOR_2D: { - code += vformat(" %s = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0).xy;\n", p_output_vars[p_index], make_unique_id(p_type, p_id, p_texture_name)); + code += vformat(" %s = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0).xy;\n", p_output_vars[p_index], make_unique_id(p_type, p_id, p_texture_name)); } break; case PORT_TYPE_VECTOR_3D: { if (mode_2d) { - code += vformat(" %s = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0).xy;\n", p_output_vars[p_index], make_unique_id(p_type, p_id, p_texture_name)); + code += vformat(" %s = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0).xy;\n", p_output_vars[p_index], make_unique_id(p_type, p_id, p_texture_name)); } else { - code += vformat(" %s = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0).xyz;\n", p_output_vars[p_index], make_unique_id(p_type, p_id, p_texture_name)); + code += vformat(" %s = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0).xyz;\n", p_output_vars[p_index], make_unique_id(p_type, p_id, p_texture_name)); } } break; default: @@ -392,25 +392,27 @@ String VisualShaderNodeParticleMeshEmitter::_generate_code(VisualShader::Type p_ String VisualShaderNodeParticleMeshEmitter::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; - code += " __scalar_ibuff = int(__rand_from_seed(__seed) * 65535.0) % " + itos(position_texture->get_width()) + ";\n"; + code += " {\n"; + code += " int __scalar_ibuff = int(__rand_from_seed(__seed) * 65535.0) % " + itos(position_texture->get_width()) + ";\n"; code += _generate_code(p_type, p_id, p_output_vars, 0, "mesh_vx", VisualShaderNode::PORT_TYPE_VECTOR_3D); code += _generate_code(p_type, p_id, p_output_vars, 1, "mesh_nm", VisualShaderNode::PORT_TYPE_VECTOR_3D); if (is_output_port_connected(2) || is_output_port_connected(3)) { - code += vformat(" __vec4_buff = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0);\n", make_unique_id(p_type, p_id, "mesh_col")); + code += vformat(" vec4 __vec4_buff = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0);\n", make_unique_id(p_type, p_id, "mesh_col")); if (is_output_port_connected(2)) { - code += " " + p_output_vars[2] + " = __vec4_buff.rgb;\n"; + code += " " + p_output_vars[2] + " = __vec4_buff.rgb;\n"; } if (is_output_port_connected(3)) { - code += " " + p_output_vars[3] + " = __vec4_buff.a;\n"; + code += " " + p_output_vars[3] + " = __vec4_buff.a;\n"; } } code += _generate_code(p_type, p_id, p_output_vars, 4, "mesh_uv", VisualShaderNode::PORT_TYPE_VECTOR_2D); code += _generate_code(p_type, p_id, p_output_vars, 5, "mesh_uv2", VisualShaderNode::PORT_TYPE_VECTOR_2D); + code += " }\n"; return code; } @@ -737,6 +739,8 @@ VisualShaderNodeParticleMeshEmitter::VisualShaderNodeParticleMeshEmitter() { color_texture.instantiate(); uv_texture.instantiate(); uv2_texture.instantiate(); + + simple_decl = false; } // VisualShaderNodeParticleMultiplyByAxisAngle @@ -879,22 +883,26 @@ bool VisualShaderNodeParticleConeVelocity::has_output_port_preview(int p_port) c String VisualShaderNodeParticleConeVelocity::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; - code += " __radians = radians(" + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n"; - code += " __scalar_buff1 = __rand_from_seed_m1_p1(__seed) * __radians;\n"; - code += " __scalar_buff2 = __rand_from_seed_m1_p1(__seed) * __radians;\n"; - code += " __vec3_buff1 = " + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + ";\n"; - code += " __scalar_buff1 += __vec3_buff1.z != 0.0 ? atan(__vec3_buff1.x, __vec3_buff1.z) : sign(__vec3_buff1.x) * (PI / 2.0);\n"; - code += " __scalar_buff2 += __vec3_buff1.z != 0.0 ? atan(__vec3_buff1.y, abs(__vec3_buff1.z)) : (__vec3_buff1.x != 0.0 ? atan(__vec3_buff1.y, abs(__vec3_buff1.x)) : sign(__vec3_buff1.y) * (PI / 2.0));\n"; - code += " __vec3_buff1 = vec3(sin(__scalar_buff1), 0.0, cos(__scalar_buff1));\n"; - code += " __vec3_buff2 = vec3(0.0, sin(__scalar_buff2), cos(__scalar_buff2));\n"; - code += " __vec3_buff2.z = __vec3_buff2.z / max(0.0001, sqrt(abs(__vec3_buff2.z)));\n"; - code += " " + p_output_vars[0] + " = normalize(vec3(__vec3_buff1.x * __vec3_buff2.z, __vec3_buff2.y, __vec3_buff1.z * __vec3_buff2.z));\n"; + code += " {\n"; + code += " float __radians = radians(" + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n"; + code += " float __scalar_buff1 = __rand_from_seed_m1_p1(__seed) * __radians;\n"; + code += " float __scalar_buff2 = __rand_from_seed_m1_p1(__seed) * __radians;\n"; + code += " vec3 __vec3_buff1 = " + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + ";\n"; + code += " __scalar_buff1 += __vec3_buff1.z != 0.0 ? atan(__vec3_buff1.x, __vec3_buff1.z) : sign(__vec3_buff1.x) * (PI / 2.0);\n"; + code += " __scalar_buff2 += __vec3_buff1.z != 0.0 ? atan(__vec3_buff1.y, abs(__vec3_buff1.z)) : (__vec3_buff1.x != 0.0 ? atan(__vec3_buff1.y, abs(__vec3_buff1.x)) : sign(__vec3_buff1.y) * (PI / 2.0));\n"; + code += " __vec3_buff1 = vec3(sin(__scalar_buff1), 0.0, cos(__scalar_buff1));\n"; + code += " vec3 __vec3_buff2 = vec3(0.0, sin(__scalar_buff2), cos(__scalar_buff2));\n"; + code += " __vec3_buff2.z = __vec3_buff2.z / max(0.0001, sqrt(abs(__vec3_buff2.z)));\n"; + code += " " + p_output_vars[0] + " = normalize(vec3(__vec3_buff1.x * __vec3_buff2.z, __vec3_buff2.y, __vec3_buff1.z * __vec3_buff2.z));\n"; + code += " }\n"; return code; } VisualShaderNodeParticleConeVelocity::VisualShaderNodeParticleConeVelocity() { set_input_port_default_value(0, Vector3(1, 0, 0)); set_input_port_default_value(1, 45.0); + + simple_decl = false; } // VisualShaderNodeParticleRandomness @@ -1086,21 +1094,26 @@ String VisualShaderNodeParticleAccelerator::get_input_port_name(int p_port) cons String VisualShaderNodeParticleAccelerator::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; + code += " {\n"; switch (mode) { case MODE_LINEAR: - code += " " + p_output_vars[0] + " = length(VELOCITY) > 0.0 ? " + "normalize(VELOCITY) * " + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + " * mix(1.0, __rand_from_seed(__seed), " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ") : vec3(0.0);\n"; + code += " " + p_output_vars[0] + " = length(VELOCITY) > 0.0 ? " + "normalize(VELOCITY) * " + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + " * mix(1.0, __rand_from_seed(__seed), " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ") : vec3(0.0);\n"; break; case MODE_RADIAL: - code += " " + p_output_vars[0] + " = length(__diff) > 0.0 ? __ndiff * " + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + " * mix(1.0, __rand_from_seed(__seed), " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ") : vec3(0.0);\n"; + code += " vec3 __diff = TRANSFORM[3].xyz - EMISSION_TRANSFORM[3].xyz;\n"; + code += " vec3 __ndiff = normalize(__diff);\n\n"; + code += " " + p_output_vars[0] + " = length(__diff) > 0.0 ? __ndiff * " + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + " * mix(1.0, __rand_from_seed(__seed), " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ") : vec3(0.0);\n"; break; case MODE_TANGENTIAL: - code += " __vec3_buff1 = cross(__ndiff, normalize(" + (p_input_vars[2].is_empty() ? "vec3" + (String)get_input_port_default_value(2) : p_input_vars[2]) + "));\n"; - code += " " + p_output_vars[0] + " = length(__vec3_buff1) > 0.0 ? normalize(__vec3_buff1) * (" + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + " * mix(1.0, __rand_from_seed(__seed), " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ")) : vec3(0.0);\n"; + code += " vec3 __diff = TRANSFORM[3].xyz - EMISSION_TRANSFORM[3].xyz;\n"; + code += " vec3 __ndiff = normalize(__diff);\n\n"; + code += " vec3 __vec3_buff1 = cross(__ndiff, normalize(" + (p_input_vars[2].is_empty() ? "vec3" + (String)get_input_port_default_value(2) : p_input_vars[2]) + "));\n"; + code += " " + p_output_vars[0] + " = length(__vec3_buff1) > 0.0 ? normalize(__vec3_buff1) * (" + (p_input_vars[0].is_empty() ? "vec3" + (String)get_input_port_default_value(0) : p_input_vars[0]) + " * mix(1.0, __rand_from_seed(__seed), " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ")) : vec3(0.0);\n"; break; default: break; } - + code += " }\n"; return code; } @@ -1125,6 +1138,8 @@ VisualShaderNodeParticleAccelerator::VisualShaderNodeParticleAccelerator() { set_input_port_default_value(0, Vector3(1, 1, 1)); set_input_port_default_value(1, 0.0); set_input_port_default_value(2, Vector3(0, -9.8, 0)); + + simple_decl = false; } // VisualShaderNodeParticleOutput |