Added Line2D node that draws a polygon-based line

It supports unlimited width, color gradient, texture and some geometry
options for joints and caps. Also transparency without artifacts
(provided that line joints aren't too sharp).
This commit is contained in:
Marc Gilleron 2016-12-09 02:52:40 +01:00
parent dab73c701a
commit e2fba10b95
12 changed files with 1489 additions and 0 deletions

307
scene/2d/line_2d.cpp Normal file
View file

@ -0,0 +1,307 @@
#include "line_2d.h"
#include "core_string_names.h"
// Needed so we can bind functions
VARIANT_ENUM_CAST(LineJointMode)
VARIANT_ENUM_CAST(LineCapMode)
VARIANT_ENUM_CAST(LineTextureMode)
Line2D::Line2D() : Node2D() {
_joint_mode = LINE_JOINT_SHARP;
_begin_cap_mode = LINE_CAP_NONE;
_end_cap_mode = LINE_CAP_NONE;
_width = 10;
_default_color = Color(0.4,0.5,1);
_sharp_limit = 2.f;
_round_precision = 8;
}
void Line2D::set_points(const PoolVector<Vector2> &p_points) {
_points = p_points;
update();
}
void Line2D::set_width(float width) {
if(width < 0.0)
width = 0.0;
_width = width;
update();
}
float Line2D::get_width() const {
return _width;
}
PoolVector<Vector2> Line2D::get_points() const {
return _points;
}
void Line2D::set_point_pos(int i, Vector2 pos) {
_points.set(i, pos);
update();
}
Vector2 Line2D::get_point_pos(int i) const {
return _points.get(i);
}
int Line2D::get_point_count() const {
return _points.size();
}
void Line2D::add_point(Vector2 pos) {
_points.append(pos);
update();
}
void Line2D::remove_point(int i) {
_points.remove(i);
update();
}
void Line2D::set_default_color(Color color) {
_default_color = color;
update();
}
Color Line2D::get_default_color() const {
return _default_color;
}
void Line2D::set_gradient(const Ref<ColorRamp>& gradient) {
// Cleanup previous connection if any
if(_gradient.is_valid()) {
(**_gradient).disconnect(CoreStringNames::get_singleton()->changed, this, "_gradient_changed");
}
_gradient = gradient;
// Connect to the gradient so the line will update when the ColorRamp is changed
if(_gradient.is_valid()) {
(**_gradient).connect(CoreStringNames::get_singleton()->changed, this, "_gradient_changed");
}
update();
}
Ref<ColorRamp> Line2D::get_gradient() const {
return _gradient;
}
void Line2D::set_texture(const Ref<Texture>& texture) {
_texture = texture;
update();
}
Ref<Texture> Line2D::get_texture() const {
return _texture;
}
void Line2D::set_texture_mode(const LineTextureMode mode) {
_texture_mode = mode;
update();
}
LineTextureMode Line2D::get_texture_mode() const {
return _texture_mode;
}
void Line2D::set_joint_mode(LineJointMode mode) {
_joint_mode = mode;
update();
}
LineJointMode Line2D::get_joint_mode() const {
return _joint_mode;
}
void Line2D::set_begin_cap_mode(LineCapMode mode) {
_begin_cap_mode = mode;
update();
}
LineCapMode Line2D::get_begin_cap_mode() const {
return _begin_cap_mode;
}
void Line2D::set_end_cap_mode(LineCapMode mode) {
_end_cap_mode = mode;
update();
}
LineCapMode Line2D::get_end_cap_mode() const {
return _end_cap_mode;
}
void Line2D::_notification(int p_what) {
switch(p_what) {
case NOTIFICATION_DRAW:
_draw();
break;
}
}
void Line2D::set_sharp_limit(float limit) {
if(limit < 0.f)
limit = 0.f;
_sharp_limit = limit;
update();
}
float Line2D::get_sharp_limit() const {
return _sharp_limit;
}
void Line2D::set_round_precision(int precision) {
if(precision < 1)
precision = 1;
_round_precision = precision;
update();
}
int Line2D::get_round_precision() const {
return _round_precision;
}
void Line2D::_draw() {
if(_points.size() <= 1 || _width == 0.f)
return;
// TODO Is this really needed?
// Copy points for faster access
Vector<Vector2> points;
points.resize(_points.size());
int len = points.size(); {
PoolVector<Vector2>::Read points_read = _points.read();
for(int i = 0; i < len; ++i) {
points[i] = points_read[i];
}
}
// TODO Maybe have it as member rather than copying parameters and allocating memory?
LineBuilder lb;
lb.points = points;
lb.default_color = _default_color;
lb.gradient = *_gradient;
lb.texture_mode = _texture_mode;
lb.joint_mode = _joint_mode;
lb.begin_cap_mode = _begin_cap_mode;
lb.end_cap_mode = _end_cap_mode;
lb.round_precision = _round_precision;
lb.sharp_limit = _sharp_limit;
lb.width = _width;
lb.build();
RID texture_rid;
if(_texture.is_valid())
texture_rid = (**_texture).get_rid();
VS::get_singleton()->canvas_item_add_triangle_array(
get_canvas_item(),
lb.indices,
lb.vertices,
lb.colors,
lb.uvs,
texture_rid);
// DEBUG
// Draw wireframe
// if(lb.indices.size() % 3 == 0) {
// Color col(0,0,0);
// for(int i = 0; i < lb.indices.size(); i += 3) {
// int vi = lb.indices[i];
// int lbvsize = lb.vertices.size();
// Vector2 a = lb.vertices[lb.indices[i]];
// Vector2 b = lb.vertices[lb.indices[i+1]];
// Vector2 c = lb.vertices[lb.indices[i+2]];
// draw_line(a, b, col);
// draw_line(b, c, col);
// draw_line(c, a, col);
// }
// for(int i = 0; i < lb.vertices.size(); ++i) {
// Vector2 p = lb.vertices[i];
// draw_rect(Rect2(p.x-1, p.y-1, 2, 2), Color(0,0,0,0.5));
// }
// }
}
void Line2D::_gradient_changed() {
update();
}
// static
void Line2D::_bind_methods() {
ClassDB::bind_method(_MD("set_points","points"), &Line2D::set_points);
ClassDB::bind_method(_MD("get_points"), &Line2D::get_points);
ClassDB::bind_method(_MD("set_point_pos","i", "pos"), &Line2D::set_point_pos);
ClassDB::bind_method(_MD("get_point_pos", "i"), &Line2D::get_point_pos);
ClassDB::bind_method(_MD("get_point_count"), &Line2D::get_point_count);
ClassDB::bind_method(_MD("add_point", "pos"), &Line2D::add_point);
ClassDB::bind_method(_MD("remove_point", "i"), &Line2D::remove_point);
ClassDB::bind_method(_MD("set_width","width"), &Line2D::set_width);
ClassDB::bind_method(_MD("get_width"), &Line2D::get_width);
ClassDB::bind_method(_MD("set_default_color", "color"), &Line2D::set_default_color);
ClassDB::bind_method(_MD("get_default_color"), &Line2D::get_default_color);
ClassDB::bind_method(_MD("set_gradient", "color"), &Line2D::set_gradient);
ClassDB::bind_method(_MD("get_gradient"), &Line2D::get_gradient);
ClassDB::bind_method(_MD("set_texture", "texture"), &Line2D::set_texture);
ClassDB::bind_method(_MD("get_texture"), &Line2D::get_texture);
ClassDB::bind_method(_MD("set_texture_mode", "mode"), &Line2D::set_texture_mode);
ClassDB::bind_method(_MD("get_texture_mode"), &Line2D::get_texture_mode);
ClassDB::bind_method(_MD("set_joint_mode", "mode"), &Line2D::set_joint_mode);
ClassDB::bind_method(_MD("get_joint_mode"), &Line2D::get_joint_mode);
ClassDB::bind_method(_MD("set_begin_cap_mode", "mode"), &Line2D::set_begin_cap_mode);
ClassDB::bind_method(_MD("get_begin_cap_mode"), &Line2D::get_begin_cap_mode);
ClassDB::bind_method(_MD("set_end_cap_mode", "mode"), &Line2D::set_end_cap_mode);
ClassDB::bind_method(_MD("get_end_cap_mode"), &Line2D::get_end_cap_mode);
ClassDB::bind_method(_MD("set_sharp_limit", "limit"), &Line2D::set_sharp_limit);
ClassDB::bind_method(_MD("get_sharp_limit"), &Line2D::get_sharp_limit);
ClassDB::bind_method(_MD("set_round_precision", "precision"), &Line2D::set_round_precision);
ClassDB::bind_method(_MD("get_round_precision"), &Line2D::get_round_precision);
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "points"), _SCS("set_points"), _SCS("get_points"));
ADD_PROPERTY(PropertyInfo(Variant::REAL, "width"), _SCS("set_width"), _SCS("get_width"));
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "default_color"), _SCS("set_default_color"), _SCS("get_default_color"));
ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "ColorRamp"), _SCS("set_gradient"), _SCS("get_gradient" ) );
ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), _SCS("set_texture"), _SCS("get_texture" ) );
ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "texture_mode", PROPERTY_HINT_ENUM, "None,Tile" ), _SCS("set_texture_mode"),_SCS("get_texture_mode" ) );
ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "joint_mode", PROPERTY_HINT_ENUM, "Sharp,Bevel,Round" ), _SCS("set_joint_mode"),_SCS("get_joint_mode" ) );
ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "begin_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round" ), _SCS("set_begin_cap_mode"),_SCS("get_begin_cap_mode" ) );
ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "end_cap_mode", PROPERTY_HINT_ENUM, "None,Box,Round" ), _SCS("set_end_cap_mode"),_SCS("get_end_cap_mode" ) );
ADD_PROPERTY(PropertyInfo(Variant::REAL, "sharp_limit"), _SCS("set_sharp_limit"), _SCS("get_sharp_limit"));
ADD_PROPERTY(PropertyInfo(Variant::INT, "round_precision"), _SCS("set_round_precision"), _SCS("get_round_precision"));
BIND_CONSTANT(LINE_JOINT_SHARP);
BIND_CONSTANT(LINE_JOINT_BEVEL);
BIND_CONSTANT(LINE_JOINT_ROUND);
BIND_CONSTANT(LINE_CAP_NONE);
BIND_CONSTANT(LINE_CAP_BOX);
BIND_CONSTANT(LINE_CAP_ROUND);
BIND_CONSTANT(LINE_TEXTURE_NONE);
BIND_CONSTANT(LINE_TEXTURE_TILE);
ClassDB::bind_method(_MD("_gradient_changed"), &Line2D::_gradient_changed);
}

