Ver código fonte

tools: add ability so specify a box collider when importing sprites

Daniele Bartolini 7 anos atrás
pai
commit
0dd34cd208

+ 157 - 0
tools/level_editor/project.vala

@@ -147,6 +147,11 @@ namespace Crown
 
 
 			Vector2 pivot_xy = sprite_cell_pivot_xy(cell_w, cell_h, sid.pivot.active);
 			Vector2 pivot_xy = sprite_cell_pivot_xy(cell_w, cell_h, sid.pivot.active);
 
 
+			int collision_x = (int)sid.collision_x.value;
+			int collision_y = (int)sid.collision_y.value;
+			int collision_w = (int)sid.collision_w.value;
+			int collision_h = (int)sid.collision_h.value;
+
 			sid.destroy();
 			sid.destroy();
 
 
 			foreach (unowned string filename_i in filenames)
 			foreach (unowned string filename_i in filenames)
@@ -287,6 +292,158 @@ namespace Crown
 					}
 					}
 				}
 				}
 
 
+				// Create collider (geometry)
+				{
+					// d-----a
+					// |     |
+					// |     |
+					// c-----b
+					Vector2 a = {};
+					Vector2 b = {};
+					Vector2 c = {};
+					Vector2 d = {};
+
+					double PIXELS_PER_METER = 32.0;
+
+					a.x = (collision_w + collision_x - pivot_xy.x) / PIXELS_PER_METER;
+					a.y = (              collision_y - pivot_xy.y) / PIXELS_PER_METER;
+
+					b.x = (collision_w + collision_x - pivot_xy.x) / PIXELS_PER_METER;
+					b.y = (collision_h + collision_y - pivot_xy.y) / PIXELS_PER_METER;
+
+					c.x = (              collision_x - pivot_xy.x) / PIXELS_PER_METER;
+					c.y = (collision_h + collision_y - pivot_xy.y) / PIXELS_PER_METER;
+
+					d.x = (              collision_x - pivot_xy.x) / PIXELS_PER_METER;
+					d.y = (              collision_y - pivot_xy.y) / PIXELS_PER_METER;
+
+					// Invert Y axis
+					a.y = a.y == 0.0f ? a.y : -a.y;
+					b.y = b.y == 0.0f ? b.y : -b.y;
+					c.y = c.y == 0.0f ? c.y : -c.y;
+					d.y = d.y == 0.0f ? d.y : -d.y;
+
+					ArrayList<Value?> position = new ArrayList<Value?>();
+					// Up face
+					position.add(a.x); position.add( 0.25); position.add(a.y);
+					position.add(b.x); position.add( 0.25); position.add(b.y);
+					position.add(c.x); position.add( 0.25); position.add(c.y);
+					position.add(d.x); position.add( 0.25); position.add(d.y);
+					// Bottom face
+					position.add(a.x); position.add(-0.25); position.add(a.y);
+					position.add(b.x); position.add(-0.25); position.add(b.y);
+					position.add(c.x); position.add(-0.25); position.add(c.y);
+					position.add(d.x); position.add(-0.25); position.add(d.y);
+
+					ArrayList<Value?> indices_data = new ArrayList<Value?>();
+					indices_data.add(0); indices_data.add(2); indices_data.add(3);
+					indices_data.add(7); indices_data.add(5); indices_data.add(4);
+					indices_data.add(4); indices_data.add(1); indices_data.add(0);
+					indices_data.add(5); indices_data.add(2); indices_data.add(1);
+					indices_data.add(6); indices_data.add(3); indices_data.add(2);
+					indices_data.add(0); indices_data.add(7); indices_data.add(4);
+					indices_data.add(0); indices_data.add(1); indices_data.add(2);
+					indices_data.add(7); indices_data.add(6); indices_data.add(5);
+					indices_data.add(4); indices_data.add(5); indices_data.add(1);
+					indices_data.add(5); indices_data.add(6); indices_data.add(2);
+					indices_data.add(6); indices_data.add(7); indices_data.add(3);
+					indices_data.add(0); indices_data.add(3); indices_data.add(7);
+
+					ArrayList<Value?> data = new ArrayList<Value?>();
+					data.add(indices_data);
+
+					Hashtable indices = new Hashtable();
+					indices["size"] = indices_data.size;
+					indices["data"] = data;
+
+					Hashtable geometries_collider = new Hashtable();
+					geometries_collider["position"] = position;
+					geometries_collider["indices"] = indices;
+
+					Hashtable geometries = new Hashtable();
+					geometries["collider"] = geometries_collider;
+
+					ArrayList<Value?> matrix_local = new ArrayList<Value?>();
+					matrix_local.add(1.0); matrix_local.add(0.0); matrix_local.add(0.0); matrix_local.add(0.0);
+					matrix_local.add(0.0); matrix_local.add(1.0); matrix_local.add(0.0); matrix_local.add(0.0);
+					matrix_local.add(0.0); matrix_local.add(0.0); matrix_local.add(1.0); matrix_local.add(0.0);
+					matrix_local.add(0.0); matrix_local.add(0.0); matrix_local.add(0.0); matrix_local.add(1.0);
+
+					Hashtable nodes_collider = new Hashtable();
+					nodes_collider["matrix_local"] = matrix_local;
+
+					Hashtable nodes = new Hashtable();
+					nodes["collider"] = nodes_collider;
+
+					Hashtable mesh = new Hashtable();
+					mesh["geometries"] = geometries;
+					mesh["nodes"] = nodes;
+
+					SJSON.save(mesh, Path.build_filename(_source_dir.get_path(), resource_name) + ".mesh");
+				}
+
+				// Create collider
+				{
+					Guid id = Guid.new_guid();
+
+					if (!unit.has_component("collider", ref id))
+					{
+						db.create(id);
+						db.set_property_string(id, "data.shape", "box");
+						db.set_property_string(id, "data.scene", resource_name);
+						db.set_property_string(id, "data.name", "collider");
+						db.set_property_string(id, "data.material", "default");
+						db.set_property_string(id, "type", "collider");
+
+						db.add_to_set(GUID_ZERO, "components", id);
+					}
+					else
+					{
+						unit.set_component_property_string(id, "data.shape", "box");
+						unit.set_component_property_string(id, "data.scene", resource_name);
+						unit.set_component_property_string(id, "data.name", "collider");
+						unit.set_component_property_string(id, "data.material", "default");
+						unit.set_component_property_string(id, "type", "collider");
+					}
+				}
+
+				// Create actor
+				{
+					Guid id = Guid.new_guid();
+
+					if (!unit.has_component("actor", ref id))
+					{
+						db.create(id);
+						db.set_property_string(id, "data.class", "static");
+						db.set_property_string(id, "data.collision_filter", "default");
+						db.set_property_bool  (id, "data.lock_rotation_x", true);
+						db.set_property_bool  (id, "data.lock_rotation_y", true);
+						db.set_property_bool  (id, "data.lock_rotation_z", true);
+						db.set_property_bool  (id, "data.lock_translation_x", false);
+						db.set_property_bool  (id, "data.lock_translation_y", true);
+						db.set_property_bool  (id, "data.lock_translation_z", false);
+						db.set_property_double(id, "data.mass", 10);
+						db.set_property_string(id, "data.material", "default");
+						db.set_property_string(id, "type", "actor");
+
+						db.add_to_set(GUID_ZERO, "components", id);
+					}
+					else
+					{
+						unit.set_component_property_string(id, "data.class", "static");
+						unit.set_component_property_string(id, "data.collision_filter", "default");
+						unit.set_component_property_bool  (id, "data.lock_rotation_x", true);
+						unit.set_component_property_bool  (id, "data.lock_rotation_y", true);
+						unit.set_component_property_bool  (id, "data.lock_rotation_z", true);
+						unit.set_component_property_bool  (id, "data.lock_translation_x", false);
+						unit.set_component_property_bool  (id, "data.lock_translation_y", true);
+						unit.set_component_property_bool  (id, "data.lock_translation_z", false);
+						unit.set_component_property_double(id, "data.mass", 10);
+						unit.set_component_property_string(id, "data.material", "default");
+						unit.set_component_property_string(id, "type", "actor");
+					}
+				}
+
 				db.save(unit_name);
 				db.save(unit_name);
 			}
 			}
 		}
 		}

