mirror of
https://github.com/godotengine/godot.git
synced 2024-11-14 16:13:08 +00:00
Added Curve resource
- New resource for curves in y(x) form - CurveTexture now has a Curve - Curve and CurveTexture share the same editor
This commit is contained in:
parent
00e5ba3143
commit
659897cfb8
@ -6114,7 +6114,7 @@ EditorNode::EditorNode() {
|
||||
add_editor_plugin(memnew(GradientEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(GradientTextureEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(CollisionShape2DEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(CurveTextureEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(CurveEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(TextureEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
|
||||
//add_editor_plugin( memnew( MaterialEditorPlugin(this) ) );
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -27,69 +27,119 @@
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#ifndef CURVE_EDITOR_PLUGIN_H
|
||||
#define CURVE_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_plugin.h"
|
||||
#include "scene/resources/curve.h"
|
||||
|
||||
class CurveTextureEdit : public Control {
|
||||
// Edits a y(x) curve
|
||||
class CurveEditor : public Control {
|
||||
GDCLASS(CurveEditor, Control)
|
||||
public:
|
||||
CurveEditor();
|
||||
|
||||
GDCLASS(CurveTextureEdit, Control);
|
||||
Size2 get_minimum_size() const;
|
||||
|
||||
struct Point {
|
||||
void set_curve(Ref<Curve> curve);
|
||||
|
||||
float offset;
|
||||
float height;
|
||||
bool operator<(const Point &p_ponit) const {
|
||||
return offset < p_ponit.offset;
|
||||
}
|
||||
enum PresetID {
|
||||
PRESET_FLAT0 = 0,
|
||||
PRESET_FLAT1,
|
||||
PRESET_LINEAR,
|
||||
PRESET_EASE_IN,
|
||||
PRESET_EASE_OUT,
|
||||
PRESET_SMOOTHSTEP,
|
||||
PRESET_COUNT
|
||||
};
|
||||
|
||||
bool grabbing;
|
||||
int grabbed;
|
||||
Vector<Point> points;
|
||||
float max, min;
|
||||
enum ContextAction {
|
||||
CONTEXT_ADD_POINT = 0,
|
||||
CONTEXT_REMOVE_POINT
|
||||
};
|
||||
|
||||
void _plot_curve(const Vector2 &p_a, const Vector2 &p_b, const Vector2 &p_c, const Vector2 &p_d);
|
||||
enum TangentIndex {
|
||||
TANGENT_NONE = -1,
|
||||
TANGENT_LEFT = 0,
|
||||
TANGENT_RIGHT = 1
|
||||
};
|
||||
|
||||
protected:
|
||||
void _gui_input(const Ref<InputEvent> &p_event);
|
||||
void _notification(int p_what);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_range(float p_min, float p_max);
|
||||
void set_points(const Vector<Vector2> &p_points);
|
||||
Vector<Vector2> get_points() const;
|
||||
virtual Size2 get_minimum_size() const;
|
||||
CurveTextureEdit();
|
||||
private:
|
||||
void on_gui_input(const Ref<InputEvent> &p_event);
|
||||
void on_preset_item_selected(int preset_id);
|
||||
void _curve_changed();
|
||||
void on_context_menu_item_selected(int action_id);
|
||||
|
||||
void open_context_menu(Vector2 pos);
|
||||
int get_point_at(Vector2 pos) const;
|
||||
int get_tangent_at(Vector2 pos) const;
|
||||
void add_point(Vector2 pos);
|
||||
void remove_point(int index);
|
||||
void set_selected_point(int index);
|
||||
void set_hover_point_index(int index);
|
||||
void push_undo(Array previous_curve_data);
|
||||
void update_view_transform();
|
||||
|
||||
Vector2 get_tangent_view_pos(int i, TangentIndex tangent) const;
|
||||
Vector2 get_view_pos(Vector2 world_pos) const;
|
||||
Vector2 get_world_pos(Vector2 view_pos) const;
|
||||
|
||||
void _draw();
|
||||
|
||||
void stroke_rect(Rect2 rect, Color color);
|
||||
|
||||
private:
|
||||
Rect2 _world_rect;
|
||||
Transform2D _world_to_view;
|
||||
|
||||
Ref<Curve> _curve_ref;
|
||||
PopupMenu *_context_menu;
|
||||
PopupMenu *_presets_menu;
|
||||
|
||||
Array _undo_data;
|
||||
bool _has_undo_data;
|
||||
bool _undo_no_commit;
|
||||
|
||||
Vector2 _context_click_pos;
|
||||
int _selected_point;
|
||||
int _hover_point;
|
||||
int _selected_tangent;
|
||||
bool _dragging;
|
||||
|
||||
// Constant
|
||||
float _hover_radius;
|
||||
float _tangents_length;
|
||||
};
|
||||
|
||||
class CurveTextureEditorPlugin : public EditorPlugin {
|
||||
|
||||
GDCLASS(CurveTextureEditorPlugin, EditorPlugin);
|
||||
|
||||
CurveTextureEdit *curve_editor;
|
||||
Ref<CurveTexture> curve_texture_ref;
|
||||
EditorNode *editor;
|
||||
ToolButton *curve_button;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _curve_changed();
|
||||
void _undo_redo_curve_texture(const PoolVector<Vector2> &points);
|
||||
void _curve_settings_changed();
|
||||
|
||||
class CurveEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(CurveEditorPlugin, EditorPlugin)
|
||||
public:
|
||||
virtual String get_name() const { return "CurveTexture"; }
|
||||
bool has_main_screen() const { return false; }
|
||||
virtual void edit(Object *p_node);
|
||||
virtual bool handles(Object *p_node) const;
|
||||
virtual void make_visible(bool p_visible);
|
||||
CurveEditorPlugin(EditorNode *p_node);
|
||||
~CurveEditorPlugin();
|
||||
|
||||
CurveTextureEditorPlugin(EditorNode *p_node);
|
||||
~CurveTextureEditorPlugin();
|
||||
String get_name() const { return "Curve"; }
|
||||
bool has_main_screen() const { return false; }
|
||||
void edit(Object *p_object);
|
||||
bool handles(Object *p_object) const;
|
||||
void make_visible(bool p_visible);
|
||||
|
||||
private:
|
||||
static void _bind_methods();
|
||||
|
||||
void _curve_texture_changed();
|
||||
|
||||
private:
|
||||
CurveEditor *_view;
|
||||
Ref<Resource> _current_ref;
|
||||
EditorNode *_editor_node;
|
||||
ToolButton *_toggle_button;
|
||||
};
|
||||
|
||||
#endif // CURVE_EDITOR_PLUGIN_H
|
||||
|
@ -61,9 +61,21 @@ void TextureEditor::_notification(int p_what) {
|
||||
tex_height = texture->get_height() * tex_width / texture->get_width();
|
||||
}
|
||||
|
||||
// Prevent the texture from being unpreviewable after the rescale, so that we can still see something
|
||||
if (tex_height <= 0)
|
||||
tex_height = 1;
|
||||
if (tex_width <= 0)
|
||||
tex_width = 1;
|
||||
|
||||
int ofs_x = (size.width - tex_width) / 2;
|
||||
int ofs_y = (size.height - tex_height) / 2;
|
||||
|
||||
if (texture->cast_to<CurveTexture>()) {
|
||||
// In the case of CurveTextures we know they are 1 in height, so fill the preview to see the gradient
|
||||
ofs_y = 0;
|
||||
tex_height = size.height;
|
||||
}
|
||||
|
||||
draw_texture_rect(texture, Rect2(ofs_x, ofs_y, tex_width, tex_height));
|
||||
|
||||
Ref<Font> font = get_font("font", "Label");
|
||||
|
@ -1082,16 +1082,9 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture>
|
||||
case PARAM_SCALE: {
|
||||
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->scale_texture, p_texture);
|
||||
|
||||
Ref<CurveTexture> curve = p_texture;
|
||||
if (curve.is_valid()) {
|
||||
if (curve->get_min() == 0 && curve->get_max() == 1) {
|
||||
|
||||
curve->set_max(32);
|
||||
PoolVector<Vector2> points;
|
||||
points.push_back(Vector2(0, 1));
|
||||
points.push_back(Vector2(1, 1));
|
||||
curve->set_points(points);
|
||||
}
|
||||
Ref<CurveTexture> curve_tex = p_texture;
|
||||
if (curve_tex.is_valid()) {
|
||||
curve_tex->ensure_default_setup();
|
||||
}
|
||||
|
||||
} break;
|
||||
@ -1257,14 +1250,7 @@ void ParticlesMaterial::set_trail_size_modifier(const Ref<CurveTexture> &p_trail
|
||||
|
||||
Ref<CurveTexture> curve = trail_size_modifier;
|
||||
if (curve.is_valid()) {
|
||||
if (curve->get_min() == 0 && curve->get_max() == 1) {
|
||||
|
||||
curve->set_max(32);
|
||||
PoolVector<Vector2> points;
|
||||
points.push_back(Vector2(0, 1));
|
||||
points.push_back(Vector2(1, 1));
|
||||
curve->set_points(points);
|
||||
}
|
||||
curve->ensure_default_setup();
|
||||
}
|
||||
|
||||
RID texture;
|
||||
|
@ -580,6 +580,7 @@ void register_scene_types() {
|
||||
ClassDB::register_class<Animation>();
|
||||
ClassDB::register_virtual_class<Font>();
|
||||
ClassDB::register_class<BitmapFont>();
|
||||
ClassDB::register_class<Curve>();
|
||||
|
||||
ClassDB::register_class<DynamicFontData>();
|
||||
ClassDB::register_class<DynamicFont>();
|
||||
|
@ -380,6 +380,365 @@ Curve2D::Curve2D()
|
||||
|
||||
#endif
|
||||
|
||||
Curve::Curve() {
|
||||
_bake_resolution = 100;
|
||||
_baked_cache_dirty = false;
|
||||
#ifdef TOOLS_ENABLED
|
||||
_disable_set_data = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
int Curve::add_point(Vector2 p_pos, real_t left_tangent, real_t right_tangent) {
|
||||
// Add a point and preserve order
|
||||
|
||||
// Curve bounds is in 0..1
|
||||
if (p_pos.x > MAX_X)
|
||||
p_pos.x = MAX_X;
|
||||
else if (p_pos.x < MIN_X)
|
||||
p_pos.x = MIN_X;
|
||||
|
||||
int ret = -1;
|
||||
|
||||
if (_points.size() == 0) {
|
||||
_points.push_back(Point(p_pos, left_tangent, right_tangent));
|
||||
ret = 0;
|
||||
|
||||
} else if (_points.size() == 1) {
|
||||
// TODO Is the `else` able to handle this block already?
|
||||
|
||||
real_t diff = p_pos.x - _points[0].pos.x;
|
||||
|
||||
if (diff > 0) {
|
||||
_points.push_back(Point(p_pos, left_tangent, right_tangent));
|
||||
ret = 1;
|
||||
} else {
|
||||
_points.insert(0, Point(p_pos, left_tangent, right_tangent));
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
int i = get_index(p_pos.x);
|
||||
|
||||
int nearest_index = i;
|
||||
if (i + 1 < _points.size()) {
|
||||
real_t diff0 = p_pos.x - _points[i].pos.x;
|
||||
real_t diff1 = _points[i + 1].pos.x - p_pos.x;
|
||||
|
||||
if (diff1 < diff0)
|
||||
nearest_index = i + 1;
|
||||
}
|
||||
|
||||
if (i == 0 && p_pos.x < _points[0].pos.x) {
|
||||
// Insert before anything else
|
||||
_points.insert(0, Point(p_pos, left_tangent, right_tangent));
|
||||
ret = 0;
|
||||
} else {
|
||||
// Insert between i and i+1
|
||||
++i;
|
||||
_points.insert(i, Point(p_pos, left_tangent, right_tangent));
|
||||
ret = i;
|
||||
}
|
||||
}
|
||||
|
||||
mark_dirty();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Curve::get_index(real_t offset) const {
|
||||
|
||||
// Lower-bound float binary search
|
||||
|
||||
int imin = 0;
|
||||
int imax = _points.size() - 1;
|
||||
|
||||
while (imax - imin > 1) {
|
||||
int m = (imin + imax) / 2;
|
||||
|
||||
real_t a = _points[m].pos.x;
|
||||
real_t b = _points[m + 1].pos.x;
|
||||
|
||||
if (a < offset && b < offset) {
|
||||
imin = m;
|
||||
|
||||
} else if (a > offset) {
|
||||
imax = m;
|
||||
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
// Will happen if the offset is out of bounds
|
||||
if (offset > _points[imax].pos.x)
|
||||
return imax;
|
||||
return imin;
|
||||
}
|
||||
|
||||
void Curve::clean_dupes() {
|
||||
|
||||
bool dirty = false;
|
||||
|
||||
for (int i = 1; i < _points.size(); ++i) {
|
||||
real_t diff = _points[i - 1].pos.x - _points[i].pos.x;
|
||||
if (diff <= CMP_EPSILON) {
|
||||
_points.remove(i);
|
||||
--i;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
mark_dirty();
|
||||
}
|
||||
|
||||
void Curve::set_point_left_tangent(int i, real_t tangent) {
|
||||
ERR_FAIL_INDEX(i, _points.size());
|
||||
_points[i].left_tangent = tangent;
|
||||
mark_dirty();
|
||||
}
|
||||
|
||||
void Curve::set_point_right_tangent(int i, real_t tangent) {
|
||||
ERR_FAIL_INDEX(i, _points.size());
|
||||
_points[i].right_tangent = tangent;
|
||||
mark_dirty();
|
||||
}
|
||||
|
||||
real_t Curve::get_point_left_tangent(int i) const {
|
||||
ERR_FAIL_INDEX_V(i, _points.size(), 0);
|
||||
return _points[i].left_tangent;
|
||||
}
|
||||
|
||||
real_t Curve::get_point_right_tangent(int i) const {
|
||||
ERR_FAIL_INDEX_V(i, _points.size(), 0);
|
||||
return _points[i].right_tangent;
|
||||
}
|
||||
|
||||
void Curve::remove_point(int p_index) {
|
||||
ERR_FAIL_INDEX(p_index, _points.size());
|
||||
_points.remove(p_index);
|
||||
mark_dirty();
|
||||
}
|
||||
|
||||
void Curve::clear_points() {
|
||||
_points.clear();
|
||||
mark_dirty();
|
||||
}
|
||||
|
||||
void Curve::set_point_value(int p_index, real_t pos) {
|
||||
ERR_FAIL_INDEX(p_index, _points.size());
|
||||
_points[p_index].pos.y = pos;
|
||||
mark_dirty();
|
||||
}
|
||||
|
||||
int Curve::set_point_offset(int p_index, float offset) {
|
||||
ERR_FAIL_INDEX_V(p_index, _points.size(), -1);
|
||||
Point p = _points[p_index];
|
||||
remove_point(p_index);
|
||||
int i = add_point(Vector2(offset, p.pos.y));
|
||||
_points[i].left_tangent = p.left_tangent;
|
||||
_points[i].right_tangent = p.right_tangent;
|
||||
return i;
|
||||
}
|
||||
|
||||
Vector2 Curve::get_point_pos(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, _points.size(), Vector2(0, 0));
|
||||
return _points[p_index].pos;
|
||||
}
|
||||
|
||||
real_t Curve::interpolate(real_t offset) const {
|
||||
if (_points.size() == 0)
|
||||
return 0;
|
||||
if (_points.size() == 1)
|
||||
return _points[0].pos.y;
|
||||
|
||||
int i = get_index(offset);
|
||||
|
||||
if (i == _points.size() - 1)
|
||||
return _points[i].pos.y;
|
||||
|
||||
real_t local = offset - _points[i].pos.x;
|
||||
|
||||
if (i == 0 && local <= 0)
|
||||
return _points[0].pos.y;
|
||||
|
||||
return interpolate_local_nocheck(i, local);
|
||||
}
|
||||
|
||||
real_t Curve::interpolate_local_nocheck(int index, real_t local_offset) const {
|
||||
|
||||
const Point a = _points[index];
|
||||
const Point b = _points[index + 1];
|
||||
|
||||
// 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
|
||||
|
||||
// Control points are chosen at equal distances
|
||||
real_t d = b.pos.x - a.pos.x;
|
||||
if (Math::abs(d) <= CMP_EPSILON)
|
||||
return b.pos.y;
|
||||
local_offset /= d;
|
||||
d /= 3.0;
|
||||
real_t yac = a.pos.y + d * a.right_tangent;
|
||||
real_t ybc = b.pos.y - d * b.left_tangent;
|
||||
|
||||
real_t y = _bezier_interp(local_offset, a.pos.y, yac, ybc, b.pos.y);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
void Curve::mark_dirty() {
|
||||
_baked_cache_dirty = true;
|
||||
emit_signal(CoreStringNames::get_singleton()->changed);
|
||||
}
|
||||
|
||||
Array Curve::get_data() const {
|
||||
|
||||
Array output;
|
||||
output.resize(_points.size() * 3);
|
||||
|
||||
for (int j = 0; j < _points.size(); ++j) {
|
||||
|
||||
const Point p = _points[j];
|
||||
int i = j * 3;
|
||||
|
||||
output[i] = p.pos;
|
||||
output[i + 1] = p.left_tangent;
|
||||
output[i + 2] = p.right_tangent;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void Curve::set_data(Array input) {
|
||||
ERR_FAIL_COND(input.size() % 3 != 0);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (_disable_set_data)
|
||||
return;
|
||||
#endif
|
||||
|
||||
_points.clear();
|
||||
|
||||
// Validate input
|
||||
for (int i = 0; i < input.size(); i += 3) {
|
||||
ERR_FAIL_COND(input[i].get_type() != Variant::VECTOR2);
|
||||
ERR_FAIL_COND(input[i + 1].get_type() != Variant::REAL);
|
||||
ERR_FAIL_COND(input[i + 2].get_type() != Variant::REAL);
|
||||
}
|
||||
|
||||
_points.resize(input.size() / 3);
|
||||
|
||||
for (int j = 0; j < _points.size(); ++j) {
|
||||
|
||||
Point &p = _points[j];
|
||||
int i = j * 3;
|
||||
|
||||
p.pos = input[i];
|
||||
p.left_tangent = input[i + 1];
|
||||
p.right_tangent = input[i + 2];
|
||||
}
|
||||
|
||||
mark_dirty();
|
||||
}
|
||||
|
||||
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);
|
||||
_baked_cache[i] = y;
|
||||
}
|
||||
|
||||
if (_points.size() != 0) {
|
||||
_baked_cache[0] = _points[0].pos.y;
|
||||
_baked_cache[_baked_cache.size() - 1] = _points[_points.size() - 1].pos.y;
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
real_t Curve::interpolate_baked(real_t offset) {
|
||||
if (_baked_cache_dirty) {
|
||||
// Last-second bake if not done already
|
||||
bake();
|
||||
}
|
||||
|
||||
// Special cases if the cache is too small
|
||||
if (_baked_cache.size() == 0) {
|
||||
if (_points.size() == 0)
|
||||
return 0;
|
||||
return _points[0].pos.y;
|
||||
} else if (_baked_cache.size() == 1) {
|
||||
return _baked_cache[0];
|
||||
}
|
||||
|
||||
// Get interpolation index
|
||||
real_t fi = offset * _baked_cache.size();
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
void Curve::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_point", "pos", "left_tangent", "right_tangent"), &Curve::add_point, DEFVAL(0), DEFVAL(0));
|
||||
ClassDB::bind_method(D_METHOD("remove_point", "index"), &Curve::remove_point);
|
||||
ClassDB::bind_method(D_METHOD("clear_points"), &Curve::clear_points);
|
||||
ClassDB::bind_method(D_METHOD("get_point_pos", "index"), &Curve::get_point_pos);
|
||||
ClassDB::bind_method(D_METHOD("set_point_value", "index, y"), &Curve::set_point_value);
|
||||
ClassDB::bind_method(D_METHOD("set_point_offset", "index, offset"), &Curve::set_point_value);
|
||||
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);
|
||||
ClassDB::bind_method(D_METHOD("get_point_right_tangent", "index"), &Curve::get_point_left_tangent);
|
||||
ClassDB::bind_method(D_METHOD("set_point_left_tangent", "index", "tangent"), &Curve::set_point_left_tangent);
|
||||
ClassDB::bind_method(D_METHOD("set_point_right_tangent", "index", "tangent"), &Curve::set_point_left_tangent);
|
||||
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);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_resolution", PROPERTY_HINT_RANGE, "1,1000,1"), "set_bake_resolution", "get_bake_resolution");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data");
|
||||
}
|
||||
|
||||
int Curve2D::get_point_count() const {
|
||||
|
||||
return points.size();
|
||||
|
@ -82,6 +82,78 @@ public:
|
||||
|
||||
#endif
|
||||
|
||||
// y(x) curve
|
||||
class Curve : public Resource {
|
||||
GDCLASS(Curve, Resource)
|
||||
public:
|
||||
static const int MIN_X = 0.f;
|
||||
static const int MAX_X = 1.f;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool _disable_set_data;
|
||||
#endif
|
||||
|
||||
struct Point {
|
||||
Vector2 pos;
|
||||
real_t left_tangent;
|
||||
real_t right_tangent;
|
||||
|
||||
Point() {
|
||||
left_tangent = 0;
|
||||
right_tangent = 0;
|
||||
}
|
||||
|
||||
Point(Vector2 p, real_t left = 0, real_t right = 0) {
|
||||
pos = p;
|
||||
left_tangent = left;
|
||||
right_tangent = right;
|
||||
}
|
||||
};
|
||||
|
||||
Curve();
|
||||
|
||||
int get_point_count() const { return _points.size(); }
|
||||
|
||||
int add_point(Vector2 p_pos, real_t left_tangent = 0, real_t right_tangent = 0);
|
||||
void remove_point(int p_index);
|
||||
void clear_points();
|
||||
|
||||
int get_index(real_t offset) const;
|
||||
|
||||
void set_point_value(int p_index, real_t pos);
|
||||
int set_point_offset(int p_index, float offset);
|
||||
Vector2 get_point_pos(int p_index) const;
|
||||
|
||||
real_t interpolate(real_t offset) const;
|
||||
real_t interpolate_local_nocheck(int index, real_t local_offset) const;
|
||||
|
||||
void clean_dupes();
|
||||
|
||||
void set_point_left_tangent(int i, real_t tangent);
|
||||
void set_point_right_tangent(int i, real_t tangent);
|
||||
real_t get_point_left_tangent(int i) const;
|
||||
real_t get_point_right_tangent(int i) const;
|
||||
|
||||
Array get_data() const;
|
||||
void set_data(Array input);
|
||||
|
||||
void bake();
|
||||
int get_bake_resolution() const { return _bake_resolution; }
|
||||
void set_bake_resolution(int p_interval);
|
||||
real_t interpolate_baked(real_t offset);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
void mark_dirty();
|
||||
|
||||
Vector<Point> _points;
|
||||
bool _baked_cache_dirty;
|
||||
Vector<real_t> _baked_cache;
|
||||
int _bake_resolution;
|
||||
};
|
||||
|
||||
class Curve2D : public Resource {
|
||||
|
||||
GDCLASS(Curve2D, Resource);
|
||||
|
@ -30,7 +30,9 @@
|
||||
#include "texture.h"
|
||||
#include "core/method_bind_ext.inc"
|
||||
#include "core/os/os.h"
|
||||
#include "core_string_names.h"
|
||||
#include "io/image_loader.h"
|
||||
|
||||
Size2 Texture::get_size() const {
|
||||
|
||||
return Size2(get_width(), get_height());
|
||||
@ -1376,254 +1378,126 @@ void CurveTexture::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_width", "width"), &CurveTexture::set_width);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_points", "points"), &CurveTexture::set_points);
|
||||
ClassDB::bind_method(D_METHOD("get_points"), &CurveTexture::get_points);
|
||||
ClassDB::bind_method(D_METHOD("set_curve", "curve:Curve"), &CurveTexture::set_curve);
|
||||
ClassDB::bind_method(D_METHOD("get_curve:Curve"), &CurveTexture::get_curve);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_update"), &CurveTexture::_update);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "min", PROPERTY_HINT_RANGE, "-1024,1024"), "set_min", "get_min");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max", PROPERTY_HINT_RANGE, "-1024,1024"), "set_max", "get_max");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "32,4096"), "set_width", "get_width");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "points"), "set_points", "get_points");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
|
||||
}
|
||||
void CurveTexture::set_max(float p_max) {
|
||||
|
||||
max = p_max;
|
||||
_max = p_max;
|
||||
emit_changed();
|
||||
}
|
||||
float CurveTexture::get_max() const {
|
||||
|
||||
return max;
|
||||
return _max;
|
||||
}
|
||||
|
||||
void CurveTexture::set_min(float p_min) {
|
||||
|
||||
min = p_min;
|
||||
_min = p_min;
|
||||
emit_changed();
|
||||
}
|
||||
float CurveTexture::get_min() const {
|
||||
|
||||
return min;
|
||||
return _min;
|
||||
}
|
||||
void CurveTexture::set_width(int p_width) {
|
||||
|
||||
ERR_FAIL_COND(p_width < 32 || p_width > 4096);
|
||||
width = p_width;
|
||||
if (points.size())
|
||||
set_points(points);
|
||||
_width = p_width;
|
||||
_update();
|
||||
}
|
||||
int CurveTexture::get_width() const {
|
||||
|
||||
return width;
|
||||
return _width;
|
||||
}
|
||||
|
||||
static void _plot_curve(const Vector2 &p_a, const Vector2 &p_b, const Vector2 &p_c, const Vector2 &p_d, float *p_heights, bool *p_useds, int p_width, float p_min, float p_max) {
|
||||
void CurveTexture::ensure_default_setup() {
|
||||
|
||||
float geometry[4][4];
|
||||
float tmp1[4][4];
|
||||
float tmp2[4][4];
|
||||
float deltas[4][4];
|
||||
double x, dx, dx2, dx3;
|
||||
double y, dy, dy2, dy3;
|
||||
double d, d2, d3;
|
||||
int lastx;
|
||||
int newx;
|
||||
float lasty;
|
||||
float newy;
|
||||
int ntimes;
|
||||
int i, j;
|
||||
|
||||
int xmax = p_width;
|
||||
|
||||
/* construct the geometry matrix from the segment */
|
||||
for (i = 0; i < 4; i++) {
|
||||
geometry[i][2] = 0;
|
||||
geometry[i][3] = 0;
|
||||
if (_curve.is_null()) {
|
||||
Ref<Curve> curve = Ref<Curve>(memnew(Curve));
|
||||
curve->add_point(Vector2(0, 1));
|
||||
curve->add_point(Vector2(1, 1));
|
||||
set_curve(curve);
|
||||
}
|
||||
|
||||
geometry[0][0] = (p_a[0] * xmax);
|
||||
geometry[1][0] = (p_b[0] * xmax);
|
||||
geometry[2][0] = (p_c[0] * xmax);
|
||||
geometry[3][0] = (p_d[0] * xmax);
|
||||
|
||||
geometry[0][1] = (p_a[1]);
|
||||
geometry[1][1] = (p_b[1]);
|
||||
geometry[2][1] = (p_c[1]);
|
||||
geometry[3][1] = (p_d[1]);
|
||||
|
||||
/* subdivide the curve ntimes (1000) times */
|
||||
ntimes = 4 * xmax;
|
||||
/* ntimes can be adjusted to give a finer or coarser curve */
|
||||
d = 1.0 / ntimes;
|
||||
d2 = d * d;
|
||||
d3 = d * d * d;
|
||||
|
||||
/* construct a temporary matrix for determining the forward differencing deltas */
|
||||
tmp2[0][0] = 0;
|
||||
tmp2[0][1] = 0;
|
||||
tmp2[0][2] = 0;
|
||||
tmp2[0][3] = 1;
|
||||
tmp2[1][0] = d3;
|
||||
tmp2[1][1] = d2;
|
||||
tmp2[1][2] = d;
|
||||
tmp2[1][3] = 0;
|
||||
tmp2[2][0] = 6 * d3;
|
||||
tmp2[2][1] = 2 * d2;
|
||||
tmp2[2][2] = 0;
|
||||
tmp2[2][3] = 0;
|
||||
tmp2[3][0] = 6 * d3;
|
||||
tmp2[3][1] = 0;
|
||||
tmp2[3][2] = 0;
|
||||
tmp2[3][3] = 0;
|
||||
|
||||
/* compose the basis and geometry matrices */
|
||||
|
||||
static const float CR_basis[4][4] = {
|
||||
{ -0.5, 1.5, -1.5, 0.5 },
|
||||
{ 1.0, -2.5, 2.0, -0.5 },
|
||||
{ -0.5, 0.0, 0.5, 0.0 },
|
||||
{ 0.0, 1.0, 0.0, 0.0 },
|
||||
};
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
tmp1[i][j] = (CR_basis[i][0] * geometry[0][j] +
|
||||
CR_basis[i][1] * geometry[1][j] +
|
||||
CR_basis[i][2] * geometry[2][j] +
|
||||
CR_basis[i][3] * geometry[3][j]);
|
||||
}
|
||||
}
|
||||
/* compose the above results to get the deltas matrix */
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
deltas[i][j] = (tmp2[i][0] * tmp1[0][j] +
|
||||
tmp2[i][1] * tmp1[1][j] +
|
||||
tmp2[i][2] * tmp1[2][j] +
|
||||
tmp2[i][3] * tmp1[3][j]);
|
||||
}
|
||||
}
|
||||
|
||||
/* extract the x deltas */
|
||||
x = deltas[0][0];
|
||||
dx = deltas[1][0];
|
||||
dx2 = deltas[2][0];
|
||||
dx3 = deltas[3][0];
|
||||
|
||||
/* extract the y deltas */
|
||||
y = deltas[0][1];
|
||||
dy = deltas[1][1];
|
||||
dy2 = deltas[2][1];
|
||||
dy3 = deltas[3][1];
|
||||
|
||||
lastx = CLAMP(x, 0, xmax);
|
||||
lasty = y;
|
||||
|
||||
p_heights[lastx] = lasty;
|
||||
p_useds[lastx] = true;
|
||||
|
||||
/* loop over the curve */
|
||||
for (i = 0; i < ntimes; i++) {
|
||||
/* increment the x values */
|
||||
x += dx;
|
||||
dx += dx2;
|
||||
dx2 += dx3;
|
||||
|
||||
/* increment the y values */
|
||||
y += dy;
|
||||
dy += dy2;
|
||||
dy2 += dy3;
|
||||
|
||||
newx = CLAMP((Math::round(x)), 0, xmax);
|
||||
newy = CLAMP(y, p_min, p_max);
|
||||
|
||||
/* if this point is different than the last one...then draw it */
|
||||
if ((lastx != newx) || (lasty != newy)) {
|
||||
p_useds[newx] = true;
|
||||
p_heights[newx] = newy;
|
||||
}
|
||||
|
||||
lastx = newx;
|
||||
lasty = newy;
|
||||
if (get_min() == 0 && get_max() == 1) {
|
||||
set_max(32);
|
||||
}
|
||||
}
|
||||
|
||||
void CurveTexture::set_points(const PoolVector<Vector2> &p_points) {
|
||||
void CurveTexture::set_curve(Ref<Curve> p_curve) {
|
||||
if (_curve != p_curve) {
|
||||
if (_curve.is_valid()) {
|
||||
_curve->disconnect(CoreStringNames::get_singleton()->changed, this, "_update");
|
||||
}
|
||||
_curve = p_curve;
|
||||
if (_curve.is_valid()) {
|
||||
_curve->connect(CoreStringNames::get_singleton()->changed, this, "_update");
|
||||
}
|
||||
_update();
|
||||
}
|
||||
}
|
||||
|
||||
points = p_points;
|
||||
void CurveTexture::_update() {
|
||||
|
||||
PoolVector<uint8_t> data;
|
||||
PoolVector<bool> used;
|
||||
data.resize(width * sizeof(float));
|
||||
used.resize(width);
|
||||
data.resize(_width * sizeof(float));
|
||||
|
||||
// The array is locked in that scope
|
||||
{
|
||||
PoolVector<uint8_t>::Write wd8 = data.write();
|
||||
float *wd = (float *)wd8.ptr();
|
||||
PoolVector<bool>::Write wu = used.write();
|
||||
int pc = p_points.size();
|
||||
PoolVector<Vector2>::Read pr = p_points.read();
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
wd[i] = 0.0;
|
||||
wu[i] = false;
|
||||
}
|
||||
|
||||
Vector2 prev = Vector2(0, 0);
|
||||
Vector2 prev2 = Vector2(0, 0);
|
||||
|
||||
for (int i = -1; i < pc; i++) {
|
||||
|
||||
Vector2 next;
|
||||
Vector2 next2;
|
||||
if (i + 1 >= pc) {
|
||||
next = Vector2(1, 0);
|
||||
} else {
|
||||
next = Vector2(pr[i + 1].x, pr[i + 1].y);
|
||||
if (_curve.is_valid()) {
|
||||
Curve &curve = **_curve;
|
||||
float height = _max - _min;
|
||||
for (int i = 0; i < _width; ++i) {
|
||||
float t = i / static_cast<float>(_width);
|
||||
float v = curve.interpolate_baked(t);
|
||||
wd[i] = CLAMP(_min + v * height, _min, _max);
|
||||
}
|
||||
|
||||
if (i + 2 >= pc) {
|
||||
next2 = Vector2(1, 0);
|
||||
} else {
|
||||
next2 = Vector2(pr[i + 2].x, pr[i + 2].y);
|
||||
} else {
|
||||
for (int i = 0; i < _width; ++i) {
|
||||
wd[i] = 0;
|
||||
}
|
||||
|
||||
/*if (i==-1 && prev.offset==next.offset) {
|
||||
prev=next;
|
||||
continue;
|
||||
}*/
|
||||
|
||||
_plot_curve(prev2, prev, next, next2, wd, wu.ptr(), width, min, max);
|
||||
|
||||
prev2 = prev;
|
||||
prev = next;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Image> image = memnew(Image(width, 1, false, Image::FORMAT_RF, data));
|
||||
Ref<Image> image = memnew(Image(_width, 1, false, Image::FORMAT_RF, data));
|
||||
|
||||
VS::get_singleton()->texture_allocate(texture, width, 1, Image::FORMAT_RF, VS::TEXTURE_FLAG_FILTER);
|
||||
VS::get_singleton()->texture_set_data(texture, image);
|
||||
VS::get_singleton()->texture_allocate(_texture, _width, 1, Image::FORMAT_RF, VS::TEXTURE_FLAG_FILTER);
|
||||
VS::get_singleton()->texture_set_data(_texture, image);
|
||||
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
PoolVector<Vector2> CurveTexture::get_points() const {
|
||||
Ref<Curve> CurveTexture::get_curve() const {
|
||||
|
||||
return points;
|
||||
return _curve;
|
||||
}
|
||||
|
||||
RID CurveTexture::get_rid() const {
|
||||
|
||||
return texture;
|
||||
return _texture;
|
||||
}
|
||||
|
||||
CurveTexture::CurveTexture() {
|
||||
|
||||
max = 1;
|
||||
min = 0;
|
||||
width = 2048;
|
||||
texture = VS::get_singleton()->texture_create();
|
||||
_max = 1;
|
||||
_min = 0;
|
||||
_width = 2048;
|
||||
_texture = VS::get_singleton()->texture_create();
|
||||
}
|
||||
CurveTexture::~CurveTexture() {
|
||||
VS::get_singleton()->free(texture);
|
||||
VS::get_singleton()->free(_texture);
|
||||
}
|
||||
//////////////////
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#ifndef TEXTURE_H
|
||||
#define TEXTURE_H
|
||||
|
||||
#include "curve.h"
|
||||
#include "io/resource_loader.h"
|
||||
#include "math_2d.h"
|
||||
#include "resource.h"
|
||||
@ -389,20 +390,22 @@ public:
|
||||
~CubeMap();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(CubeMap::Flags);
|
||||
VARIANT_ENUM_CAST(CubeMap::Side);
|
||||
VARIANT_ENUM_CAST(CubeMap::Storage);
|
||||
VARIANT_ENUM_CAST(CubeMap::Flags)
|
||||
VARIANT_ENUM_CAST(CubeMap::Side)
|
||||
VARIANT_ENUM_CAST(CubeMap::Storage)
|
||||
|
||||
class CurveTexture : public Texture {
|
||||
|
||||
GDCLASS(CurveTexture, Texture);
|
||||
RES_BASE_EXTENSION("curvetex");
|
||||
GDCLASS(CurveTexture, Texture)
|
||||
RES_BASE_EXTENSION("curvetex")
|
||||
|
||||
private:
|
||||
RID texture;
|
||||
PoolVector<Vector2> points;
|
||||
float min, max;
|
||||
int width;
|
||||
RID _texture;
|
||||
Ref<Curve> _curve;
|
||||
float _min, _max;
|
||||
int _width;
|
||||
|
||||
void _update();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
@ -417,8 +420,10 @@ public:
|
||||
void set_width(int p_width);
|
||||
int get_width() const;
|
||||
|
||||
void set_points(const PoolVector<Vector2> &p_points);
|
||||
PoolVector<Vector2> get_points() const;
|
||||
void ensure_default_setup();
|
||||
|
||||
void set_curve(Ref<Curve> p_curve);
|
||||
Ref<Curve> get_curve() const;
|
||||
|
||||
virtual RID get_rid() const;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user