80
scene/2d/line_2d.h Normal file
View file

@ -0,0 +1,80 @@
#ifndef LINE2D_H
#define LINE2D_H
#include "node_2d.h"
#include "line_builder.h"
class Line2D : public Node2D {
GDCLASS(Line2D, Node2D)
public:
Line2D();
void set_points(const PoolVector<Vector2> & p_points);
PoolVector<Vector2> get_points() const;
void set_point_pos(int i, Vector2 pos);
Vector2 get_point_pos(int i) const;
int get_point_count() const;
void add_point(Vector2 pos);
void remove_point(int i);
void set_width(float width);
float get_width() const;
void set_default_color(Color color);
Color get_default_color() const;
void set_gradient(const Ref<ColorRamp>& gradient);
Ref<ColorRamp> get_gradient() const;
void set_texture(const Ref<Texture>& texture);
Ref<Texture> get_texture() const;
void set_texture_mode(const LineTextureMode mode);
LineTextureMode get_texture_mode() const;
void set_joint_mode(LineJointMode mode);
LineJointMode get_joint_mode() const;
void set_begin_cap_mode(LineCapMode mode);
LineCapMode get_begin_cap_mode() const;
void set_end_cap_mode(LineCapMode mode);
LineCapMode get_end_cap_mode() const;
void set_sharp_limit(float limit);
float get_sharp_limit() const;
void set_round_precision(int precision);
int get_round_precision() const;
protected:
void _notification(int p_what);
void _draw();
static void _bind_methods();
private:
void _gradient_changed();
private:
PoolVector<Vector2> _points;
LineJointMode _joint_mode;
LineCapMode _begin_cap_mode;
LineCapMode _end_cap_mode;
float _width;
Color _default_color;
Ref<ColorRamp> _gradient;
Ref<Texture> _texture;
LineTextureMode _texture_mode;
float _sharp_limit;
int _round_precision;
};
#endif // LINE2D_H

