mirror of
https://github.com/godotengine/godot
synced 2024-09-05 14:18:29 +00:00
Add base support for 2D meshes in Godot, including Sprite -> Mesh2D conversion.
This commit is contained in:
parent
1c77fdcc85
commit
9e3a1e5401
|
@ -68,6 +68,7 @@ thirdparty_sources = [
|
|||
"md5.cpp",
|
||||
"pcg.cpp",
|
||||
"triangulator.cpp",
|
||||
"clipper.cpp",
|
||||
]
|
||||
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
|
||||
env.add_source_files(env.core_sources, thirdparty_sources)
|
||||
|
|
|
@ -1607,6 +1607,8 @@ Variant::operator Vector3() const {
|
|||
|
||||
if (type == VECTOR3)
|
||||
return *reinterpret_cast<const Vector3 *>(_data._mem);
|
||||
else if (type == VECTOR2)
|
||||
return Vector3(reinterpret_cast<const Vector2 *>(_data._mem)->x, reinterpret_cast<const Vector2 *>(_data._mem)->y, 0.0);
|
||||
else
|
||||
return Vector3();
|
||||
}
|
||||
|
|
|
@ -450,6 +450,16 @@ void RasterizerCanvasGLES3::_draw_gui_primitive(int p_points, const Vector2 *p_v
|
|||
storage->frame.canvas_draw_commands++;
|
||||
}
|
||||
|
||||
static const GLenum gl_primitive[] = {
|
||||
GL_POINTS,
|
||||
GL_LINES,
|
||||
GL_LINE_STRIP,
|
||||
GL_LINE_LOOP,
|
||||
GL_TRIANGLES,
|
||||
GL_TRIANGLE_STRIP,
|
||||
GL_TRIANGLE_FAN
|
||||
};
|
||||
|
||||
void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip) {
|
||||
|
||||
int cc = p_item->commands.size();
|
||||
|
@ -735,6 +745,36 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
|
|||
#endif
|
||||
|
||||
} break;
|
||||
case Item::Command::TYPE_MESH: {
|
||||
|
||||
Item::CommandMesh *mesh = static_cast<Item::CommandMesh *>(c);
|
||||
_set_texture_rect_mode(false);
|
||||
|
||||
RasterizerStorageGLES3::Texture *texture = _bind_canvas_texture(mesh->texture, mesh->normal_map);
|
||||
|
||||
if (texture) {
|
||||
Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
|
||||
state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
|
||||
}
|
||||
|
||||
RasterizerStorageGLES3::Mesh *mesh_data = storage->mesh_owner.getornull(mesh->mesh);
|
||||
if (mesh_data) {
|
||||
|
||||
for (int j = 0; j < mesh_data->surfaces.size(); j++) {
|
||||
RasterizerStorageGLES3::Surface *s = mesh_data->surfaces[j];
|
||||
// materials are ignored in 2D meshes, could be added but many things (ie, lighting mode, reading from screen, etc) would break as they are not meant be set up at this point of drawing
|
||||
glBindVertexArray(s->array_id);
|
||||
|
||||
if (s->index_array_len) {
|
||||
glDrawElements(gl_primitive[s->primitive], s->index_array_len, (s->array_len >= (1 << 16)) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0);
|
||||
} else {
|
||||
glDrawArrays(gl_primitive[s->primitive], 0, s->array_len);
|
||||
}
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case Item::Command::TYPE_PARTICLES: {
|
||||
|
||||
Item::CommandParticles *particles_cmd = static_cast<Item::CommandParticles *>(c);
|
||||
|
|
|
@ -100,6 +100,7 @@
|
|||
#include "editor/plugins/shader_editor_plugin.h"
|
||||
#include "editor/plugins/shader_graph_editor_plugin.h"
|
||||
#include "editor/plugins/spatial_editor_plugin.h"
|
||||
#include "editor/plugins/sprite_editor_plugin.h"
|
||||
#include "editor/plugins/sprite_frames_editor_plugin.h"
|
||||
#include "editor/plugins/style_box_editor_plugin.h"
|
||||
#include "editor/plugins/texture_editor_plugin.h"
|
||||
|
@ -5671,6 +5672,7 @@ EditorNode::EditorNode() {
|
|||
add_editor_plugin(memnew(AnimationTreeEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(MeshLibraryEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(StyleBoxEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(SpriteEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(ParticlesEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(ResourcePreloaderEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(ItemListEditorPlugin(this)));
|
||||
|
@ -5693,7 +5695,7 @@ EditorNode::EditorNode() {
|
|||
add_editor_plugin(memnew(CollisionShape2DEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(CurveEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(TextureEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(MeshEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
|
||||
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
|
||||
add_editor_plugin(memnew(NavigationMeshEditorPlugin(this)));
|
||||
|
||||
|
|
396
editor/plugins/sprite_editor_plugin.cpp
Normal file
396
editor/plugins/sprite_editor_plugin.cpp
Normal file
|
@ -0,0 +1,396 @@
|
|||
#include "sprite_editor_plugin.h"
|
||||
|
||||
#include "canvas_item_editor_plugin.h"
|
||||
#include "scene/2d/mesh_instance_2d.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "thirdparty/misc/clipper.hpp"
|
||||
|
||||
void SpriteEditor::_node_removed(Node *p_node) {
|
||||
|
||||
if (p_node == node) {
|
||||
node = NULL;
|
||||
options->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteEditor::edit(Sprite *p_sprite) {
|
||||
|
||||
node = p_sprite;
|
||||
}
|
||||
|
||||
#define PRECISION 10.0
|
||||
|
||||
Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float epsilon = 2.0) {
|
||||
int size = points.size();
|
||||
ERR_FAIL_COND_V(size < 2, Vector<Vector2>());
|
||||
|
||||
ClipperLib::Path subj;
|
||||
ClipperLib::PolyTree solution;
|
||||
ClipperLib::PolyTree out;
|
||||
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
|
||||
subj << ClipperLib::IntPoint(points[i].x * PRECISION, points[i].y * PRECISION);
|
||||
}
|
||||
ClipperLib::ClipperOffset co;
|
||||
co.AddPath(subj, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
||||
co.Execute(solution, epsilon * PRECISION);
|
||||
|
||||
ClipperLib::PolyNode *p = solution.GetFirst();
|
||||
|
||||
ERR_FAIL_COND_V(!p, points);
|
||||
|
||||
while (p->IsHole()) {
|
||||
p = p->GetNext();
|
||||
}
|
||||
|
||||
//turn the result into simply polygon (AKA, fix overlap)
|
||||
|
||||
//clamp into the specified rect
|
||||
ClipperLib::Clipper cl;
|
||||
cl.StrictlySimple(true);
|
||||
cl.AddPath(p->Contour, ClipperLib::ptSubject, true);
|
||||
//create the clipping rect
|
||||
ClipperLib::Path clamp;
|
||||
clamp.push_back(ClipperLib::IntPoint(0, 0));
|
||||
clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, 0));
|
||||
clamp.push_back(ClipperLib::IntPoint(rect.size.width * PRECISION, rect.size.height * PRECISION));
|
||||
clamp.push_back(ClipperLib::IntPoint(0, rect.size.height * PRECISION));
|
||||
cl.AddPath(clamp, ClipperLib::ptClip, true);
|
||||
cl.Execute(ClipperLib::ctIntersection, out);
|
||||
|
||||
Vector<Vector2> outPoints;
|
||||
ClipperLib::PolyNode *p2 = out.GetFirst();
|
||||
while (p2->IsHole()) {
|
||||
p2 = p2->GetNext();
|
||||
}
|
||||
|
||||
int lasti = p2->Contour.size() - 1;
|
||||
Vector2 prev = Vector2(p2->Contour[lasti].X / PRECISION, p2->Contour[lasti].Y / PRECISION);
|
||||
for (int i = 0; i < p2->Contour.size(); i++) {
|
||||
|
||||
Vector2 cur = Vector2(p2->Contour[i].X / PRECISION, p2->Contour[i].Y / PRECISION);
|
||||
if (cur.distance_to(prev) > 0.5) {
|
||||
outPoints.push_back(cur);
|
||||
prev = cur;
|
||||
}
|
||||
}
|
||||
return outPoints;
|
||||
}
|
||||
|
||||
void SpriteEditor::_menu_option(int p_option) {
|
||||
|
||||
switch (p_option) {
|
||||
case MENU_OPTION_CREATE_MESH_2D: {
|
||||
|
||||
_update_mesh_data();
|
||||
debug_uv_dialog->popup_centered();
|
||||
debug_uv->update();
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void SpriteEditor::_update_mesh_data() {
|
||||
|
||||
Ref<Texture> texture = node->get_texture();
|
||||
if (texture.is_null()) {
|
||||
err_dialog->set_text(TTR("Sprite is empty!"));
|
||||
err_dialog->popup_centered_minsize();
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->get_hframes() > 1 || node->get_vframes() > 1) {
|
||||
err_dialog->set_text(TTR("Can't convert a sprite using animation frames to mesh."));
|
||||
err_dialog->popup_centered_minsize();
|
||||
return;
|
||||
}
|
||||
Ref<Image> image = texture->get_data();
|
||||
ERR_FAIL_COND(image.is_null());
|
||||
Rect2 rect;
|
||||
if (node->is_region())
|
||||
rect = node->get_region_rect();
|
||||
else
|
||||
rect.size = Size2(image->get_width(), image->get_height());
|
||||
|
||||
Ref<BitMap> bm;
|
||||
bm.instance();
|
||||
bm->create_from_image_alpha(image);
|
||||
|
||||
int grow = island_merging->get_value();
|
||||
if (grow > 0) {
|
||||
bm->grow_mask(grow, rect);
|
||||
}
|
||||
|
||||
float epsilon = simplification->get_value();
|
||||
|
||||
Vector<Vector<Vector2> > lines = bm->clip_opaque_to_polygons(rect, epsilon);
|
||||
|
||||
print_line("lines: " + itos(lines.size()));
|
||||
uv_lines.clear();
|
||||
|
||||
computed_vertices.clear();
|
||||
computed_indices.clear();
|
||||
|
||||
Size2 img_size = Vector2(image->get_width(), image->get_height());
|
||||
for (int j = 0; j < lines.size(); j++) {
|
||||
lines[j] = expand(lines[j], rect, epsilon);
|
||||
|
||||
int index_ofs = computed_vertices.size();
|
||||
|
||||
for (int i = 0; i < lines[j].size(); i++) {
|
||||
Vector2 vtx = lines[j][i];
|
||||
computed_uv.push_back(vtx / img_size);
|
||||
|
||||
vtx -= rect.position; //offset by rect position
|
||||
|
||||
//flip if flipped
|
||||
if (node->is_flipped_h())
|
||||
vtx.x = rect.size.x - vtx.x - 1.0;
|
||||
if (node->is_flipped_v())
|
||||
vtx.y = rect.size.y - vtx.y - 1.0;
|
||||
|
||||
if (node->is_centered())
|
||||
vtx -= rect.size / 2.0;
|
||||
|
||||
computed_vertices.push_back(vtx);
|
||||
}
|
||||
#if 0
|
||||
Vector<Vector<Vector2> > polys = Geometry::decompose_polygon(lines[j]);
|
||||
print_line("polygon: " + itos(polys.size()));
|
||||
|
||||
for (int i = 0; i < polys.size(); i++) {
|
||||
for (int k = 0; k < polys[i].size(); k++) {
|
||||
|
||||
int idxn = (k + 1) % polys[i].size();
|
||||
uv_lines.push_back(polys[i][k]);
|
||||
uv_lines.push_back(polys[i][idxn]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
|
||||
Vector<int> poly = Geometry::triangulate_polygon(lines[j]);
|
||||
|
||||
for (int i = 0; i < poly.size(); i += 3) {
|
||||
for (int k = 0; k < 3; k++) {
|
||||
int idx = i + k;
|
||||
int idxn = i + (k + 1) % 3;
|
||||
uv_lines.push_back(lines[j][poly[idx]]);
|
||||
uv_lines.push_back(lines[j][poly[idxn]]);
|
||||
|
||||
computed_indices.push_back(poly[idx] + index_ofs);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
for (int i = 0; i < lines[j].size() - 1; i++) {
|
||||
uv_lines.push_back(lines[j][i]);
|
||||
uv_lines.push_back(lines[j][i + 1]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
debug_uv->update();
|
||||
}
|
||||
|
||||
void SpriteEditor::_create_mesh_node() {
|
||||
|
||||
if (computed_vertices.size() < 3) {
|
||||
err_dialog->set_text(TTR("Invalid geometry, can't replace by mesh."));
|
||||
err_dialog->popup_centered_minsize();
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<ArrayMesh> mesh;
|
||||
mesh.instance();
|
||||
|
||||
Array a;
|
||||
a.resize(Mesh::ARRAY_MAX);
|
||||
a[Mesh::ARRAY_VERTEX] = computed_vertices;
|
||||
a[Mesh::ARRAY_TEX_UV] = computed_uv;
|
||||
a[Mesh::ARRAY_INDEX] = computed_indices;
|
||||
|
||||
mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Mesh::ARRAY_FLAG_USE_2D_VERTICES);
|
||||
|
||||
MeshInstance2D *mesh_instance = memnew(MeshInstance2D);
|
||||
mesh_instance->set_mesh(mesh);
|
||||
EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, mesh_instance);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void SpriteEditor::_create_uv_lines() {
|
||||
|
||||
Ref<Mesh> sprite = node->get_sprite();
|
||||
ERR_FAIL_COND(!sprite.is_valid());
|
||||
|
||||
Set<SpriteEditorEdgeSort> edges;
|
||||
uv_lines.clear();
|
||||
for (int i = 0; i < sprite->get_surface_count(); i++) {
|
||||
if (sprite->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES)
|
||||
continue;
|
||||
Array a = sprite->surface_get_arrays(i);
|
||||
|
||||
PoolVector<Vector2> uv = a[p_layer == 0 ? Mesh::ARRAY_TEX_UV : Mesh::ARRAY_TEX_UV2];
|
||||
if (uv.size() == 0) {
|
||||
err_dialog->set_text(TTR("Model has no UV in this layer"));
|
||||
err_dialog->popup_centered_minsize();
|
||||
return;
|
||||
}
|
||||
|
||||
PoolVector<Vector2>::Read r = uv.read();
|
||||
|
||||
PoolVector<int> indices = a[Mesh::ARRAY_INDEX];
|
||||
PoolVector<int>::Read ri;
|
||||
|
||||
int ic;
|
||||
bool use_indices;
|
||||
|
||||
if (indices.size()) {
|
||||
ic = indices.size();
|
||||
ri = indices.read();
|
||||
use_indices = true;
|
||||
} else {
|
||||
ic = uv.size();
|
||||
use_indices = false;
|
||||
}
|
||||
|
||||
for (int j = 0; j < ic; j += 3) {
|
||||
|
||||
for (int k = 0; k < 3; k++) {
|
||||
|
||||
SpriteEditorEdgeSort edge;
|
||||
if (use_indices) {
|
||||
edge.a = r[ri[j + k]];
|
||||
edge.b = r[ri[j + ((k + 1) % 3)]];
|
||||
} else {
|
||||
edge.a = r[j + k];
|
||||
edge.b = r[j + ((k + 1) % 3)];
|
||||
}
|
||||
|
||||
if (edges.has(edge))
|
||||
continue;
|
||||
|
||||
uv_lines.push_back(edge.a);
|
||||
uv_lines.push_back(edge.b);
|
||||
edges.insert(edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug_uv_dialog->popup_centered_minsize();
|
||||
}
|
||||
#endif
|
||||
void SpriteEditor::_debug_uv_draw() {
|
||||
|
||||
if (uv_lines.size() == 0)
|
||||
return;
|
||||
|
||||
Ref<Texture> tex = node->get_texture();
|
||||
ERR_FAIL_COND(!tex.is_valid());
|
||||
debug_uv->set_clip_contents(true);
|
||||
debug_uv->draw_texture(tex, Point2());
|
||||
debug_uv->set_custom_minimum_size(tex->get_size());
|
||||
//debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size());
|
||||
debug_uv->draw_multiline(uv_lines, Color(1.0, 0.8, 0.7));
|
||||
}
|
||||
|
||||
void SpriteEditor::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method("_menu_option", &SpriteEditor::_menu_option);
|
||||
ClassDB::bind_method("_debug_uv_draw", &SpriteEditor::_debug_uv_draw);
|
||||
ClassDB::bind_method("_update_mesh_data", &SpriteEditor::_update_mesh_data);
|
||||
ClassDB::bind_method("_create_mesh_node", &SpriteEditor::_create_mesh_node);
|
||||
}
|
||||
|
||||
SpriteEditor::SpriteEditor() {
|
||||
|
||||
options = memnew(MenuButton);
|
||||
|
||||
CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options);
|
||||
|
||||
options->set_text(TTR("Sprite"));
|
||||
options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Sprite", "EditorIcons"));
|
||||
|
||||
options->get_popup()->add_item(TTR("Convert to 2D Mesh"), MENU_OPTION_CREATE_MESH_2D);
|
||||
|
||||
options->get_popup()->connect("id_pressed", this, "_menu_option");
|
||||
|
||||
err_dialog = memnew(AcceptDialog);
|
||||
add_child(err_dialog);
|
||||
|
||||
debug_uv_dialog = memnew(ConfirmationDialog);
|
||||
debug_uv_dialog->get_ok()->set_text(TTR("Create 2D Mesh"));
|
||||
debug_uv_dialog->set_title("Mesh 2D Preview");
|
||||
VBoxContainer *vb = memnew(VBoxContainer);
|
||||
debug_uv_dialog->add_child(vb);
|
||||
ScrollContainer *scroll = memnew(ScrollContainer);
|
||||
scroll->set_custom_minimum_size(Size2(800, 500) * EDSCALE);
|
||||
scroll->set_enable_h_scroll(true);
|
||||
scroll->set_enable_v_scroll(true);
|
||||
vb->add_margin_child(TTR("Preview:"), scroll, true);
|
||||
debug_uv = memnew(Control);
|
||||
debug_uv->connect("draw", this, "_debug_uv_draw");
|
||||
scroll->add_child(debug_uv);
|
||||
debug_uv_dialog->connect("confirmed", this, "_create_mesh_node");
|
||||
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
hb->add_child(memnew(Label(TTR("Simplification: "))));
|
||||
simplification = memnew(SpinBox);
|
||||
simplification->set_min(0.01);
|
||||
simplification->set_max(10.00);
|
||||
simplification->set_step(0.01);
|
||||
simplification->set_value(2);
|
||||
hb->add_child(simplification);
|
||||
hb->add_spacer();
|
||||
hb->add_child(memnew(Label(TTR("Grow (Pixels): "))));
|
||||
island_merging = memnew(SpinBox);
|
||||
island_merging->set_min(0);
|
||||
island_merging->set_max(10);
|
||||
island_merging->set_step(1);
|
||||
island_merging->set_value(2);
|
||||
hb->add_child(island_merging);
|
||||
hb->add_spacer();
|
||||
update_preview = memnew(Button);
|
||||
update_preview->set_text(TTR("Update Preview"));
|
||||
update_preview->connect("pressed", this, "_update_mesh_data");
|
||||
hb->add_child(update_preview);
|
||||
vb->add_margin_child(TTR("Settings:"), hb);
|
||||
|
||||
add_child(debug_uv_dialog);
|
||||
}
|
||||
|
||||
void SpriteEditorPlugin::edit(Object *p_object) {
|
||||
|
||||
sprite_editor->edit(Object::cast_to<Sprite>(p_object));
|
||||
}
|
||||
|
||||
bool SpriteEditorPlugin::handles(Object *p_object) const {
|
||||
|
||||
return p_object->is_class("Sprite");
|
||||
}
|
||||
|
||||
void SpriteEditorPlugin::make_visible(bool p_visible) {
|
||||
|
||||
if (p_visible) {
|
||||
sprite_editor->options->show();
|
||||
} else {
|
||||
|
||||
sprite_editor->options->hide();
|
||||
sprite_editor->edit(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
SpriteEditorPlugin::SpriteEditorPlugin(EditorNode *p_node) {
|
||||
|
||||
editor = p_node;
|
||||
sprite_editor = memnew(SpriteEditor);
|
||||
editor->get_viewport()->add_child(sprite_editor);
|
||||
|
||||
//sprite_editor->options->hide();
|
||||
}
|
||||
|
||||
SpriteEditorPlugin::~SpriteEditorPlugin() {
|
||||
}
|
73
editor/plugins/sprite_editor_plugin.h
Normal file
73
editor/plugins/sprite_editor_plugin.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#ifndef SPRITE_EDITOR_PLUGIN_H
|
||||
#define SPRITE_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_plugin.h"
|
||||
#include "scene/2d/sprite.h"
|
||||
#include "scene/gui/spin_box.h"
|
||||
|
||||
class SpriteEditor : public Control {
|
||||
|
||||
GDCLASS(SpriteEditor, Control);
|
||||
|
||||
enum Menu {
|
||||
MENU_OPTION_CREATE_MESH_2D,
|
||||
};
|
||||
|
||||
Sprite *node;
|
||||
|
||||
MenuButton *options;
|
||||
|
||||
ConfirmationDialog *outline_dialog;
|
||||
|
||||
AcceptDialog *err_dialog;
|
||||
|
||||
ConfirmationDialog *debug_uv_dialog;
|
||||
Control *debug_uv;
|
||||
Vector<Vector2> uv_lines;
|
||||
|
||||
Vector<Vector2> computed_vertices;
|
||||
Vector<Vector2> computed_uv;
|
||||
Vector<int> computed_indices;
|
||||
|
||||
SpinBox *simplification;
|
||||
SpinBox *island_merging;
|
||||
Button *update_preview;
|
||||
|
||||
void _menu_option(int p_option);
|
||||
|
||||
//void _create_uv_lines();
|
||||
friend class SpriteEditorPlugin;
|
||||
|
||||
void _debug_uv_draw();
|
||||
void _update_mesh_data();
|
||||
void _create_mesh_node();
|
||||
|
||||
protected:
|
||||
void _node_removed(Node *p_node);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void edit(Sprite *p_sprite);
|
||||
SpriteEditor();
|
||||
};
|
||||
|
||||
class SpriteEditorPlugin : public EditorPlugin {
|
||||
|
||||
GDCLASS(SpriteEditorPlugin, EditorPlugin);
|
||||
|
||||
SpriteEditor *sprite_editor;
|
||||
EditorNode *editor;
|
||||
|
||||
public:
|
||||
virtual String get_name() const { return "Sprite"; }
|
||||
bool has_main_screen() const { return false; }
|
||||
virtual void edit(Object *p_object);
|
||||
virtual bool handles(Object *p_object) const;
|
||||
virtual void make_visible(bool p_visible);
|
||||
|
||||
SpriteEditorPlugin(EditorNode *p_node);
|
||||
~SpriteEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // SPRITE_EDITOR_PLUGIN_H
|
|
@ -1402,73 +1402,80 @@ void SceneTreeDock::_create() {
|
|||
Node *newnode = Object::cast_to<Node>(c);
|
||||
ERR_FAIL_COND(!newnode);
|
||||
|
||||
List<PropertyInfo> pinfo;
|
||||
n->get_property_list(&pinfo);
|
||||
|
||||
for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
|
||||
if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
|
||||
continue;
|
||||
if (E->get().name == "__meta__")
|
||||
continue;
|
||||
newnode->set(E->get().name, n->get(E->get().name));
|
||||
}
|
||||
|
||||
editor->push_item(NULL);
|
||||
|
||||
//reconnect signals
|
||||
List<MethodInfo> sl;
|
||||
|
||||
n->get_signal_list(&sl);
|
||||
for (List<MethodInfo>::Element *E = sl.front(); E; E = E->next()) {
|
||||
|
||||
List<Object::Connection> cl;
|
||||
n->get_signal_connection_list(E->get().name, &cl);
|
||||
|
||||
for (List<Object::Connection>::Element *F = cl.front(); F; F = F->next()) {
|
||||
|
||||
Object::Connection &c = F->get();
|
||||
if (!(c.flags & Object::CONNECT_PERSIST))
|
||||
continue;
|
||||
newnode->connect(c.signal, c.target, c.method, varray(), Object::CONNECT_PERSIST);
|
||||
}
|
||||
}
|
||||
|
||||
String newname = n->get_name();
|
||||
|
||||
List<Node *> to_erase;
|
||||
for (int i = 0; i < n->get_child_count(); i++) {
|
||||
if (n->get_child(i)->get_owner() == NULL && n->is_owned_by_parent()) {
|
||||
to_erase.push_back(n->get_child(i));
|
||||
}
|
||||
}
|
||||
n->replace_by(newnode, true);
|
||||
|
||||
if (n == edited_scene) {
|
||||
edited_scene = newnode;
|
||||
editor->set_edited_scene(newnode);
|
||||
newnode->set_editable_instances(n->get_editable_instances());
|
||||
}
|
||||
|
||||
//small hack to make collisionshapes and other kind of nodes to work
|
||||
for (int i = 0; i < newnode->get_child_count(); i++) {
|
||||
Node *c = newnode->get_child(i);
|
||||
c->call("set_transform", c->call("get_transform"));
|
||||
}
|
||||
editor_data->get_undo_redo().clear_history();
|
||||
newnode->set_name(newname);
|
||||
|
||||
editor->push_item(newnode);
|
||||
|
||||
memdelete(n);
|
||||
|
||||
while (to_erase.front()) {
|
||||
memdelete(to_erase.front()->get());
|
||||
to_erase.pop_front();
|
||||
}
|
||||
replace_node(n, newnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node) {
|
||||
|
||||
Node *n = p_node;
|
||||
Node *newnode = p_by_node;
|
||||
List<PropertyInfo> pinfo;
|
||||
n->get_property_list(&pinfo);
|
||||
|
||||
for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
|
||||
if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
|
||||
continue;
|
||||
if (E->get().name == "__meta__")
|
||||
continue;
|
||||
newnode->set(E->get().name, n->get(E->get().name));
|
||||
}
|
||||
|
||||
editor->push_item(NULL);
|
||||
|
||||
//reconnect signals
|
||||
List<MethodInfo> sl;
|
||||
|
||||
n->get_signal_list(&sl);
|
||||
for (List<MethodInfo>::Element *E = sl.front(); E; E = E->next()) {
|
||||
|
||||
List<Object::Connection> cl;
|
||||
n->get_signal_connection_list(E->get().name, &cl);
|
||||
|
||||
for (List<Object::Connection>::Element *F = cl.front(); F; F = F->next()) {
|
||||
|
||||
Object::Connection &c = F->get();
|
||||
if (!(c.flags & Object::CONNECT_PERSIST))
|
||||
continue;
|
||||
newnode->connect(c.signal, c.target, c.method, varray(), Object::CONNECT_PERSIST);
|
||||
}
|
||||
}
|
||||
|
||||
String newname = n->get_name();
|
||||
|
||||
List<Node *> to_erase;
|
||||
for (int i = 0; i < n->get_child_count(); i++) {
|
||||
if (n->get_child(i)->get_owner() == NULL && n->is_owned_by_parent()) {
|
||||
to_erase.push_back(n->get_child(i));
|
||||
}
|
||||
}
|
||||
n->replace_by(newnode, true);
|
||||
|
||||
if (n == edited_scene) {
|
||||
edited_scene = newnode;
|
||||
editor->set_edited_scene(newnode);
|
||||
newnode->set_editable_instances(n->get_editable_instances());
|
||||
}
|
||||
|
||||
//small hack to make collisionshapes and other kind of nodes to work
|
||||
for (int i = 0; i < newnode->get_child_count(); i++) {
|
||||
Node *c = newnode->get_child(i);
|
||||
c->call("set_transform", c->call("get_transform"));
|
||||
}
|
||||
editor_data->get_undo_redo().clear_history();
|
||||
newnode->set_name(newname);
|
||||
|
||||
editor->push_item(newnode);
|
||||
|
||||
memdelete(n);
|
||||
|
||||
while (to_erase.front()) {
|
||||
memdelete(to_erase.front()->get());
|
||||
to_erase.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeDock::set_edited_scene(Node *p_scene) {
|
||||
|
||||
edited_scene = p_scene;
|
||||
|
|
|
@ -208,6 +208,8 @@ public:
|
|||
void show_tab_buttons();
|
||||
void hide_tab_buttons();
|
||||
|
||||
void replace_node(Node *p_node, Node *p_by_node);
|
||||
|
||||
void open_script_dialog(Node *p_for_node);
|
||||
SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data);
|
||||
};
|
||||
|
|
|
@ -684,7 +684,7 @@ void CanvasItem::draw_texture(const Ref<Texture> &p_texture, const Point2 &p_pos
|
|||
|
||||
ERR_FAIL_COND(p_texture.is_null());
|
||||
|
||||
p_texture->draw(canvas_item, p_pos, p_modulate);
|
||||
p_texture->draw(canvas_item, p_pos, p_modulate, false, p_normal_map);
|
||||
}
|
||||
|
||||
void CanvasItem::draw_texture_rect(const Ref<Texture> &p_texture, const Rect2 &p_rect, bool p_tile, const Color &p_modulate, bool p_transpose, const Ref<Texture> &p_normal_map) {
|
||||
|
@ -779,6 +779,22 @@ void CanvasItem::draw_colored_polygon(const Vector<Point2> &p_points, const Colo
|
|||
VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item, p_points, colors, p_uvs, rid, rid_normal, p_antialiased);
|
||||
}
|
||||
|
||||
void CanvasItem::draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, RID p_skeleton) {
|
||||
|
||||
ERR_FAIL_COND(p_mesh.is_null());
|
||||
RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
|
||||
RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
|
||||
|
||||
VisualServer::get_singleton()->canvas_item_add_mesh(canvas_item, p_mesh->get_rid(), texture_rid, normal_map_rid, p_skeleton);
|
||||
}
|
||||
void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map) {
|
||||
|
||||
ERR_FAIL_COND(p_multimesh.is_null());
|
||||
RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
|
||||
RID normal_map_rid = p_normal_map.is_valid() ? p_normal_map->get_rid() : RID();
|
||||
VisualServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid, normal_map_rid);
|
||||
}
|
||||
|
||||
void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w) {
|
||||
|
||||
if (!drawing) {
|
||||
|
@ -1016,6 +1032,8 @@ void CanvasItem::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture", "normal_map", "antialiased"), &CanvasItem::draw_colored_polygon, DEFVAL(PoolVector2Array()), DEFVAL(Variant()), DEFVAL(Variant()), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("draw_string", "font", "position", "text", "modulate", "clip_w"), &CanvasItem::draw_string, DEFVAL(Color(1, 1, 1)), DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("draw_char", "font", "position", "char", "next", "modulate"), &CanvasItem::draw_char, DEFVAL(Color(1, 1, 1)));
|
||||
ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "normal_map", "skeleton"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>()), DEFVAL(RID()));
|
||||
ClassDB::bind_method(D_METHOD("draw_multimesh", "mesh", "texture", "normal_map"), &CanvasItem::draw_mesh, DEFVAL(Ref<Texture>()));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform);
|
||||
ClassDB::bind_method(D_METHOD("draw_set_transform_matrix", "xform"), &CanvasItem::draw_set_transform_matrix);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "scene/main/node.h"
|
||||
#include "scene/main/scene_tree.h"
|
||||
#include "scene/resources/material.h"
|
||||
#include "scene/resources/multimesh.h"
|
||||
#include "scene/resources/shader.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
|
@ -282,6 +283,9 @@ public:
|
|||
void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false);
|
||||
void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture> p_texture = Ref<Texture>(), const Ref<Texture> &p_normal_map = Ref<Texture>(), bool p_antialiased = false);
|
||||
|
||||
void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map, RID p_skeleton);
|
||||
void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture> &p_texture, const Ref<Texture> &p_normal_map);
|
||||
|
||||
void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1);
|
||||
float draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", const Color &p_modulate = Color(1, 1, 1));
|
||||
|
||||
|
|
76
scene/2d/mesh_instance_2d.cpp
Normal file
76
scene/2d/mesh_instance_2d.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include "mesh_instance_2d.h"
|
||||
|
||||
void MeshInstance2D::_notification(int p_what) {
|
||||
|
||||
if (p_what == NOTIFICATION_DRAW) {
|
||||
if (mesh.is_valid()) {
|
||||
draw_mesh(mesh, texture, normal_map, RID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MeshInstance2D::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &MeshInstance2D::set_mesh);
|
||||
ClassDB::bind_method(D_METHOD("get_mesh"), &MeshInstance2D::get_mesh);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &MeshInstance2D::set_texture);
|
||||
ClassDB::bind_method(D_METHOD("get_texture"), &MeshInstance2D::get_texture);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_normal_map", "normal_map"), &MeshInstance2D::set_normal_map);
|
||||
ClassDB::bind_method(D_METHOD("get_normal_map"), &MeshInstance2D::get_normal_map);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
|
||||
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture");
|
||||
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "normal_map", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_map", "get_normal_map");
|
||||
}
|
||||
|
||||
void MeshInstance2D::set_mesh(const Ref<Mesh> &p_mesh) {
|
||||
|
||||
mesh = p_mesh;
|
||||
update();
|
||||
}
|
||||
|
||||
Ref<Mesh> MeshInstance2D::get_mesh() const {
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void MeshInstance2D::set_texture(const Ref<Texture> &p_texture) {
|
||||
|
||||
if (p_texture == texture)
|
||||
return;
|
||||
texture = p_texture;
|
||||
update();
|
||||
emit_signal("texture_changed");
|
||||
_change_notify("texture");
|
||||
}
|
||||
|
||||
void MeshInstance2D::set_normal_map(const Ref<Texture> &p_texture) {
|
||||
|
||||
normal_map = p_texture;
|
||||
update();
|
||||
}
|
||||
|
||||
Ref<Texture> MeshInstance2D::get_normal_map() const {
|
||||
|
||||
return normal_map;
|
||||
}
|
||||
|
||||
Ref<Texture> MeshInstance2D::get_texture() const {
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
Rect2 MeshInstance2D::_edit_get_rect() const {
|
||||
|
||||
if (mesh.is_valid()) {
|
||||
AABB aabb = mesh->get_aabb();
|
||||
return Rect2(aabb.position.x, aabb.position.y, aabb.size.x, aabb.size.y);
|
||||
}
|
||||
|
||||
return Node2D::_edit_get_rect();
|
||||
}
|
||||
|
||||
MeshInstance2D::MeshInstance2D() {
|
||||
}
|
33
scene/2d/mesh_instance_2d.h
Normal file
33
scene/2d/mesh_instance_2d.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#ifndef MESH_INSTANCE_2D_H
|
||||
#define MESH_INSTANCE_2D_H
|
||||
|
||||
#include "scene/2d/node_2d.h"
|
||||
|
||||
class MeshInstance2D : public Node2D {
|
||||
GDCLASS(MeshInstance2D, Node2D)
|
||||
|
||||
Ref<Mesh> mesh;
|
||||
|
||||
Ref<Texture> texture;
|
||||
Ref<Texture> normal_map;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_mesh(const Ref<Mesh> &p_mesh);
|
||||
Ref<Mesh> get_mesh() const;
|
||||
|
||||
void set_texture(const Ref<Texture> &p_texture);
|
||||
Ref<Texture> get_texture() const;
|
||||
|
||||
void set_normal_map(const Ref<Texture> &p_texture);
|
||||
Ref<Texture> get_normal_map() const;
|
||||
|
||||
virtual Rect2 _edit_get_rect() const;
|
||||
|
||||
MeshInstance2D();
|
||||
};
|
||||
|
||||
#endif // MESH_INSTANCE_2D_H
|
|
@ -46,6 +46,7 @@
|
|||
#include "scene/2d/light_2d.h"
|
||||
#include "scene/2d/light_occluder_2d.h"
|
||||
#include "scene/2d/line_2d.h"
|
||||
#include "scene/2d/mesh_instance_2d.h"
|
||||
#include "scene/2d/navigation2d.h"
|
||||
#include "scene/2d/parallax_background.h"
|
||||
#include "scene/2d/parallax_layer.h"
|
||||
|
@ -434,6 +435,7 @@ void register_scene_types() {
|
|||
ClassDB::register_class<AnimatedSprite>();
|
||||
ClassDB::register_class<Position2D>();
|
||||
ClassDB::register_class<Line2D>();
|
||||
ClassDB::register_class<MeshInstance2D>();
|
||||
ClassDB::register_virtual_class<CollisionObject2D>();
|
||||
ClassDB::register_virtual_class<PhysicsBody2D>();
|
||||
ClassDB::register_class<StaticBody2D>();
|
||||
|
|
|
@ -42,7 +42,7 @@ void BitMap::create(const Size2 &p_size) {
|
|||
zeromem(bitmask.ptrw(), bitmask.size());
|
||||
}
|
||||
|
||||
void BitMap::create_from_image_alpha(const Ref<Image> &p_image) {
|
||||
void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_treshold) {
|
||||
|
||||
ERR_FAIL_COND(p_image.is_null() || p_image->empty());
|
||||
Ref<Image> img = p_image->duplicate();
|
||||
|
@ -58,8 +58,9 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image) {
|
|||
|
||||
int bbyte = i / 8;
|
||||
int bbit = i % 8;
|
||||
if (r[i * 2])
|
||||
if (r[i * 2 + 1] / 255.0 > p_treshold) {
|
||||
w[bbyte] |= (1 << bbit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,10 +169,351 @@ Dictionary BitMap::_get_data() const {
|
|||
return d;
|
||||
}
|
||||
|
||||
Vector<Vector2> BitMap::_march_square(const Rect2i &rect, const Point2i &start) const {
|
||||
|
||||
int stepx = 0;
|
||||
int stepy = 0;
|
||||
int prevx = 0;
|
||||
int prevy = 0;
|
||||
int startx = start.x;
|
||||
int starty = start.y;
|
||||
int curx = startx;
|
||||
int cury = starty;
|
||||
unsigned int count = 0;
|
||||
Set<Point2i> case9s;
|
||||
Set<Point2i> case6s;
|
||||
int i;
|
||||
Vector<Vector2> _points;
|
||||
do {
|
||||
int sv = 0;
|
||||
{ //square value
|
||||
|
||||
/*
|
||||
checking the 2x2 pixel grid, assigning these values to each pixel, if not transparent
|
||||
+---+---+
|
||||
| 1 | 2 |
|
||||
+---+---+
|
||||
| 4 | 8 | <- current pixel (curx,cury)
|
||||
+---+---+
|
||||
*/
|
||||
//NOTE: due to the way we pick points from texture, rect needs to be smaller, otherwise it goes outside 1 pixel
|
||||
Rect2i fixed_rect = Rect2i(rect.position, rect.size - Size2i(2, 2));
|
||||
Point2i tl = Point2i(curx - 1, cury - 1);
|
||||
sv += (fixed_rect.has_point(tl) && get_bit(tl)) ? 1 : 0;
|
||||
Point2i tr = Point2i(curx, cury - 1);
|
||||
sv += (fixed_rect.has_point(tr) && get_bit(tr)) ? 2 : 0;
|
||||
Point2i bl = Point2i(curx - 1, cury);
|
||||
sv += (fixed_rect.has_point(bl) && get_bit(bl)) ? 4 : 0;
|
||||
Point2i br = Point2i(curx, cury);
|
||||
sv += (fixed_rect.has_point(br) && get_bit(br)) ? 8 : 0;
|
||||
ERR_FAIL_COND_V(sv == 0 || sv == 15, Vector<Vector2>());
|
||||
}
|
||||
|
||||
switch (sv) {
|
||||
|
||||
case 1:
|
||||
case 5:
|
||||
case 13:
|
||||
/* going UP with these cases:
|
||||
1 5 13
|
||||
+---+---+ +---+---+ +---+---+
|
||||
| 1 | | | 1 | | | 1 | |
|
||||
+---+---+ +---+---+ +---+---+
|
||||
| | | | 4 | | | 4 | 8 |
|
||||
+---+---+ +---+---+ +---+---+
|
||||
*/
|
||||
stepx = 0;
|
||||
stepy = -1;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
case 10:
|
||||
case 11:
|
||||
/* going DOWN with these cases:
|
||||
8 10 11
|
||||
+---+---+ +---+---+ +---+---+
|
||||
| | | | | 2 | | 1 | 2 |
|
||||
+---+---+ +---+---+ +---+---+
|
||||
| | 8 | | | 8 | | | 8 |
|
||||
+---+---+ +---+---+ +---+---+
|
||||
*/
|
||||
stepx = 0;
|
||||
stepy = 1;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case 12:
|
||||
case 14:
|
||||
/* going LEFT with these cases:
|
||||
4 12 14
|
||||
+---+---+ +---+---+ +---+---+
|
||||
| | | | | | | | 2 |
|
||||
+---+---+ +---+---+ +---+---+
|
||||
| 4 | | | 4 | 8 | | 4 | 8 |
|
||||
+---+---+ +---+---+ +---+---+
|
||||
*/
|
||||
stepx = -1;
|
||||
stepy = 0;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 7:
|
||||
/* going RIGHT with these cases:
|
||||
2 3 7
|
||||
+---+---+ +---+---+ +---+---+
|
||||
| | 2 | | 1 | 2 | | 1 | 2 |
|
||||
+---+---+ +---+---+ +---+---+
|
||||
| | | | | | | 4 | |
|
||||
+---+---+ +---+---+ +---+---+
|
||||
*/
|
||||
stepx = 1;
|
||||
stepy = 0;
|
||||
break;
|
||||
case 9:
|
||||
/*
|
||||
+---+---+
|
||||
| 1 | |
|
||||
+---+---+
|
||||
| | 8 |
|
||||
+---+---+
|
||||
this should normally go UP, but if we already been here, we go down
|
||||
*/
|
||||
if (case9s.has(Point2i(curx, cury))) {
|
||||
//found, so we go down, and delete from case9s;
|
||||
stepx = 0;
|
||||
stepy = 1;
|
||||
case9s.erase(Point2i(curx, cury));
|
||||
} else {
|
||||
//not found, we go up, and add to case9s;
|
||||
stepx = 0;
|
||||
stepy = -1;
|
||||
case9s.insert(Point2i(curx, cury));
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
/*
|
||||
6
|
||||
+---+---+
|
||||
| | 2 |
|
||||
+---+---+
|
||||
| 4 | |
|
||||
+---+---+
|
||||
this normally go RIGHT, but if its coming from UP, it should go LEFT
|
||||
*/
|
||||
if (case6s.has(Point2i(curx, cury))) {
|
||||
//found, so we go down, and delete from case6s;
|
||||
stepx = -1;
|
||||
stepy = 0;
|
||||
case6s.erase(Point2i(curx, cury));
|
||||
} else {
|
||||
//not found, we go up, and add to case6s;
|
||||
stepx = 1;
|
||||
stepy = 0;
|
||||
case6s.insert(Point2i(curx, cury));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ERR_PRINT("this shouldn't happen.");
|
||||
}
|
||||
//little optimization
|
||||
// if previous direction is same as current direction,
|
||||
// then we should modify the last vec to current
|
||||
curx += stepx;
|
||||
cury += stepy;
|
||||
if (stepx == prevx && stepy == prevy) {
|
||||
_points[_points.size() - 1].x = (float)(curx - rect.position.x);
|
||||
_points[_points.size() - 1].y = (float)(cury + rect.position.y);
|
||||
} else {
|
||||
_points.push_back(Vector2((float)(curx - rect.position.x), (float)(cury + rect.position.y)));
|
||||
}
|
||||
|
||||
count++;
|
||||
prevx = stepx;
|
||||
prevy = stepy;
|
||||
|
||||
ERR_FAIL_COND_V(count > width * height, _points);
|
||||
} while (curx != startx || cury != starty);
|
||||
return _points;
|
||||
}
|
||||
|
||||
static float perpendicular_distance(const Vector2 &i, const Vector2 &start, const Vector2 &end) {
|
||||
float res;
|
||||
float slope;
|
||||
float intercept;
|
||||
|
||||
if (start.x == end.x) {
|
||||
res = Math::absf(i.x - end.x);
|
||||
} else if (start.y == end.y) {
|
||||
res = Math::absf(i.y - end.y);
|
||||
} else {
|
||||
slope = (end.y - start.y) / (end.x - start.x);
|
||||
intercept = start.y - (slope * start.x);
|
||||
res = Math::absf(slope * i.x - i.y + intercept) / Math::sqrt(Math::pow(slope, 2.0f) + 1.0);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static Vector<Vector2> rdp(const Vector<Vector2> &v, float optimization) {
|
||||
if (v.size() < 3)
|
||||
return v;
|
||||
|
||||
int index = -1;
|
||||
float dist = 0;
|
||||
//not looping first and last point
|
||||
for (size_t i = 1, size = v.size(); i < size - 1; ++i) {
|
||||
float cdist = perpendicular_distance(v[i], v[0], v[v.size() - 1]);
|
||||
if (cdist > dist) {
|
||||
dist = cdist;
|
||||
index = static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
if (dist > optimization) {
|
||||
|
||||
Vector<Vector2> left, right;
|
||||
left.resize(index);
|
||||
for (int i = 0; i < index; i++) {
|
||||
left[i] = v[i];
|
||||
}
|
||||
right.resize(v.size() - index);
|
||||
for (int i = 0; i < right.size(); i++) {
|
||||
right[i] = v[index + i];
|
||||
}
|
||||
Vector<Vector2> r1 = rdp(left, optimization);
|
||||
Vector<Vector2> r2 = rdp(right, optimization);
|
||||
|
||||
int middle = r1.size();
|
||||
r1.resize(r1.size() + r2.size());
|
||||
for (int i = 0; i < r2.size(); i++) {
|
||||
r1[middle + i] = r2[i];
|
||||
}
|
||||
return r1;
|
||||
} else {
|
||||
Vector<Vector2> ret;
|
||||
ret.push_back(v[0]);
|
||||
ret.push_back(v[v.size() - 1]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static Vector<Vector2> reduce(const Vector<Vector2> &points, const Rect2i &rect, float epsilon) {
|
||||
int size = points.size();
|
||||
// if there are less than 3 points, then we have nothing
|
||||
ERR_FAIL_COND_V(size < 3, Vector<Vector2>());
|
||||
// if there are less than 9 points (but more than 3), then we don't need to reduce it
|
||||
if (size < 9) {
|
||||
return points;
|
||||
}
|
||||
|
||||
float maxEp = MIN(rect.size.width, rect.size.height);
|
||||
float ep = CLAMP(epsilon, 0.0, maxEp / 2);
|
||||
Vector<Vector2> result = rdp(points, ep);
|
||||
|
||||
Vector2 last = result[result.size() - 1];
|
||||
|
||||
if (last.y > result[0].y && last.distance_to(result[0]) < ep * 0.5f) {
|
||||
result[0].y = last.y;
|
||||
result.resize(result.size() - 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void fill_bits(const BitMap *p_src, Ref<BitMap> &p_map, const Point2i &p_pos, const Rect2i &rect) {
|
||||
|
||||
for (int i = p_pos.x - 1; i <= p_pos.x + 1; i++) {
|
||||
for (int j = p_pos.y - 1; j <= p_pos.y + 1; j++) {
|
||||
|
||||
if (i < rect.position.x || i >= rect.position.x + rect.size.x)
|
||||
continue;
|
||||
if (j < rect.position.y || j >= rect.position.y + rect.size.y)
|
||||
continue;
|
||||
|
||||
if (p_map->get_bit(Vector2(i, j)))
|
||||
continue;
|
||||
|
||||
else if (p_src->get_bit(Vector2(i, j))) {
|
||||
p_map->set_bit(Vector2(i, j), true);
|
||||
fill_bits(p_src, p_map, Point2i(i, j), rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vector<Vector<Vector2> > BitMap::clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon) const {
|
||||
|
||||
Rect2i r = Rect2i(0, 0, width, height).clip(p_rect);
|
||||
|
||||
print_line("Rect: " + r);
|
||||
Point2i from;
|
||||
Ref<BitMap> fill;
|
||||
fill.instance();
|
||||
fill->create(get_size());
|
||||
|
||||
Vector<Vector<Vector2> > polygons;
|
||||
for (int i = r.position.y; i < r.position.y + r.size.height; i++) {
|
||||
for (int j = r.position.x; j < r.position.x + r.size.width; j++) {
|
||||
if (!fill->get_bit(Point2(j, i)) && get_bit(Point2(j, i))) {
|
||||
|
||||
Vector<Vector2> polygon = _march_square(r, Point2i(j, i));
|
||||
print_line("pre reduce: " + itos(polygon.size()));
|
||||
polygon = reduce(polygon, r, p_epsilon);
|
||||
print_line("post reduce: " + itos(polygon.size()));
|
||||
polygons.push_back(polygon);
|
||||
fill_bits(this, fill, Point2i(j, i), r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return polygons;
|
||||
}
|
||||
|
||||
void BitMap::grow_mask(int p_pixels, const Rect2 &p_rect) {
|
||||
|
||||
Rect2i r = Rect2i(0, 0, width, height).clip(p_rect);
|
||||
|
||||
Ref<BitMap> copy;
|
||||
copy.instance();
|
||||
copy->create(get_size());
|
||||
copy->bitmask = bitmask;
|
||||
|
||||
for (int i = r.position.y; i < r.position.y + r.size.height; i++) {
|
||||
for (int j = r.position.x; j < r.position.x + r.size.width; j++) {
|
||||
if (copy->get_bit(Point2(j, i)))
|
||||
continue;
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (int y = i - p_pixels; y <= i + p_pixels; y++) {
|
||||
for (int x = j - p_pixels; x <= j + p_pixels; x++) {
|
||||
|
||||
if (x < p_rect.position.x || x >= p_rect.position.x + p_rect.size.x)
|
||||
continue;
|
||||
if (y < p_rect.position.y || y >= p_rect.position.y + p_rect.size.y)
|
||||
continue;
|
||||
|
||||
float d = Point2(j, i).distance_to(Point2(x, y)) - CMP_EPSILON;
|
||||
if (d > p_pixels)
|
||||
continue;
|
||||
|
||||
if (copy->get_bit(Point2(x, y))) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
set_bit(Point2(j, i), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BitMap::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create);
|
||||
ClassDB::bind_method(D_METHOD("create_from_image_alpha", "image"), &BitMap::create_from_image_alpha);
|
||||
ClassDB::bind_method(D_METHOD("create_from_image_alpha", "image", "treshold"), &BitMap::create_from_image_alpha, DEFVAL(0.1));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_bit", "position", "bit"), &BitMap::set_bit);
|
||||
ClassDB::bind_method(D_METHOD("get_bit", "position"), &BitMap::get_bit);
|
||||
|
|
|
@ -44,6 +44,8 @@ class BitMap : public Resource {
|
|||
int width;
|
||||
int height;
|
||||
|
||||
Vector<Vector2> _march_square(const Rect2i &rect, const Point2i &start) const;
|
||||
|
||||
protected:
|
||||
void _set_data(const Dictionary &p_d);
|
||||
Dictionary _get_data() const;
|
||||
|
@ -52,7 +54,7 @@ protected:
|
|||
|
||||
public:
|
||||
void create(const Size2 &p_size);
|
||||
void create_from_image_alpha(const Ref<Image> &p_image);
|
||||
void create_from_image_alpha(const Ref<Image> &p_image, float p_treshold = 0.1);
|
||||
|
||||
void set_bit(const Point2 &p_pos, bool p_value);
|
||||
bool get_bit(const Point2 &p_pos) const;
|
||||
|
@ -61,6 +63,10 @@ public:
|
|||
|
||||
Size2 get_size() const;
|
||||
|
||||
void grow_mask(int p_pixels, const Rect2 &p_rect);
|
||||
|
||||
Vector<Vector<Vector2> > clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon = 2.0) const;
|
||||
|
||||
BitMap();
|
||||
};
|
||||
|
||||
|
|
|
@ -750,6 +750,8 @@ public:
|
|||
|
||||
RID mesh;
|
||||
RID skeleton;
|
||||
RID texture;
|
||||
RID normal_map;
|
||||
CommandMesh() { type = TYPE_MESH; }
|
||||
};
|
||||
|
||||
|
@ -757,6 +759,8 @@ public:
|
|||
|
||||
RID multimesh;
|
||||
RID skeleton;
|
||||
RID texture;
|
||||
RID normal_map;
|
||||
CommandMultiMesh() { type = TYPE_MULTIMESH; }
|
||||
};
|
||||
|
||||
|
|
|
@ -742,7 +742,7 @@ void VisualServerCanvas::canvas_item_add_set_transform(RID p_item, const Transfo
|
|||
canvas_item->commands.push_back(tr);
|
||||
}
|
||||
|
||||
void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_skeleton) {
|
||||
void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture, RID p_normal_map, RID p_skeleton) {
|
||||
|
||||
Item *canvas_item = canvas_item_owner.getornull(p_item);
|
||||
ERR_FAIL_COND(!canvas_item);
|
||||
|
@ -750,6 +750,8 @@ void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID
|
|||
Item::CommandMesh *m = memnew(Item::CommandMesh);
|
||||
ERR_FAIL_COND(!m);
|
||||
m->mesh = p_mesh;
|
||||
m->texture = p_texture;
|
||||
m->normal_map = p_normal_map;
|
||||
m->skeleton = p_skeleton;
|
||||
|
||||
canvas_item->commands.push_back(m);
|
||||
|
@ -774,7 +776,7 @@ void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles,
|
|||
canvas_item->commands.push_back(part);
|
||||
}
|
||||
|
||||
void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_skeleton) {
|
||||
void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture, RID p_normal_map, RID p_skeleton) {
|
||||
|
||||
Item *canvas_item = canvas_item_owner.getornull(p_item);
|
||||
ERR_FAIL_COND(!canvas_item);
|
||||
|
@ -783,6 +785,8 @@ void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p
|
|||
ERR_FAIL_COND(!mm);
|
||||
mm->multimesh = p_mesh;
|
||||
mm->skeleton = p_skeleton;
|
||||
mm->texture = p_texture;
|
||||
mm->normal_map = p_normal_map;
|
||||
|
||||
canvas_item->rect_dirty = true;
|
||||
canvas_item->commands.push_back(mm);
|
||||
|
|
|
@ -182,8 +182,8 @@ public:
|
|||
void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0, RID p_normal_map = RID());
|
||||
void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), RID p_normal_map = RID(), bool p_antialiased = false);
|
||||
void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID());
|
||||
void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_skeleton = RID());
|
||||
void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_skeleton = RID());
|
||||
void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID(), RID p_skeleton = RID());
|
||||
void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID(), RID p_skeleton = RID());
|
||||
void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames);
|
||||
void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform);
|
||||
void canvas_item_add_clip_ignore(RID p_item, bool p_ignore);
|
||||
|
|
|
@ -581,8 +581,8 @@ public:
|
|||
BIND7(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float, RID)
|
||||
BIND7(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, RID, bool)
|
||||
BIND8(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, int, RID)
|
||||
BIND3(canvas_item_add_mesh, RID, const RID &, RID)
|
||||
BIND3(canvas_item_add_multimesh, RID, RID, RID)
|
||||
BIND5(canvas_item_add_mesh, RID, const RID &, RID, RID, RID)
|
||||
BIND5(canvas_item_add_multimesh, RID, RID, RID, RID, RID)
|
||||
BIND6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
|
||||
BIND2(canvas_item_add_set_transform, RID, const Transform2D &)
|
||||
BIND2(canvas_item_add_clip_ignore, RID, bool)
|
||||
|
|
|
@ -499,8 +499,8 @@ public:
|
|||
FUNC7(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float, RID)
|
||||
FUNC7(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, RID, bool)
|
||||
FUNC8(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, int, RID)
|
||||
FUNC3(canvas_item_add_mesh, RID, const RID &, RID)
|
||||
FUNC3(canvas_item_add_multimesh, RID, RID, RID)
|
||||
FUNC5(canvas_item_add_mesh, RID, const RID &, RID, RID, RID)
|
||||
FUNC5(canvas_item_add_multimesh, RID, RID, RID, RID, RID)
|
||||
FUNC6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
|
||||
FUNC2(canvas_item_add_set_transform, RID, const Transform2D &)
|
||||
FUNC2(canvas_item_add_clip_ignore, RID, bool)
|
||||
|
|
|
@ -846,8 +846,8 @@ public:
|
|||
virtual void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0, RID p_normal_map = RID()) = 0;
|
||||
virtual void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), RID p_normal_map = RID(), bool p_antialiased = false) = 0;
|
||||
virtual void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID()) = 0;
|
||||
virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_skeleton = RID()) = 0;
|
||||
virtual void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_skeleton = RID()) = 0;
|
||||
virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID(), RID p_skeleton = RID()) = 0;
|
||||
virtual void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID(), RID p_skeleton = RID()) = 0;
|
||||
virtual void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map, int p_h_frames, int p_v_frames) = 0;
|
||||
virtual void canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform) = 0;
|
||||
virtual void canvas_item_add_clip_ignore(RID p_item, bool p_ignore) = 0;
|
||||
|
|
4629
thirdparty/misc/clipper.cpp
vendored
Normal file
4629
thirdparty/misc/clipper.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
406
thirdparty/misc/clipper.hpp
vendored
Normal file
406
thirdparty/misc/clipper.hpp
vendored
Normal file
|
@ -0,0 +1,406 @@
|
|||
/*******************************************************************************
|
||||
* *
|
||||
* Author : Angus Johnson *
|
||||
* Version : 6.4.2 *
|
||||
* Date : 27 February 2017 *
|
||||
* Website : http://www.angusj.com *
|
||||
* Copyright : Angus Johnson 2010-2017 *
|
||||
* *
|
||||
* License: *
|
||||
* Use, modification & distribution is subject to Boost Software License Ver 1. *
|
||||
* http://www.boost.org/LICENSE_1_0.txt *
|
||||
* *
|
||||
* Attributions: *
|
||||
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
|
||||
* "A generic solution to polygon clipping" *
|
||||
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
|
||||
* http://portal.acm.org/citation.cfm?id=129906 *
|
||||
* *
|
||||
* Computer graphics and geometric modeling: implementation and algorithms *
|
||||
* By Max K. Agoston *
|
||||
* Springer; 1 edition (January 4, 2005) *
|
||||
* http://books.google.com/books?q=vatti+clipping+agoston *
|
||||
* *
|
||||
* See also: *
|
||||
* "Polygon Offsetting by Computing Winding Numbers" *
|
||||
* Paper no. DETC2005-85513 pp. 565-575 *
|
||||
* ASME 2005 International Design Engineering Technical Conferences *
|
||||
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
|
||||
* September 24-28, 2005 , Long Beach, California, USA *
|
||||
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef clipper_hpp
|
||||
#define clipper_hpp
|
||||
|
||||
#define CLIPPER_VERSION "6.4.2"
|
||||
|
||||
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
|
||||
//improve performance but coordinate values are limited to the range +/- 46340
|
||||
//#define use_int32
|
||||
|
||||
//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.
|
||||
//#define use_xyz
|
||||
|
||||
//use_lines: Enables line clipping. Adds a very minor cost to performance.
|
||||
#define use_lines
|
||||
|
||||
//use_deprecated: Enables temporary support for the obsolete functions
|
||||
//#define use_deprecated
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
|
||||
namespace ClipperLib {
|
||||
|
||||
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
|
||||
enum PolyType { ptSubject, ptClip };
|
||||
//By far the most widely used winding rules for polygon filling are
|
||||
//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
|
||||
//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
|
||||
//see http://glprogramming.com/red/chapter11.html
|
||||
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
|
||||
|
||||
#ifdef use_int32
|
||||
typedef int cInt;
|
||||
static cInt const loRange = 0x7FFF;
|
||||
static cInt const hiRange = 0x7FFF;
|
||||
#else
|
||||
typedef signed long long cInt;
|
||||
static cInt const loRange = 0x3FFFFFFF;
|
||||
static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
|
||||
typedef signed long long long64; //used by Int128 class
|
||||
typedef unsigned long long ulong64;
|
||||
|
||||
#endif
|
||||
|
||||
struct IntPoint {
|
||||
cInt X;
|
||||
cInt Y;
|
||||
#ifdef use_xyz
|
||||
cInt Z;
|
||||
IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {};
|
||||
#else
|
||||
IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {};
|
||||
#endif
|
||||
|
||||
friend inline bool operator== (const IntPoint& a, const IntPoint& b)
|
||||
{
|
||||
return a.X == b.X && a.Y == b.Y;
|
||||
}
|
||||
friend inline bool operator!= (const IntPoint& a, const IntPoint& b)
|
||||
{
|
||||
return a.X != b.X || a.Y != b.Y;
|
||||
}
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef std::vector< IntPoint > Path;
|
||||
typedef std::vector< Path > Paths;
|
||||
|
||||
inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
|
||||
inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
|
||||
|
||||
std::ostream& operator <<(std::ostream &s, const IntPoint &p);
|
||||
std::ostream& operator <<(std::ostream &s, const Path &p);
|
||||
std::ostream& operator <<(std::ostream &s, const Paths &p);
|
||||
|
||||
struct DoublePoint
|
||||
{
|
||||
double X;
|
||||
double Y;
|
||||
DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
|
||||
DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef use_xyz
|
||||
typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);
|
||||
#endif
|
||||
|
||||
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
|
||||
enum JoinType {jtSquare, jtRound, jtMiter};
|
||||
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
|
||||
|
||||
class PolyNode;
|
||||
typedef std::vector< PolyNode* > PolyNodes;
|
||||
|
||||
class PolyNode
|
||||
{
|
||||
public:
|
||||
PolyNode();
|
||||
virtual ~PolyNode(){};
|
||||
Path Contour;
|
||||
PolyNodes Childs;
|
||||
PolyNode* Parent;
|
||||
PolyNode* GetNext() const;
|
||||
bool IsHole() const;
|
||||
bool IsOpen() const;
|
||||
int ChildCount() const;
|
||||
private:
|
||||
//PolyNode& operator =(PolyNode& other);
|
||||
unsigned Index; //node index in Parent.Childs
|
||||
bool m_IsOpen;
|
||||
JoinType m_jointype;
|
||||
EndType m_endtype;
|
||||
PolyNode* GetNextSiblingUp() const;
|
||||
void AddChild(PolyNode& child);
|
||||
friend class Clipper; //to access Index
|
||||
friend class ClipperOffset;
|
||||
};
|
||||
|
||||
class PolyTree: public PolyNode
|
||||
{
|
||||
public:
|
||||
~PolyTree(){ Clear(); };
|
||||
PolyNode* GetFirst() const;
|
||||
void Clear();
|
||||
int Total() const;
|
||||
private:
|
||||
//PolyTree& operator =(PolyTree& other);
|
||||
PolyNodes AllNodes;
|
||||
friend class Clipper; //to access AllNodes
|
||||
};
|
||||
|
||||
bool Orientation(const Path &poly);
|
||||
double Area(const Path &poly);
|
||||
int PointInPolygon(const IntPoint &pt, const Path &path);
|
||||
|
||||
void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
|
||||
void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);
|
||||
void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);
|
||||
|
||||
void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
|
||||
void CleanPolygon(Path& poly, double distance = 1.415);
|
||||
void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415);
|
||||
void CleanPolygons(Paths& polys, double distance = 1.415);
|
||||
|
||||
void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
|
||||
void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);
|
||||
void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
|
||||
|
||||
void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
|
||||
void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths);
|
||||
void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);
|
||||
|
||||
void ReversePath(Path& p);
|
||||
void ReversePaths(Paths& p);
|
||||
|
||||
struct IntRect { cInt left; cInt top; cInt right; cInt bottom; };
|
||||
|
||||
//enums that are used internally ...
|
||||
enum EdgeSide { esLeft = 1, esRight = 2};
|
||||
|
||||
//forward declarations (for stuff used internally) ...
|
||||
struct TEdge;
|
||||
struct IntersectNode;
|
||||
struct LocalMinimum;
|
||||
struct OutPt;
|
||||
struct OutRec;
|
||||
struct Join;
|
||||
|
||||
typedef std::vector < OutRec* > PolyOutList;
|
||||
typedef std::vector < TEdge* > EdgeList;
|
||||
typedef std::vector < Join* > JoinList;
|
||||
typedef std::vector < IntersectNode* > IntersectList;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//ClipperBase is the ancestor to the Clipper class. It should not be
|
||||
//instantiated directly. This class simply abstracts the conversion of sets of
|
||||
//polygon coordinates into edge objects that are stored in a LocalMinima list.
|
||||
class ClipperBase
|
||||
{
|
||||
public:
|
||||
ClipperBase();
|
||||
virtual ~ClipperBase();
|
||||
virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
|
||||
bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);
|
||||
virtual void Clear();
|
||||
IntRect GetBounds();
|
||||
bool PreserveCollinear() {return m_PreserveCollinear;};
|
||||
void PreserveCollinear(bool value) {m_PreserveCollinear = value;};
|
||||
protected:
|
||||
void DisposeLocalMinimaList();
|
||||
TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);
|
||||
virtual void Reset();
|
||||
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
|
||||
void InsertScanbeam(const cInt Y);
|
||||
bool PopScanbeam(cInt &Y);
|
||||
bool LocalMinimaPending();
|
||||
bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin);
|
||||
OutRec* CreateOutRec();
|
||||
void DisposeAllOutRecs();
|
||||
void DisposeOutRec(PolyOutList::size_type index);
|
||||
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
|
||||
void DeleteFromAEL(TEdge *e);
|
||||
void UpdateEdgeIntoAEL(TEdge *&e);
|
||||
|
||||
typedef std::vector<LocalMinimum> MinimaList;
|
||||
MinimaList::iterator m_CurrentLM;
|
||||
MinimaList m_MinimaList;
|
||||
|
||||
bool m_UseFullRange;
|
||||
EdgeList m_edges;
|
||||
bool m_PreserveCollinear;
|
||||
bool m_HasOpenPaths;
|
||||
PolyOutList m_PolyOuts;
|
||||
TEdge *m_ActiveEdges;
|
||||
|
||||
typedef std::priority_queue<cInt> ScanbeamList;
|
||||
ScanbeamList m_Scanbeam;
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class Clipper : public virtual ClipperBase
|
||||
{
|
||||
public:
|
||||
Clipper(int initOptions = 0);
|
||||
bool Execute(ClipType clipType,
|
||||
Paths &solution,
|
||||
PolyFillType fillType = pftEvenOdd);
|
||||
bool Execute(ClipType clipType,
|
||||
Paths &solution,
|
||||
PolyFillType subjFillType,
|
||||
PolyFillType clipFillType);
|
||||
bool Execute(ClipType clipType,
|
||||
PolyTree &polytree,
|
||||
PolyFillType fillType = pftEvenOdd);
|
||||
bool Execute(ClipType clipType,
|
||||
PolyTree &polytree,
|
||||
PolyFillType subjFillType,
|
||||
PolyFillType clipFillType);
|
||||
bool ReverseSolution() { return m_ReverseOutput; };
|
||||
void ReverseSolution(bool value) {m_ReverseOutput = value;};
|
||||
bool StrictlySimple() {return m_StrictSimple;};
|
||||
void StrictlySimple(bool value) {m_StrictSimple = value;};
|
||||
//set the callback function for z value filling on intersections (otherwise Z is 0)
|
||||
#ifdef use_xyz
|
||||
void ZFillFunction(ZFillCallback zFillFunc);
|
||||
#endif
|
||||
protected:
|
||||
virtual bool ExecuteInternal();
|
||||
private:
|
||||
JoinList m_Joins;
|
||||
JoinList m_GhostJoins;
|
||||
IntersectList m_IntersectList;
|
||||
ClipType m_ClipType;
|
||||
typedef std::list<cInt> MaximaList;
|
||||
MaximaList m_Maxima;
|
||||
TEdge *m_SortedEdges;
|
||||
bool m_ExecuteLocked;
|
||||
PolyFillType m_ClipFillType;
|
||||
PolyFillType m_SubjFillType;
|
||||
bool m_ReverseOutput;
|
||||
bool m_UsingPolyTree;
|
||||
bool m_StrictSimple;
|
||||
#ifdef use_xyz
|
||||
ZFillCallback m_ZFill; //custom callback
|
||||
#endif
|
||||
void SetWindingCount(TEdge& edge);
|
||||
bool IsEvenOddFillType(const TEdge& edge) const;
|
||||
bool IsEvenOddAltFillType(const TEdge& edge) const;
|
||||
void InsertLocalMinimaIntoAEL(const cInt botY);
|
||||
void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge);
|
||||
void AddEdgeToSEL(TEdge *edge);
|
||||
bool PopEdgeFromSEL(TEdge *&edge);
|
||||
void CopyAELToSEL();
|
||||
void DeleteFromSEL(TEdge *e);
|
||||
void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
|
||||
bool IsContributing(const TEdge& edge) const;
|
||||
bool IsTopHorz(const cInt XPos);
|
||||
void DoMaxima(TEdge *e);
|
||||
void ProcessHorizontals();
|
||||
void ProcessHorizontal(TEdge *horzEdge);
|
||||
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||
OutRec* GetOutRec(int idx);
|
||||
void AppendPolygon(TEdge *e1, TEdge *e2);
|
||||
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
|
||||
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
|
||||
OutPt* GetLastOutPt(TEdge *e);
|
||||
bool ProcessIntersections(const cInt topY);
|
||||
void BuildIntersectList(const cInt topY);
|
||||
void ProcessIntersectList();
|
||||
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
|
||||
void BuildResult(Paths& polys);
|
||||
void BuildResult2(PolyTree& polytree);
|
||||
void SetHoleState(TEdge *e, OutRec *outrec);
|
||||
void DisposeIntersectNodes();
|
||||
bool FixupIntersectionOrder();
|
||||
void FixupOutPolygon(OutRec &outrec);
|
||||
void FixupOutPolyline(OutRec &outrec);
|
||||
bool IsHole(TEdge *e);
|
||||
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
|
||||
void FixHoleLinkage(OutRec &outrec);
|
||||
void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);
|
||||
void ClearJoins();
|
||||
void ClearGhostJoins();
|
||||
void AddGhostJoin(OutPt *op, const IntPoint offPt);
|
||||
bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);
|
||||
void JoinCommonEdges();
|
||||
void DoSimplePolygons();
|
||||
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
|
||||
void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec);
|
||||
void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec);
|
||||
#ifdef use_xyz
|
||||
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
|
||||
#endif
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ClipperOffset
|
||||
{
|
||||
public:
|
||||
ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25);
|
||||
~ClipperOffset();
|
||||
void AddPath(const Path& path, JoinType joinType, EndType endType);
|
||||
void AddPaths(const Paths& paths, JoinType joinType, EndType endType);
|
||||
void Execute(Paths& solution, double delta);
|
||||
void Execute(PolyTree& solution, double delta);
|
||||
void Clear();
|
||||
double MiterLimit;
|
||||
double ArcTolerance;
|
||||
private:
|
||||
Paths m_destPolys;
|
||||
Path m_srcPoly;
|
||||
Path m_destPoly;
|
||||
std::vector<DoublePoint> m_normals;
|
||||
double m_delta, m_sinA, m_sin, m_cos;
|
||||
double m_miterLim, m_StepsPerRad;
|
||||
IntPoint m_lowest;
|
||||
PolyNode m_polyNodes;
|
||||
|
||||
void FixOrientations();
|
||||
void DoOffset(double delta);
|
||||
void OffsetPoint(int j, int& k, JoinType jointype);
|
||||
void DoSquare(int j, int k);
|
||||
void DoMiter(int j, int k, double r);
|
||||
void DoRound(int j, int k);
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class clipperException : public std::exception
|
||||
{
|
||||
public:
|
||||
clipperException(const char* description): m_descr(description) {}
|
||||
virtual ~clipperException() throw() {}
|
||||
virtual const char* what() const throw() {return m_descr.c_str();}
|
||||
private:
|
||||
std::string m_descr;
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} //ClipperLib namespace
|
||||
|
||||
#endif //clipper_hpp
|
||||
|
||||
|
Loading…
Reference in a new issue