+ 104 - 0
tools/level_editor/sprite_import_dialog.vala

@@ -102,6 +102,7 @@ public class SpriteImportDialog : Gtk.Dialog
 	public Gdk.Pixbuf _pixbuf;
 	public Gdk.Pixbuf _pixbuf;
 	public Gtk.DrawingArea _drawing_area;
 	public Gtk.DrawingArea _drawing_area;
 	public Gtk.ScrolledWindow _scrolled_window;
 	public Gtk.ScrolledWindow _scrolled_window;
+	public Gtk.DrawingArea _preview;
 
 
 	public Gtk.Label resolution;
 	public Gtk.Label resolution;
 	public Gtk.SpinButton cells_h;
 	public Gtk.SpinButton cells_h;
@@ -117,6 +118,11 @@ public class SpriteImportDialog : Gtk.Dialog
 	public SpinButtonDouble layer;
 	public SpinButtonDouble layer;
 	public SpinButtonDouble depth;
 	public SpinButtonDouble depth;
 
 
+	public Gtk.SpinButton collision_x;
+	public Gtk.SpinButton collision_y;
+	public Gtk.SpinButton collision_w;
+	public Gtk.SpinButton collision_h;
+
 	// Widgets
 	// Widgets
 	public SpriteImportDialog(string png)
 	public SpriteImportDialog(string png)
 	{
 	{
@@ -223,6 +229,57 @@ public class SpriteImportDialog : Gtk.Dialog
 		_scrolled_window.min_content_height = 640;
 		_scrolled_window.min_content_height = 640;
 		_scrolled_window.add(_drawing_area);
 		_scrolled_window.add(_drawing_area);
 
 
+		_preview = new Gtk.DrawingArea();
+		_preview.set_size_request(128, 128);
+
+		_preview.draw.connect((cr) => {
+				cr.set_source_rgb(0.1, 0.1, 0.1);
+				cr.paint();
+
+				Vector2 cell = sprite_cell_xy(0
+					, 0
+					, (int)offset_x.value
+					, (int)offset_y.value
+					, (int)cell_w.value
+					, (int)cell_h.value
+					, (int)spacing_x.value
+					, (int)spacing_y.value
+					);
+
+				int x0 = (int)cell.x;
+				int y0 = (int)cell.y;
+				int x1 = x0+(int)cell_w.value;
+				int y2 = y0+(int)cell_h.value;
+
+				// Draw checkered background
+				cr.save();
+				cr.set_source_surface(_checker, 0, 0);
+				Cairo.Pattern pattern = cr.get_source();
+				pattern.set_filter(Cairo.Filter.NEAREST);
+				pattern.set_extend(Cairo.Extend.REPEAT);
+				cr.rectangle(x0, y0, x1, y2);
+				cr.clip();
+				cr.new_path(); // path not consumed by clip()
+				cr.paint();
+				cr.restore();
+
+				// Draw sprite
+				cr.save();
+				Gdk.cairo_set_source_pixbuf(cr, _pixbuf, 0, 0);
+				cr.rectangle(x0, y0, x1, y2);
+				cr.clip();
+				cr.new_path(); // path not consumed by clip()
+				cr.paint();
+				cr.restore();
+
+				// Draw collision
+				cr.rectangle(collision_x.value, collision_y.value, collision_w.value, collision_h.value);
+				cr.set_source_rgba(0.3, 0.3, 0.3, 0.6);
+				cr.fill();
+
+				return true;
+			});
+
 		resolution = new Gtk.Label(_pixbuf.width.to_string() + " × " + _pixbuf.height.to_string());
 		resolution = new Gtk.Label(_pixbuf.width.to_string() + " × " + _pixbuf.height.to_string());
 		resolution.halign = Gtk.Align.START;
 		resolution.halign = Gtk.Align.START;
 
 
@@ -243,6 +300,15 @@ public class SpriteImportDialog : Gtk.Dialog
 		spacing_x = new Gtk.SpinButton.with_range(0, 128.0, 1.0);
 		spacing_x = new Gtk.SpinButton.with_range(0, 128.0, 1.0);
 		spacing_y = new Gtk.SpinButton.with_range(0, 128.0, 1.0);
 		spacing_y = new Gtk.SpinButton.with_range(0, 128.0, 1.0);
 
 
+		collision_x = new Gtk.SpinButton.with_range(0.0, 256.0, 1.0);
+		collision_x.value = 0;
+		collision_y = new Gtk.SpinButton.with_range(0.0, 256.0, 1.0);
+		collision_y.value = 0;
+		collision_w = new Gtk.SpinButton.with_range(0.0, 256.0, 1.0);
+		collision_w.value = 32;
+		collision_h = new Gtk.SpinButton.with_range(0.0, 256.0, 1.0);
+		collision_h.value = 32;
+
 		cells_h.value_changed.connect (() => {
 		cells_h.value_changed.connect (() => {
 			if (cell_wh_auto.active)
 			if (cell_wh_auto.active)
 			{
 			{
@@ -250,6 +316,7 @@ public class SpriteImportDialog : Gtk.Dialog
 				cell_h.value = _pixbuf.height / cells_v.value;
 				cell_h.value = _pixbuf.height / cells_v.value;
 			}
 			}
 			_drawing_area.queue_draw();
 			_drawing_area.queue_draw();
+			_preview.queue_draw();
 		});
 		});
 
 
 		cells_v.value_changed.connect(() => {
 		cells_v.value_changed.connect(() => {
@@ -259,6 +326,7 @@ public class SpriteImportDialog : Gtk.Dialog
 				cell_h.value = _pixbuf.height / cells_v.value;
 				cell_h.value = _pixbuf.height / cells_v.value;
 			}
 			}
 			_drawing_area.queue_draw();
 			_drawing_area.queue_draw();
+			_preview.queue_draw();
 		});
 		});
 
 
 		cell_wh_auto.toggled.connect(() => {
 		cell_wh_auto.toggled.connect(() => {
@@ -267,30 +335,53 @@ public class SpriteImportDialog : Gtk.Dialog
 			cell_w.value = _pixbuf.width / cells_h.value;
 			cell_w.value = _pixbuf.width / cells_h.value;
 			cell_h.value = _pixbuf.height / cells_v.value;
 			cell_h.value = _pixbuf.height / cells_v.value;
 			_drawing_area.queue_draw();
 			_drawing_area.queue_draw();
+			_preview.queue_draw();
 		});
 		});
 
 
 		cell_w.value_changed.connect (() => {
 		cell_w.value_changed.connect (() => {
 			_drawing_area.queue_draw();
 			_drawing_area.queue_draw();
+			_preview.queue_draw();
 		});
 		});
 
 
 		cell_h.value_changed.connect(() => {
 		cell_h.value_changed.connect(() => {
 			_drawing_area.queue_draw();
 			_drawing_area.queue_draw();
+			_preview.queue_draw();
 		});
 		});
 
 
 		offset_x.value_changed.connect(() => {
 		offset_x.value_changed.connect(() => {
 			_drawing_area.queue_draw();
 			_drawing_area.queue_draw();
+			_preview.queue_draw();
 		});
 		});
 
 
 		offset_y.value_changed.connect(() => {
 		offset_y.value_changed.connect(() => {
 			_drawing_area.queue_draw();
 			_drawing_area.queue_draw();
+			_preview.queue_draw();
 		});
 		});
 
 
 		spacing_x.value_changed.connect(() => {
 		spacing_x.value_changed.connect(() => {
 			_drawing_area.queue_draw();
 			_drawing_area.queue_draw();
+			_preview.queue_draw();
 		});
 		});
 
 
 		spacing_y.value_changed.connect(() => {
 		spacing_y.value_changed.connect(() => {
 			_drawing_area.queue_draw();
 			_drawing_area.queue_draw();
+			_preview.queue_draw();
+		});
+
+		collision_x.value_changed.connect(() => {
+			_preview.queue_draw();
+		});
+
+		collision_y.value_changed.connect(() => {
+			_preview.queue_draw();
+		});
+
+		collision_w.value_changed.connect(() => {
+			_preview.queue_draw();
+		});
+
+		collision_h.value_changed.connect(() => {
+			_preview.queue_draw();
 		});
 		});
 
 
 		pivot = new Gtk.ComboBoxText();
 		pivot = new Gtk.ComboBoxText();