563
scene/2d/line_builder.cpp Normal file
View file

@ -0,0 +1,563 @@
#include "line_builder.h"
//----------------------------------------------------------------------------
// Util
//----------------------------------------------------------------------------
enum SegmentIntersectionResult {
SEGMENT_PARALLEL = 0,
SEGMENT_NO_INTERSECT = 1,
SEGMENT_INTERSECT = 2
};
static SegmentIntersectionResult segment_intersection(
Vector2 a, Vector2 b, Vector2 c, Vector2 d,
Vector2 * out_intersection)
{
// http://paulbourke.net/geometry/pointlineplane/ <-- Good stuff
Vector2 cd = d - c;
Vector2 ab = b - a;
float div = cd.y*ab.x - cd.x*ab.y;
if(Math::abs(div) > 0.001f) {
float ua = (cd.x * (a.y-c.y) - cd.y * (a.x-c.x)) / div;
float ub = (ab.x * (a.y-c.y) - ab.y * (a.x-c.x)) / div;
*out_intersection = a + ua * ab;
if(ua >= 0.f && ua <= 1.f &&
ub >= 0.f && ub <= 1.f)
return SEGMENT_INTERSECT;
return SEGMENT_NO_INTERSECT;
}
return SEGMENT_PARALLEL;
}
// TODO I'm pretty sure there is an even faster way to swap things
template <typename T>
static inline void swap(T & a, T & b) {
T tmp = a;
a = b;
b = tmp;
}
static float calculate_total_distance(const Vector<Vector2> & points) {
float d = 0.f;
for(int i = 1; i < points.size(); ++i) {
d += points[i].distance_to(points[i-1]);
}
return d;
}
static inline Vector2 rotate90(const Vector2 & v) {
// Note: the 2D referential is X-right, Y-down
return Vector2(v.y, -v.x);
}
static inline Vector2 interpolate(const Rect2 & r, const Vector2 & v) {
return Vector2(
Math::lerp(r.get_pos().x, r.get_pos().x + r.get_size().x, v.x),
Math::lerp(r.get_pos().y, r.get_pos().y + r.get_size().y, v.y)
);
}
//----------------------------------------------------------------------------
// LineBuilder
//----------------------------------------------------------------------------
LineBuilder::LineBuilder() {
joint_mode = LINE_JOINT_SHARP;
width = 10;
default_color = Color(0.4,0.5,1);
gradient = NULL;
sharp_limit = 2.f;
round_precision = 8;
begin_cap_mode = LINE_CAP_NONE;
end_cap_mode = LINE_CAP_NONE;
_interpolate_color = false;
_last_index[0] = 0;
_last_index[1] = 0;
}
void LineBuilder::clear_output() {
vertices.clear();
colors.clear();
indices.clear();
}
void LineBuilder::build() {
// Need at least 2 points to draw a line
if(points.size() < 2) {
clear_output();
return;
}
const float hw = width / 2.f;
const float hw_sq = hw*hw;
const float sharp_limit_sq = sharp_limit * sharp_limit;
const int len = points.size();
// Initial values
Vector2 pos0 = points[0];
Vector2 pos1 = points[1];
Vector2 f0 = (pos1 - pos0).normalized();
Vector2 u0 = rotate90(f0);
Vector2 pos_up0 = pos0 + u0 * hw;
Vector2 pos_down0 = pos0 - u0 * hw;
Color color0;
Color color1;
float current_distance0 = 0.f;
float current_distance1 = 0.f;
float total_distance;
_interpolate_color = gradient != NULL;
bool distance_required = _interpolate_color || texture_mode == LINE_TEXTURE_TILE;
if(distance_required)
total_distance = calculate_total_distance(points);
if(_interpolate_color)
color0 = gradient->get_color(0);
else
colors.push_back(default_color);
float uvx0 = 0.f;
float uvx1 = 0.f;
// Begin cap
if(begin_cap_mode == LINE_CAP_BOX) {
// Push back first vertices a little bit
pos_up0 -= f0 * hw;
pos_down0 -= f0 * hw;
// The line's outer length will be a little higher due to begin and end caps
total_distance += width;
current_distance0 += hw;
current_distance1 = current_distance0;
}
else if(begin_cap_mode == LINE_CAP_ROUND) {
if(texture_mode == LINE_TEXTURE_TILE) {
uvx0 = 0.5f;
}
new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, 1.f, 1.f));
total_distance += width;
current_distance0 += hw;
current_distance1 = current_distance0;
}
strip_begin(pos_up0, pos_down0, color0, uvx0);
// pos_up0 ------------- pos_up1 --------------------
// | |
// pos0 - - - - - - - - - pos1 - - - - - - - - - pos2
// | |
// pos_down0 ------------ pos_down1 ------------------
//
// i-1 i i+1
// http://labs.hyperandroid.com/tag/opengl-lines
// (not the same implementation but visuals help a lot)
// For each additional segment
for(int i = 1; i < len-1; ++i) {
pos1 = points[i];
Vector2 pos2 = points[i+1];
Vector2 f1 = (pos2 - pos1).normalized();
Vector2 u1 = rotate90(f1);
// Determine joint orientation
const float dp = u0.dot(f1);
const Orientation orientation = (dp > 0.f ? UP : DOWN);
Vector2 inner_normal0, inner_normal1;
if(orientation == UP) {
inner_normal0 = u0 * hw;
inner_normal1 = u1 * hw;
}
else {
inner_normal0 = -u0 * hw;
inner_normal1 = -u1 * hw;
}
// ---------------------------
// /
// 0 / 1
// / /
// --------------------x------ /
// / / (here shown with orientation == DOWN)
// / /
// / /
// / /
// 2 /
// /
// Find inner intersection at the joint
Vector2 corner_pos_in, corner_pos_out;
SegmentIntersectionResult intersection_result = segment_intersection(
pos0 + inner_normal0, pos1 + inner_normal0,
pos1 + inner_normal1, pos2 + inner_normal1,
&corner_pos_in);
if(intersection_result == SEGMENT_INTERSECT)
// Inner parts of the segments intersect
corner_pos_out = 2.f * pos1 - corner_pos_in;
else {
// No intersection, segments are either parallel or too sharp
corner_pos_in = pos1 + inner_normal0;
corner_pos_out = pos1 - inner_normal0;
}
Vector2 corner_pos_up, corner_pos_down;
if(orientation == UP) {
corner_pos_up = corner_pos_in;
corner_pos_down = corner_pos_out;
}
else {
corner_pos_up = corner_pos_out;
corner_pos_down = corner_pos_in;
}
LineJointMode current_joint_mode = joint_mode;
Vector2 pos_up1, pos_down1;
if(intersection_result == SEGMENT_INTERSECT) {
// Fallback on bevel if sharp angle is too high (because it would produce very long miters)
if(current_joint_mode == LINE_JOINT_SHARP && corner_pos_out.distance_squared_to(pos1) / hw_sq > sharp_limit_sq) {
current_joint_mode = LINE_JOINT_BEVEL;
}
if(current_joint_mode == LINE_JOINT_SHARP) {
// In this case, we won't create joint geometry,
// The previous and next line quads will directly share an edge.
pos_up1 = corner_pos_up;
pos_down1 = corner_pos_down;
}
else {
// Bevel or round
if(orientation == UP) {
pos_up1 = corner_pos_up;
pos_down1 = pos1 - u0 * hw;
}
else {
pos_up1 = pos1 + u0 * hw;
pos_down1 = corner_pos_down;
}
}
}
else {
// No intersection: fallback
pos_up1 = corner_pos_up;
pos_down1 = corner_pos_down;
}
// Add current line body quad
// Triangles are clockwise
if(distance_required) {
current_distance1 += pos0.distance_to(pos1);
}
if(_interpolate_color) {
color1 = gradient->get_color_at_offset(current_distance1 / total_distance);
}
if(texture_mode == LINE_TEXTURE_TILE) {
uvx0 = current_distance0 / width;
uvx1 = current_distance1 / width;
}
strip_add_quad(pos_up1, pos_down1, color1, uvx1);
// Swap vars for use in the next line
color0 = color1;
u0 = u1;
f0 = f1;
pos0 = pos1;
current_distance0 = current_distance1;
if(intersection_result == SEGMENT_INTERSECT) {
if(current_joint_mode == LINE_JOINT_SHARP) {
pos_up0 = pos_up1;
pos_down0 = pos_down1;
}
else {
if(orientation == UP) {
pos_up0 = corner_pos_up;
pos_down0 = pos1 - u1 * hw;
}
else {
pos_up0 = pos1 + u1 * hw;
pos_down0 = corner_pos_down;
}
}
}
else {
pos_up0 = pos1 + u1 * hw;
pos_down0 = pos1 - u1 * hw;
}
// From this point, bu0 and bd0 concern the next segment
// Add joint geometry
if(current_joint_mode != LINE_JOINT_SHARP) {
// ________________ cbegin
// / \
// / \
// ____________/_ _ _\ cend
// | |
// | |
// | |
Vector2 cbegin, cend;
if(orientation == UP) {
cbegin = pos_down1;
cend = pos_down0;
}
else {
cbegin = pos_up1;
cend = pos_up0;
}
if(current_joint_mode == LINE_JOINT_BEVEL) {
strip_add_tri(cend, orientation);
}
else if(current_joint_mode == LINE_JOINT_ROUND) {
Vector2 vbegin = cbegin - pos1;
Vector2 vend = cend - pos1;
strip_add_arc(pos1, vend.angle_to(vbegin), orientation);
}
if(intersection_result != SEGMENT_INTERSECT)
// In this case the joint is too fucked up to be re-used,
// start again the strip with fallback points
strip_begin(pos_up0, pos_down0, color1, uvx1);
}
}
// Last (or only) segment
pos1 = points[points.size()-1];
Vector2 pos_up1 = pos1 + u0 * hw;
Vector2 pos_down1 = pos1 - u0 * hw;
// End cap (box)
if(end_cap_mode == LINE_CAP_BOX) {
pos_up1 += f0 * hw;
pos_down1 += f0 * hw;
}
if(distance_required) {
current_distance1 += pos0.distance_to(pos1);
}
if(_interpolate_color) {
color1 = gradient->get_color(gradient->get_points_count()-1);
}
if(texture_mode == LINE_TEXTURE_TILE) {
uvx1 = current_distance1 / width;
}
strip_add_quad(pos_up1, pos_down1, color1, uvx1);
// End cap (round)
if(end_cap_mode == LINE_CAP_ROUND) {
// Note: color is not used in case we don't interpolate...
Color color = _interpolate_color ? gradient->get_color(gradient->get_points_count()-1) : Color(0,0,0);
new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1-0.5f, 0.f, 1.f, 1.f));
}
}
void LineBuilder::strip_begin(Vector2 up, Vector2 down, Color color, float uvx) {
int vi = vertices.size();
vertices.push_back(up);
vertices.push_back(down);
if(_interpolate_color) {
colors.push_back(color);
colors.push_back(color);
}
if(texture_mode != LINE_TEXTURE_NONE) {
uvs.push_back(Vector2(uvx, 0.f));
uvs.push_back(Vector2(uvx, 1.f));
}
_last_index[UP] = vi;
_last_index[DOWN] = vi+1;
}
void LineBuilder::strip_new_quad(Vector2 up, Vector2 down, Color color, float uvx) {
int vi = vertices.size();
vertices.push_back(vertices[_last_index[UP]]);
vertices.push_back(vertices[_last_index[DOWN]]);
vertices.push_back(up);
vertices.push_back(down);
if(_interpolate_color) {
colors.push_back(color);
colors.push_back(color);
colors.push_back(color);
colors.push_back(color);
}
if(texture_mode != LINE_TEXTURE_NONE) {
uvs.push_back(uvs[_last_index[UP]]);
uvs.push_back(uvs[_last_index[DOWN]]);
uvs.push_back(Vector2(uvx, UP));
uvs.push_back(Vector2(uvx, DOWN));
}
indices.push_back(vi);
indices.push_back(vi+3);
indices.push_back(vi+1);
indices.push_back(vi);
indices.push_back(vi+2);
indices.push_back(vi+3);
_last_index[UP] = vi+2;
_last_index[DOWN] = vi+3;
}
void LineBuilder::strip_add_quad(Vector2 up, Vector2 down, Color color, float uvx) {
int vi = vertices.size();
vertices.push_back(up);
vertices.push_back(down);
if(_interpolate_color) {
colors.push_back(color);
colors.push_back(color);
}
if(texture_mode != LINE_TEXTURE_NONE) {
uvs.push_back(Vector2(uvx, 0.f));
uvs.push_back(Vector2(uvx, 1.f));
}
indices.push_back(_last_index[UP]);
indices.push_back(vi+1);
indices.push_back(_last_index[DOWN]);
indices.push_back(_last_index[UP]);
indices.push_back(vi);
indices.push_back(vi+1);
_last_index[UP] = vi;
_last_index[DOWN] = vi+1;
}
void LineBuilder::strip_add_tri(Vector2 up, Orientation orientation) {
int vi = vertices.size();
vertices.push_back(up);
if(_interpolate_color) {
colors.push_back(colors[colors.size()-1]);
}
Orientation opposite_orientation = orientation == UP ? DOWN : UP;
if(texture_mode != LINE_TEXTURE_NONE) {
// UVs are just one slice of the texture all along
// (otherwise we can't share the bottom vertice)
uvs.push_back(uvs[_last_index[opposite_orientation]]);
}
indices.push_back(_last_index[opposite_orientation]);
indices.push_back(vi);
indices.push_back(_last_index[orientation]);
_last_index[opposite_orientation] = vi;
}
void LineBuilder::strip_add_arc(Vector2 center, float angle_delta, Orientation orientation) {
// Take the two last vertices and extrude an arc made of triangles
// that all share one of the initial vertices
Orientation opposite_orientation = orientation == UP ? DOWN : UP;
Vector2 vbegin = vertices[_last_index[opposite_orientation]] - center;
float radius = vbegin.length();
float angle_step = Math_PI / static_cast<float>(round_precision);
float steps = Math::abs(angle_delta) / angle_step;
if(angle_delta < 0.f)
angle_step = -angle_step;
float t = vbegin.angle_to(Vector2(1, 0));
float end_angle = t + angle_delta;
Vector2 rpos(0,0);
// Arc vertices
for(int ti = 0; ti < steps; ++ti, t += angle_step) {
rpos = center + Vector2(Math::cos(t), Math::sin(t)) * radius;
strip_add_tri(rpos, orientation);
}
// Last arc vertice
rpos = center + Vector2(Math::cos(end_angle), Math::sin(end_angle)) * radius;
strip_add_tri(rpos, orientation);
}
void LineBuilder::new_arc(Vector2 center, Vector2 vbegin, float angle_delta, Color color, Rect2 uv_rect) {
// Make a standalone arc that doesn't use existing vertices,
// with undistorted UVs from withing a square section
float radius = vbegin.length();
float angle_step = Math_PI / static_cast<float>(round_precision);
float steps = Math::abs(angle_delta) / angle_step;
if(angle_delta < 0.f)
angle_step = -angle_step;
float t = vbegin.angle_to(Vector2(1, 0));
float end_angle = t + angle_delta;
Vector2 rpos(0,0);
float tt_begin = -Math_PI / 2.f;
float tt = tt_begin;
// Center vertice
int vi = vertices.size();
vertices.push_back(center);
if(_interpolate_color)
colors.push_back(color);
if(texture_mode != LINE_TEXTURE_NONE)
uvs.push_back(interpolate(uv_rect, Vector2(0.5f, 0.5f)));
// Arc vertices
for(int ti = 0; ti < steps; ++ti, t += angle_step) {
Vector2 sc = Vector2(Math::cos(t), Math::sin(t));
rpos = center + sc * radius;
vertices.push_back(rpos);
if(_interpolate_color)
colors.push_back(color);
if(texture_mode != LINE_TEXTURE_NONE) {
Vector2 tsc = Vector2(Math::cos(tt), Math::sin(tt));
uvs.push_back(interpolate(uv_rect, 0.5f*(tsc+Vector2(1.f,1.f))));
tt += angle_step;
}
}
// Last arc vertice
Vector2 sc = Vector2(Math::cos(end_angle), Math::sin(end_angle));
rpos = center + sc * radius;
vertices.push_back(rpos);
if(_interpolate_color)
colors.push_back(color);
if(texture_mode != LINE_TEXTURE_NONE) {
tt = tt_begin + angle_delta;
Vector2 tsc = Vector2(Math::cos(tt), Math::sin(tt));
uvs.push_back(interpolate(uv_rect, 0.5f*(tsc+Vector2(1.f,1.f))));
}
// Make up triangles
int vi0 = vi;
for(int ti = 0; ti < steps; ++ti) {
indices.push_back(vi0);
indices.push_back(++vi);
indices.push_back(vi+1);
}
}

