2014-02-10 01:10:30 +00:00
|
|
|
/*************************************************************************/
|
|
|
|
/* curve.cpp */
|
|
|
|
/*************************************************************************/
|
|
|
|
/* This file is part of: */
|
|
|
|
/* GODOT ENGINE */
|
2017-08-27 12:16:55 +00:00
|
|
|
/* https://godotengine.org */
|
2014-02-10 01:10:30 +00:00
|
|
|
/*************************************************************************/
|
2022-01-03 20:27:34 +00:00
|
|
|
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
|
|
|
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
2014-02-10 01:10:30 +00:00
|
|
|
/* */
|
|
|
|
/* 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. */
|
|
|
|
/*************************************************************************/
|
2018-01-04 23:50:27 +00:00
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
#include "curve.h"
|
2017-08-27 19:07:15 +00:00
|
|
|
|
2018-09-11 16:13:45 +00:00
|
|
|
#include "core/core_string_names.h"
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
template <class T>
|
2021-10-15 20:32:26 +00:00
|
|
|
static _FORCE_INLINE_ T _bezier_interp(real_t p_t, T p_start, T p_control_1, T p_control_2, T p_end) {
|
2017-03-05 15:44:50 +00:00
|
|
|
/* Formula from Wikipedia article on Bezier curves. */
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t omt = (1.0 - p_t);
|
2017-03-05 15:44:50 +00:00
|
|
|
real_t omt2 = omt * omt;
|
|
|
|
real_t omt3 = omt2 * omt;
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t t2 = p_t * p_t;
|
|
|
|
real_t t3 = t2 * p_t;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2017-06-26 21:39:35 +00:00
|
|
|
const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed";
|
|
|
|
|
2017-04-30 14:27:10 +00:00
|
|
|
Curve::Curve() {
|
|
|
|
}
|
|
|
|
|
2022-02-12 18:12:22 +00:00
|
|
|
void Curve::set_point_count(int p_count) {
|
|
|
|
ERR_FAIL_COND(p_count < 0);
|
|
|
|
if (_points.size() >= p_count) {
|
|
|
|
_points.resize(p_count);
|
|
|
|
mark_dirty();
|
2022-06-22 12:17:28 +00:00
|
|
|
notify_property_list_changed();
|
2022-02-12 18:12:22 +00:00
|
|
|
} else {
|
|
|
|
for (int i = p_count - _points.size(); i > 0; i--) {
|
|
|
|
add_point(Vector2());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-22 12:17:28 +00:00
|
|
|
int Curve::_add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_tangent, TangentMode p_left_mode, TangentMode p_right_mode) {
|
2017-04-30 14:27:10 +00:00
|
|
|
// Add a point and preserve order
|
|
|
|
|
|
|
|
// Curve bounds is in 0..1
|
2021-10-15 20:32:26 +00:00
|
|
|
if (p_position.x > MAX_X) {
|
|
|
|
p_position.x = MAX_X;
|
|
|
|
} else if (p_position.x < MIN_X) {
|
|
|
|
p_position.x = MIN_X;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-04-30 14:27:10 +00:00
|
|
|
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (_points.size() == 0) {
|
2021-10-15 20:32:26 +00:00
|
|
|
_points.push_back(Point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode));
|
2017-04-30 14:27:10 +00:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
} else if (_points.size() == 1) {
|
|
|
|
// TODO Is the `else` able to handle this block already?
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t diff = p_position.x - _points[0].position.x;
|
2017-04-30 14:27:10 +00:00
|
|
|
|
|
|
|
if (diff > 0) {
|
2021-10-15 20:32:26 +00:00
|
|
|
_points.push_back(Point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode));
|
2017-04-30 14:27:10 +00:00
|
|
|
ret = 1;
|
|
|
|
} else {
|
2021-10-15 20:32:26 +00:00
|
|
|
_points.insert(0, Point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode));
|
2017-04-30 14:27:10 +00:00
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2021-10-15 20:32:26 +00:00
|
|
|
int i = get_index(p_position.x);
|
2017-04-30 14:27:10 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
if (i == 0 && p_position.x < _points[0].position.x) {
|
2017-04-30 14:27:10 +00:00
|
|
|
// Insert before anything else
|
2021-10-15 20:32:26 +00:00
|
|
|
_points.insert(0, Point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode));
|
2017-04-30 14:27:10 +00:00
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
// Insert between i and i+1
|
|
|
|
++i;
|
2021-10-15 20:32:26 +00:00
|
|
|
_points.insert(i, Point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode));
|
2017-04-30 14:27:10 +00:00
|
|
|
ret = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-28 03:41:43 +00:00
|
|
|
update_auto_tangents(ret);
|
2017-06-28 00:42:38 +00:00
|
|
|
|
2017-04-30 14:27:10 +00:00
|
|
|
mark_dirty();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-06-22 12:17:28 +00:00
|
|
|
int Curve::add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_tangent, TangentMode p_left_mode, TangentMode p_right_mode) {
|
|
|
|
int ret = _add_point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode);
|
|
|
|
notify_property_list_changed();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
int Curve::get_index(real_t p_offset) const {
|
2017-04-30 14:27:10 +00:00
|
|
|
// Lower-bound float binary search
|
|
|
|
|
|
|
|
int imin = 0;
|
|
|
|
int imax = _points.size() - 1;
|
|
|
|
|
|
|
|
while (imax - imin > 1) {
|
|
|
|
int m = (imin + imax) / 2;
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t a = _points[m].position.x;
|
|
|
|
real_t b = _points[m + 1].position.x;
|
2017-04-30 14:27:10 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
if (a < p_offset && b < p_offset) {
|
2017-04-30 14:27:10 +00:00
|
|
|
imin = m;
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
} else if (a > p_offset) {
|
2017-04-30 14:27:10 +00:00
|
|
|
imax = m;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Will happen if the offset is out of bounds
|
2021-10-15 20:32:26 +00:00
|
|
|
if (p_offset > _points[imax].position.x) {
|
2017-04-30 14:27:10 +00:00
|
|
|
return imax;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-04-30 14:27:10 +00:00
|
|
|
return imin;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Curve::clean_dupes() {
|
|
|
|
bool dirty = false;
|
|
|
|
|
|
|
|
for (int i = 1; i < _points.size(); ++i) {
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t diff = _points[i - 1].position.x - _points[i].position.x;
|
2017-04-30 14:27:10 +00:00
|
|
|
if (diff <= CMP_EPSILON) {
|
2021-07-03 22:17:03 +00:00
|
|
|
_points.remove_at(i);
|
2017-04-30 14:27:10 +00:00
|
|
|
--i;
|
|
|
|
dirty = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (dirty) {
|
2017-04-30 14:27:10 +00:00
|
|
|
mark_dirty();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve::set_point_left_tangent(int p_index, real_t p_tangent) {
|
|
|
|
ERR_FAIL_INDEX(p_index, _points.size());
|
|
|
|
_points.write[p_index].left_tangent = p_tangent;
|
|
|
|
_points.write[p_index].left_mode = TANGENT_FREE;
|
2017-04-30 14:27:10 +00:00
|
|
|
mark_dirty();
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve::set_point_right_tangent(int p_index, real_t p_tangent) {
|
|
|
|
ERR_FAIL_INDEX(p_index, _points.size());
|
|
|
|
_points.write[p_index].right_tangent = p_tangent;
|
|
|
|
_points.write[p_index].right_mode = TANGENT_FREE;
|
2017-06-26 21:39:35 +00:00
|
|
|
mark_dirty();
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve::set_point_left_mode(int p_index, TangentMode p_mode) {
|
|
|
|
ERR_FAIL_INDEX(p_index, _points.size());
|
|
|
|
_points.write[p_index].left_mode = p_mode;
|
|
|
|
if (p_index > 0) {
|
2017-06-26 21:39:35 +00:00
|
|
|
if (p_mode == TANGENT_LINEAR) {
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector2 v = (_points[p_index - 1].position - _points[p_index].position).normalized();
|
|
|
|
_points.write[p_index].left_tangent = v.y / v.x;
|
2017-06-26 21:39:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
mark_dirty();
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve::set_point_right_mode(int p_index, TangentMode p_mode) {
|
|
|
|
ERR_FAIL_INDEX(p_index, _points.size());
|
|
|
|
_points.write[p_index].right_mode = p_mode;
|
|
|
|
if (p_index + 1 < _points.size()) {
|
2017-06-26 21:39:35 +00:00
|
|
|
if (p_mode == TANGENT_LINEAR) {
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector2 v = (_points[p_index + 1].position - _points[p_index].position).normalized();
|
|
|
|
_points.write[p_index].right_tangent = v.y / v.x;
|
2017-06-26 21:39:35 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-30 14:27:10 +00:00
|
|
|
mark_dirty();
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve::get_point_left_tangent(int p_index) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_index, _points.size(), 0);
|
|
|
|
return _points[p_index].left_tangent;
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve::get_point_right_tangent(int p_index) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_index, _points.size(), 0);
|
|
|
|
return _points[p_index].right_tangent;
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Curve::TangentMode Curve::get_point_left_mode(int p_index) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_index, _points.size(), TANGENT_FREE);
|
|
|
|
return _points[p_index].left_mode;
|
2017-06-26 21:39:35 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Curve::TangentMode Curve::get_point_right_mode(int p_index) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_index, _points.size(), TANGENT_FREE);
|
|
|
|
return _points[p_index].right_mode;
|
2017-06-26 21:39:35 +00:00
|
|
|
}
|
|
|
|
|
2022-06-22 12:17:28 +00:00
|
|
|
void Curve::_remove_point(int p_index) {
|
2017-04-30 14:27:10 +00:00
|
|
|
ERR_FAIL_INDEX(p_index, _points.size());
|
2021-07-03 22:17:03 +00:00
|
|
|
_points.remove_at(p_index);
|
2017-04-30 14:27:10 +00:00
|
|
|
mark_dirty();
|
|
|
|
}
|
|
|
|
|
2022-06-22 12:17:28 +00:00
|
|
|
void Curve::remove_point(int p_index) {
|
|
|
|
_remove_point(p_index);
|
|
|
|
notify_property_list_changed();
|
|
|
|
}
|
|
|
|
|
2017-04-30 14:27:10 +00:00
|
|
|
void Curve::clear_points() {
|
|
|
|
_points.clear();
|
|
|
|
mark_dirty();
|
2022-06-22 12:17:28 +00:00
|
|
|
notify_property_list_changed();
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve::set_point_value(int p_index, real_t p_position) {
|
2017-04-30 14:27:10 +00:00
|
|
|
ERR_FAIL_INDEX(p_index, _points.size());
|
2021-10-15 20:32:26 +00:00
|
|
|
_points.write[p_index].position.y = p_position;
|
2017-06-26 21:39:35 +00:00
|
|
|
update_auto_tangents(p_index);
|
2017-04-30 14:27:10 +00:00
|
|
|
mark_dirty();
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
int Curve::set_point_offset(int p_index, real_t p_offset) {
|
2017-04-30 14:27:10 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_index, _points.size(), -1);
|
|
|
|
Point p = _points[p_index];
|
2022-06-22 12:17:28 +00:00
|
|
|
_remove_point(p_index);
|
|
|
|
int i = _add_point(Vector2(p_offset, p.position.y));
|
2018-07-25 01:11:03 +00:00
|
|
|
_points.write[i].left_tangent = p.left_tangent;
|
|
|
|
_points.write[i].right_tangent = p.right_tangent;
|
|
|
|
_points.write[i].left_mode = p.left_mode;
|
|
|
|
_points.write[i].right_mode = p.right_mode;
|
2020-05-14 14:41:43 +00:00
|
|
|
if (p_index != i) {
|
2017-06-26 21:39:35 +00:00
|
|
|
update_auto_tangents(p_index);
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-06-26 21:39:35 +00:00
|
|
|
update_auto_tangents(i);
|
2017-04-30 14:27:10 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2017-09-10 13:37:49 +00:00
|
|
|
Vector2 Curve::get_point_position(int p_index) const {
|
2017-04-30 14:27:10 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_index, _points.size(), Vector2(0, 0));
|
2021-10-15 20:32:26 +00:00
|
|
|
return _points[p_index].position;
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
2017-06-28 00:42:38 +00:00
|
|
|
Curve::Point Curve::get_point(int p_index) const {
|
|
|
|
ERR_FAIL_INDEX_V(p_index, _points.size(), Point());
|
|
|
|
return _points[p_index];
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve::update_auto_tangents(int p_index) {
|
|
|
|
Point &p = _points.write[p_index];
|
2017-06-26 21:39:35 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
if (p_index > 0) {
|
2017-06-26 21:39:35 +00:00
|
|
|
if (p.left_mode == TANGENT_LINEAR) {
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector2 v = (_points[p_index - 1].position - p.position).normalized();
|
2017-06-26 21:39:35 +00:00
|
|
|
p.left_tangent = v.y / v.x;
|
|
|
|
}
|
2021-10-15 20:32:26 +00:00
|
|
|
if (_points[p_index - 1].right_mode == TANGENT_LINEAR) {
|
|
|
|
Vector2 v = (_points[p_index - 1].position - p.position).normalized();
|
|
|
|
_points.write[p_index - 1].right_tangent = v.y / v.x;
|
2017-06-26 21:39:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
if (p_index + 1 < _points.size()) {
|
2019-04-08 09:03:37 +00:00
|
|
|
if (p.right_mode == TANGENT_LINEAR) {
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector2 v = (_points[p_index + 1].position - p.position).normalized();
|
2017-06-26 21:39:35 +00:00
|
|
|
p.right_tangent = v.y / v.x;
|
|
|
|
}
|
2021-10-15 20:32:26 +00:00
|
|
|
if (_points[p_index + 1].left_mode == TANGENT_LINEAR) {
|
|
|
|
Vector2 v = (_points[p_index + 1].position - p.position).normalized();
|
|
|
|
_points.write[p_index + 1].left_tangent = v.y / v.x;
|
2017-06-26 21:39:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MIN_Y_RANGE 0.01
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve::set_min_value(real_t p_min) {
|
2019-06-21 14:06:59 +00:00
|
|
|
if (_minmax_set_once & 0b11 && p_min > _max_value - MIN_Y_RANGE) {
|
2017-06-26 21:39:35 +00:00
|
|
|
_min_value = _max_value - MIN_Y_RANGE;
|
2019-06-21 14:06:59 +00:00
|
|
|
} else {
|
|
|
|
_minmax_set_once |= 0b10; // first bit is "min set"
|
2017-06-26 21:39:35 +00:00
|
|
|
_min_value = p_min;
|
2019-06-21 14:06:59 +00:00
|
|
|
}
|
2017-06-26 21:39:35 +00:00
|
|
|
// Note: min and max are indicative values,
|
|
|
|
// it's still possible that existing points are out of range at this point.
|
2021-07-17 21:22:52 +00:00
|
|
|
emit_signal(SNAME(SIGNAL_RANGE_CHANGED));
|
2017-06-26 21:39:35 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve::set_max_value(real_t p_max) {
|
2019-06-21 14:06:59 +00:00
|
|
|
if (_minmax_set_once & 0b11 && p_max < _min_value + MIN_Y_RANGE) {
|
2017-06-26 21:39:35 +00:00
|
|
|
_max_value = _min_value + MIN_Y_RANGE;
|
2019-06-21 14:06:59 +00:00
|
|
|
} else {
|
|
|
|
_minmax_set_once |= 0b01; // second bit is "max set"
|
2017-06-26 21:39:35 +00:00
|
|
|
_max_value = p_max;
|
2019-06-21 14:06:59 +00:00
|
|
|
}
|
2021-07-17 21:22:52 +00:00
|
|
|
emit_signal(SNAME(SIGNAL_RANGE_CHANGED));
|
2017-06-26 21:39:35 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve::interpolate(real_t p_offset) const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (_points.size() == 0) {
|
2017-04-30 14:27:10 +00:00
|
|
|
return 0;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
|
|
|
if (_points.size() == 1) {
|
2021-10-15 20:32:26 +00:00
|
|
|
return _points[0].position.y;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-04-30 14:27:10 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
int i = get_index(p_offset);
|
2017-04-30 14:27:10 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (i == _points.size() - 1) {
|
2021-10-15 20:32:26 +00:00
|
|
|
return _points[i].position.y;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-04-30 14:27:10 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t local = p_offset - _points[i].position.x;
|
2017-04-30 14:27:10 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (i == 0 && local <= 0) {
|
2021-10-15 20:32:26 +00:00
|
|
|
return _points[0].position.y;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-04-30 14:27:10 +00:00
|
|
|
|
|
|
|
return interpolate_local_nocheck(i, local);
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve::interpolate_local_nocheck(int p_index, real_t p_local_offset) const {
|
|
|
|
const Point a = _points[p_index];
|
|
|
|
const Point b = _points[p_index + 1];
|
2017-04-30 14:27:10 +00:00
|
|
|
|
2017-09-07 19:48:50 +00:00
|
|
|
/* Cubic bezier
|
|
|
|
*
|
|
|
|
* ac-----bc
|
|
|
|
* / \
|
|
|
|
* / \ Here with a.right_tangent > 0
|
|
|
|
* / \ and b.left_tangent < 0
|
|
|
|
* / \
|
|
|
|
* a b
|
|
|
|
*
|
|
|
|
* |-d1--|-d2--|-d3--|
|
|
|
|
*
|
|
|
|
* d1 == d2 == d3 == d / 3
|
|
|
|
*/
|
2017-04-30 14:27:10 +00:00
|
|
|
|
|
|
|
// Control points are chosen at equal distances
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t d = b.position.x - a.position.x;
|
|
|
|
if (Math::is_zero_approx(d)) {
|
|
|
|
return b.position.y;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2021-10-15 20:32:26 +00:00
|
|
|
p_local_offset /= d;
|
2017-04-30 14:27:10 +00:00
|
|
|
d /= 3.0;
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t yac = a.position.y + d * a.right_tangent;
|
|
|
|
real_t ybc = b.position.y - d * b.left_tangent;
|
2017-04-30 14:27:10 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t y = _bezier_interp(p_local_offset, a.position.y, yac, ybc, b.position.y);
|
2017-04-30 14:27:10 +00:00
|
|
|
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Curve::mark_dirty() {
|
|
|
|
_baked_cache_dirty = true;
|
|
|
|
emit_signal(CoreStringNames::get_singleton()->changed);
|
|
|
|
}
|
|
|
|
|
|
|
|
Array Curve::get_data() const {
|
|
|
|
Array output;
|
2017-06-28 00:42:38 +00:00
|
|
|
const unsigned int ELEMS = 5;
|
|
|
|
output.resize(_points.size() * ELEMS);
|
2017-04-30 14:27:10 +00:00
|
|
|
|
|
|
|
for (int j = 0; j < _points.size(); ++j) {
|
|
|
|
const Point p = _points[j];
|
2017-06-28 00:42:38 +00:00
|
|
|
int i = j * ELEMS;
|
2017-04-30 14:27:10 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
output[i] = p.position;
|
2017-04-30 14:27:10 +00:00
|
|
|
output[i + 1] = p.left_tangent;
|
|
|
|
output[i + 2] = p.right_tangent;
|
2017-06-28 00:42:38 +00:00
|
|
|
output[i + 3] = p.left_mode;
|
|
|
|
output[i + 4] = p.right_mode;
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve::set_data(const Array p_input) {
|
2017-06-28 00:42:38 +00:00
|
|
|
const unsigned int ELEMS = 5;
|
2021-10-15 20:32:26 +00:00
|
|
|
ERR_FAIL_COND(p_input.size() % ELEMS != 0);
|
2017-04-30 14:27:10 +00:00
|
|
|
|
|
|
|
_points.clear();
|
|
|
|
|
|
|
|
// Validate input
|
2021-10-15 20:32:26 +00:00
|
|
|
for (int i = 0; i < p_input.size(); i += ELEMS) {
|
|
|
|
ERR_FAIL_COND(p_input[i].get_type() != Variant::VECTOR2);
|
|
|
|
ERR_FAIL_COND(!p_input[i + 1].is_num());
|
|
|
|
ERR_FAIL_COND(p_input[i + 2].get_type() != Variant::FLOAT);
|
2017-06-26 21:39:35 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
ERR_FAIL_COND(p_input[i + 3].get_type() != Variant::INT);
|
|
|
|
int left_mode = p_input[i + 3];
|
2017-06-26 21:39:35 +00:00
|
|
|
ERR_FAIL_COND(left_mode < 0 || left_mode >= TANGENT_MODE_COUNT);
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
ERR_FAIL_COND(p_input[i + 4].get_type() != Variant::INT);
|
|
|
|
int right_mode = p_input[i + 4];
|
2017-06-26 21:39:35 +00:00
|
|
|
ERR_FAIL_COND(right_mode < 0 || right_mode >= TANGENT_MODE_COUNT);
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
_points.resize(p_input.size() / ELEMS);
|
2017-04-30 14:27:10 +00:00
|
|
|
|
|
|
|
for (int j = 0; j < _points.size(); ++j) {
|
2018-07-25 01:11:03 +00:00
|
|
|
Point &p = _points.write[j];
|
2017-06-28 00:42:38 +00:00
|
|
|
int i = j * ELEMS;
|
2017-04-30 14:27:10 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
p.position = p_input[i];
|
|
|
|
p.left_tangent = p_input[i + 1];
|
|
|
|
p.right_tangent = p_input[i + 2];
|
|
|
|
int left_mode = p_input[i + 3];
|
|
|
|
int right_mode = p_input[i + 4];
|
2017-06-26 21:39:35 +00:00
|
|
|
p.left_mode = (TangentMode)left_mode;
|
|
|
|
p.right_mode = (TangentMode)right_mode;
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mark_dirty();
|
2022-06-22 12:17:28 +00:00
|
|
|
notify_property_list_changed();
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Curve::bake() {
|
|
|
|
_baked_cache.clear();
|
|
|
|
|
|
|
|
_baked_cache.resize(_bake_resolution);
|
|
|
|
|
|
|
|
for (int i = 1; i < _bake_resolution - 1; ++i) {
|
|
|
|
real_t x = i / static_cast<real_t>(_bake_resolution);
|
|
|
|
real_t y = interpolate(x);
|
2018-07-25 01:11:03 +00:00
|
|
|
_baked_cache.write[i] = y;
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_points.size() != 0) {
|
2021-10-15 20:32:26 +00:00
|
|
|
_baked_cache.write[0] = _points[0].position.y;
|
|
|
|
_baked_cache.write[_baked_cache.size() - 1] = _points[_points.size() - 1].position.y;
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_baked_cache_dirty = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Curve::set_bake_resolution(int p_resolution) {
|
|
|
|
ERR_FAIL_COND(p_resolution < 1);
|
|
|
|
ERR_FAIL_COND(p_resolution > 1000);
|
|
|
|
_bake_resolution = p_resolution;
|
|
|
|
_baked_cache_dirty = true;
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve::interpolate_baked(real_t p_offset) const {
|
2017-04-30 14:27:10 +00:00
|
|
|
if (_baked_cache_dirty) {
|
|
|
|
// Last-second bake if not done already
|
2021-04-27 15:43:49 +00:00
|
|
|
const_cast<Curve *>(this)->bake();
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Special cases if the cache is too small
|
|
|
|
if (_baked_cache.size() == 0) {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (_points.size() == 0) {
|
2017-04-30 14:27:10 +00:00
|
|
|
return 0;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2021-10-15 20:32:26 +00:00
|
|
|
return _points[0].position.y;
|
2017-04-30 14:27:10 +00:00
|
|
|
} else if (_baked_cache.size() == 1) {
|
|
|
|
return _baked_cache[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get interpolation index
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t fi = p_offset * _baked_cache.size();
|
2017-04-30 14:27:10 +00:00
|
|
|
int i = Math::floor(fi);
|
|
|
|
if (i < 0) {
|
|
|
|
i = 0;
|
|
|
|
fi = 0;
|
|
|
|
} else if (i >= _baked_cache.size()) {
|
|
|
|
i = _baked_cache.size() - 1;
|
|
|
|
fi = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Interpolate
|
|
|
|
if (i + 1 < _baked_cache.size()) {
|
|
|
|
real_t t = fi - i;
|
|
|
|
return Math::lerp(_baked_cache[i], _baked_cache[i + 1], t);
|
|
|
|
} else {
|
|
|
|
return _baked_cache[_baked_cache.size() - 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve::ensure_default_setup(real_t p_min, real_t p_max) {
|
2018-07-06 23:21:13 +00:00
|
|
|
if (_points.size() == 0 && _min_value == 0 && _max_value == 1) {
|
|
|
|
add_point(Vector2(0, 1));
|
|
|
|
add_point(Vector2(1, 1));
|
|
|
|
set_min_value(p_min);
|
|
|
|
set_max_value(p_max);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-12 18:12:22 +00:00
|
|
|
bool Curve::_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()) {
|
|
|
|
int point_index = components[0].trim_prefix("point_").to_int();
|
|
|
|
String property = components[1];
|
|
|
|
if (property == "position") {
|
|
|
|
Vector2 position = p_value.operator Vector2();
|
|
|
|
set_point_offset(point_index, position.x);
|
|
|
|
set_point_value(point_index, position.y);
|
|
|
|
return true;
|
|
|
|
} else if (property == "left_tangent") {
|
|
|
|
set_point_left_tangent(point_index, p_value);
|
|
|
|
return true;
|
|
|
|
} else if (property == "left_mode") {
|
|
|
|
int mode = p_value;
|
|
|
|
set_point_left_mode(point_index, (TangentMode)mode);
|
|
|
|
return true;
|
|
|
|
} else if (property == "right_tangent") {
|
|
|
|
set_point_right_tangent(point_index, p_value);
|
|
|
|
return true;
|
|
|
|
} else if (property == "right_mode") {
|
|
|
|
int mode = p_value;
|
|
|
|
set_point_right_mode(point_index, (TangentMode)mode);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Curve::_get(const StringName &p_name, Variant &r_ret) const {
|
|
|
|
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()) {
|
|
|
|
int point_index = components[0].trim_prefix("point_").to_int();
|
|
|
|
String property = components[1];
|
|
|
|
if (property == "position") {
|
|
|
|
r_ret = get_point_position(point_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "left_tangent") {
|
|
|
|
r_ret = get_point_left_tangent(point_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "left_mode") {
|
|
|
|
r_ret = get_point_left_mode(point_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "right_tangent") {
|
|
|
|
r_ret = get_point_right_tangent(point_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "right_mode") {
|
|
|
|
r_ret = get_point_right_mode(point_index);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Curve::_get_property_list(List<PropertyInfo> *p_list) const {
|
|
|
|
for (int i = 0; i < _points.size(); i++) {
|
|
|
|
PropertyInfo pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/position", i));
|
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
|
p_list->push_back(pi);
|
|
|
|
|
|
|
|
if (i != 0) {
|
|
|
|
pi = PropertyInfo(Variant::FLOAT, vformat("point_%d/left_tangent", i));
|
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
|
p_list->push_back(pi);
|
|
|
|
|
|
|
|
pi = PropertyInfo(Variant::INT, vformat("point_%d/left_mode", i), PROPERTY_HINT_ENUM, "Free,Linear");
|
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
|
p_list->push_back(pi);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i != _points.size() - 1) {
|
|
|
|
pi = PropertyInfo(Variant::FLOAT, vformat("point_%d/right_tangent", i));
|
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
|
p_list->push_back(pi);
|
|
|
|
|
|
|
|
pi = PropertyInfo(Variant::INT, vformat("point_%d/right_mode", i), PROPERTY_HINT_ENUM, "Free,Linear");
|
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
|
p_list->push_back(pi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-30 14:27:10 +00:00
|
|
|
void Curve::_bind_methods() {
|
2018-09-07 18:07:44 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_point_count"), &Curve::get_point_count);
|
2022-02-12 18:12:22 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve::set_point_count);
|
2017-09-10 13:37:49 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_point", "position", "left_tangent", "right_tangent", "left_mode", "right_mode"), &Curve::add_point, DEFVAL(0), DEFVAL(0), DEFVAL(TANGENT_FREE), DEFVAL(TANGENT_FREE));
|
2017-04-30 14:27:10 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("remove_point", "index"), &Curve::remove_point);
|
|
|
|
ClassDB::bind_method(D_METHOD("clear_points"), &Curve::clear_points);
|
2017-09-10 13:37:49 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_point_position", "index"), &Curve::get_point_position);
|
2017-06-25 11:05:39 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_value", "index", "y"), &Curve::set_point_value);
|
2017-10-02 07:39:12 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_offset", "index", "offset"), &Curve::set_point_offset);
|
2017-04-30 14:27:10 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("interpolate", "offset"), &Curve::interpolate);
|
|
|
|
ClassDB::bind_method(D_METHOD("interpolate_baked", "offset"), &Curve::interpolate_baked);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_point_left_tangent", "index"), &Curve::get_point_left_tangent);
|
2017-10-02 07:39:12 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_point_right_tangent", "index"), &Curve::get_point_right_tangent);
|
2017-06-26 21:39:35 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_point_left_mode", "index"), &Curve::get_point_left_mode);
|
2017-10-02 07:39:12 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_point_right_mode", "index"), &Curve::get_point_right_mode);
|
2017-04-30 14:27:10 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_left_tangent", "index", "tangent"), &Curve::set_point_left_tangent);
|
2017-10-02 07:39:12 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_right_tangent", "index", "tangent"), &Curve::set_point_right_tangent);
|
2017-06-26 21:39:35 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_left_mode", "index", "mode"), &Curve::set_point_left_mode);
|
2017-10-02 07:39:12 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_right_mode", "index", "mode"), &Curve::set_point_right_mode);
|
2017-06-26 21:39:35 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_min_value"), &Curve::get_min_value);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_min_value", "min"), &Curve::set_min_value);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_max_value"), &Curve::get_max_value);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_max_value", "max"), &Curve::set_max_value);
|
2017-04-30 14:27:10 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("clean_dupes"), &Curve::clean_dupes);
|
|
|
|
ClassDB::bind_method(D_METHOD("bake"), &Curve::bake);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_bake_resolution"), &Curve::get_bake_resolution);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_bake_resolution", "resolution"), &Curve::set_bake_resolution);
|
|
|
|
ClassDB::bind_method(D_METHOD("_get_data"), &Curve::get_data);
|
|
|
|
ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve::set_data);
|
|
|
|
|
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_min_value", "get_min_value");
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_max_value", "get_max_value");
|
2017-04-30 14:27:10 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_resolution", PROPERTY_HINT_RANGE, "1,1000,1"), "set_bake_resolution", "get_bake_resolution");
|
2021-11-03 22:06:17 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
|
2022-02-12 18:12:22 +00:00
|
|
|
ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_");
|
2017-06-26 21:39:35 +00:00
|
|
|
|
|
|
|
ADD_SIGNAL(MethodInfo(SIGNAL_RANGE_CHANGED));
|
2017-09-12 19:09:06 +00:00
|
|
|
|
|
|
|
BIND_ENUM_CONSTANT(TANGENT_FREE);
|
|
|
|
BIND_ENUM_CONSTANT(TANGENT_LINEAR);
|
|
|
|
BIND_ENUM_CONSTANT(TANGENT_MODE_COUNT);
|
2017-04-30 14:27:10 +00:00
|
|
|
}
|
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
int Curve2D::get_point_count() const {
|
2014-02-10 01:10:30 +00:00
|
|
|
return points.size();
|
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2022-02-12 18:12:22 +00:00
|
|
|
void Curve2D::set_point_count(int p_count) {
|
|
|
|
ERR_FAIL_COND(p_count < 0);
|
|
|
|
if (points.size() >= p_count) {
|
|
|
|
points.resize(p_count);
|
|
|
|
mark_dirty();
|
|
|
|
baked_cache_dirty = true;
|
|
|
|
emit_signal(CoreStringNames::get_singleton()->changed);
|
|
|
|
} else {
|
|
|
|
for (int i = p_count - points.size(); i > 0; i--) {
|
|
|
|
add_point(Vector2());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve2D::add_point(const Vector2 &p_position, const Vector2 &p_in, const Vector2 &p_out, int p_atpos) {
|
2014-02-10 01:10:30 +00:00
|
|
|
Point n;
|
2021-10-15 20:32:26 +00:00
|
|
|
n.position = p_position;
|
2017-03-05 15:44:50 +00:00
|
|
|
n.in = p_in;
|
|
|
|
n.out = p_out;
|
2020-05-14 14:41:43 +00:00
|
|
|
if (p_atpos >= 0 && p_atpos < points.size()) {
|
2017-03-05 15:44:50 +00:00
|
|
|
points.insert(p_atpos, n);
|
2020-05-14 14:41:43 +00:00
|
|
|
} else {
|
2014-02-10 01:10:30 +00:00
|
|
|
points.push_back(n);
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve2D::set_point_position(int p_index, const Vector2 &p_position) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_index, points.size());
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
points.write[p_index].position = p_position;
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2017-09-10 13:37:49 +00:00
|
|
|
Vector2 Curve2D::get_point_position(int p_index) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_index, points.size(), Vector2());
|
2021-10-15 20:32:26 +00:00
|
|
|
return points[p_index].position;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void Curve2D::set_point_in(int p_index, const Vector2 &p_in) {
|
|
|
|
ERR_FAIL_INDEX(p_index, points.size());
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2018-07-25 01:11:03 +00:00
|
|
|
points.write[p_index].in = p_in;
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
Vector2 Curve2D::get_point_in(int p_index) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_index, points.size(), Vector2());
|
2014-02-10 01:10:30 +00:00
|
|
|
return points[p_index].in;
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void Curve2D::set_point_out(int p_index, const Vector2 &p_out) {
|
|
|
|
ERR_FAIL_INDEX(p_index, points.size());
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2018-07-25 01:11:03 +00:00
|
|
|
points.write[p_index].out = p_out;
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
Vector2 Curve2D::get_point_out(int p_index) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_index, points.size(), Vector2());
|
2014-02-10 01:10:30 +00:00
|
|
|
return points[p_index].out;
|
|
|
|
}
|
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
void Curve2D::remove_point(int p_index) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_index, points.size());
|
2021-07-03 22:17:03 +00:00
|
|
|
points.remove_at(p_index);
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2016-12-03 20:35:59 +00:00
|
|
|
void Curve2D::clear_points() {
|
2020-12-15 12:04:21 +00:00
|
|
|
if (!points.is_empty()) {
|
2016-12-03 20:35:59 +00:00
|
|
|
points.clear();
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2016-12-03 20:35:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector2 Curve2D::interpolate(int p_index, const real_t p_offset) const {
|
2014-02-10 01:10:30 +00:00
|
|
|
int pc = points.size();
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_COND_V(pc == 0, Vector2());
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (p_index >= pc - 1) {
|
2021-10-15 20:32:26 +00:00
|
|
|
return points[pc - 1].position;
|
2020-05-14 14:41:43 +00:00
|
|
|
} else if (p_index < 0) {
|
2021-10-15 20:32:26 +00:00
|
|
|
return points[0].position;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector2 p0 = points[p_index].position;
|
2017-03-05 15:44:50 +00:00
|
|
|
Vector2 p1 = p0 + points[p_index].out;
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector2 p3 = points[p_index + 1].position;
|
2017-03-05 15:44:50 +00:00
|
|
|
Vector2 p2 = p3 + points[p_index + 1].in;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
return _bezier_interp(p_offset, p0, p1, p2, p3);
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
Vector2 Curve2D::interpolatef(real_t p_findex) const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (p_findex < 0) {
|
2017-03-05 15:44:50 +00:00
|
|
|
p_findex = 0;
|
2020-05-14 14:41:43 +00:00
|
|
|
} else if (p_findex >= points.size()) {
|
2017-03-05 15:44:50 +00:00
|
|
|
p_findex = points.size();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
return interpolate((int)p_findex, Math::fmod(p_findex, (real_t)1.0));
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2022-02-12 18:11:08 +00:00
|
|
|
void Curve2D::mark_dirty() {
|
|
|
|
baked_cache_dirty = true;
|
|
|
|
emit_signal(CoreStringNames::get_singleton()->changed);
|
2022-02-12 18:12:22 +00:00
|
|
|
notify_property_list_changed();
|
2022-02-12 18:11:08 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 16:17:02 +00:00
|
|
|
void Curve2D::_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 {
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t mp = p_begin + (p_end - p_begin) * 0.5;
|
2017-03-05 15:44:50 +00:00
|
|
|
Vector2 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b);
|
|
|
|
Vector2 mid = _bezier_interp(mp, p_a, p_a + p_out, p_b + p_in, p_b);
|
|
|
|
Vector2 end = _bezier_interp(p_end, p_a, p_a + p_out, p_b + p_in, p_b);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
Vector2 na = (mid - beg).normalized();
|
|
|
|
Vector2 nb = (end - mid).normalized();
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t dp = na.dot(nb);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (dp < Math::cos(Math::deg2rad(p_tol))) {
|
|
|
|
r_bake[mp] = mid;
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (p_depth < p_max_depth) {
|
|
|
|
_bake_segment2d(r_bake, p_begin, mp, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_tol);
|
|
|
|
_bake_segment2d(r_bake, mp, p_end, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_tol);
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
void Curve2D::_bake() const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (!baked_cache_dirty) {
|
2014-07-06 14:49:27 +00:00
|
|
|
return;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
baked_max_ofs = 0;
|
|
|
|
baked_cache_dirty = false;
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (points.size() == 0) {
|
2022-02-01 18:19:13 +00:00
|
|
|
baked_point_cache.clear();
|
|
|
|
baked_dist_cache.clear();
|
2014-07-06 14:49:27 +00:00
|
|
|
return;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (points.size() == 1) {
|
2014-07-06 14:49:27 +00:00
|
|
|
baked_point_cache.resize(1);
|
2021-10-15 20:32:26 +00:00
|
|
|
baked_point_cache.set(0, points[0].position);
|
2021-08-21 07:57:59 +00:00
|
|
|
|
|
|
|
baked_dist_cache.resize(1);
|
|
|
|
baked_dist_cache.set(0, 0.0);
|
2014-07-06 14:49:27 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector2 position = points[0].position;
|
|
|
|
real_t dist = 0.0;
|
2021-08-21 07:57:59 +00:00
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
List<Vector2> pointlist;
|
2021-10-15 20:32:26 +00:00
|
|
|
List<real_t> distlist;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
// Start always from origin.
|
|
|
|
pointlist.push_back(position);
|
2021-08-21 07:57:59 +00:00
|
|
|
distlist.push_back(0.0);
|
2016-05-21 13:29:25 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int i = 0; i < points.size() - 1; i++) {
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t step = 0.1; // at least 10 substeps ought to be enough?
|
|
|
|
real_t p = 0.0;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
while (p < 1.0) {
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t np = p + step;
|
2020-05-14 14:41:43 +00:00
|
|
|
if (np > 1.0) {
|
2017-03-05 15:44:50 +00:00
|
|
|
np = 1.0;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector2 npp = _bezier_interp(np, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
|
|
|
|
real_t d = position.distance_to(npp);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (d > bake_interval) {
|
2014-07-06 14:49:27 +00:00
|
|
|
// OK! between P and NP there _has_ to be Something, let's go searching!
|
|
|
|
|
|
|
|
int iterations = 10; //lots of detail!
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t low = p;
|
|
|
|
real_t hi = np;
|
|
|
|
real_t mid = low + (hi - low) * 0.5;
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int j = 0; j < iterations; j++) {
|
2021-10-15 20:32:26 +00:00
|
|
|
npp = _bezier_interp(mid, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
|
|
|
|
d = position.distance_to(npp);
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (bake_interval < d) {
|
2017-03-05 15:44:50 +00:00
|
|
|
hi = mid;
|
2020-05-14 14:41:43 +00:00
|
|
|
} else {
|
2017-03-05 15:44:50 +00:00
|
|
|
low = mid;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-03-05 15:44:50 +00:00
|
|
|
mid = low + (hi - low) * 0.5;
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
position = npp;
|
2017-03-05 15:44:50 +00:00
|
|
|
p = mid;
|
2021-08-21 07:57:59 +00:00
|
|
|
dist += d;
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
pointlist.push_back(position);
|
2021-08-21 07:57:59 +00:00
|
|
|
distlist.push_back(dist);
|
2014-07-06 14:49:27 +00:00
|
|
|
} else {
|
2017-03-05 15:44:50 +00:00
|
|
|
p = np;
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
Vector2 npp = points[i + 1].position;
|
|
|
|
real_t d = position.distance_to(npp);
|
|
|
|
|
|
|
|
position = npp;
|
|
|
|
dist += d;
|
|
|
|
|
|
|
|
pointlist.push_back(position);
|
|
|
|
distlist.push_back(dist);
|
|
|
|
}
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2021-08-21 07:57:59 +00:00
|
|
|
baked_max_ofs = dist;
|
2014-07-06 14:49:27 +00:00
|
|
|
|
|
|
|
baked_point_cache.resize(pointlist.size());
|
2021-08-21 07:57:59 +00:00
|
|
|
baked_dist_cache.resize(distlist.size());
|
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
Vector2 *w = baked_point_cache.ptrw();
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t *wd = baked_dist_cache.ptrw();
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2021-08-21 07:57:59 +00:00
|
|
|
for (int i = 0; i < pointlist.size(); i++) {
|
|
|
|
w[i] = pointlist[i];
|
|
|
|
wd[i] = distlist[i];
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve2D::get_baked_length() const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2014-07-06 14:49:27 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
return baked_max_ofs;
|
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector2 Curve2D::interpolate_baked(real_t p_offset, bool p_cubic) const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2014-07-06 14:49:27 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
// Validate: Curve may not have baked points.
|
2014-07-06 14:49:27 +00:00
|
|
|
int pc = baked_point_cache.size();
|
2019-08-08 20:11:48 +00:00
|
|
|
ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D.");
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (pc == 1) {
|
2014-07-06 14:49:27 +00:00
|
|
|
return baked_point_cache.get(0);
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
const Vector2 *r = baked_point_cache.ptr();
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (p_offset < 0) {
|
2014-07-06 14:49:27 +00:00
|
|
|
return r[0];
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
|
|
|
if (p_offset >= baked_max_ofs) {
|
2021-09-15 13:40:33 +00:00
|
|
|
return r[pc - 1];
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
int start = 0;
|
|
|
|
int end = pc;
|
|
|
|
int idx = (end + start) / 2;
|
|
|
|
// Binary search to find baked points.
|
2021-08-21 07:57:59 +00:00
|
|
|
while (start < idx) {
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t offset = baked_dist_cache[idx];
|
2021-08-21 07:57:59 +00:00
|
|
|
if (p_offset <= offset) {
|
|
|
|
end = idx;
|
|
|
|
} else {
|
|
|
|
start = idx;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2021-08-21 07:57:59 +00:00
|
|
|
idx = (end + start) / 2;
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t offset_begin = baked_dist_cache[idx];
|
|
|
|
real_t offset_end = baked_dist_cache[idx + 1];
|
2021-08-21 07:57:59 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t idx_interval = offset_end - offset_begin;
|
2021-08-21 07:57:59 +00:00
|
|
|
ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "failed to find baked segment");
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t frac = (p_offset - offset_begin) / idx_interval;
|
2021-08-21 07:57:59 +00:00
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
if (p_cubic) {
|
2017-03-05 15:44:50 +00:00
|
|
|
Vector2 pre = idx > 0 ? r[idx - 1] : r[idx];
|
2021-09-15 13:40:33 +00:00
|
|
|
Vector2 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1];
|
2017-03-05 15:44:50 +00:00
|
|
|
return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac);
|
2014-07-06 14:49:27 +00:00
|
|
|
} else {
|
2020-03-16 09:07:33 +00:00
|
|
|
return r[idx].lerp(r[idx + 1], frac);
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
PackedVector2Array Curve2D::get_baked_points() const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2014-07-06 14:49:27 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
return baked_point_cache;
|
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve2D::set_bake_interval(real_t p_tolerance) {
|
2017-03-05 15:44:50 +00:00
|
|
|
bake_interval = p_tolerance;
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve2D::get_bake_interval() const {
|
2014-07-06 14:49:27 +00:00
|
|
|
return bake_interval;
|
|
|
|
}
|
|
|
|
|
2018-04-24 23:28:17 +00:00
|
|
|
Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const {
|
2021-10-07 19:36:31 +00:00
|
|
|
// Brute force method.
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2018-04-24 23:28:17 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
// Validate: Curve may not have baked points.
|
2018-04-24 23:28:17 +00:00
|
|
|
int pc = baked_point_cache.size();
|
2019-08-08 20:11:48 +00:00
|
|
|
ERR_FAIL_COND_V_MSG(pc == 0, Vector2(), "No points in Curve2D.");
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (pc == 1) {
|
2018-04-24 23:28:17 +00:00
|
|
|
return baked_point_cache.get(0);
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
const Vector2 *r = baked_point_cache.ptr();
|
2018-04-24 23:28:17 +00:00
|
|
|
|
|
|
|
Vector2 nearest;
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t nearest_dist = -1.0f;
|
2018-04-24 23:28:17 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < pc - 1; i++) {
|
|
|
|
Vector2 origin = r[i];
|
|
|
|
Vector2 direction = (r[i + 1] - origin) / bake_interval;
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
|
2018-04-24 23:28:17 +00:00
|
|
|
Vector2 proj = origin + direction * d;
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t dist = proj.distance_squared_to(p_to_point);
|
2018-04-24 23:28:17 +00:00
|
|
|
|
|
|
|
if (nearest_dist < 0.0f || dist < nearest_dist) {
|
|
|
|
nearest = proj;
|
|
|
|
nearest_dist = dist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nearest;
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve2D::get_closest_offset(const Vector2 &p_to_point) const {
|
2021-10-07 19:36:31 +00:00
|
|
|
// Brute force method.
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2018-04-24 23:28:17 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
// Validate: Curve may not have baked points.
|
2018-04-24 23:28:17 +00:00
|
|
|
int pc = baked_point_cache.size();
|
2019-08-08 20:11:48 +00:00
|
|
|
ERR_FAIL_COND_V_MSG(pc == 0, 0.0f, "No points in Curve2D.");
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (pc == 1) {
|
2018-04-24 23:28:17 +00:00
|
|
|
return 0.0f;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
const Vector2 *r = baked_point_cache.ptr();
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t nearest = 0.0f;
|
|
|
|
real_t nearest_dist = -1.0f;
|
|
|
|
real_t offset = 0.0f;
|
2018-04-24 23:28:17 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < pc - 1; i++) {
|
|
|
|
Vector2 origin = r[i];
|
|
|
|
Vector2 direction = (r[i + 1] - origin) / bake_interval;
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
|
2018-04-24 23:28:17 +00:00
|
|
|
Vector2 proj = origin + direction * d;
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t dist = proj.distance_squared_to(p_to_point);
|
2018-04-24 23:28:17 +00:00
|
|
|
|
|
|
|
if (nearest_dist < 0.0f || dist < nearest_dist) {
|
|
|
|
nearest = offset + d;
|
|
|
|
nearest_dist = dist;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += bake_interval;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nearest;
|
|
|
|
}
|
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
Dictionary Curve2D::_get_data() const {
|
|
|
|
Dictionary dc;
|
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
PackedVector2Array d;
|
2017-03-05 15:44:50 +00:00
|
|
|
d.resize(points.size() * 3);
|
2020-02-17 21:06:54 +00:00
|
|
|
Vector2 *w = d.ptrw();
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int i = 0; i < points.size(); i++) {
|
|
|
|
w[i * 3 + 0] = points[i].in;
|
|
|
|
w[i * 3 + 1] = points[i].out;
|
2021-10-15 20:32:26 +00:00
|
|
|
w[i * 3 + 2] = points[i].position;
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
dc["points"] = d;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
return dc;
|
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void Curve2D::_set_data(const Dictionary &p_data) {
|
2014-07-06 14:49:27 +00:00
|
|
|
ERR_FAIL_COND(!p_data.has("points"));
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
PackedVector2Array rp = p_data["points"];
|
2014-07-06 14:49:27 +00:00
|
|
|
int pc = rp.size();
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_COND(pc % 3 != 0);
|
|
|
|
points.resize(pc / 3);
|
2020-02-17 21:06:54 +00:00
|
|
|
const Vector2 *r = rp.ptr();
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int i = 0; i < points.size(); i++) {
|
2018-07-25 01:11:03 +00:00
|
|
|
points.write[i].in = r[i * 3 + 0];
|
|
|
|
points.write[i].out = r[i * 3 + 1];
|
2021-10-15 20:32:26 +00:00
|
|
|
points.write[i].position = r[i * 3 + 2];
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
baked_cache_dirty = true;
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
PackedVector2Array Curve2D::tessellate(int p_max_stages, real_t p_tolerance) const {
|
2020-02-17 21:06:54 +00:00
|
|
|
PackedVector2Array tess;
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (points.size() == 0) {
|
2014-07-06 14:49:27 +00:00
|
|
|
return tess;
|
|
|
|
}
|
2022-05-16 16:17:02 +00:00
|
|
|
|
|
|
|
// The current implementation requires a sorted map.
|
|
|
|
Vector<RBMap<real_t, Vector2>> midpoints;
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
midpoints.resize(points.size() - 1);
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
int pc = 1;
|
|
|
|
for (int i = 0; i < points.size() - 1; i++) {
|
2021-10-15 20:32:26 +00:00
|
|
|
_bake_segment2d(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_tolerance);
|
2014-07-06 14:49:27 +00:00
|
|
|
pc++;
|
2017-03-05 15:44:50 +00:00
|
|
|
pc += midpoints[i].size();
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tess.resize(pc);
|
2020-02-17 21:06:54 +00:00
|
|
|
Vector2 *bpw = tess.ptrw();
|
2021-10-15 20:32:26 +00:00
|
|
|
bpw[0] = points[0].position;
|
2017-03-05 15:44:50 +00:00
|
|
|
int pidx = 0;
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int i = 0; i < points.size() - 1; i++) {
|
2021-10-15 20:32:26 +00:00
|
|
|
for (const KeyValue<real_t, Vector2> &E : midpoints[i]) {
|
2014-07-06 14:49:27 +00:00
|
|
|
pidx++;
|
2021-08-09 20:13:42 +00:00
|
|
|
bpw[pidx] = E.value;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2014-07-06 14:49:27 +00:00
|
|
|
|
|
|
|
pidx++;
|
2021-10-15 20:32:26 +00:00
|
|
|
bpw[pidx] = points[i + 1].position;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
return tess;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2022-02-12 18:12:22 +00:00
|
|
|
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()) {
|
|
|
|
int point_index = components[0].trim_prefix("point_").to_int();
|
|
|
|
String property = components[1];
|
|
|
|
if (property == "position") {
|
|
|
|
set_point_position(point_index, p_value);
|
|
|
|
return true;
|
|
|
|
} else if (property == "in") {
|
|
|
|
set_point_in(point_index, p_value);
|
|
|
|
return true;
|
|
|
|
} else if (property == "out") {
|
|
|
|
set_point_out(point_index, p_value);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Curve2D::_get(const StringName &p_name, Variant &r_ret) const {
|
|
|
|
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()) {
|
|
|
|
int point_index = components[0].trim_prefix("point_").to_int();
|
|
|
|
String property = components[1];
|
|
|
|
if (property == "position") {
|
|
|
|
r_ret = get_point_position(point_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "in") {
|
|
|
|
r_ret = get_point_in(point_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "out") {
|
|
|
|
r_ret = get_point_out(point_index);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Curve2D::_get_property_list(List<PropertyInfo> *p_list) const {
|
|
|
|
for (int i = 0; i < points.size(); i++) {
|
|
|
|
PropertyInfo pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/position", i));
|
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
|
p_list->push_back(pi);
|
|
|
|
|
|
|
|
if (i != 0) {
|
|
|
|
pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/in", i));
|
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
|
p_list->push_back(pi);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i != points.size() - 1) {
|
|
|
|
pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/out", i));
|
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
|
p_list->push_back(pi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
void Curve2D::_bind_methods() {
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_point_count"), &Curve2D::get_point_count);
|
2022-02-12 18:12:22 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve2D::set_point_count);
|
2017-09-10 13:37:49 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "at_position"), &Curve2D::add_point, DEFVAL(Vector2()), DEFVAL(Vector2()), DEFVAL(-1));
|
|
|
|
ClassDB::bind_method(D_METHOD("set_point_position", "idx", "position"), &Curve2D::set_point_position);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_point_position", "idx"), &Curve2D::get_point_position);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_point_in", "idx", "position"), &Curve2D::set_point_in);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_point_in", "idx"), &Curve2D::get_point_in);
|
2017-09-10 13:37:49 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_out", "idx", "position"), &Curve2D::set_point_out);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_point_out", "idx"), &Curve2D::get_point_out);
|
|
|
|
ClassDB::bind_method(D_METHOD("remove_point", "idx"), &Curve2D::remove_point);
|
|
|
|
ClassDB::bind_method(D_METHOD("clear_points"), &Curve2D::clear_points);
|
|
|
|
ClassDB::bind_method(D_METHOD("interpolate", "idx", "t"), &Curve2D::interpolate);
|
|
|
|
ClassDB::bind_method(D_METHOD("interpolatef", "fofs"), &Curve2D::interpolatef);
|
2017-02-13 11:47:24 +00:00
|
|
|
//ClassDB::bind_method(D_METHOD("bake","subdivs"),&Curve2D::bake,DEFVAL(10));
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_bake_interval", "distance"), &Curve2D::set_bake_interval);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_bake_interval"), &Curve2D::get_bake_interval);
|
2017-02-13 11:47:24 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve2D::get_baked_length);
|
|
|
|
ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve2D::interpolate_baked, DEFVAL(false));
|
|
|
|
ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve2D::get_baked_points);
|
2018-04-24 23:28:17 +00:00
|
|
|
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);
|
2017-07-18 19:03:34 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve2D::tessellate, DEFVAL(5), DEFVAL(4));
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("_get_data"), &Curve2D::_get_data);
|
|
|
|
ClassDB::bind_method(D_METHOD("_set_data"), &Curve2D::_set_data);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval");
|
2021-11-03 22:06:17 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
|
2022-02-12 18:12:22 +00:00
|
|
|
ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_");
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
Curve2D::Curve2D() {}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
/***********************************************************************************/
|
|
|
|
/***********************************************************************************/
|
|
|
|
/***********************************************************************************/
|
|
|
|
/***********************************************************************************/
|
|
|
|
/***********************************************************************************/
|
|
|
|
/***********************************************************************************/
|
|
|
|
|
|
|
|
int Curve3D::get_point_count() const {
|
|
|
|
return points.size();
|
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2022-02-12 18:12:22 +00:00
|
|
|
void Curve3D::set_point_count(int p_count) {
|
|
|
|
ERR_FAIL_COND(p_count < 0);
|
|
|
|
if (points.size() >= p_count) {
|
|
|
|
points.resize(p_count);
|
|
|
|
mark_dirty();
|
|
|
|
} else {
|
|
|
|
for (int i = p_count - points.size(); i > 0; i--) {
|
|
|
|
add_point(Vector3());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve3D::add_point(const Vector3 &p_position, const Vector3 &p_in, const Vector3 &p_out, int p_atpos) {
|
2014-07-06 14:49:27 +00:00
|
|
|
Point n;
|
2021-10-15 20:32:26 +00:00
|
|
|
n.position = p_position;
|
2017-03-05 15:44:50 +00:00
|
|
|
n.in = p_in;
|
|
|
|
n.out = p_out;
|
2020-05-14 14:41:43 +00:00
|
|
|
if (p_atpos >= 0 && p_atpos < points.size()) {
|
2017-03-05 15:44:50 +00:00
|
|
|
points.insert(p_atpos, n);
|
2020-05-14 14:41:43 +00:00
|
|
|
} else {
|
2014-07-06 14:49:27 +00:00
|
|
|
points.push_back(n);
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve3D::set_point_position(int p_index, const Vector3 &p_position) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_index, points.size());
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
points.write[p_index].position = p_position;
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2017-09-10 13:37:49 +00:00
|
|
|
Vector3 Curve3D::get_point_position(int p_index) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_index, points.size(), Vector3());
|
2021-10-15 20:32:26 +00:00
|
|
|
return points[p_index].position;
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve3D::set_point_tilt(int p_index, real_t p_tilt) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_index, points.size());
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2018-07-25 01:11:03 +00:00
|
|
|
points.write[p_index].tilt = p_tilt;
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve3D::get_point_tilt(int p_index) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_index, points.size(), 0);
|
2014-07-06 14:49:27 +00:00
|
|
|
return points[p_index].tilt;
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void Curve3D::set_point_in(int p_index, const Vector3 &p_in) {
|
|
|
|
ERR_FAIL_INDEX(p_index, points.size());
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2018-07-25 01:11:03 +00:00
|
|
|
points.write[p_index].in = p_in;
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2014-07-06 14:49:27 +00:00
|
|
|
Vector3 Curve3D::get_point_in(int p_index) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_index, points.size(), Vector3());
|
2014-07-06 14:49:27 +00:00
|
|
|
return points[p_index].in;
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void Curve3D::set_point_out(int p_index, const Vector3 &p_out) {
|
|
|
|
ERR_FAIL_INDEX(p_index, points.size());
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2018-07-25 01:11:03 +00:00
|
|
|
points.write[p_index].out = p_out;
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Vector3 Curve3D::get_point_out(int p_index) const {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX_V(p_index, points.size(), Vector3());
|
2014-07-06 14:49:27 +00:00
|
|
|
return points[p_index].out;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Curve3D::remove_point(int p_index) {
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_INDEX(p_index, points.size());
|
2021-07-03 22:17:03 +00:00
|
|
|
points.remove_at(p_index);
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
|
|
|
|
2016-12-03 20:35:59 +00:00
|
|
|
void Curve3D::clear_points() {
|
2020-12-15 12:04:21 +00:00
|
|
|
if (!points.is_empty()) {
|
2016-12-03 20:35:59 +00:00
|
|
|
points.clear();
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2016-12-03 20:35:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector3 Curve3D::interpolate(int p_index, real_t p_offset) const {
|
2014-07-06 14:49:27 +00:00
|
|
|
int pc = points.size();
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_COND_V(pc == 0, Vector3());
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (p_index >= pc - 1) {
|
2021-10-15 20:32:26 +00:00
|
|
|
return points[pc - 1].position;
|
2020-05-14 14:41:43 +00:00
|
|
|
} else if (p_index < 0) {
|
2021-10-15 20:32:26 +00:00
|
|
|
return points[0].position;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector3 p0 = points[p_index].position;
|
2017-03-05 15:44:50 +00:00
|
|
|
Vector3 p1 = p0 + points[p_index].out;
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector3 p3 = points[p_index + 1].position;
|
2017-03-05 15:44:50 +00:00
|
|
|
Vector3 p2 = p3 + points[p_index + 1].in;
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
return _bezier_interp(p_offset, p0, p1, p2, p3);
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Vector3 Curve3D::interpolatef(real_t p_findex) const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (p_findex < 0) {
|
2017-03-05 15:44:50 +00:00
|
|
|
p_findex = 0;
|
2020-05-14 14:41:43 +00:00
|
|
|
} else if (p_findex >= points.size()) {
|
2017-03-05 15:44:50 +00:00
|
|
|
p_findex = points.size();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-07-06 14:49:27 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
return interpolate((int)p_findex, Math::fmod(p_findex, (real_t)1.0));
|
2014-07-06 14:49:27 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2022-02-12 18:11:08 +00:00
|
|
|
void Curve3D::mark_dirty() {
|
|
|
|
baked_cache_dirty = true;
|
|
|
|
emit_signal(CoreStringNames::get_singleton()->changed);
|
2022-02-12 18:12:22 +00:00
|
|
|
notify_property_list_changed();
|
2022-02-12 18:11:08 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 16:17:02 +00:00
|
|
|
void Curve3D::_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 {
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t mp = p_begin + (p_end - p_begin) * 0.5;
|
2017-03-05 15:44:50 +00:00
|
|
|
Vector3 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b);
|
|
|
|
Vector3 mid = _bezier_interp(mp, p_a, p_a + p_out, p_b + p_in, p_b);
|
|
|
|
Vector3 end = _bezier_interp(p_end, p_a, p_a + p_out, p_b + p_in, p_b);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
Vector3 na = (mid - beg).normalized();
|
|
|
|
Vector3 nb = (end - mid).normalized();
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t dp = na.dot(nb);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (dp < Math::cos(Math::deg2rad(p_tol))) {
|
|
|
|
r_bake[mp] = mid;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
2017-03-05 15:44:50 +00:00
|
|
|
if (p_depth < p_max_depth) {
|
|
|
|
_bake_segment3d(r_bake, p_begin, mp, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_tol);
|
|
|
|
_bake_segment3d(r_bake, mp, p_end, p_a, p_out, p_b, p_in, p_depth + 1, p_max_depth, p_tol);
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Curve3D::_bake() const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (!baked_cache_dirty) {
|
2014-02-10 01:10:30 +00:00
|
|
|
return;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
baked_max_ofs = 0;
|
|
|
|
baked_cache_dirty = false;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (points.size() == 0) {
|
2022-02-01 18:19:13 +00:00
|
|
|
baked_point_cache.clear();
|
|
|
|
baked_tilt_cache.clear();
|
|
|
|
baked_up_vector_cache.clear();
|
|
|
|
baked_dist_cache.clear();
|
2014-02-10 01:10:30 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (points.size() == 1) {
|
2014-02-10 01:10:30 +00:00
|
|
|
baked_point_cache.resize(1);
|
2021-10-15 20:32:26 +00:00
|
|
|
baked_point_cache.set(0, points[0].position);
|
2014-02-10 01:10:30 +00:00
|
|
|
baked_tilt_cache.resize(1);
|
2017-03-05 15:44:50 +00:00
|
|
|
baked_tilt_cache.set(0, points[0].tilt);
|
2021-08-21 07:57:59 +00:00
|
|
|
baked_dist_cache.resize(1);
|
|
|
|
baked_dist_cache.set(0, 0.0);
|
2018-05-18 22:14:25 +00:00
|
|
|
|
|
|
|
if (up_vector_enabled) {
|
|
|
|
baked_up_vector_cache.resize(1);
|
|
|
|
baked_up_vector_cache.set(0, Vector3(0, 1, 0));
|
2020-05-14 14:41:43 +00:00
|
|
|
} else {
|
2022-02-01 18:19:13 +00:00
|
|
|
baked_up_vector_cache.clear();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-05-18 22:14:25 +00:00
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector3 position = points[0].position;
|
|
|
|
real_t dist = 0.0;
|
2014-02-10 01:10:30 +00:00
|
|
|
List<Plane> pointlist;
|
2021-10-15 20:32:26 +00:00
|
|
|
List<real_t> distlist;
|
2021-08-21 07:57:59 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
// Start always from origin.
|
2021-10-15 20:32:26 +00:00
|
|
|
pointlist.push_back(Plane(position, points[0].tilt));
|
2021-08-21 07:57:59 +00:00
|
|
|
distlist.push_back(0.0);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int i = 0; i < points.size() - 1; i++) {
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t step = 0.1; // at least 10 substeps ought to be enough?
|
|
|
|
real_t p = 0.0;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
while (p < 1.0) {
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t np = p + step;
|
2020-05-14 14:41:43 +00:00
|
|
|
if (np > 1.0) {
|
2017-03-05 15:44:50 +00:00
|
|
|
np = 1.0;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector3 npp = _bezier_interp(np, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
|
|
|
|
real_t d = position.distance_to(npp);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (d > bake_interval) {
|
2014-02-10 01:10:30 +00:00
|
|
|
// OK! between P and NP there _has_ to be Something, let's go searching!
|
|
|
|
|
|
|
|
int iterations = 10; //lots of detail!
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t low = p;
|
|
|
|
real_t hi = np;
|
|
|
|
real_t mid = low + (hi - low) * 0.5;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int j = 0; j < iterations; j++) {
|
2021-10-15 20:32:26 +00:00
|
|
|
npp = _bezier_interp(mid, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
|
|
|
|
d = position.distance_to(npp);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (bake_interval < d) {
|
2017-03-05 15:44:50 +00:00
|
|
|
hi = mid;
|
2020-05-14 14:41:43 +00:00
|
|
|
} else {
|
2017-03-05 15:44:50 +00:00
|
|
|
low = mid;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2017-03-05 15:44:50 +00:00
|
|
|
mid = low + (hi - low) * 0.5;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
position = npp;
|
2017-03-05 15:44:50 +00:00
|
|
|
p = mid;
|
2014-02-10 01:10:30 +00:00
|
|
|
Plane post;
|
2021-10-15 20:32:26 +00:00
|
|
|
post.normal = position;
|
2020-05-10 14:47:11 +00:00
|
|
|
post.d = Math::lerp(points[i].tilt, points[i + 1].tilt, mid);
|
2021-08-21 07:57:59 +00:00
|
|
|
dist += d;
|
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
pointlist.push_back(post);
|
2021-08-21 07:57:59 +00:00
|
|
|
distlist.push_back(dist);
|
2014-02-10 01:10:30 +00:00
|
|
|
} else {
|
2017-03-05 15:44:50 +00:00
|
|
|
p = np;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
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);
|
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-08-21 07:57:59 +00:00
|
|
|
baked_max_ofs = dist;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
|
|
|
baked_point_cache.resize(pointlist.size());
|
2020-02-17 21:06:54 +00:00
|
|
|
Vector3 *w = baked_point_cache.ptrw();
|
2017-03-05 15:44:50 +00:00
|
|
|
int idx = 0;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
|
|
|
baked_tilt_cache.resize(pointlist.size());
|
2020-02-17 21:06:54 +00:00
|
|
|
real_t *wt = baked_tilt_cache.ptrw();
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2018-05-18 22:14:25 +00:00
|
|
|
baked_up_vector_cache.resize(up_vector_enabled ? pointlist.size() : 0);
|
2020-02-17 21:06:54 +00:00
|
|
|
Vector3 *up_write = baked_up_vector_cache.ptrw();
|
2018-05-18 22:14:25 +00:00
|
|
|
|
2021-08-21 07:57:59 +00:00
|
|
|
baked_dist_cache.resize(pointlist.size());
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t *wd = baked_dist_cache.ptrw();
|
2021-08-21 07:57:59 +00:00
|
|
|
|
2018-05-18 22:14:25 +00:00
|
|
|
Vector3 sideways;
|
|
|
|
Vector3 up;
|
|
|
|
Vector3 forward;
|
|
|
|
|
|
|
|
Vector3 prev_sideways = Vector3(1, 0, 0);
|
|
|
|
Vector3 prev_up = Vector3(0, 1, 0);
|
|
|
|
Vector3 prev_forward = Vector3(0, 0, 1);
|
|
|
|
|
2021-07-24 13:46:25 +00:00
|
|
|
for (const Plane &E : pointlist) {
|
2021-07-16 03:45:57 +00:00
|
|
|
w[idx] = E.normal;
|
|
|
|
wt[idx] = E.d;
|
2021-08-21 07:57:59 +00:00
|
|
|
wd[idx] = distlist[idx];
|
2018-05-18 22:14:25 +00:00
|
|
|
|
|
|
|
if (!up_vector_enabled) {
|
|
|
|
idx++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
forward = idx > 0 ? (w[idx] - w[idx - 1]).normalized() : prev_forward;
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t y_dot = prev_up.dot(forward);
|
2018-05-18 22:14:25 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (idx == 1) {
|
2018-05-18 22:14:25 +00:00
|
|
|
up_write[0] = up;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-05-18 22:14:25 +00:00
|
|
|
|
|
|
|
up_write[idx] = up;
|
|
|
|
|
|
|
|
prev_sideways = sideways;
|
|
|
|
prev_up = up;
|
|
|
|
prev_forward = forward;
|
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve3D::get_baked_length() const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2014-02-10 01:10:30 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
|
|
|
return baked_max_ofs;
|
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector3 Curve3D::interpolate_baked(real_t p_offset, bool p_cubic) const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2014-02-10 01:10:30 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
// Validate: Curve may not have baked points.
|
2014-02-10 01:10:30 +00:00
|
|
|
int pc = baked_point_cache.size();
|
2019-08-08 20:11:48 +00:00
|
|
|
ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D.");
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (pc == 1) {
|
2014-02-10 01:10:30 +00:00
|
|
|
return baked_point_cache.get(0);
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
const Vector3 *r = baked_point_cache.ptr();
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (p_offset < 0) {
|
2014-02-10 01:10:30 +00:00
|
|
|
return r[0];
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
|
|
|
if (p_offset >= baked_max_ofs) {
|
2021-09-15 13:40:33 +00:00
|
|
|
return r[pc - 1];
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
int start = 0;
|
|
|
|
int end = pc;
|
|
|
|
int idx = (end + start) / 2;
|
|
|
|
// Binary search to find baked points.
|
2021-08-21 07:57:59 +00:00
|
|
|
while (start < idx) {
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t offset = baked_dist_cache[idx];
|
2021-08-21 07:57:59 +00:00
|
|
|
if (p_offset <= offset) {
|
|
|
|
end = idx;
|
|
|
|
} else {
|
|
|
|
start = idx;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2021-08-21 07:57:59 +00:00
|
|
|
idx = (end + start) / 2;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t offset_begin = baked_dist_cache[idx];
|
|
|
|
real_t offset_end = baked_dist_cache[idx + 1];
|
2021-08-21 07:57:59 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t idx_interval = offset_end - offset_begin;
|
2021-08-21 07:57:59 +00:00
|
|
|
ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(), "failed to find baked segment");
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t frac = (p_offset - offset_begin) / idx_interval;
|
2021-08-21 07:57:59 +00:00
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
if (p_cubic) {
|
2017-03-05 15:44:50 +00:00
|
|
|
Vector3 pre = idx > 0 ? r[idx - 1] : r[idx];
|
2021-09-15 13:40:33 +00:00
|
|
|
Vector3 post = (idx < (pc - 2)) ? r[idx + 2] : r[idx + 1];
|
2017-03-05 15:44:50 +00:00
|
|
|
return r[idx].cubic_interpolate(r[idx + 1], pre, post, frac);
|
2014-02-10 01:10:30 +00:00
|
|
|
} else {
|
2020-03-16 09:07:33 +00:00
|
|
|
return r[idx].lerp(r[idx + 1], frac);
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve3D::interpolate_baked_tilt(real_t p_offset) const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2014-02-10 01:10:30 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
// Validate: Curve may not have baked tilts.
|
2014-02-10 01:10:30 +00:00
|
|
|
int pc = baked_tilt_cache.size();
|
2019-08-08 20:11:48 +00:00
|
|
|
ERR_FAIL_COND_V_MSG(pc == 0, 0, "No tilts in Curve3D.");
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (pc == 1) {
|
2014-02-10 01:10:30 +00:00
|
|
|
return baked_tilt_cache.get(0);
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
const real_t *r = baked_tilt_cache.ptr();
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (p_offset < 0) {
|
2014-02-10 01:10:30 +00:00
|
|
|
return r[0];
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
|
|
|
if (p_offset >= baked_max_ofs) {
|
2021-09-15 13:40:33 +00:00
|
|
|
return r[pc - 1];
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
int start = 0;
|
|
|
|
int end = pc;
|
|
|
|
int idx = (end + start) / 2;
|
|
|
|
// Binary search to find baked points.
|
2021-09-15 13:40:33 +00:00
|
|
|
while (start < idx) {
|
|
|
|
real_t offset = baked_dist_cache[idx];
|
|
|
|
if (p_offset <= offset) {
|
|
|
|
end = idx;
|
|
|
|
} else {
|
|
|
|
start = idx;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2021-09-15 13:40:33 +00:00
|
|
|
idx = (end + start) / 2;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-09-15 13:40:33 +00:00
|
|
|
real_t offset_begin = baked_dist_cache[idx];
|
|
|
|
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, 0, "failed to find baked segment");
|
|
|
|
|
|
|
|
real_t frac = (p_offset - offset_begin) / idx_interval;
|
|
|
|
|
2021-08-09 22:15:17 +00:00
|
|
|
return Math::lerp(r[idx], r[idx + 1], (real_t)frac);
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
Vector3 Curve3D::interpolate_baked_up_vector(real_t p_offset, bool p_apply_tilt) const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2018-05-18 22:14:25 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-05-18 22:14:25 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
// Validate: Curve may not have baked up vectors.
|
2018-05-18 22:14:25 +00:00
|
|
|
int count = baked_up_vector_cache.size();
|
2019-08-08 20:11:48 +00:00
|
|
|
ERR_FAIL_COND_V_MSG(count == 0, Vector3(0, 1, 0), "No up vectors in Curve3D.");
|
2018-05-18 22:14:25 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (count == 1) {
|
2018-05-18 22:14:25 +00:00
|
|
|
return baked_up_vector_cache.get(0);
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-05-18 22:14:25 +00:00
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
const Vector3 *r = baked_up_vector_cache.ptr();
|
|
|
|
const Vector3 *rp = baked_point_cache.ptr();
|
|
|
|
const real_t *rt = baked_tilt_cache.ptr();
|
2018-05-18 22:14:25 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
int start = 0;
|
|
|
|
int end = count;
|
|
|
|
int idx = (end + start) / 2;
|
|
|
|
// Binary search to find baked points.
|
2021-09-15 13:40:33 +00:00
|
|
|
while (start < idx) {
|
|
|
|
real_t offset = baked_dist_cache[idx];
|
|
|
|
if (p_offset <= offset) {
|
|
|
|
end = idx;
|
|
|
|
} else {
|
|
|
|
start = idx;
|
|
|
|
}
|
|
|
|
idx = (end + start) / 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
real_t offset_begin = baked_dist_cache[idx];
|
|
|
|
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(0, 1, 0), "failed to find baked segment");
|
2018-05-18 22:14:25 +00:00
|
|
|
|
2021-09-15 13:40:33 +00:00
|
|
|
real_t frac = (p_offset - offset_begin) / idx_interval;
|
2018-05-18 22:14:25 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (idx == count - 1) {
|
2018-05-18 22:14:25 +00:00
|
|
|
return p_apply_tilt ? r[idx].rotated((rp[idx] - rp[idx - 1]).normalized(), rt[idx]) : r[idx];
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-05-18 22:14:25 +00:00
|
|
|
|
|
|
|
Vector3 forward = (rp[idx + 1] - rp[idx]).normalized();
|
|
|
|
Vector3 up = r[idx];
|
|
|
|
Vector3 up1 = r[idx + 1];
|
|
|
|
|
|
|
|
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 axis = up.cross(up1);
|
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (axis.length_squared() < CMP_EPSILON2) {
|
2018-05-18 22:14:25 +00:00
|
|
|
axis = forward;
|
2020-05-14 14:41:43 +00:00
|
|
|
} else {
|
2018-05-18 22:14:25 +00:00
|
|
|
axis.normalize();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-05-18 22:14:25 +00:00
|
|
|
|
|
|
|
return up.rotated(axis, up.angle_to(up1) * frac);
|
|
|
|
}
|
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
PackedVector3Array Curve3D::get_baked_points() const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2014-02-10 01:10:30 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
|
|
|
return baked_point_cache;
|
|
|
|
}
|
|
|
|
|
2021-08-09 22:15:17 +00:00
|
|
|
Vector<real_t> Curve3D::get_baked_tilts() const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2014-02-10 01:10:30 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2014-02-10 01:10:30 +00:00
|
|
|
|
|
|
|
return baked_tilt_cache;
|
|
|
|
}
|
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
PackedVector3Array Curve3D::get_baked_up_vectors() const {
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2018-05-18 22:14:25 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-05-18 22:14:25 +00:00
|
|
|
|
|
|
|
return baked_up_vector_cache;
|
|
|
|
}
|
|
|
|
|
2018-04-24 23:28:17 +00:00
|
|
|
Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const {
|
2021-10-07 19:36:31 +00:00
|
|
|
// Brute force method.
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2018-04-24 23:28:17 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
// Validate: Curve may not have baked points.
|
2018-04-24 23:28:17 +00:00
|
|
|
int pc = baked_point_cache.size();
|
2019-08-08 20:11:48 +00:00
|
|
|
ERR_FAIL_COND_V_MSG(pc == 0, Vector3(), "No points in Curve3D.");
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (pc == 1) {
|
2018-04-24 23:28:17 +00:00
|
|
|
return baked_point_cache.get(0);
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
const Vector3 *r = baked_point_cache.ptr();
|
2018-04-24 23:28:17 +00:00
|
|
|
|
|
|
|
Vector3 nearest;
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t nearest_dist = -1.0f;
|
2018-04-24 23:28:17 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < pc - 1; i++) {
|
|
|
|
Vector3 origin = r[i];
|
|
|
|
Vector3 direction = (r[i + 1] - origin) / bake_interval;
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
|
2018-04-24 23:28:17 +00:00
|
|
|
Vector3 proj = origin + direction * d;
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t dist = proj.distance_squared_to(p_to_point);
|
2018-04-24 23:28:17 +00:00
|
|
|
|
|
|
|
if (nearest_dist < 0.0f || dist < nearest_dist) {
|
|
|
|
nearest = proj;
|
|
|
|
nearest_dist = dist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nearest;
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
|
2021-10-07 19:36:31 +00:00
|
|
|
// Brute force method.
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (baked_cache_dirty) {
|
2018-04-24 23:28:17 +00:00
|
|
|
_bake();
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
// Validate: Curve may not have baked points.
|
2018-04-24 23:28:17 +00:00
|
|
|
int pc = baked_point_cache.size();
|
2019-08-08 20:11:48 +00:00
|
|
|
ERR_FAIL_COND_V_MSG(pc == 0, 0.0f, "No points in Curve3D.");
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2020-05-14 14:41:43 +00:00
|
|
|
if (pc == 1) {
|
2018-04-24 23:28:17 +00:00
|
|
|
return 0.0f;
|
2020-05-14 14:41:43 +00:00
|
|
|
}
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
const Vector3 *r = baked_point_cache.ptr();
|
2018-04-24 23:28:17 +00:00
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t nearest = 0.0f;
|
|
|
|
real_t nearest_dist = -1.0f;
|
|
|
|
real_t offset = 0.0f;
|
2018-04-24 23:28:17 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < pc - 1; i++) {
|
|
|
|
Vector3 origin = r[i];
|
|
|
|
Vector3 direction = (r[i + 1] - origin) / bake_interval;
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
|
2018-04-24 23:28:17 +00:00
|
|
|
Vector3 proj = origin + direction * d;
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t dist = proj.distance_squared_to(p_to_point);
|
2018-04-24 23:28:17 +00:00
|
|
|
|
|
|
|
if (nearest_dist < 0.0f || dist < nearest_dist) {
|
|
|
|
nearest = offset + d;
|
|
|
|
nearest_dist = dist;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += bake_interval;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nearest;
|
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
void Curve3D::set_bake_interval(real_t p_tolerance) {
|
2017-03-05 15:44:50 +00:00
|
|
|
bake_interval = p_tolerance;
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
real_t Curve3D::get_bake_interval() const {
|
2014-02-10 01:10:30 +00:00
|
|
|
return bake_interval;
|
|
|
|
}
|
|
|
|
|
2018-05-18 22:14:25 +00:00
|
|
|
void Curve3D::set_up_vector_enabled(bool p_enable) {
|
|
|
|
up_vector_enabled = p_enable;
|
2022-02-12 18:11:08 +00:00
|
|
|
mark_dirty();
|
2018-05-18 22:14:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Curve3D::is_up_vector_enabled() const {
|
|
|
|
return up_vector_enabled;
|
|
|
|
}
|
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
Dictionary Curve3D::_get_data() const {
|
|
|
|
Dictionary dc;
|
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
PackedVector3Array d;
|
2017-03-05 15:44:50 +00:00
|
|
|
d.resize(points.size() * 3);
|
2020-02-17 21:06:54 +00:00
|
|
|
Vector3 *w = d.ptrw();
|
2021-08-09 22:15:17 +00:00
|
|
|
Vector<real_t> t;
|
2014-02-10 01:10:30 +00:00
|
|
|
t.resize(points.size());
|
2020-02-17 21:06:54 +00:00
|
|
|
real_t *wt = t.ptrw();
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int i = 0; i < points.size(); i++) {
|
|
|
|
w[i * 3 + 0] = points[i].in;
|
|
|
|
w[i * 3 + 1] = points[i].out;
|
2021-10-15 20:32:26 +00:00
|
|
|
w[i * 3 + 2] = points[i].position;
|
2017-03-05 15:44:50 +00:00
|
|
|
wt[i] = points[i].tilt;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
dc["points"] = d;
|
|
|
|
dc["tilts"] = t;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
|
|
|
return dc;
|
|
|
|
}
|
2020-05-14 12:29:06 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
void Curve3D::_set_data(const Dictionary &p_data) {
|
2014-02-10 01:10:30 +00:00
|
|
|
ERR_FAIL_COND(!p_data.has("points"));
|
|
|
|
ERR_FAIL_COND(!p_data.has("tilts"));
|
|
|
|
|
2020-02-17 21:06:54 +00:00
|
|
|
PackedVector3Array rp = p_data["points"];
|
2014-02-10 01:10:30 +00:00
|
|
|
int pc = rp.size();
|
2017-03-05 15:44:50 +00:00
|
|
|
ERR_FAIL_COND(pc % 3 != 0);
|
|
|
|
points.resize(pc / 3);
|
2020-02-17 21:06:54 +00:00
|
|
|
const Vector3 *r = rp.ptr();
|
2021-08-09 22:15:17 +00:00
|
|
|
Vector<real_t> rtl = p_data["tilts"];
|
2020-02-17 21:06:54 +00:00
|
|
|
const real_t *rt = rtl.ptr();
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int i = 0; i < points.size(); i++) {
|
2018-07-25 01:11:03 +00:00
|
|
|
points.write[i].in = r[i * 3 + 0];
|
|
|
|
points.write[i].out = r[i * 3 + 1];
|
2021-10-15 20:32:26 +00:00
|
|
|
points.write[i].position = r[i * 3 + 2];
|
2018-07-25 01:11:03 +00:00
|
|
|
points.write[i].tilt = rt[i];
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
baked_cache_dirty = true;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-15 20:32:26 +00:00
|
|
|
PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) const {
|
2020-02-17 21:06:54 +00:00
|
|
|
PackedVector3Array tess;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
if (points.size() == 0) {
|
2014-02-10 01:10:30 +00:00
|
|
|
return tess;
|
|
|
|
}
|
2022-05-16 16:17:02 +00:00
|
|
|
Vector<RBMap<real_t, Vector3>> midpoints;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
midpoints.resize(points.size() - 1);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
int pc = 1;
|
|
|
|
for (int i = 0; i < points.size() - 1; i++) {
|
2021-10-15 20:32:26 +00:00
|
|
|
_bake_segment3d(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_tolerance);
|
2014-02-10 01:10:30 +00:00
|
|
|
pc++;
|
2017-03-05 15:44:50 +00:00
|
|
|
pc += midpoints[i].size();
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tess.resize(pc);
|
2020-02-17 21:06:54 +00:00
|
|
|
Vector3 *bpw = tess.ptrw();
|
2021-10-15 20:32:26 +00:00
|
|
|
bpw[0] = points[0].position;
|
2017-03-05 15:44:50 +00:00
|
|
|
int pidx = 0;
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
for (int i = 0; i < points.size() - 1; i++) {
|
2021-10-15 20:32:26 +00:00
|
|
|
for (const KeyValue<real_t, Vector3> &E : midpoints[i]) {
|
2014-02-10 01:10:30 +00:00
|
|
|
pidx++;
|
2021-08-09 20:13:42 +00:00
|
|
|
bpw[pidx] = E.value;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pidx++;
|
2021-10-15 20:32:26 +00:00
|
|
|
bpw[pidx] = points[i + 1].position;
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return tess;
|
|
|
|
}
|
|
|
|
|
2022-02-12 18:12:22 +00:00
|
|
|
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()) {
|
|
|
|
int point_index = components[0].trim_prefix("point_").to_int();
|
|
|
|
String property = components[1];
|
|
|
|
if (property == "position") {
|
|
|
|
set_point_position(point_index, p_value);
|
|
|
|
return true;
|
|
|
|
} else if (property == "in") {
|
|
|
|
set_point_in(point_index, p_value);
|
|
|
|
return true;
|
|
|
|
} else if (property == "out") {
|
|
|
|
set_point_out(point_index, p_value);
|
|
|
|
return true;
|
|
|
|
} else if (property == "tilt") {
|
|
|
|
set_point_tilt(point_index, p_value);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Curve3D::_get(const StringName &p_name, Variant &r_ret) const {
|
|
|
|
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()) {
|
|
|
|
int point_index = components[0].trim_prefix("point_").to_int();
|
|
|
|
String property = components[1];
|
|
|
|
if (property == "position") {
|
|
|
|
r_ret = get_point_position(point_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "in") {
|
|
|
|
r_ret = get_point_in(point_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "out") {
|
|
|
|
r_ret = get_point_out(point_index);
|
|
|
|
return true;
|
|
|
|
} else if (property == "tilt") {
|
|
|
|
r_ret = get_point_tilt(point_index);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Curve3D::_get_property_list(List<PropertyInfo> *p_list) const {
|
|
|
|
for (int i = 0; i < points.size(); i++) {
|
|
|
|
PropertyInfo pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/position", i));
|
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
|
p_list->push_back(pi);
|
|
|
|
|
|
|
|
if (i != 0) {
|
|
|
|
pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/in", i));
|
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
|
p_list->push_back(pi);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i != points.size() - 1) {
|
|
|
|
pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/out", i));
|
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
|
p_list->push_back(pi);
|
|
|
|
}
|
|
|
|
|
|
|
|
pi = PropertyInfo(Variant::FLOAT, vformat("point_%d/tilt", i));
|
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
|
p_list->push_back(pi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-10 01:10:30 +00:00
|
|
|
void Curve3D::_bind_methods() {
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_point_count"), &Curve3D::get_point_count);
|
2022-02-12 18:12:22 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve3D::set_point_count);
|
2017-09-10 13:37:49 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("add_point", "position", "in", "out", "at_position"), &Curve3D::add_point, DEFVAL(Vector3()), DEFVAL(Vector3()), DEFVAL(-1));
|
|
|
|
ClassDB::bind_method(D_METHOD("set_point_position", "idx", "position"), &Curve3D::set_point_position);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_point_position", "idx"), &Curve3D::get_point_position);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_tilt", "idx", "tilt"), &Curve3D::set_point_tilt);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_point_tilt", "idx"), &Curve3D::get_point_tilt);
|
2017-09-10 13:37:49 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_in", "idx", "position"), &Curve3D::set_point_in);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_point_in", "idx"), &Curve3D::get_point_in);
|
2017-09-10 13:37:49 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_point_out", "idx", "position"), &Curve3D::set_point_out);
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_point_out", "idx"), &Curve3D::get_point_out);
|
|
|
|
ClassDB::bind_method(D_METHOD("remove_point", "idx"), &Curve3D::remove_point);
|
|
|
|
ClassDB::bind_method(D_METHOD("clear_points"), &Curve3D::clear_points);
|
|
|
|
ClassDB::bind_method(D_METHOD("interpolate", "idx", "t"), &Curve3D::interpolate);
|
|
|
|
ClassDB::bind_method(D_METHOD("interpolatef", "fofs"), &Curve3D::interpolatef);
|
2017-02-13 11:47:24 +00:00
|
|
|
//ClassDB::bind_method(D_METHOD("bake","subdivs"),&Curve3D::bake,DEFVAL(10));
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_bake_interval", "distance"), &Curve3D::set_bake_interval);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_bake_interval"), &Curve3D::get_bake_interval);
|
2018-05-18 22:14:25 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("set_up_vector_enabled", "enable"), &Curve3D::set_up_vector_enabled);
|
|
|
|
ClassDB::bind_method(D_METHOD("is_up_vector_enabled"), &Curve3D::is_up_vector_enabled);
|
2017-02-13 11:47:24 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve3D::get_baked_length);
|
|
|
|
ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve3D::interpolate_baked, DEFVAL(false));
|
2018-05-18 22:14:25 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("interpolate_baked_up_vector", "offset", "apply_tilt"), &Curve3D::interpolate_baked_up_vector, DEFVAL(false));
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve3D::get_baked_points);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_baked_tilts"), &Curve3D::get_baked_tilts);
|
2018-05-18 22:14:25 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("get_baked_up_vectors"), &Curve3D::get_baked_up_vectors);
|
2018-04-24 23:28:17 +00:00
|
|
|
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);
|
2017-07-18 19:03:34 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve3D::tessellate, DEFVAL(5), DEFVAL(4));
|
2014-02-10 01:10:30 +00:00
|
|
|
|
2017-03-05 15:44:50 +00:00
|
|
|
ClassDB::bind_method(D_METHOD("_get_data"), &Curve3D::_get_data);
|
|
|
|
ClassDB::bind_method(D_METHOD("_set_data"), &Curve3D::_set_data);
|
2014-02-10 01:10:30 +00:00
|
|
|
|
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 18:20:53 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval");
|
2021-11-03 22:06:17 +00:00
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
|
2022-02-12 18:12:22 +00:00
|
|
|
ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_");
|
2018-05-18 22:14:25 +00:00
|
|
|
|
|
|
|
ADD_GROUP("Up Vector", "up_vector_");
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "up_vector_enabled"), "set_up_vector_enabled", "is_up_vector_enabled");
|
2014-02-10 01:10:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-07 19:36:31 +00:00
|
|
|
Curve3D::Curve3D() {}
|