@@ -307,6 +398,7 @@ public class SpriteImportDialog : Gtk.Dialog
 
 
 		pivot.changed.connect(() => {
 		pivot.changed.connect(() => {
 			_drawing_area.queue_draw();
 			_drawing_area.queue_draw();
+			_preview.queue_draw();
 		});
 		});
 
 
 		layer = new SpinButtonDouble(0.0, 0.0, 7.0);
 		layer = new SpinButtonDouble(0.0, 0.0, 7.0);
@@ -327,6 +419,11 @@ public class SpriteImportDialog : Gtk.Dialog
 		grid.attach(label_with_alignment("Layer", Gtk.Align.END),        0, 11, 1, 1);
 		grid.attach(label_with_alignment("Layer", Gtk.Align.END),        0, 11, 1, 1);
 		grid.attach(label_with_alignment("Depth", Gtk.Align.END),        0, 12, 1, 1);
 		grid.attach(label_with_alignment("Depth", Gtk.Align.END),        0, 12, 1, 1);
 
 
+		grid.attach(label_with_alignment("Collision X", Gtk.Align.END),  0, 13, 1, 1);
+		grid.attach(label_with_alignment("Collision Y", Gtk.Align.END),  0, 14, 1, 1);
+		grid.attach(label_with_alignment("Collision W", Gtk.Align.END),  0, 15, 1, 1);
+		grid.attach(label_with_alignment("Collision H", Gtk.Align.END),  0, 16, 1, 1);
+
 		grid.attach(resolution,   1,  0, 1, 1);
 		grid.attach(resolution,   1,  0, 1, 1);
 		grid.attach(cells_h,      1,  1, 1, 1);
 		grid.attach(cells_h,      1,  1, 1, 1);
 		grid.attach(cells_v,      1,  2, 1, 1);
 		grid.attach(cells_v,      1,  2, 1, 1);
@@ -340,11 +437,18 @@ public class SpriteImportDialog : Gtk.Dialog
 		grid.attach(pivot,        1, 10, 1, 1);
 		grid.attach(pivot,        1, 10, 1, 1);
 		grid.attach(layer,        1, 11, 1, 1);
 		grid.attach(layer,        1, 11, 1, 1);
 		grid.attach(depth,        1, 12, 1, 1);
 		grid.attach(depth,        1, 12, 1, 1);
+
+		grid.attach(collision_x,  1, 13, 1, 1);
+		grid.attach(collision_y,  1, 14, 1, 1);
+		grid.attach(collision_w,  1, 15, 1, 1);
+		grid.attach(collision_h,  1, 16, 1, 1);
+
 		grid.row_spacing = 6;
 		grid.row_spacing = 6;
 		grid.column_spacing = 12;
 		grid.column_spacing = 12;
 
 
 		Gtk.Box box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 12);
 		Gtk.Box box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 12);
 		box.pack_start(_scrolled_window, true, true);
 		box.pack_start(_scrolled_window, true, true);
+		box.pack_start(_preview, true, true);
 		box.pack_end(grid, false, false);
 		box.pack_end(grid, false, false);
 		box.margin_bottom = 18;
 		box.margin_bottom = 18;