76
scene/2d/line_builder.h Normal file
View file

@ -0,0 +1,76 @@
#ifndef LINE_BUILDER_H
#define LINE_BUILDER_H
#include "math_2d.h"
#include "color.h"
#include "scene/resources/color_ramp.h"
enum LineJointMode {
LINE_JOINT_SHARP = 0,
LINE_JOINT_BEVEL,
LINE_JOINT_ROUND
};
enum LineCapMode {
LINE_CAP_NONE = 0,
LINE_CAP_BOX,
LINE_CAP_ROUND
};
enum LineTextureMode {
LINE_TEXTURE_NONE = 0,
LINE_TEXTURE_TILE
// TODO STRETCH mode
};
class LineBuilder {
public:
// TODO Move in a struct and reference it
// Input
Vector<Vector2> points;
LineJointMode joint_mode;
LineCapMode begin_cap_mode;
LineCapMode end_cap_mode;
float width;
Color default_color;
ColorRamp* gradient;
LineTextureMode texture_mode;
float sharp_limit;
int round_precision;
// TODO offset_joints option (offers alternative implementation of round joints)
// TODO Move in a struct and reference it
// Output
Vector<Vector2> vertices;
Vector<Color> colors;
Vector<Vector2> uvs;
Vector<int> indices;
LineBuilder();
void build();
void clear_output();
private:
enum Orientation {
UP = 0,
DOWN = 1
};
// Triangle-strip methods
void strip_begin(Vector2 up, Vector2 down, Color color, float uvx);
void strip_new_quad(Vector2 up, Vector2 down, Color color, float uvx);
void strip_add_quad(Vector2 up, Vector2 down, Color color, float uvx);
void strip_add_tri(Vector2 up, Orientation orientation);
void strip_add_arc(Vector2 center, float angle_delta, Orientation orientation);
void new_arc(Vector2 center, Vector2 vbegin, float angle_delta, Color color, Rect2 uv_rect);
private:
bool _interpolate_color;
int _last_index[2]; // Index of last up and down vertices of the strip
};
#endif // LINE_BUILDER_H

View file

@ -120,6 +120,8 @@
#include "scene/2d/position_2d.h"
#include "scene/2d/tile_map.h"
//#include "scene/2d/tile_map.h"
#include "scene/2d/line_2d.h"
#include "scene/resources/tile_set.h"
#include "scene/animation/animation_player.h"
@ -498,6 +500,7 @@ void register_scene_types() {
ClassDB::register_class<SpriteFrames>();
ClassDB::register_class<AnimatedSprite>();
ClassDB::register_class<Position2D>();
ClassDB::register_class<Line2D>();
ClassDB::register_virtual_class<CollisionObject2D>();
ClassDB::register_virtual_class<PhysicsBody2D>();
ClassDB::register_class<StaticBody2D>();

View file

@ -27,6 +27,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "color_ramp.h"
#include "core_string_names.h"
//setter and getter names for property serialization
#define COLOR_RAMP_GET_OFFSETS "get_offsets"
@ -104,6 +105,7 @@ void ColorRamp::set_offsets(const Vector<float>& p_offsets) {
points[i].offset = p_offsets[i];
}
is_sorted = false;
emit_signal(CoreStringNames::get_singleton()->changed);
}
void ColorRamp::set_colors(const Vector<Color>& p_colors) {
@ -114,6 +116,7 @@ void ColorRamp::set_colors(const Vector<Color>& p_colors) {
{
points[i].color = p_colors[i];
}
emit_signal(CoreStringNames::get_singleton()->changed);
}
Vector<ColorRamp::Point>& ColorRamp::get_points() {
@ -128,6 +131,7 @@ void ColorRamp::add_point(float p_offset, const Color& p_color) {
is_sorted=false;
points.push_back(p);
emit_signal(CoreStringNames::get_singleton()->changed);
}
void ColorRamp::remove_point(int p_index) {
@ -135,11 +139,13 @@ void ColorRamp::remove_point(int p_index) {
ERR_FAIL_INDEX(p_index,points.size());
ERR_FAIL_COND(points.size()<=2);
points.remove(p_index);
emit_signal(CoreStringNames::get_singleton()->changed);
}
void ColorRamp::set_points(Vector<ColorRamp::Point>& p_points) {
points = p_points;
is_sorted = false;
emit_signal(CoreStringNames::get_singleton()->changed);
}
void ColorRamp::set_offset(int pos, const float offset) {
@ -147,6 +153,7 @@ void ColorRamp::set_offset(int pos, const float offset) {
points.resize(pos + 1);
points[pos].offset = offset;
is_sorted = false;
emit_signal(CoreStringNames::get_singleton()->changed);
}
float ColorRamp::get_offset(int pos) const {
@ -162,6 +169,7 @@ void ColorRamp::set_color(int pos, const Color& color) {
is_sorted = false;
}
points[pos].color = color;
emit_signal(CoreStringNames::get_singleton()->changed);
}
Color ColorRamp::get_color(int pos) const {

View file

@ -88,6 +88,7 @@
#include "plugins/script_editor_plugin.h"
#include "plugins/script_text_editor.h"
#include "plugins/path_2d_editor_plugin.h"
#include "plugins/line_2d_editor_plugin.h"
#include "plugins/particles_editor_plugin.h"
#include "plugins/particles_2d_editor_plugin.h"
#include "plugins/animation_tree_editor_plugin.h"
@ -6599,6 +6600,7 @@ EditorNode::EditorNode() {
add_editor_plugin( memnew( Path2DEditorPlugin(this) ) );
//add_editor_plugin( memnew( PathEditorPlugin(this) ) );
//add_editor_plugin( memnew( BakedLightEditorPlugin(this) ) );
add_editor_plugin( memnew( Line2DEditorPlugin(this) ) );
add_editor_plugin( memnew( Polygon2DEditorPlugin(this) ) );
add_editor_plugin( memnew( LightOccluder2DEditorPlugin(this) ) );
add_editor_plugin( memnew( NavigationPolygonEditorPlugin(this) ) );

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

View file

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 16 16"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
inkscape:export-filename="D:\PROJETS\INFO\GODOT\ENGINE\godot_fork\tools\editor\icons\2x\icon_line_2d.png"
inkscape:export-xdpi="180"
inkscape:export-ydpi="180"
sodipodi:docname="icon_line_2d.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="31.999999"
inkscape:cx="7.5587317"
inkscape:cy="9.1781644"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:snap-grids="true"
inkscape:object-nodes="true"
showguides="false">
<inkscape:grid
type="xygrid"
id="grid3336"
empspacing="4" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
<path
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#a5b7f3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529"
d="m 2,1045.3622 3,4 3,-10 3,6 3,-2"
id="path4135"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,283 @@
#include "line_2d_editor_plugin.h"
#include "canvas_item_editor_plugin.h"
#include "os/file_access.h"
#include "tools/editor/editor_settings.h"
#include "os/keyboard.h"
//----------------------------------------------------------------------------
// Line2DEditor
//----------------------------------------------------------------------------
void Line2DEditor::_node_removed(Node *p_node) {
if(p_node == node) {
node=NULL;
hide();
}
}
void Line2DEditor::_notification(int p_what) {
switch(p_what) {
case NOTIFICATION_VISIBILITY_CHANGED:
// This widget is not a child but should have the same visibility state
base_hb->set_visible(is_visible());
break;
}
}
Vector2 Line2DEditor::mouse_to_local_pos(Vector2 gpoint, bool alt) {
Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
return !alt? canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint))
: node->get_global_transform().affine_inverse().xform(
canvas_item_editor->snap_point(
canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) );
}
int Line2DEditor::get_point_index_at(Vector2 gpos) {
ERR_FAIL_COND_V(node == 0, -1);
real_t grab_treshold = EDITOR_DEF("poly_editor/point_grab_radius", 8);
Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
for(int i = 0; i < node->get_point_count(); ++i) {
Point2 p = xform.xform( node->get_point_pos(i) );
if(gpos.distance_to(p) < grab_treshold) {
return i;
}
}
return -1;
}
bool Line2DEditor::forward_input_event(const InputEvent& p_event) {
if (!node)
return false;
if (!node->is_visible())
return false;
switch(p_event.type) {
case InputEvent::MOUSE_BUTTON: {
const InputEventMouseButton &mb = p_event.mouse_button;
Vector2 gpoint = Point2(mb.x,mb.y);
Vector2 cpoint = mouse_to_local_pos(gpoint, mb.mod.alt);
if(mb.pressed && _dragging == false) {
int i = get_point_index_at(gpoint);
if(i != -1) {
if (mb.button_index == BUTTON_LEFT && !mb.mod.shift && mode == MODE_EDIT) {
_dragging = true;
action_point = i;
moving_from = node->get_point_pos(i);
moving_screen_from = gpoint;
}
else if((mb.button_index == BUTTON_RIGHT && mode == MODE_EDIT) || (mb.button_index == BUTTON_LEFT && mode == MODE_DELETE)) {
undo_redo->create_action(TTR("Remove Point from Line2D"));
undo_redo->add_do_method(node, "remove_point", i);
undo_redo->add_undo_method(node, "add_point", node->get_point_pos(i), i);
undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->commit_action();
}
}
return true;
}
if(mb.pressed && mb.button_index == BUTTON_LEFT && ((mb.mod.command && mode == MODE_EDIT) || mode == MODE_CREATE)) {
undo_redo->create_action(TTR("Add Point to Line2D"));
undo_redo->add_do_method(node, "add_point", cpoint);
undo_redo->add_undo_method(node, "remove_point", node->get_point_count());
undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->commit_action();
_dragging = true;
action_point = node->get_point_count()-1;
moving_from = node->get_point_pos(action_point);
moving_screen_from = gpoint;
canvas_item_editor->get_viewport_control()->update();
return true;
}
if(!mb.pressed && mb.button_index == BUTTON_LEFT && _dragging) {
undo_redo->create_action(TTR("Move Point in Line2D"));
undo_redo->add_do_method(node, "set_point_pos", action_point, cpoint);
undo_redo->add_undo_method(node, "set_point_pos", action_point, moving_from);
undo_redo->add_do_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->add_undo_method(canvas_item_editor->get_viewport_control(), "update");
undo_redo->commit_action();
_dragging = false;
return true;
}
}
break;
case InputEvent::MOUSE_MOTION: {
if (_dragging) {
const InputEventMouseMotion &mm = p_event.mouse_motion;
Vector2 cpoint = mouse_to_local_pos(Vector2(mm.x, mm.y), mm.mod.alt);
node->set_point_pos(action_point,cpoint);
canvas_item_editor->get_viewport_control()->update();
return true;
}
}
break;
}
return false;
}
void Line2DEditor::_canvas_draw() {
if (!node)
return;
if (!node->is_visible())
return;
Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons");
Size2 handle_size = handle->get_size();
int len = node->get_point_count();
Control *vpc = canvas_item_editor->get_viewport_control();
for(int i=0; i < len; ++i) {
Vector2 point = xform.xform(node->get_point_pos(i));
vpc->draw_texture_rect(handle, Rect2(point - handle_size * 0.5, handle_size), false);
}
}
void Line2DEditor::_node_visibility_changed() {
if (!node)
return;
canvas_item_editor->get_viewport_control()->update();
}
void Line2DEditor::edit(Node *p_line2d) {
if (!canvas_item_editor)
canvas_item_editor = CanvasItemEditor::get_singleton();
if (p_line2d) {
node = p_line2d->cast_to<Line2D>();
if (!canvas_item_editor->get_viewport_control()->is_connected("draw", this, "_canvas_draw"))
canvas_item_editor->get_viewport_control()->connect("draw", this, "_canvas_draw");
if (!node->is_connected("visibility_changed", this, "_node_visibility_changed"))
node->connect("visibility_changed", this, "_node_visibility_changed");
}
else {
if (canvas_item_editor->get_viewport_control()->is_connected("draw", this, "_canvas_draw"))
canvas_item_editor->get_viewport_control()->disconnect("draw", this, "_canvas_draw");
// node may have been deleted at this point
if (node && node->is_connected("visibility_changed", this, "_node_visibility_changed"))
node->disconnect("visibility_changed", this, "_node_visibility_changed");
node = NULL;
}
}
void Line2DEditor::_bind_methods() {
ClassDB::bind_method(_MD("_canvas_draw"), &Line2DEditor::_canvas_draw);
ClassDB::bind_method(_MD("_node_visibility_changed"), &Line2DEditor::_node_visibility_changed);
ClassDB::bind_method(_MD("_mode_selected"), &Line2DEditor::_mode_selected);
}
void Line2DEditor::_mode_selected(int p_mode) {
for(unsigned int i = 0; i < _MODE_COUNT; ++i) {
toolbar_buttons[i]->set_pressed(i == p_mode);
}
mode = Mode(p_mode);
}
Line2DEditor::Line2DEditor(EditorNode *p_editor) {
canvas_item_editor = NULL;
editor = p_editor;
undo_redo = editor->get_undo_redo();
_dragging = false;
base_hb = memnew( HBoxContainer );
CanvasItemEditor::get_singleton()->add_control_to_menu_panel(base_hb);
sep = memnew( VSeparator);
base_hb->add_child(sep);
{
ToolButton * b = memnew(ToolButton);
b->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveEdit", "EditorIcons"));
b->set_toggle_mode(true);
b->set_focus_mode(Control::FOCUS_NONE);
b->set_tooltip(
TTR("Select Points")+"\n"
+ TTR("Shift+Drag: Select Control Points")+"\n"
+ keycode_get_string(KEY_MASK_CMD)
+ TTR("Click: Add Point")+"\n"
+ TTR("Right Click: Delete Point"));
b->connect("pressed", this, "_mode_selected", varray(MODE_EDIT));
toolbar_buttons[MODE_EDIT] = b;
base_hb->add_child(b);
}
{
ToolButton * b = memnew(ToolButton);
b->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveCreate", "EditorIcons"));
b->set_toggle_mode(true);
b->set_focus_mode(Control::FOCUS_NONE);
b->set_tooltip(TTR("Add Point (in empty space)")+"\n"+TTR("Split Segment (in line)"));
b->connect("pressed", this, "_mode_selected", varray(MODE_CREATE));
toolbar_buttons[MODE_CREATE] = b;
base_hb->add_child(b);
}
{
ToolButton * b = memnew( ToolButton );
b->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveDelete", "EditorIcons"));
b->set_toggle_mode(true);
b->set_focus_mode(Control::FOCUS_NONE);
b->set_tooltip(TTR("Delete Point"));
b->connect("pressed", this, "_mode_selected", varray(MODE_DELETE));
toolbar_buttons[MODE_DELETE] = b;
base_hb->add_child(b);
}
base_hb->hide();
hide();
_mode_selected(MODE_CREATE);
}
//----------------------------------------------------------------------------
// Line2DEditorPlugin
//----------------------------------------------------------------------------
void Line2DEditorPlugin::edit(Object *p_object) {
line2d_editor->edit(p_object->cast_to<Node>());
}
bool Line2DEditorPlugin::handles(Object *p_object) const {
return p_object->is_class("Line2D");
}
void Line2DEditorPlugin::make_visible(bool p_visible) {
line2d_editor->set_visible(p_visible);
if(p_visible == false)
line2d_editor->edit(NULL);
}
Line2DEditorPlugin::Line2DEditorPlugin(EditorNode *p_node) {
editor=p_node;
line2d_editor = memnew( Line2DEditor(p_node) );
CanvasItemEditor::get_singleton()->add_control_to_menu_panel(line2d_editor);
line2d_editor->hide();
}

View file

@ -0,0 +1,85 @@
#ifndef LINE_2D_EDITOR_PLUGIN_H
#define LINE_2D_EDITOR_PLUGIN_H
#include "tools/editor/editor_plugin.h"
#include "tools/editor/editor_node.h"
#include "scene/2d/path_2d.h"
#include "scene/gui/tool_button.h"
#include "scene/gui/button_group.h"
#include "scene/2d/line_2d.h"
class CanvasItemEditor;
class Line2DEditor : public HBoxContainer {
GDCLASS(Line2DEditor, HBoxContainer)
public:
bool forward_input_event(const InputEvent& p_event);
void edit(Node *p_line2d);
Line2DEditor(EditorNode *p_editor);
protected:
void _node_removed(Node *p_node);
void _notification(int p_what);
Vector2 mouse_to_local_pos(Vector2 mpos);
static void _bind_methods();
private:
void _mode_selected(int p_mode);
void _canvas_draw();
void _node_visibility_changed();
int get_point_index_at(Vector2 gpos);
Vector2 mouse_to_local_pos(Vector2 gpos, bool alt);
UndoRedo *undo_redo;
CanvasItemEditor *canvas_item_editor;
EditorNode *editor;
Panel *panel;
Line2D *node;
HBoxContainer *base_hb;
Separator *sep;
enum Mode {
MODE_CREATE = 0,
MODE_EDIT,
MODE_DELETE,
_MODE_COUNT
};
Mode mode;
ToolButton* toolbar_buttons[_MODE_COUNT];
bool _dragging;
int action_point;
Point2 moving_from;
Point2 moving_screen_from;
};
class Line2DEditorPlugin : public EditorPlugin {
GDCLASS( Line2DEditorPlugin, EditorPlugin )
public:
virtual bool forward_canvas_input_event(const Transform2D& p_canvas_xform,const InputEvent& p_event) { return line2d_editor->forward_input_event(p_event); }
virtual String get_name() const { return "Line2D"; }
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);
Line2DEditorPlugin(EditorNode *p_node);
private:
Line2DEditor *line2d_editor;
EditorNode *editor;
};
#endif // LINE_2D_EDITOR_PLUGIN_H