simple composable grid context and grid controller, fiend sprite, godotxel addon
This commit is contained in:
commit
9441aa63cb
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
1
.import/.gdignore
Normal file
1
.import/.gdignore
Normal file
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,3 @@
|
||||
source_md5="02600b8740456771affee06983e57ba2"
|
||||
dest_md5="da049aced7d51eead426dccecd83e17d"
|
||||
|
BIN
.import/BrushCircle.png-dd250909fee7964ffc38f7e4fcfe9c07.stex
Normal file
BIN
.import/BrushCircle.png-dd250909fee7964ffc38f7e4fcfe9c07.stex
Normal file
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="fdff01762759c532cf06f6c399898003"
|
||||
dest_md5="fe9b15be81847e11ef7929564db675ca"
|
||||
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="3364be2e9af88c9893e2a69af65b0547"
|
||||
dest_md5="e754226789026560da0e12e720b31660"
|
||||
|
BIN
.import/BrushHLine.png-9182ec8ac804af16d356bf911782e299.stex
Normal file
BIN
.import/BrushHLine.png-9182ec8ac804af16d356bf911782e299.stex
Normal file
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="c767c1f5f8db79fd5b9edf9508fc4d1b"
|
||||
dest_md5="14567b18c33c4cd5dee7d4b11f07b7f2"
|
||||
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="0ec1c2f2a7018aa4122bf76285f87443"
|
||||
dest_md5="955c166c2c50a7fc7bb522de89df91ce"
|
||||
|
BIN
.import/BrushRect.png-2b2d0ae4889c1fbc5c7bee7ae5515663.stex
Normal file
BIN
.import/BrushRect.png-2b2d0ae4889c1fbc5c7bee7ae5515663.stex
Normal file
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="b2f8bc01566c3fa839501355155541c6"
|
||||
dest_md5="d10f6c9b72dba11f0356fa63b0efbddd"
|
||||
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="3e197fa301ef03f312365bd01429d08b"
|
||||
dest_md5="0913df5f9c2a29dd74c7b88f6a7f37e3"
|
||||
|
BIN
.import/BrushVLine.png-022220d888fe2fe2f8a081bcca62b4b2.stex
Normal file
BIN
.import/BrushVLine.png-022220d888fe2fe2f8a081bcca62b4b2.stex
Normal file
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="9d4fb491eb4bf3869fca7d038c928759"
|
||||
dest_md5="9ebfff15dc061a68c6bf82d1cb83e6af"
|
||||
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="e1db1064c72265a01a992e8d77615cd0"
|
||||
dest_md5="8de5f0178bb0f6b0773e245a52d2df9a"
|
||||
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="5a6b6ab9dca02fb1f80c7468b43a982c"
|
||||
dest_md5="e5448547844aef6e50771770909354c4"
|
||||
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="8a10fc33b613b33b4d51afda12ea1d74"
|
||||
dest_md5="22b5b124b8176533aa5cd11928ff8e1a"
|
||||
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="9e8801de4bef4983cca7e993585bb944"
|
||||
dest_md5="75483eb7190f06ec5b7c17b158dff864"
|
||||
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="f4c7f3ffcadcda366aedce818d6efbfe"
|
||||
dest_md5="e02525cb50c283e4e58be85a22e82e3a"
|
||||
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="7ba250dc0369bd9ea174a98fa96fd159"
|
||||
dest_md5="56c8662891cd27a8926447a80462218e"
|
||||
|
BIN
.import/arrow_down.png-d2bd93428c0bc172a28a43c55aac576e.stex
Normal file
BIN
.import/arrow_down.png-d2bd93428c0bc172a28a43c55aac576e.stex
Normal file
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="2117353e2e3a97b4461460d303806021"
|
||||
dest_md5="1e89a6da5bb0a41a9f96eed98d099ecb"
|
||||
|
BIN
.import/arrow_up.png-2598e148d1b795a628ce80a4fd5cf401.stex
Normal file
BIN
.import/arrow_up.png-2598e148d1b795a628ce80a4fd5cf401.stex
Normal file
Binary file not shown.
3
.import/fiend.png-46f0c65314f5bbca8d0599edf0d36a2b.md5
Normal file
3
.import/fiend.png-46f0c65314f5bbca8d0599edf0d36a2b.md5
Normal file
@ -0,0 +1,3 @@
|
||||
source_md5="7c1193f6500d1fc0411cdeb3000c36df"
|
||||
dest_md5="81ad6cf73db3ebed1cf028a8ba220086"
|
||||
|
BIN
.import/fiend.png-46f0c65314f5bbca8d0599edf0d36a2b.stex
Normal file
BIN
.import/fiend.png-46f0c65314f5bbca8d0599edf0d36a2b.stex
Normal file
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="854d565fe87c02a40a91d093e1942111"
|
||||
dest_md5="ad1a81cfcbcbdb6d5a9f99dd98a9e5e0"
|
||||
|
Binary file not shown.
3
.import/grid.png-e3d637acacdb891e09f422df261dbd1e.md5
Normal file
3
.import/grid.png-e3d637acacdb891e09f422df261dbd1e.md5
Normal file
@ -0,0 +1,3 @@
|
||||
source_md5="5e9bc42ebbb41300af1c572f986960ac"
|
||||
dest_md5="9761c0d50600309e43d1d28fd72b54f5"
|
||||
|
BIN
.import/grid.png-e3d637acacdb891e09f422df261dbd1e.stex
Normal file
BIN
.import/grid.png-e3d637acacdb891e09f422df261dbd1e.stex
Normal file
Binary file not shown.
3
.import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5
Normal file
3
.import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5
Normal file
@ -0,0 +1,3 @@
|
||||
source_md5="47313fa4c47a9963fddd764e1ec6e4a8"
|
||||
dest_md5="26ea799ea0a3da9e753b3ebe822e0570"
|
||||
|
BIN
.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex
Normal file
BIN
.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex
Normal file
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="1f208aa17bb746f72d0c708960e161b8"
|
||||
dest_md5="9a39a80a2cbcca6412c335d0d192b71e"
|
||||
|
BIN
.import/lock_layer.png-076954b389746de9e13c853ed5d9ba59.stex
Normal file
BIN
.import/lock_layer.png-076954b389746de9e13c853ed5d9ba59.stex
Normal file
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="f5c365385f1229bba28c2118b09557f0"
|
||||
dest_md5="215730718d238613f5ecfb60c77fd1c3"
|
||||
|
BIN
.import/lock_layer_1.png-4848d5f2cd0f48c68b880712b6b38776.stex
Normal file
BIN
.import/lock_layer_1.png-4848d5f2cd0f48c68b880712b6b38776.stex
Normal file
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="e0848eef5591cbefcdd580e2266df32a"
|
||||
dest_md5="7e23ebf9e94e4bc4758723749616c112"
|
||||
|
BIN
.import/minidotta.png-adac81df344972ef82e2499656aa288e.stex
Normal file
BIN
.import/minidotta.png-adac81df344972ef82e2499656aa288e.stex
Normal file
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="ed7ea9ed6750cf2fbe5141c6745f1a80"
|
||||
dest_md5="365e095f05917944fca1b11c61acdd5b"
|
||||
|
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
source_md5="14b94da08f95d2ae578c2be11f83ccd8"
|
||||
dest_md5="ece14a0ab6aa8577445cdcf27ed7ac33"
|
||||
|
BIN
.import/unlock_layer.png-ae7c97a04fb889522c7c466fdc9dd8f6.stex
Normal file
BIN
.import/unlock_layer.png-ae7c97a04fb889522c7c466fdc9dd8f6.stex
Normal file
Binary file not shown.
24
addons/Godoxel/Animation.gd
Normal file
24
addons/Godoxel/Animation.gd
Normal file
@ -0,0 +1,24 @@
|
||||
extends Node
|
||||
class_name GEAnimation
|
||||
|
||||
|
||||
var frames = []
|
||||
var anim_idx = -1
|
||||
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
|
||||
func add_frame(frame):
|
||||
frames.append(frame)
|
||||
|
||||
|
||||
func get_anim_index():
|
||||
return anim_idx
|
||||
|
||||
|
||||
func set_anim_index(index):
|
||||
anim_idx = index
|
||||
|
||||
|
106
addons/Godoxel/BrushPrefabs.gd
Normal file
106
addons/Godoxel/BrushPrefabs.gd
Normal file
@ -0,0 +1,106 @@
|
||||
class_name BrushPrefabs
|
||||
|
||||
|
||||
const list = [
|
||||
[ Vector2(0, -1),
|
||||
Vector2(-1, 0), Vector2(0, 0), Vector2(1, 0),
|
||||
Vector2(0, 1)
|
||||
],
|
||||
[Vector2(-1, -1), Vector2(0, -1), Vector2(1, -1),
|
||||
Vector2(-1, 0), Vector2(0, 0), Vector2(1, 0),
|
||||
Vector2(-1, 1), Vector2(0, 1), Vector2(1, 1),
|
||||
],
|
||||
[
|
||||
Vector2(-1, 0), Vector2(0, 0), Vector2(1, 0),
|
||||
],
|
||||
[ Vector2(0, -1),
|
||||
Vector2(0, 0),
|
||||
Vector2(0, 1)
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
enum Type {
|
||||
V_LINE,
|
||||
H_LINE,
|
||||
RECT,
|
||||
CIRCLE,
|
||||
}
|
||||
|
||||
static func get_brush(type, size: int):
|
||||
var pixels = []
|
||||
if size < 1:
|
||||
size = 1
|
||||
|
||||
match type:
|
||||
Type.CIRCLE:
|
||||
size += 1
|
||||
var center = Vector2.ZERO
|
||||
var last = center
|
||||
var radius = size / 2.0
|
||||
for x in range(size):
|
||||
for y in range(size):
|
||||
if Vector2(x - radius, y - radius).length() < size / 3.0:
|
||||
pixels.append(Vector2(x, y))
|
||||
|
||||
var avg = Vector2(size / 2, size / 2)
|
||||
avg = Vector2(floor(avg.x), floor(avg.y))
|
||||
|
||||
for i in range(pixels.size()):
|
||||
pixels[i] -= avg
|
||||
|
||||
Type.RECT:
|
||||
var center = Vector2.ZERO
|
||||
var last = center
|
||||
for x in range(size):
|
||||
for y in range(size):
|
||||
pixels.append(Vector2(x, y))
|
||||
|
||||
var avg = Vector2.ZERO
|
||||
for cell in pixels:
|
||||
avg += cell
|
||||
|
||||
avg.x /= pixels.size()
|
||||
avg.y /= pixels.size()
|
||||
|
||||
avg = Vector2(floor(avg.x), floor(avg.y))
|
||||
|
||||
for i in range(pixels.size()):
|
||||
pixels[i] -= avg
|
||||
|
||||
Type.V_LINE:
|
||||
var center = Vector2.ZERO
|
||||
var last = center
|
||||
pixels.append(Vector2.ZERO)
|
||||
|
||||
for i in range(size - 1):
|
||||
var sig = sign(last.y)
|
||||
if sig == 0:
|
||||
sig = 1
|
||||
|
||||
if last.y < 0:
|
||||
center.y = abs(last.y) * -sig
|
||||
else:
|
||||
center.y = abs(last.y+1) * -sig
|
||||
last = center
|
||||
pixels.append(center)
|
||||
Type.H_LINE:
|
||||
var center = Vector2.ZERO
|
||||
var last = center
|
||||
pixels.append(Vector2.ZERO)
|
||||
|
||||
for i in range(size - 1):
|
||||
var sig = sign(last.x)
|
||||
if sig == 0:
|
||||
sig = 1
|
||||
|
||||
if last.x < 0:
|
||||
center.x = abs(last.x) * -sig
|
||||
else:
|
||||
center.x = abs(last.x+1) * -sig
|
||||
last = center
|
||||
pixels.append(center)
|
||||
|
||||
return pixels
|
||||
|
||||
|
517
addons/Godoxel/Canvas.gd
Normal file
517
addons/Godoxel/Canvas.gd
Normal file
@ -0,0 +1,517 @@
|
||||
extends Control
|
||||
class_name GECanvas
|
||||
tool
|
||||
|
||||
export var pixel_size: float = 16 setget set_pixel_size
|
||||
export(int, 1, 2500) var canvas_width = 48 setget set_canvas_width # == pixels
|
||||
export(int, 1, 2500) var canvas_height = 28 setget set_canvas_height # == pixels
|
||||
export var grid_size = 16 setget set_grid_size
|
||||
export var big_grid_size = 10 setget set_big_grid_size
|
||||
export var can_draw = true
|
||||
|
||||
var mouse_in_region
|
||||
var mouse_on_top
|
||||
|
||||
var frame: GEFrame
|
||||
|
||||
#var layers : Array = [] # Key: layer_name, val: GELayer
|
||||
var active_layer: GELayer
|
||||
var preview_layer: GELayer
|
||||
var tool_layer: GELayer
|
||||
var canvas_layers: Control
|
||||
|
||||
var canvas
|
||||
var grid
|
||||
var big_grid
|
||||
var selected_pixels = []
|
||||
|
||||
var symmetry_x = false
|
||||
var symmetry_y = false
|
||||
|
||||
|
||||
func _ready():
|
||||
#-------------------------------
|
||||
# Set nodes
|
||||
#-------------------------------
|
||||
canvas = find_node("Canvas")
|
||||
grid = find_node("Grid")
|
||||
big_grid = find_node("BigGrid")
|
||||
canvas_layers = find_node("CanvasLayers")
|
||||
|
||||
#-------------------------------
|
||||
# setup layers and canvas
|
||||
#-------------------------------
|
||||
if not is_connected("mouse_entered", self, "_on_mouse_entered"):
|
||||
connect("mouse_entered", self, "_on_mouse_entered")
|
||||
if not is_connected("mouse_exited", self, "_on_mouse_exited"):
|
||||
connect("mouse_exited", self, "_on_mouse_exited")
|
||||
|
||||
#-------------------------------
|
||||
# setup layers and canvas
|
||||
#-------------------------------
|
||||
#canvas_size = Vector2(int(rect_size.x / grid_size), int(rect_size.y / grid_size))
|
||||
#pixel_size = canvas_size
|
||||
|
||||
create_preview_layer()
|
||||
create_tool_layer()
|
||||
|
||||
set_process(true)
|
||||
|
||||
|
||||
func _process(delta):
|
||||
if not is_visible_in_tree():
|
||||
return
|
||||
var mouse_position = get_local_mouse_position()
|
||||
var rect = Rect2(Vector2(0, 0), rect_size)
|
||||
mouse_in_region = rect.has_point(mouse_position)
|
||||
|
||||
|
||||
func _draw():
|
||||
if preview_layer:
|
||||
preview_layer.update_texture()
|
||||
if tool_layer:
|
||||
tool_layer.update_texture()
|
||||
# for layer in layers:
|
||||
# layer.update_texture()
|
||||
# TODO
|
||||
#if frame:
|
||||
#frame._draw()
|
||||
|
||||
|
||||
func resize(width: int, height: int):
|
||||
if width < 0:
|
||||
width = 1
|
||||
if height < 0:
|
||||
height = 1
|
||||
|
||||
# TODO move resize to editor.gd -> for all frames
|
||||
|
||||
set_canvas_width(width)
|
||||
set_canvas_height(height)
|
||||
|
||||
preview_layer.resize(width, height)
|
||||
tool_layer.resize(width, height)
|
||||
|
||||
#frame.resize(width, height)
|
||||
|
||||
|
||||
|
||||
func set_frame(new_frame):
|
||||
frame = new_frame
|
||||
if canvas_layers.get_child_count() > 0:
|
||||
canvas_layers.remove_child(canvas_layers.get_child(0))
|
||||
canvas_layers.add_child(frame)
|
||||
|
||||
frame.width = canvas_width
|
||||
frame.height = canvas_height
|
||||
|
||||
frame.anchor_right = 1
|
||||
frame.anchor_bottom = 1
|
||||
frame.margin_right = 0
|
||||
frame.margin_bottom = 0
|
||||
|
||||
if not frame.layers.empty():
|
||||
active_layer = frame.layers[owner.current_layer_idx]
|
||||
|
||||
|
||||
|
||||
################################################################
|
||||
# Export
|
||||
################################################################
|
||||
|
||||
func get_current_frame_image() -> Image:
|
||||
var image = Image.new()
|
||||
image.create(canvas_width, canvas_height, true, Image.FORMAT_RGBA8)
|
||||
image.lock()
|
||||
image.fill(Color.transparent)
|
||||
image.unlock()
|
||||
image.lock()
|
||||
|
||||
for layer in frame.layers:
|
||||
if not layer.visible:
|
||||
continue
|
||||
|
||||
for x in range(frame.width):
|
||||
for y in range(frame.height):
|
||||
var color = layer.get_pixel(x, y)
|
||||
var image_color = image.get_pixel(x, y)
|
||||
|
||||
if color.a != 0:
|
||||
image.set_pixel(x, y, color)
|
||||
else:
|
||||
image.set_pixel(x, y, image_color.blend(color))
|
||||
image.unlock()
|
||||
return image
|
||||
|
||||
|
||||
func get_current_layer_image() -> Image:
|
||||
var image = Image.new()
|
||||
image.create(canvas_width, canvas_height, true, Image.FORMAT_RGBA8)
|
||||
image.lock()
|
||||
image.fill(Color.transparent)
|
||||
image.unlock()
|
||||
image.lock()
|
||||
|
||||
for layer in frame.layers:
|
||||
if layer != active_layer:
|
||||
continue
|
||||
|
||||
for x in range(frame.width):
|
||||
for y in range(frame.height):
|
||||
var color = layer.get_pixel(x, y)
|
||||
var image_color = image.get_pixel(x, y)
|
||||
|
||||
if color.a != 0:
|
||||
image.set_pixel(x, y, color)
|
||||
else:
|
||||
image.set_pixel(x, y, image_color.blend(color))
|
||||
image.unlock()
|
||||
return image
|
||||
|
||||
|
||||
|
||||
################################################################
|
||||
# Pixel/Grid size
|
||||
################################################################
|
||||
|
||||
func set_pixel_size(size: float):
|
||||
pixel_size = size
|
||||
set_grid_size(grid_size)
|
||||
set_big_grid_size(big_grid_size)
|
||||
set_canvas_width(canvas_width)
|
||||
set_canvas_height(canvas_height)
|
||||
|
||||
|
||||
func set_grid_size(size):
|
||||
grid_size = size
|
||||
if not find_node("Grid"):
|
||||
return
|
||||
find_node("Grid").size = size * pixel_size
|
||||
|
||||
|
||||
func set_big_grid_size(size):
|
||||
big_grid_size = size
|
||||
if not find_node("BigGrid"):
|
||||
return
|
||||
find_node("BigGrid").size = size * pixel_size
|
||||
|
||||
|
||||
func set_canvas_width(val: int):
|
||||
canvas_width = val
|
||||
rect_size.x = canvas_width * pixel_size
|
||||
|
||||
|
||||
func set_canvas_height(val: int):
|
||||
canvas_height = val
|
||||
rect_size.y = canvas_height * pixel_size
|
||||
|
||||
|
||||
|
||||
#-------------------------------
|
||||
# Layer
|
||||
#-------------------------------
|
||||
|
||||
func toggle_alpha_locked(layer_name: String):
|
||||
var layer = find_layer_by_name(layer_name)
|
||||
layer.toggle_alpha_locked()
|
||||
|
||||
|
||||
func is_alpha_locked() -> bool:
|
||||
return active_layer.alpha_locked
|
||||
|
||||
|
||||
func get_content_margin() -> Rect2:
|
||||
return frame.get_content_margin()
|
||||
|
||||
|
||||
func crop_to_content():
|
||||
frame.crop_to_content()
|
||||
|
||||
|
||||
func get_active_layer() -> GELayer:
|
||||
return active_layer
|
||||
|
||||
|
||||
func get_preview_layer():
|
||||
return preview_layer
|
||||
|
||||
|
||||
func clear_active_layer():
|
||||
active_layer.clear()
|
||||
|
||||
|
||||
func clear_preview_layer():
|
||||
preview_layer.clear()
|
||||
|
||||
|
||||
func clear_layer(layer_name: String):
|
||||
for layer in frame.layers:
|
||||
if layer.name == layer_name:
|
||||
layer.clear()
|
||||
break
|
||||
|
||||
|
||||
func remove_layer(layer_name: String):
|
||||
# change current layer if the active layer is removed
|
||||
var del_layer = find_layer_by_name(layer_name)
|
||||
del_layer.clear()
|
||||
if del_layer == active_layer:
|
||||
for layer in frame.layers:
|
||||
if layer == preview_layer or layer == active_layer or layer == tool_layer:
|
||||
continue
|
||||
active_layer = layer
|
||||
print("Select active layer: ", active_layer)
|
||||
break
|
||||
frame.layers.erase(del_layer)
|
||||
return active_layer
|
||||
|
||||
|
||||
func create_preview_layer():
|
||||
var layer = GELayer.new()
|
||||
layer.create($PreviewLayer, canvas_width, canvas_height)
|
||||
preview_layer = layer
|
||||
return layer
|
||||
|
||||
|
||||
func create_tool_layer():
|
||||
var layer = GELayer.new()
|
||||
layer.create($ToolPreviewLayer, canvas_width, canvas_height)
|
||||
tool_layer = layer
|
||||
return layer
|
||||
|
||||
|
||||
func duplicate_layer(layer: GELayer):
|
||||
for existing_layer in frame.layers:
|
||||
if layer.name == existing_layer.name:
|
||||
return
|
||||
|
||||
var new_layer: GELayer = GELayer.new()
|
||||
new_layer.image.copy_from(layer.image)
|
||||
return new_layer
|
||||
|
||||
|
||||
func toggle_layer_visibility(layer_name: String):
|
||||
var layer_idx = get_layer_index(layer_name)
|
||||
assert(layer_idx != -1, "Layer name not found!")
|
||||
var layer = frame.layers[layer_idx]
|
||||
layer.visible = not layer.visible
|
||||
return layer.visible
|
||||
|
||||
|
||||
func get_layer_index(layer_name: String):
|
||||
var idx = 0
|
||||
for layer in frame.layers:
|
||||
if layer.name == layer_name:
|
||||
return idx
|
||||
idx += 1
|
||||
return -1
|
||||
|
||||
|
||||
func find_layer_by_name(layer_name: String):
|
||||
for layer in frame.layers:
|
||||
if layer.name == layer_name:
|
||||
return layer
|
||||
return null
|
||||
|
||||
|
||||
func toggle_lock_layer(layer_name: String):
|
||||
find_layer_by_name(layer_name).toggle_lock()
|
||||
|
||||
|
||||
func is_active_layer_locked() -> bool:
|
||||
return active_layer.locked
|
||||
|
||||
|
||||
func get_active_layer_index() -> int:
|
||||
return get_layer_index(active_layer.name)
|
||||
|
||||
|
||||
func move_layer_forward(layer_name: String):
|
||||
var layer = find_layer_by_name(layer_name).texture_rect_ref
|
||||
var new_idx = max(layer.get_index() - 1, 0)
|
||||
layer.get_parent().move_child(layer, new_idx)
|
||||
|
||||
|
||||
func move_layer_back(layer_name: String):
|
||||
var layer = find_layer_by_name(layer_name).texture_rect_ref
|
||||
layer.get_parent().move_child(layer, layer.get_index() + 1)
|
||||
|
||||
|
||||
func select_layer(layer: GELayer):
|
||||
active_layer = layer
|
||||
|
||||
|
||||
#-------------------------------
|
||||
# Check
|
||||
#-------------------------------
|
||||
|
||||
func _on_mouse_entered():
|
||||
mouse_on_top = true
|
||||
|
||||
|
||||
func _on_mouse_exited():
|
||||
mouse_on_top = false
|
||||
|
||||
|
||||
func is_inside_canvas(x, y):
|
||||
if x < 0 or y < 0:
|
||||
return false
|
||||
if x >= canvas_width or y >= canvas_height:
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
|
||||
#-------------------------------
|
||||
# Basic pixel-layer options
|
||||
#-------------------------------
|
||||
|
||||
|
||||
#Note: Arrays are always passed by reference. To get a copy of an array which
|
||||
# can be modified independently of the original array, use duplicate.
|
||||
# (https://docs.godotengine.org/en/stable/classes/class_array.html)
|
||||
func set_pixel_arr(pixels: Array, color: Color):
|
||||
for pixel in pixels:
|
||||
_set_pixel(active_layer, pixel.x, pixel.y, color)
|
||||
|
||||
|
||||
func set_pixel_v(pos: Vector2, color: Color):
|
||||
set_pixel(pos.x, pos.y, color)
|
||||
|
||||
|
||||
func set_pixel(x: int, y: int, color: Color):
|
||||
_set_pixel(active_layer, x, y, color)
|
||||
|
||||
|
||||
func _set_pixel_v(layer: GELayer, v: Vector2, color: Color):
|
||||
_set_pixel(layer, v.x, v.y, color)
|
||||
|
||||
|
||||
func _set_pixel(layer: GELayer, x: int, y: int, color: Color):
|
||||
if not is_inside_canvas(x, y):
|
||||
return
|
||||
layer.set_pixel(x, y, color)
|
||||
|
||||
|
||||
func get_pixel_v(pos: Vector2):
|
||||
return get_pixel(pos.x, pos.y)
|
||||
|
||||
|
||||
func get_pixel(x: int, y: int):
|
||||
if active_layer:
|
||||
return active_layer.get_pixel(x, y)
|
||||
return null
|
||||
|
||||
|
||||
func set_preview_pixel_v(pos: Vector2, color: Color):
|
||||
set_preview_pixel(pos.x, pos.y, color)
|
||||
|
||||
|
||||
func set_preview_pixel(x:int, y: int, color: Color):
|
||||
if not is_inside_canvas(x, y):
|
||||
return
|
||||
preview_layer.set_pixel(x, y, color)
|
||||
|
||||
|
||||
func get_preview_pixel_v(pos: Vector2):
|
||||
return get_preview_pixel(pos.x, pos.y)
|
||||
|
||||
|
||||
func get_preview_pixel(x: int, y: int):
|
||||
if not preview_layer:
|
||||
return null
|
||||
return preview_layer.get_pixel(x, y)
|
||||
|
||||
|
||||
|
||||
#-------------------------------
|
||||
# Grid
|
||||
#-------------------------------
|
||||
|
||||
|
||||
func toggle_grid():
|
||||
$Grid.visible = not $Grid.visible
|
||||
|
||||
|
||||
func show_grid():
|
||||
$Grid.show()
|
||||
|
||||
|
||||
func hide_grid():
|
||||
$Grid.hide()
|
||||
|
||||
|
||||
#-------------------------------
|
||||
# Handy tools
|
||||
#-------------------------------
|
||||
|
||||
|
||||
func select_color(x, y):
|
||||
print("???")
|
||||
var same_color_pixels = []
|
||||
var color = get_pixel(x, y)
|
||||
for x in range(active_layer.layer_width):
|
||||
for y in range(active_layer.layer_height):
|
||||
var pixel_color = active_layer.get_pixel(x, y)
|
||||
if pixel_color == color:
|
||||
same_color_pixels.append(color)
|
||||
return same_color_pixels
|
||||
|
||||
|
||||
func select_same_color(x, y):
|
||||
return get_neighbouring_pixels(x, y)
|
||||
|
||||
|
||||
# returns array of Vector2
|
||||
# yoinked from
|
||||
# https://www.geeksforgeeks.org/flood-fill-algorithm-implement-fill-paint/
|
||||
func get_neighbouring_pixels(pos_x: int, pos_y: int) -> Array:
|
||||
var pixels = []
|
||||
|
||||
var to_check_queue = []
|
||||
var checked_queue = []
|
||||
|
||||
to_check_queue.append(GEUtils.to_1D(pos_x, pos_y, canvas_width))
|
||||
|
||||
var color = get_pixel(pos_x, pos_y)
|
||||
|
||||
while not to_check_queue.empty():
|
||||
var idx = to_check_queue.pop_front()
|
||||
var p = GEUtils.to_2D(idx, canvas_width)
|
||||
|
||||
if idx in checked_queue:
|
||||
continue
|
||||
|
||||
checked_queue.append(idx)
|
||||
|
||||
if get_pixel(p.x, p.y) != color:
|
||||
continue
|
||||
|
||||
# add to result
|
||||
pixels.append(p)
|
||||
|
||||
# check neighbours
|
||||
var x = p.x - 1
|
||||
var y = p.y
|
||||
if is_inside_canvas(x, y):
|
||||
idx = GEUtils.to_1D(x, y, canvas_width)
|
||||
to_check_queue.append(idx)
|
||||
|
||||
x = p.x + 1
|
||||
if is_inside_canvas(x, y):
|
||||
idx = GEUtils.to_1D(x, y, canvas_width)
|
||||
to_check_queue.append(idx)
|
||||
|
||||
x = p.x
|
||||
y = p.y - 1
|
||||
if is_inside_canvas(x, y):
|
||||
idx = GEUtils.to_1D(x, y, canvas_width)
|
||||
to_check_queue.append(idx)
|
||||
|
||||
y = p.y + 1
|
||||
if is_inside_canvas(x, y):
|
||||
idx = GEUtils.to_1D(x, y, canvas_width)
|
||||
to_check_queue.append(idx)
|
||||
|
||||
return pixels
|
||||
|
31
addons/Godoxel/CanvasOutline.gd
Normal file
31
addons/Godoxel/CanvasOutline.gd
Normal file
@ -0,0 +1,31 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
export var color = Color()
|
||||
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
|
||||
func _draw():
|
||||
var size = get_parent().rect_size
|
||||
var pos = Vector2.ZERO #get_parent().rect_global_position
|
||||
draw_outline_box(pos, size, color, 1)
|
||||
|
||||
|
||||
func draw_outline_box(pos, size, color, width):
|
||||
#Top line
|
||||
draw_line(pos+Vector2(-1, 0), pos + Vector2(size.x, 0), color, width)
|
||||
#Left line
|
||||
draw_line(pos, pos + Vector2(0, size.y), color, width)
|
||||
#Bottom line
|
||||
draw_line(pos + Vector2(0, size.y), pos + Vector2(size.x, size.y), color, width)
|
||||
#Right line
|
||||
draw_line(pos + Vector2(size.x, 0), pos + Vector2(size.x, size.y), color, width)
|
||||
|
||||
|
||||
func _process(delta):
|
||||
if not is_visible_in_tree():
|
||||
return
|
||||
update()
|
34
addons/Godoxel/Colors.gd
Normal file
34
addons/Godoxel/Colors.gd
Normal file
@ -0,0 +1,34 @@
|
||||
tool
|
||||
extends GridContainer
|
||||
|
||||
signal color_change_request
|
||||
|
||||
func _enter_tree():
|
||||
for child in get_children():
|
||||
child.set("custom_styles/normal", StyleBoxFlat.new())
|
||||
child.get("custom_styles/normal").set("bg_color", Color(randf(), randf(), randf()))
|
||||
for child in get_children():
|
||||
if child.is_connected("pressed", self, "change_color_to"):
|
||||
return
|
||||
child.connect("pressed", self, "change_color_to", [child.get("custom_styles/normal").bg_color])
|
||||
|
||||
|
||||
func change_color_to(color):
|
||||
emit_signal("color_change_request", color)
|
||||
|
||||
|
||||
func add_color_prefab(color: Color):
|
||||
var dup = get_child(0).duplicate()
|
||||
add_child(dup)
|
||||
move_child(dup, 0)
|
||||
dup.set("custom_styles/normal", StyleBoxFlat.new())
|
||||
dup.get("custom_styles/normal").set("bg_color", color)
|
||||
for child in get_children():
|
||||
if child.is_connected("pressed", self, "change_color_to"):
|
||||
return
|
||||
child.connect("pressed", self, "change_color_to", [child.get("custom_styles/normal").bg_color])
|
||||
|
||||
|
||||
|
||||
|
||||
|
9
addons/Godoxel/DebugTextDisplay.gd
Normal file
9
addons/Godoxel/DebugTextDisplay.gd
Normal file
@ -0,0 +1,9 @@
|
||||
extends RichTextLabel
|
||||
tool
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
|
||||
func display_text(text):
|
||||
self.text = text
|
1577
addons/Godoxel/Editor.gd
Normal file
1577
addons/Godoxel/Editor.gd
Normal file
File diff suppressed because it is too large
Load Diff
1309
addons/Godoxel/Editor.tscn
Normal file
1309
addons/Godoxel/Editor.tscn
Normal file
File diff suppressed because one or more lines are too long
107
addons/Godoxel/Frame.gd
Normal file
107
addons/Godoxel/Frame.gd
Normal file
@ -0,0 +1,107 @@
|
||||
extends Control
|
||||
class_name GEFrame
|
||||
tool
|
||||
|
||||
|
||||
var layers = []
|
||||
|
||||
var width: int
|
||||
var height: int
|
||||
|
||||
var preview_texture: ImageTexture = ImageTexture.new()
|
||||
var preview_dirty = false
|
||||
var preview_updated = true
|
||||
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
|
||||
func _draw():
|
||||
# return
|
||||
# if not preview_dirty:
|
||||
# return
|
||||
# preview_dirty = false
|
||||
for layer in layers:
|
||||
layer.update_texture()
|
||||
#_update_preview()
|
||||
|
||||
|
||||
func _update_preview():
|
||||
var image = Image.new()
|
||||
image.create(width, height, true, Image.FORMAT_RGBA8)
|
||||
image.lock()
|
||||
image.fill(Color.transparent)
|
||||
image.unlock()
|
||||
image.lock()
|
||||
|
||||
for layer in layers:
|
||||
if not layer.visible:
|
||||
continue
|
||||
|
||||
for x in range(width):
|
||||
for y in range(height):
|
||||
var color = layer.get_pixel(x, y)
|
||||
var image_color = image.get_pixel(x, y)
|
||||
|
||||
if color.a != 0:
|
||||
image.set_pixel(x, y, color)
|
||||
else:
|
||||
image.set_pixel(x, y, image_color.blend(color))
|
||||
image.unlock()
|
||||
preview_texture.create_from_image(image)
|
||||
preview_updated = true
|
||||
|
||||
|
||||
func get_preview_texture():
|
||||
return preview_texture
|
||||
|
||||
|
||||
func set_layers(new_layers: Array):
|
||||
for layer in new_layers:
|
||||
add_frame_layer(layer)
|
||||
|
||||
|
||||
func add_frame_layer(layer: GELayer):
|
||||
layers.append(layer)
|
||||
add_child(layer.texture_rect_ref, true)
|
||||
return layer
|
||||
|
||||
|
||||
func resize(width: int, height: int):
|
||||
self.width = width
|
||||
self.height = height
|
||||
for layer in layers:
|
||||
layer.resize(width, height)
|
||||
|
||||
|
||||
func get_content_margin() -> Rect2:
|
||||
var rect = Rect2(999999, 999999, -999999, -999999)
|
||||
for layer in layers:
|
||||
var r = layer.image.get_used_rect()
|
||||
if r.position.x < rect.position.x:
|
||||
rect.position.x = r.position.x
|
||||
if r.position.y < rect.position.y:
|
||||
rect.position.y = r.position.y
|
||||
if r.size.x > rect.size.x:
|
||||
rect.size.x = r.size.x
|
||||
if r.size.y > rect.size.y:
|
||||
rect.size.y = r.size.y
|
||||
return rect
|
||||
|
||||
|
||||
func crop_to_content():
|
||||
var rect = get_content_margin()
|
||||
|
||||
#print(rect)
|
||||
|
||||
for layer in layers:
|
||||
layer.image
|
||||
|
||||
# set_canvas_width(rect.size.x)
|
||||
# set_canvas_height(rect.size.x)
|
||||
|
||||
# preview_layer.resize(width, height)
|
||||
# tool_layer.resize(width, height)
|
||||
# for layer in layers:
|
||||
# layer.resize(width, height)
|
22
addons/Godoxel/LICENSE
Normal file
22
addons/Godoxel/LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Flairieve
|
||||
Copyright (c) 2020 cobrapitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
107
addons/Godoxel/Layer.gd
Normal file
107
addons/Godoxel/Layer.gd
Normal file
@ -0,0 +1,107 @@
|
||||
extends Reference
|
||||
class_name GELayer
|
||||
tool
|
||||
|
||||
|
||||
var name
|
||||
var layer_width
|
||||
var layer_height
|
||||
var visible = true setget set_visible
|
||||
var locked = false
|
||||
var alpha_locked = false
|
||||
|
||||
var texture: ImageTexture
|
||||
var image: Image
|
||||
var texture_rect_ref
|
||||
|
||||
|
||||
func _init():
|
||||
texture = ImageTexture.new()
|
||||
|
||||
|
||||
func create(texture_rect_ref, width: int, height: int):
|
||||
self.texture_rect_ref = texture_rect_ref
|
||||
|
||||
assert(width > 0 and height > 0, "Texture size invalid!")
|
||||
|
||||
layer_width = width
|
||||
layer_height = height
|
||||
|
||||
image = Image.new()
|
||||
image.create(width, height, false, Image.FORMAT_RGBA8)
|
||||
image.fill(Color.transparent)
|
||||
update_texture()
|
||||
|
||||
|
||||
func resize(width: int, height: int):
|
||||
var pixel_colors = []
|
||||
var prev_width = layer_width
|
||||
var prev_height = layer_height
|
||||
|
||||
image.lock()
|
||||
for y in range(prev_height):
|
||||
for x in range(prev_width):
|
||||
pixel_colors.append(image.get_pixel(x, y))
|
||||
image.unlock()
|
||||
|
||||
layer_width = width
|
||||
layer_height = height
|
||||
|
||||
image.create(width, height, false, Image.FORMAT_RGBA8)
|
||||
image.fill(Color.transparent)
|
||||
|
||||
image.lock()
|
||||
for x in range(prev_width):
|
||||
for y in range(prev_height):
|
||||
if x >= width or y >= height:
|
||||
continue
|
||||
image.set_pixel(x, y, pixel_colors[GEUtils.to_1D(x, y, prev_width)])
|
||||
image.unlock()
|
||||
|
||||
update_texture()
|
||||
|
||||
|
||||
func set_pixel(x, y, color):
|
||||
image.lock()
|
||||
image.set_pixel(x, y, color)
|
||||
image.unlock()
|
||||
|
||||
|
||||
func get_pixel(x: int, y: int):
|
||||
if x < 0 or y < 0 or x >= image.get_width() or y >= image.get_height():
|
||||
return null
|
||||
image.lock()
|
||||
var pixel = image.get_pixel(x, y)
|
||||
image.unlock()
|
||||
return pixel
|
||||
|
||||
|
||||
func clear():
|
||||
image.fill(Color.transparent)
|
||||
update_texture()
|
||||
|
||||
|
||||
func update_texture():
|
||||
texture.create_from_image(image, 0)
|
||||
texture_rect_ref.texture = texture
|
||||
texture_rect_ref.margin_right = 0
|
||||
texture_rect_ref.margin_bottom = 0
|
||||
|
||||
|
||||
func set_visible(vis: bool):
|
||||
# TODO upate frame preview when hide/show
|
||||
visible = vis
|
||||
texture_rect_ref.visible = visible
|
||||
|
||||
|
||||
func toggle_lock():
|
||||
locked = not locked
|
||||
|
||||
|
||||
func toggle_alpha_locked():
|
||||
alpha_locked = not alpha_locked
|
||||
|
||||
|
||||
func copy_from(other: GELayer):
|
||||
image.copy_from(other.image)
|
||||
|
108
addons/Godoxel/LayerButton.tscn
Normal file
108
addons/Godoxel/LayerButton.tscn
Normal file
@ -0,0 +1,108 @@
|
||||
[gd_scene load_steps=11 format=2]
|
||||
|
||||
[ext_resource path="res://addons/Godoxel/assets/minidotta_invis.png" type="Texture" id=1]
|
||||
[ext_resource path="res://addons/Godoxel/assets/minidotta.png" type="Texture" id=2]
|
||||
[ext_resource path="res://addons/Godoxel/assets/arrow_down.png" type="Texture" id=3]
|
||||
[ext_resource path="res://addons/Godoxel/assets/arrow_up.png" type="Texture" id=4]
|
||||
[ext_resource path="res://addons/Godoxel/assets/lock_layer_1.png" type="Texture" id=5]
|
||||
[ext_resource path="res://addons/Godoxel/assets/unlock_layer.png" type="Texture" id=6]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id=4]
|
||||
resource_local_to_scene = true
|
||||
bg_color = Color( 0.354706, 0.497302, 0.769531, 1 )
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id=1]
|
||||
bg_color = Color( 0.25098, 0.25098, 0.25098, 0 )
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id=2]
|
||||
bg_color = Color( 0.6, 0.6, 0.6, 0 )
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id=3]
|
||||
bg_color = Color( 0.6, 0.6, 0.6, 0 )
|
||||
|
||||
[node name="Layer1" type="Panel"]
|
||||
show_behind_parent = true
|
||||
anchor_right = 0.113281
|
||||
anchor_bottom = 0.0416667
|
||||
margin_bottom = -1.90735e-06
|
||||
rect_min_size = Vector2( 0, 32 )
|
||||
mouse_filter = 2
|
||||
custom_styles/panel = SubResource( 4 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": true
|
||||
}
|
||||
|
||||
[node name="Select" type="Button" parent="." groups=["layer_button"]]
|
||||
anchor_right = 0.827586
|
||||
anchor_bottom = 1.0
|
||||
custom_styles/hover = SubResource( 1 )
|
||||
custom_styles/pressed = SubResource( 1 )
|
||||
custom_styles/focus = SubResource( 1 )
|
||||
custom_styles/disabled = SubResource( 1 )
|
||||
custom_styles/normal = SubResource( 1 )
|
||||
text = "Layer 1"
|
||||
align = 2
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": true
|
||||
}
|
||||
|
||||
[node name="Visible" type="CheckButton" parent="."]
|
||||
anchor_top = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = 3.0
|
||||
margin_top = -8.5
|
||||
margin_right = 19.0
|
||||
margin_bottom = 7.5
|
||||
custom_icons/off = ExtResource( 1 )
|
||||
custom_icons/on = ExtResource( 2 )
|
||||
custom_styles/normal = SubResource( 2 )
|
||||
pressed = true
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Lock" type="CheckButton" parent="."]
|
||||
anchor_top = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = 22.0
|
||||
margin_top = -11.0
|
||||
margin_right = 46.0
|
||||
margin_bottom = 11.0
|
||||
custom_icons/off = ExtResource( 6 )
|
||||
custom_icons/on = ExtResource( 5 )
|
||||
custom_styles/normal = SubResource( 3 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = -20.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Up" type="TextureButton" parent="VBoxContainer"]
|
||||
margin_right = 20.0
|
||||
margin_bottom = 14.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
texture_normal = ExtResource( 4 )
|
||||
texture_pressed = ExtResource( 2 )
|
||||
expand = true
|
||||
stretch_mode = 3
|
||||
|
||||
[node name="Down" type="TextureButton" parent="VBoxContainer"]
|
||||
margin_top = 18.0
|
||||
margin_right = 20.0
|
||||
margin_bottom = 32.0
|
||||
rect_min_size = Vector2( 20, 0 )
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
texture_normal = ExtResource( 3 )
|
||||
texture_pressed = ExtResource( 2 )
|
||||
expand = true
|
||||
stretch_mode = 3
|
13
addons/Godoxel/MenuButtonExtended.gd
Normal file
13
addons/Godoxel/MenuButtonExtended.gd
Normal file
@ -0,0 +1,13 @@
|
||||
tool
|
||||
extends MenuButton
|
||||
|
||||
var popup = get_popup()
|
||||
signal item_pressed
|
||||
|
||||
func _ready():
|
||||
popup.connect("id_pressed", self, "id_pressed")
|
||||
|
||||
func id_pressed(id):
|
||||
emit_signal("item_pressed", name, popup.get_item_text(id), id)
|
||||
|
||||
|
139
addons/Godoxel/Navbar.gd
Normal file
139
addons/Godoxel/Navbar.gd
Normal file
@ -0,0 +1,139 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
var editor
|
||||
var paint_canvas
|
||||
|
||||
func _ready():
|
||||
editor = owner
|
||||
paint_canvas = editor.find_node("PaintCanvas")
|
||||
|
||||
for i in get_node("Buttons").get_children():
|
||||
i.connect("item_pressed", self, "button_pressed")
|
||||
|
||||
|
||||
func button_pressed(button_name, button_item, id):
|
||||
# print("pressed: ", button_name)
|
||||
# print("pressed item is: '%s'" % button_item)
|
||||
|
||||
match button_name:
|
||||
"File":
|
||||
handle_file_menu(button_item, id)
|
||||
"Edit":
|
||||
handle_edit_menu(button_item, id)
|
||||
"Canvas":
|
||||
handle_canvas_menu(button_item, id)
|
||||
"Layer":
|
||||
handle_layer_menu(button_item, id)
|
||||
"Frame":
|
||||
handle_frame_menu(button_item, id)
|
||||
"Grid":
|
||||
handle_grid_menu(button_item, id)
|
||||
"Magic":
|
||||
handle_magic_menu(button_item, id)
|
||||
"Editor":
|
||||
handle_editor_menu(button_item, id)
|
||||
|
||||
|
||||
func handle_file_menu(pressed_item: String, id):
|
||||
match pressed_item:
|
||||
"Save Project":
|
||||
owner.get_node("SaveFileDialog").open_save_project()
|
||||
"Load Project":
|
||||
owner.get_node("LoadFileDialog").open_load_project()
|
||||
"New Project":
|
||||
owner.get_node("ConfirmationDialog").show()
|
||||
"Export (PNG)":
|
||||
owner.get_node("SaveFileDialog").open_save_current_frame()
|
||||
"Export Frame":
|
||||
owner.get_node("SaveFileDialog").open_save_current_frame()
|
||||
"Export Layer":
|
||||
owner.get_node("SaveFileDialog").open_save_current_layer()
|
||||
"Export Selection":
|
||||
print("Not implemented!")
|
||||
"Import (PNG)":
|
||||
owner.get_node("LoadFileDialog").open_import_image()
|
||||
|
||||
|
||||
func handle_edit_menu(pressed_item: String, id):
|
||||
match pressed_item:
|
||||
"Add Layer":
|
||||
editor.add_new_layer()
|
||||
|
||||
|
||||
func handle_canvas_menu(pressed_item: String, id):
|
||||
match pressed_item:
|
||||
"Change Size":
|
||||
owner.get_node("ChangeCanvasSize").show()
|
||||
"Crop To Content":
|
||||
owner.paint_canvas.crop_to_content()
|
||||
|
||||
|
||||
func handle_layer_menu(pressed_item: String, id):
|
||||
match pressed_item:
|
||||
"Add Layer":
|
||||
editor.add_new_layer()
|
||||
"Delete Layer":
|
||||
editor.remove_active_layer()
|
||||
"Duplicate Layer":
|
||||
editor.duplicate_active_layer()
|
||||
"Clear Layer":
|
||||
owner.paint_canvas.clear_active_layer()
|
||||
"Toggle Alpha Locked":
|
||||
owner.paint_canvas.active_layer.toggle_alpha_locked()
|
||||
$Buttons/Layer.get_popup().set_item_checked(id, not $Buttons/Layer.get_popup().is_item_checked(id))
|
||||
owner.find_node("LockAlpha").pressed = $Buttons/Layer.get_popup().is_item_checked(id)
|
||||
|
||||
|
||||
func handle_frame_menu(pressed_item: String, id):
|
||||
match pressed_item:
|
||||
"Add Frame":
|
||||
var frame_button = editor.anim_panel.get_animation_stripe(editor.current_animation_idx).add_new_frame_button()
|
||||
editor._on_add_frame_pressed(editor.current_animation_idx, frame_button.get_index())
|
||||
"Delete Frame":
|
||||
editor.delete_current_frame()
|
||||
"Duplicate Frame":
|
||||
pass
|
||||
"Clear Frame Layers":
|
||||
pass
|
||||
_:
|
||||
printerr("Pressed: ", pressed_item, " not found!")
|
||||
|
||||
|
||||
func handle_grid_menu(pressed_item: String, id):
|
||||
match pressed_item:
|
||||
"Change Grid Size":
|
||||
owner.get_node("ChangeGridSizeDialog").show()
|
||||
"Toggle Grid":
|
||||
owner.paint_canvas.toggle_grid()
|
||||
|
||||
|
||||
func handle_magic_menu(pressed_item: String, id):
|
||||
match pressed_item:
|
||||
"Add Layer":
|
||||
editor.add_new_layer()
|
||||
|
||||
|
||||
func handle_editor_menu(pressed_item: String, id):
|
||||
match pressed_item:
|
||||
"Settings":
|
||||
owner.get_node("Settings").show()
|
||||
"Show Preview":
|
||||
owner.preview_window.visible = not owner.preview_window.visible
|
||||
$Buttons/Editor.get_popup().set_item_checked(id, owner.preview_window.visible)
|
||||
"Toggle Grid":
|
||||
var grids_node = owner.find_node("Grids")
|
||||
grids_node.visible = !grids_node.visible
|
||||
"Reset Canvas Position":
|
||||
owner.paint_canvas_node.rect_position = Vector2(0, 0)
|
||||
"Show Animation Panel":
|
||||
owner.anim_panel.visible = not owner.anim_panel.visible
|
||||
$Buttons/Editor.get_popup().set_item_checked(id, owner.anim_panel.visible)
|
||||
|
||||
|
||||
func is_any_menu_open() -> bool:
|
||||
for child in $Buttons.get_children():
|
||||
if child.get_popup().visible:
|
||||
return true
|
||||
return false
|
||||
|
500
addons/Godoxel/PaintCanvas.gd
Normal file
500
addons/Godoxel/PaintCanvas.gd
Normal file
@ -0,0 +1,500 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
var image = Image.new()
|
||||
var last_pixel = []
|
||||
onready var canvas_image_node = get_node("CanvasImage")
|
||||
export var grid_size = 16
|
||||
export var canvas_size = Vector2(48, 28)
|
||||
export var region_size = 10
|
||||
export var can_draw = true
|
||||
|
||||
var mouse_in_region
|
||||
var mouse_on_top
|
||||
|
||||
#terms
|
||||
#global cell - a cell that has a global grid position on the canvas
|
||||
#local cell - a cell that has a local grid position in a chunk region on the canvas
|
||||
#chunk region - a set of cells contained in an even number
|
||||
|
||||
#TODO: Maybe each chunk region can hold an image resource
|
||||
# so that way the engine wouldn't lag at all when updating the canvas
|
||||
|
||||
var layers = {}
|
||||
var active_layer
|
||||
|
||||
var preview_layer = "preview"
|
||||
var preview_enabled = false
|
||||
|
||||
|
||||
func _enter_tree():
|
||||
#----------------------
|
||||
# init Layer
|
||||
#----------------------
|
||||
layers[preview_layer] = {
|
||||
"layer": null,
|
||||
"data": [],
|
||||
"chunks": null,
|
||||
}
|
||||
|
||||
canvas_size = Vector2(int(rect_size.x / grid_size), int(rect_size.y / grid_size))
|
||||
#print("canvas_size: ", canvas_size)
|
||||
|
||||
|
||||
func _ready():
|
||||
active_layer = add_existing_layer(get_tree().get_nodes_in_group("layer")[0])
|
||||
#print("active Layer: ", active_layer)
|
||||
|
||||
|
||||
func get_layer_data(layer_name):
|
||||
return layers[layer_name]
|
||||
|
||||
|
||||
func get_active_layer():
|
||||
return layers[active_layer]
|
||||
|
||||
|
||||
func get_preview_layer():
|
||||
return layers[preview_layer]
|
||||
|
||||
|
||||
func clear_active_layer():
|
||||
for pixel in layers[active_layer].data:
|
||||
set_global_cell_in_chunk(pixel[0], pixel[1], Color(0,0,0,0))
|
||||
|
||||
|
||||
func clear_layer(layer_name: String):
|
||||
for pixel in layers[layer_name].data:
|
||||
set_global_cell_in_chunk(pixel[0], pixel[1], Color(0,0,0,0))
|
||||
|
||||
|
||||
func clear_preview_layer():
|
||||
for pixel in layers["preview"].data:
|
||||
set_global_cell_in_chunk(pixel[0], pixel[1], Color(0,0,0,0))
|
||||
|
||||
|
||||
func remove_layer(layer_name):
|
||||
get_node("ChunkNodes").remove_child(layers[layer_name].chunks)
|
||||
layers[layer_name].chunks.queue_free()
|
||||
|
||||
layers.erase(layer_name)
|
||||
|
||||
if active_layer == layer_name:
|
||||
for layer in layers:
|
||||
if layer == preview_layer:
|
||||
continue
|
||||
active_layer = layer
|
||||
break
|
||||
|
||||
return active_layer
|
||||
|
||||
|
||||
|
||||
# only needed for init
|
||||
func add_existing_layer(layer):
|
||||
layers[layer.name] = {
|
||||
"layer": layer,
|
||||
"data": [],
|
||||
"chunks": null,
|
||||
}
|
||||
generate_chunks()
|
||||
return layer.name
|
||||
|
||||
|
||||
func add_new_layer(layer_name):
|
||||
layers[layer_name] = {
|
||||
"layer": null,
|
||||
"data": [],
|
||||
"chunks": null,
|
||||
}
|
||||
|
||||
generate_chunks()
|
||||
|
||||
return layer_name
|
||||
|
||||
|
||||
func duplicate_layer(layer: String, neu_layer_name: String):
|
||||
var _preview = preview_enabled
|
||||
preview_enabled = false
|
||||
var _temp = active_layer
|
||||
active_layer = neu_layer_name
|
||||
|
||||
layers[neu_layer_name] = {
|
||||
"layer": null,
|
||||
"data": layers[layer].data.duplicate(true),
|
||||
"chunks": null,
|
||||
}
|
||||
|
||||
generate_chunks()
|
||||
# get_node("ChunkNodes").remove_child(layers[neu_layer_name].chunks)
|
||||
# get_node("ChunkNodes").add_child_below_node(layers[layer].chunks, layers[neu_layer_name].chunks, true)
|
||||
|
||||
for pixel in layers[neu_layer_name].data:
|
||||
set_pixel_cell(pixel[0], pixel[1], pixel[2])
|
||||
active_layer = _temp
|
||||
|
||||
preview_enabled = _preview
|
||||
return neu_layer_name
|
||||
|
||||
|
||||
func toggle_layer_visibility(layer_name):
|
||||
layers[layer_name].chunks.visible = not layers[layer_name].chunks.visible
|
||||
#print("Layer: ", layer_name, " is now: ", layers[layer_name].chunks.visible)
|
||||
|
||||
|
||||
var util = preload("res://addons/Godoxel/Util.gd")
|
||||
|
||||
|
||||
func _on_mouse_entered():
|
||||
mouse_on_top = true
|
||||
|
||||
|
||||
func _on_mouse_exited():
|
||||
mouse_on_top = false
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
var mouse_position = get_local_mouse_position()
|
||||
var rect = Rect2(Vector2(0, 0), rect_size)
|
||||
mouse_in_region = rect.has_point(mouse_position)
|
||||
|
||||
update()
|
||||
#if not Engine.editor_hint:
|
||||
# print(mouse_on_canvas, " | ", has_focus())
|
||||
#draw_canvas_out just updates the image constantly
|
||||
#if can_draw:
|
||||
# draw_canvas_out()
|
||||
|
||||
|
||||
func generate_chunks():
|
||||
var maxium_chunk_size = get_maxium_filled_chunks()
|
||||
#TODO: We probably don't need to check for x and y anymore
|
||||
for key in layers:
|
||||
if layers[key].chunks != null:
|
||||
continue
|
||||
|
||||
var chunk_node = Control.new()
|
||||
get_node("ChunkNodes").add_child(chunk_node)
|
||||
chunk_node.owner = self
|
||||
|
||||
layers[key].chunks = chunk_node
|
||||
|
||||
for x in maxium_chunk_size.x:
|
||||
for y in maxium_chunk_size.y:
|
||||
var paint_canvas_chunk = load("res://addons/Godoxel/PaintCanvasChunk.tscn").instance()
|
||||
paint_canvas_chunk.setup(region_size)
|
||||
paint_canvas_chunk.name = "C-%s-%s" % [x, y]
|
||||
paint_canvas_chunk.rect_position = \
|
||||
Vector2(x * (grid_size * region_size), y * (grid_size * region_size))
|
||||
layers[key].chunks.add_child(paint_canvas_chunk)
|
||||
|
||||
|
||||
func get_maxium_filled_chunks():
|
||||
return Vector2(canvas_size.x / region_size, canvas_size.y / region_size).ceil()
|
||||
|
||||
|
||||
##TODO: Remake these functions with godot's setget features
|
||||
#func resize_grid(grid):
|
||||
# #print(grid)
|
||||
# if grid <= 0:
|
||||
# return
|
||||
# grid_size = grid
|
||||
# canvas_image_node.rect_scale = Vector2(grid, grid)
|
||||
#
|
||||
#
|
||||
#func resize_canvas(x, y):
|
||||
# image.unlock()
|
||||
# image.create(x, y, true, Image.FORMAT_RGBA8)
|
||||
# canvas_size = Vector2(x, y)
|
||||
# #setup_all_chunks()
|
||||
# image.lock()
|
||||
|
||||
#func draw_canvas_out(a = ""):
|
||||
# if canvas_image_node == null:
|
||||
# return
|
||||
# var image_texture = ImageTexture.new()
|
||||
# image_texture.create_from_image(image)
|
||||
# image_texture.set_flags(0)
|
||||
# canvas_image_node.texture = image_texture
|
||||
|
||||
|
||||
func get_wrapped_region_cell(x, y):
|
||||
return Vector2(wrapi(x, 0, region_size), wrapi(y, 0, region_size))
|
||||
|
||||
|
||||
func get_region_from_cell(x, y):
|
||||
return Vector2(floor(x / region_size), floor(y / region_size))
|
||||
|
||||
|
||||
func set_local_cell_in_chunk(chunk_x, chunk_y, local_cell_x, local_cell_y, color):
|
||||
var chunk_node
|
||||
|
||||
if preview_enabled:
|
||||
chunk_node = layers.preview.chunks.get_node_or_null("C-%s-%s" % [chunk_x, chunk_y])
|
||||
else:
|
||||
chunk_node = layers[active_layer].chunks.get_node_or_null("C-%s-%s" % [chunk_x, chunk_y])
|
||||
|
||||
if chunk_node == null:
|
||||
#print("Can't find chunk node!")
|
||||
return
|
||||
chunk_node.set_cell(local_cell_x, local_cell_y, color)
|
||||
|
||||
|
||||
func set_global_cell_in_chunk(cell_x, cell_y, color):
|
||||
var chunk = get_region_from_cell(cell_x, cell_y)
|
||||
var wrapped_cell = get_wrapped_region_cell(cell_x, cell_y)
|
||||
set_local_cell_in_chunk(chunk.x, chunk.y, wrapped_cell.x, wrapped_cell.y, color)
|
||||
|
||||
#func update_chunk_region_from_cell(x, y):
|
||||
# var region_to_update = get_region_from_cell(x, y)
|
||||
# update_chunk_region(region_to_update.x, region_to_update.y)
|
||||
|
||||
func get_pixel_cell_color(x, y):
|
||||
if not cell_in_canvas_region(x, y):
|
||||
return null
|
||||
var pixel_cell = get_pixel_cell(x, y)
|
||||
if pixel_cell == null:
|
||||
#We already checked that the cell can't be out of the canvas
|
||||
#region so we can assume the pixel cell is completely transparent if it's null
|
||||
return Color(0, 0, 0, 0)
|
||||
else:
|
||||
return util.color_from_array(pixel_cell[2])
|
||||
|
||||
func get_pixel_cell_color_v(vec2):
|
||||
return get_pixel_cell_color(vec2.x, vec2.y)
|
||||
|
||||
func get_pixel_cell(x, y):
|
||||
if active_layer == null:
|
||||
return
|
||||
if not cell_in_canvas_region(x, y):
|
||||
return null
|
||||
|
||||
for pixel in get_active_layer().data:
|
||||
if pixel[0] == x and pixel[1] == y:
|
||||
return pixel
|
||||
|
||||
return null
|
||||
|
||||
func get_pixel_cell_v(vec2):
|
||||
return get_pixel_cell(vec2.x, vec2.y)
|
||||
|
||||
#func remove_pixel_cell(x, y):
|
||||
# if can_draw == false:
|
||||
# return false
|
||||
# if not cell_in_canvas_region(x, y):
|
||||
# return false
|
||||
# var layer_data = get_layer_data("Layer 1")
|
||||
# for pixel in range(0, layer_data.size()):
|
||||
# if layer_data[pixel][0] == x and layer_data[pixel][1] == y:
|
||||
# layer_data.remove(pixel)
|
||||
# #update_chunk_region_from_cell(x, y)
|
||||
# #TOOD: If pixel exists in temp_pool_pixels then remove it
|
||||
# image.set_pixel(x, y, Color(0, 0, 0, 0))
|
||||
# return true
|
||||
# return false
|
||||
|
||||
#func remove_pixel_cell_v(vec2):
|
||||
# return remove_pixel_cell(vec2.x, vec2.y)
|
||||
|
||||
func set_pixel_cell(x, y, color):
|
||||
if can_draw == false:
|
||||
return false
|
||||
|
||||
if not cell_in_canvas_region(x, y):
|
||||
return false
|
||||
|
||||
var layer
|
||||
if preview_enabled:
|
||||
layer = get_preview_layer()
|
||||
else:
|
||||
layer = get_active_layer()
|
||||
|
||||
var index = 0
|
||||
for pixel in layer.data:
|
||||
#TODO: Make a better way of accessing the array because the more pixels we have,
|
||||
#the longer it takes to
|
||||
#set the pixel
|
||||
if pixel[0] == x and pixel[1] == y:
|
||||
#No reason to set the pixel again if the colors are the same
|
||||
|
||||
#If the color we are setting is 0, 0, 0, 0 then there is
|
||||
#no reason to keep the information about the pixel
|
||||
#so we remove it from the layer data
|
||||
if color == Color(0, 0, 0, 0):
|
||||
layer.data.remove(index)
|
||||
else:
|
||||
pixel[2] = color
|
||||
#TODO: The new system is going to allow chunks to each have their own TextureRect and Image
|
||||
#nodes so what we are doing in here is
|
||||
#that we are setting the local cell in the region of that image
|
||||
set_global_cell_in_chunk(x, y, color)
|
||||
last_pixel = [x, y, color]
|
||||
return true
|
||||
index += 1
|
||||
#don't append any data if the color is 0, 0, 0, 0
|
||||
if color != Color(0, 0, 0, 0):
|
||||
#if the pixel data doesn't exist then we add it in
|
||||
layer.data.append([x, y, color])
|
||||
set_global_cell_in_chunk(x, y, color)
|
||||
last_pixel = [x, y, color]
|
||||
return true
|
||||
|
||||
func set_pixel_cell_v(vec2, color):
|
||||
return set_pixel_cell(vec2.x, vec2.y, color)
|
||||
|
||||
func set_pixels_from_line(vec2_1, vec2_2, color):
|
||||
var points = get_pixels_from_line(vec2_1, vec2_2)
|
||||
for i in points:
|
||||
set_pixel_cell_v(i, color)
|
||||
|
||||
func set_random_pixels_from_line(vec2_1, vec2_2):
|
||||
var points = get_pixels_from_line(vec2_1, vec2_2)
|
||||
for i in points:
|
||||
set_pixel_cell_v(i, util.random_color_alt())
|
||||
|
||||
func get_pixels_from_line(vec2_1, vec2_2):
|
||||
var points = PoolVector2Array()
|
||||
|
||||
var dx = abs(vec2_2.x - vec2_1.x)
|
||||
var dy = abs(vec2_2.y - vec2_1.y)
|
||||
|
||||
var x = vec2_1.x
|
||||
var y = vec2_1.y
|
||||
|
||||
var sx = 0
|
||||
if vec2_1.x > vec2_2.x:
|
||||
sx = -1
|
||||
else:
|
||||
sx = 1
|
||||
|
||||
var sy = 0
|
||||
if vec2_1.y > vec2_2.y:
|
||||
sy = -1
|
||||
else:
|
||||
sy = 1
|
||||
|
||||
if dx > dy:
|
||||
var err = dx / 2
|
||||
while(true):
|
||||
if x == vec2_2.x:
|
||||
break
|
||||
points.push_back(Vector2(x, y))
|
||||
|
||||
err -= dy
|
||||
if err < 0:
|
||||
y += sy
|
||||
err += dx
|
||||
x += sx
|
||||
else:
|
||||
var err = dy / 2
|
||||
while (true):
|
||||
if y == vec2_2.y:
|
||||
break
|
||||
points.push_back(Vector2(x, y))
|
||||
|
||||
err -= dx
|
||||
if err < 0:
|
||||
x += sx
|
||||
err += dy
|
||||
y += sy
|
||||
points.push_back(Vector2(x, y))
|
||||
return points
|
||||
|
||||
|
||||
#even though the function checks for it, we can't afford adding more functions to the call stack
|
||||
#because godot has a limit until it crashes
|
||||
var flood_fill_queue = 0
|
||||
func flood_fill(x, y, target_color, replacement_color):
|
||||
#yield(get_tree().create_timer(1), "timeout")
|
||||
flood_fill_queue += 1
|
||||
if not cell_in_canvas_region(x, y):
|
||||
flood_fill_queue -= 1
|
||||
return
|
||||
if target_color == replacement_color:
|
||||
flood_fill_queue -= 1
|
||||
return
|
||||
elif not get_pixel_cell_color(x, y) == target_color:
|
||||
flood_fill_queue -= 1
|
||||
return
|
||||
else:
|
||||
set_pixel_cell(x, y, replacement_color)
|
||||
if flood_fill_queue >= 500:
|
||||
#print(flood_fill_queue)
|
||||
yield(get_tree().create_timer(0.01), "timeout")
|
||||
#up
|
||||
if get_pixel_cell_color(x, y - 1) == target_color:
|
||||
flood_fill(x, y - 1, target_color, replacement_color)
|
||||
#down
|
||||
if get_pixel_cell_color(x, y + 1) == target_color:
|
||||
flood_fill(x, y + 1, target_color, replacement_color)
|
||||
#left
|
||||
if get_pixel_cell_color(x - 1, y) == target_color:
|
||||
flood_fill(x - 1, y, target_color, replacement_color)
|
||||
#right
|
||||
if get_pixel_cell_color(x + 1, y) == target_color:
|
||||
flood_fill(x + 1, y, target_color, replacement_color)
|
||||
flood_fill_queue -= 1
|
||||
return
|
||||
|
||||
#func flood_fill_erase(x, y, target_color):
|
||||
# yield(get_tree().create_timer(0.001), "timeout")
|
||||
# if not cell_in_canvas_region(x, y):
|
||||
# print("cell not in canvas")
|
||||
# return
|
||||
# #if target_color == replacement_color:
|
||||
# # return
|
||||
# elif not get_pixel_cell_color(x, y) == target_color:
|
||||
# print("cell doesn't match pixel color")
|
||||
# return
|
||||
# elif not get_pixel_cell(x, y):
|
||||
# print("cell already erased")
|
||||
# return
|
||||
# else:
|
||||
# print("removed pixel")
|
||||
# remove_pixel_cell(x, y)
|
||||
# print("x: ", x, " y: ", y, " color: ", target_color)
|
||||
# #up
|
||||
# flood_fill_erase(x, y - 1, target_color)
|
||||
# #down
|
||||
# flood_fill_erase(x, y + 1, target_color)
|
||||
# #left
|
||||
# flood_fill_erase(x - 1, y, target_color)
|
||||
# #right
|
||||
# flood_fill_erase(x + 1, y, target_color)
|
||||
# return
|
||||
|
||||
func cell_in_canvas_region(x, y):
|
||||
if x > canvas_size.x - 1 or x < 0 or y > canvas_size.y - 1 or y < 0:
|
||||
#out of bounds, return false
|
||||
return false
|
||||
else:
|
||||
return true
|
||||
|
||||
#Both of these functions right now just return the starting
|
||||
#position of the canvas and the last position of the canvas
|
||||
func get_all_used_regions_in_canvas():
|
||||
var first_used_region = get_first_used_region_in_canvas()
|
||||
var last_used_region = get_last_used_region_in_canvas()
|
||||
var chunk_pool = PoolVector2Array()
|
||||
for chunk_x in range(first_used_region.x, last_used_region.x):
|
||||
for chunk_y in range(first_used_region.y, last_used_region.y):
|
||||
chunk_pool.append(Vector2(chunk_x, chunk_y))
|
||||
return chunk_pool
|
||||
|
||||
func get_first_used_region_in_canvas():
|
||||
return get_region_from_cell(0, 0)
|
||||
|
||||
func get_last_used_region_in_canvas():
|
||||
return get_region_from_cell(canvas_size.x - 1, canvas_size.y - 1)
|
||||
|
||||
func get_cells_in_region(x, y):
|
||||
var start_cell = Vector2(x * region_size, y * region_size)
|
||||
var end_cell = Vector2((x * region_size) + region_size, (y * region_size) + region_size)
|
||||
var cell_array = []
|
||||
for cx in range(start_cell.x, end_cell.x):
|
||||
for cy in range(start_cell.y, end_cell.y):
|
||||
var pixel_cell = get_pixel_cell(cx, cy)
|
||||
if pixel_cell == null:
|
||||
pixel_cell = [cx, cy, Color(0, 0, 0, 0)]
|
||||
cell_array.append(pixel_cell)
|
||||
return cell_array
|
29
addons/Godoxel/PaintCanvas.tscn
Normal file
29
addons/Godoxel/PaintCanvas.tscn
Normal file
@ -0,0 +1,29 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/Godoxel/PaintCanvas.gd" type="Script" id=1]
|
||||
|
||||
[node name="PaintCanvas" type="Control"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
mouse_filter = 2
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
canvas_size = Vector2( 0, 0 )
|
||||
|
||||
[node name="ChunkNodes" type="Control" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
mouse_filter = 2
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="CanvasImage" type="TextureRect" parent="."]
|
||||
visible = false
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
mouse_filter = 2
|
||||
expand = true
|
||||
stretch_mode = 3
|
27
addons/Godoxel/PaintCanvasChunk.gd
Normal file
27
addons/Godoxel/PaintCanvasChunk.gd
Normal file
@ -0,0 +1,27 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
var image = Image.new()
|
||||
var image_texture = ImageTexture.new()
|
||||
|
||||
func _ready():
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
func setup(region_size):
|
||||
image.create(region_size, region_size, true, Image.FORMAT_RGBA8)
|
||||
image.lock()
|
||||
|
||||
func update_chunk():
|
||||
image_texture.create_from_image(image)
|
||||
image_texture.set_flags(0)
|
||||
self.texture = image_texture
|
||||
|
||||
func set_cell(x, y, color):
|
||||
image.set_pixel(x, y, color)
|
||||
update_chunk()
|
||||
|
||||
func _on_VisibilityNotifier2D_screen_entered():
|
||||
visible = true
|
||||
|
||||
func _on_VisibilityNotifier2D_screen_exited():
|
||||
visible = false
|
13
addons/Godoxel/PaintCanvasChunk.tscn
Normal file
13
addons/Godoxel/PaintCanvasChunk.tscn
Normal file
@ -0,0 +1,13 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/Godoxel/PaintCanvasChunk.gd" type="Script" id=1]
|
||||
|
||||
|
||||
[node name="PaintCanvasChunk" type="TextureRect"]
|
||||
margin_right = 10.0
|
||||
margin_bottom = 10.0
|
||||
rect_scale = Vector2( 16, 16 )
|
||||
mouse_filter = 2
|
||||
expand = true
|
||||
stretch_mode = 1
|
||||
script = ExtResource( 1 )
|
2
addons/Godoxel/PaintCanvasContainer.gd
Normal file
2
addons/Godoxel/PaintCanvasContainer.gd
Normal file
@ -0,0 +1,2 @@
|
||||
tool
|
||||
extends Control
|
14
addons/Godoxel/PreviewWindow.gd
Normal file
14
addons/Godoxel/PreviewWindow.gd
Normal file
@ -0,0 +1,14 @@
|
||||
extends GEDraggableWindow
|
||||
tool
|
||||
|
||||
onready var preview_layers = $PreviewLayerTextures
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
show()
|
||||
|
||||
|
||||
func update_preview(frame: GEFrame):
|
||||
for idx in range(preview_layers.get_child_count()):
|
||||
preview_layers.get_child(idx).texture = frame.layers[idx].texture
|
||||
|
129
addons/Godoxel/SaveFileDialog.gd
Normal file
129
addons/Godoxel/SaveFileDialog.gd
Normal file
@ -0,0 +1,129 @@
|
||||
tool
|
||||
extends FileDialog
|
||||
|
||||
enum SaveMode {
|
||||
CURRENT_FRAME,
|
||||
PROJECT,
|
||||
CURRENT_LAYER,
|
||||
}
|
||||
|
||||
var save_mode = SaveMode.CURRENT_FRAME
|
||||
onready var canvas = get_parent().find_node("Canvas")
|
||||
var file_path = ""
|
||||
|
||||
|
||||
func _ready():
|
||||
# warning-ignore:return_value_discarded
|
||||
get_line_edit().connect("text_entered", self, "_on_LineEdit_text_entered")
|
||||
invalidate()
|
||||
clear_filters()
|
||||
add_filter("*.png ; PNG Images")
|
||||
|
||||
|
||||
|
||||
#######################################################
|
||||
# dialogs
|
||||
#######################################################
|
||||
|
||||
func open_save_current_frame():
|
||||
show()
|
||||
invalidate()
|
||||
clear_filters()
|
||||
add_filter("*.png ; PNG Images")
|
||||
save_mode = SaveMode.CURRENT_FRAME
|
||||
|
||||
|
||||
func open_save_project():
|
||||
show()
|
||||
invalidate()
|
||||
clear_filters()
|
||||
add_filter("*.godoxel ; Godot - Godoxel")
|
||||
save_mode = SaveMode.PROJECT
|
||||
|
||||
|
||||
func open_save_current_layer():
|
||||
show()
|
||||
invalidate()
|
||||
clear_filters()
|
||||
add_filter("*.png ; PNG Images")
|
||||
save_mode = SaveMode.CURRENT_LAYER
|
||||
|
||||
|
||||
|
||||
#######################################################
|
||||
# dialogs
|
||||
#######################################################
|
||||
|
||||
func _on_SaveFileDialog_file_selected(path: String):
|
||||
file_path = path
|
||||
match save_mode:
|
||||
SaveMode.CURRENT_FRAME:
|
||||
save_current_frame()
|
||||
SaveMode.CURRENT_LAYER:
|
||||
save_current_layer()
|
||||
SaveMode.PROJECT:
|
||||
save_project()
|
||||
|
||||
|
||||
func save_current_layer():
|
||||
var image = canvas.get_current_layer_image()
|
||||
|
||||
# overwrite image if exists
|
||||
var dir = Directory.new()
|
||||
if dir.file_exists(file_path):
|
||||
dir.remove(file_path)
|
||||
|
||||
image.save_png(file_path)
|
||||
|
||||
# update file doc if using inside the editor
|
||||
if Engine.is_editor_hint():
|
||||
EditorPlugin.new().get_editor_interface().get_resource_filesystem().scan()
|
||||
|
||||
|
||||
func save_project():
|
||||
var save_data = owner.get_save_project_data()
|
||||
|
||||
# overwrite image if exists
|
||||
var dir = Directory.new()
|
||||
if dir.file_exists(file_path):
|
||||
dir.remove(file_path)
|
||||
|
||||
var file = File.new()
|
||||
file.open(file_path, File.WRITE)
|
||||
file.store_string(JSON.print(save_data))
|
||||
file.close()
|
||||
|
||||
# update file doc if using inside the editor
|
||||
if Engine.is_editor_hint():
|
||||
EditorPlugin.new().get_editor_interface().get_resource_filesystem().scan()
|
||||
|
||||
|
||||
func save_current_frame():
|
||||
var image = canvas.get_current_frame_image()
|
||||
|
||||
# overwrite image if exists
|
||||
var dir = Directory.new()
|
||||
if dir.file_exists(file_path):
|
||||
dir.remove(file_path)
|
||||
|
||||
image.save_png(file_path)
|
||||
|
||||
# update file doc if using inside the editor
|
||||
if Engine.is_editor_hint():
|
||||
EditorPlugin.new().get_editor_interface().get_resource_filesystem().scan()
|
||||
|
||||
|
||||
func _on_SaveFileDialog_about_to_show():
|
||||
invalidate()
|
||||
|
||||
|
||||
func _on_SaveFileDialog_visibility_changed():
|
||||
invalidate()
|
||||
|
||||
|
||||
#func _on_LineEdit_text_entered(text):
|
||||
# return
|
||||
|
||||
|
||||
func _on_SaveFileDialog_confirmed():
|
||||
return
|
24
addons/Godoxel/SelectionBox.gd
Normal file
24
addons/Godoxel/SelectionBox.gd
Normal file
@ -0,0 +1,24 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
export var outline_size = 3
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
func _process(delta):
|
||||
update()
|
||||
|
||||
func _draw():
|
||||
if not rect_size == Vector2():
|
||||
draw_outline_box(rect_size, Color.gray, outline_size)
|
||||
|
||||
func draw_outline_box(size, color, width):
|
||||
#Top line
|
||||
draw_line(Vector2(0 + 1, 0), Vector2(size.x, 0), color, width)
|
||||
#Left line
|
||||
draw_line(Vector2(0 + 1, 0), Vector2(0, size.y), color, width)
|
||||
#Bottom line
|
||||
draw_line(Vector2(0 + 1, size.y), Vector2(size.x, size.y), color, width)
|
||||
#Right line
|
||||
draw_line(Vector2(size.x, 0), Vector2(size.x, size.y), color, width)
|
24
addons/Godoxel/Settings.gd
Normal file
24
addons/Godoxel/Settings.gd
Normal file
@ -0,0 +1,24 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
var editor
|
||||
var canvas_outline
|
||||
var start_time
|
||||
var end_time
|
||||
|
||||
|
||||
func _enter_tree():
|
||||
canvas_outline = get_parent().find_node("CanvasOutline")
|
||||
editor = get_parent()
|
||||
|
||||
|
||||
func _on_ColorPickerButton_color_changed(color):
|
||||
canvas_outline.color = color
|
||||
|
||||
|
||||
func _on_CheckButton_toggled(button_pressed):
|
||||
canvas_outline.visible = button_pressed
|
||||
|
||||
|
||||
func _on_Ok_pressed():
|
||||
hide()
|
64
addons/Godoxel/Settings.tscn
Normal file
64
addons/Godoxel/Settings.tscn
Normal file
@ -0,0 +1,64 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/Godoxel/Settings.gd" type="Script" id=1]
|
||||
|
||||
|
||||
[node name="Settings" type="WindowDialog"]
|
||||
visible = true
|
||||
margin_top = 20.0
|
||||
margin_right = 300.0
|
||||
margin_bottom = 120.0
|
||||
window_title = "Settings"
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="Ok" type="Button" parent="."]
|
||||
margin_left = 210.0
|
||||
margin_top = 70.0
|
||||
margin_right = 290.0
|
||||
margin_bottom = 90.0
|
||||
text = "Ok"
|
||||
|
||||
[node name="CanvasOutlineToggle" type="Control" parent="."]
|
||||
margin_left = 10.0
|
||||
margin_top = 10.0
|
||||
margin_right = 290.0
|
||||
margin_bottom = 30.0
|
||||
__meta__ = {
|
||||
"_edit_group_": true
|
||||
}
|
||||
|
||||
[node name="Label" type="Label" parent="CanvasOutlineToggle"]
|
||||
margin_right = 130.0
|
||||
margin_bottom = 20.0
|
||||
text = "Canvas Outline:"
|
||||
valign = 1
|
||||
|
||||
[node name="CheckButton" type="CheckButton" parent="CanvasOutlineToggle"]
|
||||
margin_left = 210.0
|
||||
margin_top = -10.0
|
||||
margin_right = 286.0
|
||||
margin_bottom = 30.0
|
||||
pressed = true
|
||||
|
||||
[node name="CanvasOutlineColor" type="Control" parent="."]
|
||||
margin_left = 10.0
|
||||
margin_top = 40.0
|
||||
margin_right = 290.0
|
||||
margin_bottom = 60.0
|
||||
__meta__ = {
|
||||
"_edit_group_": true
|
||||
}
|
||||
|
||||
[node name="Label" type="Label" parent="CanvasOutlineColor"]
|
||||
margin_right = 130.0
|
||||
margin_bottom = 20.0
|
||||
text = "Canvas Outline Color:"
|
||||
valign = 1
|
||||
|
||||
[node name="ColorPickerButton" type="ColorPickerButton" parent="CanvasOutlineColor"]
|
||||
margin_left = 170.0
|
||||
margin_right = 280.0
|
||||
margin_bottom = 20.0
|
||||
[connection signal="pressed" from="Ok" to="." method="_on_Ok_pressed"]
|
||||
[connection signal="toggled" from="CanvasOutlineToggle/CheckButton" to="." method="_on_CheckButton_toggled"]
|
||||
[connection signal="color_changed" from="CanvasOutlineColor/ColorPickerButton" to="." method="_on_ColorPickerButton_color_changed"]
|
39
addons/Godoxel/TextInfo.gd
Normal file
39
addons/Godoxel/TextInfo.gd
Normal file
@ -0,0 +1,39 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
var size = 240
|
||||
|
||||
#TODO: To make reading the text easier, the text info with the longest text should have it's length applied to all the
|
||||
#the other text infos
|
||||
|
||||
func add_text_info(text_name, custom_node = null):
|
||||
var last_text_info_child = null
|
||||
var child_count = get_child_count()
|
||||
if not child_count <= 0:
|
||||
last_text_info_child = get_children()[get_children().size() - 1]
|
||||
var label = Label.new()
|
||||
label.name = text_name
|
||||
label.rect_size = Vector2(size, 14)
|
||||
if not last_text_info_child == null:
|
||||
var x = last_text_info_child.rect_position.x
|
||||
var y = last_text_info_child.rect_position.y
|
||||
var temp_size = size
|
||||
if child_count == 4:
|
||||
x = 0
|
||||
y = 20
|
||||
temp_size = 0
|
||||
label.rect_position = Vector2(x + temp_size, y)
|
||||
if not custom_node == null:
|
||||
label.add_child(custom_node)
|
||||
add_child(label)
|
||||
|
||||
func update_text_info(text_name, text_value = null, node = null, node_target_value = null, node_value = null):
|
||||
var text_label = self.get_node(text_name)
|
||||
if text_label == null:
|
||||
return
|
||||
if not node == null:
|
||||
get_node(text_name).get_node(node).set(node_target_value, node_value)
|
||||
if text_value == null:
|
||||
text_label.text = "%s: %s" % [text_name, null]
|
||||
else:
|
||||
text_label.text = "%s: %s" % [text_name, String(text_value)]
|
94
addons/Godoxel/Util.gd
Normal file
94
addons/Godoxel/Util.gd
Normal file
@ -0,0 +1,94 @@
|
||||
tool
|
||||
extends Node
|
||||
class_name GEUtils
|
||||
|
||||
|
||||
static func get_pixels_in_line(from: Vector2, to: Vector2):
|
||||
var dx = to[0] - from[0]
|
||||
var dy = to[1] - from[1]
|
||||
var nx = abs(dx)
|
||||
var ny = abs(dy)
|
||||
var signX = sign(dx)
|
||||
var signY = sign(dy)
|
||||
var p = from
|
||||
var points : Array = [p]
|
||||
|
||||
var ix = 0
|
||||
var iy = 0
|
||||
|
||||
while ix < nx || iy < ny:
|
||||
if (1 + (ix << 1)) * ny < (1 + (iy << 1)) * nx:
|
||||
p[0] += signX
|
||||
ix +=1
|
||||
else:
|
||||
p[1] += signY
|
||||
iy += 1
|
||||
points.append(p)
|
||||
return points
|
||||
|
||||
|
||||
static func to_1D_v(p, w) -> int:
|
||||
return p.x + p.y * w
|
||||
|
||||
|
||||
static func to_1D(x, y, w) -> int:
|
||||
return x + y * w
|
||||
|
||||
|
||||
static func to_2D(idx, w) -> Vector2:
|
||||
var p = Vector2()
|
||||
p.x = int(idx) % int(w)
|
||||
p.y = int(idx / w)
|
||||
return p
|
||||
|
||||
|
||||
|
||||
static func color_from_array(color_array):
|
||||
var r = color_array[0]
|
||||
var g = color_array[1]
|
||||
var b = color_array[2]
|
||||
var a = color_array[3]
|
||||
return Color(r, g, b, a)
|
||||
|
||||
static func random_color():
|
||||
return Color(randf(), randf(), randf())
|
||||
|
||||
static func random_color_alt():
|
||||
var rand = randi() % 6
|
||||
|
||||
match rand:
|
||||
#red
|
||||
0:
|
||||
return Color.red
|
||||
#blue
|
||||
1:
|
||||
return Color.blue
|
||||
#green
|
||||
2:
|
||||
return Color.green
|
||||
#orange
|
||||
3:
|
||||
return Color.orange
|
||||
#yellow
|
||||
4:
|
||||
return Color.yellow
|
||||
#purple
|
||||
5:
|
||||
return Color.purple
|
||||
|
||||
static func get_line_string(file, number):
|
||||
return file.get_as_text().split("\n")[number - 1].strip_edges()
|
||||
|
||||
static func printv(variable):
|
||||
var stack = get_stack()[get_stack().size() - 1]
|
||||
var line = stack.line
|
||||
var source = stack.source
|
||||
var file = File.new()
|
||||
file.open(source, File.READ)
|
||||
var line_string = get_line_string(file, line)
|
||||
file.close()
|
||||
var left_p = line_string.find("(")
|
||||
var left_p_string = line_string.right(left_p + 1)
|
||||
var right_p = left_p_string.find(")")
|
||||
var variable_name = left_p_string.left(right_p)
|
||||
print("%s: %s" % [variable_name, variable])
|
6
addons/Godoxel/ViewportContainer.gd
Normal file
6
addons/Godoxel/ViewportContainer.gd
Normal file
@ -0,0 +1,6 @@
|
||||
extends ViewportContainer
|
||||
tool
|
||||
|
||||
func _ready():
|
||||
get_child(0).size = rect_size
|
||||
|
41
addons/Godoxel/VisualGrid.gd
Normal file
41
addons/Godoxel/VisualGrid.gd
Normal file
@ -0,0 +1,41 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
export var color = Color()
|
||||
export var size:int = 16
|
||||
export var zoom = 0
|
||||
export var offset = Vector2(0, 0)
|
||||
|
||||
|
||||
func _enter_tree():
|
||||
set_process(true)
|
||||
|
||||
|
||||
func _draw():
|
||||
if size == 0:
|
||||
size = 1
|
||||
|
||||
var temp_size = size + zoom
|
||||
|
||||
var wrap_offset = Vector2(wrapf(offset.x, 0, temp_size), wrapf(offset.y, 0, temp_size))
|
||||
|
||||
var ceil_x = ceil(rect_size.x / temp_size)
|
||||
var ceil_y = ceil(rect_size.y / temp_size)
|
||||
|
||||
for i in ceil_y:
|
||||
var start_x = Vector2(0, (i * temp_size) + wrap_offset.y)
|
||||
var end_x = Vector2(rect_size.x, (i * temp_size) + wrap_offset.y)
|
||||
# var end_x = Vector2(int(rect_size.x) + size - int(rect_size.x) % size, (i * temp_size) + wrap_offset.y)
|
||||
draw_line(start_x, end_x, color, 1)
|
||||
|
||||
for i in ceil_x:
|
||||
var start_y = Vector2((i * temp_size) + wrap_offset.x, 0)
|
||||
var end_y = Vector2((i * temp_size) + (wrap_offset.x), rect_size.y)
|
||||
# var end_y = Vector2((i * temp_size) + (wrap_offset.x), int(rect_size.y) + size - int(rect_size.y) % size)
|
||||
draw_line(start_y, end_y, color, 1)
|
||||
|
||||
|
||||
func _process(delta):
|
||||
if not is_visible_in_tree():
|
||||
return
|
||||
update()
|
9
addons/Godoxel/VisualGrid.tscn
Normal file
9
addons/Godoxel/VisualGrid.tscn
Normal file
@ -0,0 +1,9 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://addons/Godoxel/VisualGrid.gd" type="Script" id=1]
|
||||
|
||||
|
||||
[node name="VisualGrid" type="Control"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource( 1 )
|
123
addons/Godoxel/actions/Action.gd
Normal file
123
addons/Godoxel/actions/Action.gd
Normal file
@ -0,0 +1,123 @@
|
||||
extends Node
|
||||
class_name GEAction
|
||||
|
||||
|
||||
var action_data = {}
|
||||
|
||||
|
||||
func _init():
|
||||
action_data["redo"] = {}
|
||||
action_data["undo"] = {}
|
||||
action_data["preview"] = {}
|
||||
|
||||
|
||||
func do_action(canvas, data: Array):
|
||||
if not "cells" in action_data.redo:
|
||||
action_data.redo["cells"] = []
|
||||
action_data.redo["colors"] = []
|
||||
|
||||
if not "cells" in action_data.undo:
|
||||
action_data.undo["cells"] = []
|
||||
action_data.undo["colors"] = []
|
||||
|
||||
if not "cells" in action_data.preview:
|
||||
action_data.preview["cells"] = []
|
||||
action_data.preview["colors"] = []
|
||||
|
||||
if not "layer" in action_data:
|
||||
action_data["layer"] = canvas.active_layer
|
||||
|
||||
|
||||
func commit_action(canvas):
|
||||
print("NO IMPL commit_action ")
|
||||
return []
|
||||
|
||||
|
||||
func undo_action(canvas):
|
||||
print("NO IMPL undo_action ")
|
||||
|
||||
|
||||
func redo_action(canvas):
|
||||
print("NO IMPL redo_action ")
|
||||
|
||||
|
||||
func can_commit() -> bool:
|
||||
return not action_data.redo.empty()
|
||||
|
||||
|
||||
func get_x_sym_points(canvas_width, pixel):
|
||||
var p = int(canvas_width - pixel.x)
|
||||
var all_points = [pixel, Vector2(p-1, pixel.y)]
|
||||
|
||||
var points :Array = []
|
||||
for point in all_points:
|
||||
if point in points:
|
||||
continue
|
||||
points.append(point)
|
||||
return points
|
||||
|
||||
|
||||
func get_y_sym_points(canvas_height, pixel):
|
||||
var p = int(canvas_height - pixel.y)
|
||||
var all_points = [pixel, Vector2(pixel.x, p-1)]
|
||||
|
||||
var points :Array = []
|
||||
for point in all_points:
|
||||
if point in points:
|
||||
continue
|
||||
points.append(point)
|
||||
return points
|
||||
|
||||
|
||||
func get_xy_sym_points(canvas_width, canvas_height, pixel):
|
||||
var all_points = []
|
||||
var xpoints = get_x_sym_points(canvas_width, pixel)
|
||||
|
||||
all_points += get_y_sym_points(canvas_height, xpoints[0])
|
||||
all_points += get_y_sym_points(canvas_height, xpoints[1])
|
||||
|
||||
var points :Array = []
|
||||
for point in all_points:
|
||||
if point in points:
|
||||
continue
|
||||
points.append(point)
|
||||
|
||||
return points
|
||||
|
||||
|
||||
func get_points(canvas, pixel):
|
||||
var points = []
|
||||
if canvas.symmetry_x and canvas.symmetry_y:
|
||||
var sym_points = get_xy_sym_points(canvas.canvas_width, canvas.canvas_height, pixel)
|
||||
for point in sym_points:
|
||||
if point in action_data.undo.cells or canvas.get_pixel_v(point) == null:
|
||||
continue
|
||||
if canvas.is_alpha_locked() and canvas.get_pixel_v(pixel) == Color.transparent:
|
||||
continue
|
||||
points.append(point)
|
||||
elif canvas.symmetry_y:
|
||||
var sym_points = get_y_sym_points(canvas.canvas_height, pixel)
|
||||
for point in sym_points:
|
||||
if point in action_data.undo.cells or canvas.get_pixel_v(point) == null:
|
||||
continue
|
||||
if canvas.is_alpha_locked() and canvas.get_pixel_v(pixel) == Color.transparent:
|
||||
continue
|
||||
points.append(point)
|
||||
elif canvas.symmetry_x:
|
||||
var sym_points = get_x_sym_points(canvas.canvas_width, pixel)
|
||||
for point in sym_points:
|
||||
if point in action_data.undo.cells or canvas.get_pixel_v(point) == null:
|
||||
continue
|
||||
if canvas.is_alpha_locked() and canvas.get_pixel_v(pixel) == Color.transparent:
|
||||
continue
|
||||
points.append(point)
|
||||
else:
|
||||
if pixel in action_data.undo.cells or canvas.get_pixel_v(pixel) == null:
|
||||
return []
|
||||
if canvas.is_alpha_locked() and canvas.get_pixel_v(pixel) == Color.transparent:
|
||||
return []
|
||||
points.append(pixel)
|
||||
|
||||
return points
|
||||
|
||||
|
53
addons/Godoxel/actions/Brighten.gd
Normal file
53
addons/Godoxel/actions/Brighten.gd
Normal file
@ -0,0 +1,53 @@
|
||||
extends GEAction
|
||||
class_name GEBrighten
|
||||
|
||||
|
||||
const brighten_color = 0.1
|
||||
|
||||
|
||||
func do_action(canvas, data: Array):
|
||||
.do_action(canvas, data)
|
||||
|
||||
var pixels = GEUtils.get_pixels_in_line(data[0], data[1])
|
||||
for pixel in pixels:
|
||||
if canvas.get_pixel_v(pixel) == null:
|
||||
continue
|
||||
|
||||
if canvas.is_alpha_locked() and canvas.get_pixel_v(pixel) == Color.transparent:
|
||||
continue
|
||||
|
||||
if pixel in action_data.undo.cells:
|
||||
var brightened_color = canvas.get_pixel_v(pixel).lightened(0.1)
|
||||
canvas.set_pixel_v(pixel, brightened_color)
|
||||
|
||||
action_data.redo.cells.append(pixel)
|
||||
action_data.redo.colors.append(brightened_color)
|
||||
continue
|
||||
|
||||
action_data.undo.colors.append(canvas.get_pixel_v(pixel))
|
||||
action_data.undo.cells.append(pixel)
|
||||
var brightened_color = canvas.get_pixel_v(pixel).lightened(0.1)
|
||||
canvas.set_pixel_v(pixel, brightened_color)
|
||||
|
||||
action_data.redo.cells.append(pixel)
|
||||
action_data.redo.colors.append(brightened_color)
|
||||
|
||||
|
||||
func commit_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
return []
|
||||
|
||||
|
||||
func undo_action(canvas):
|
||||
var cells = action_data.undo.cells
|
||||
var colors = action_data.undo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
func redo_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
47
addons/Godoxel/actions/Brush.gd
Normal file
47
addons/Godoxel/actions/Brush.gd
Normal file
@ -0,0 +1,47 @@
|
||||
extends GEAction
|
||||
class_name GEBrush
|
||||
|
||||
|
||||
func do_action(canvas: GECanvas, data: Array):
|
||||
.do_action(canvas, data)
|
||||
|
||||
for pixel in GEUtils.get_pixels_in_line(data[0], data[1]):
|
||||
for off in BrushPrefabs.get_brush(data[3], data[4]):
|
||||
var p = pixel + off
|
||||
|
||||
if p in action_data.undo.cells or canvas.get_pixel_v(p) == null:
|
||||
continue
|
||||
|
||||
if canvas.is_alpha_locked() and canvas.get_pixel_v(p) == Color.transparent:
|
||||
continue
|
||||
|
||||
action_data.undo.colors.append(canvas.get_pixel_v(p))
|
||||
action_data.undo.cells.append(p)
|
||||
|
||||
canvas.set_pixel_v(p, data[2])
|
||||
|
||||
action_data.redo.cells.append(p)
|
||||
action_data.redo.colors.append(data[2])
|
||||
|
||||
|
||||
func commit_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
return []
|
||||
|
||||
|
||||
func undo_action(canvas):
|
||||
var cells = action_data.undo.cells
|
||||
var colors = action_data.undo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
func redo_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
|
49
addons/Godoxel/actions/Bucket.gd
Normal file
49
addons/Godoxel/actions/Bucket.gd
Normal file
@ -0,0 +1,49 @@
|
||||
extends GEAction
|
||||
class_name GEBucket
|
||||
|
||||
|
||||
|
||||
func do_action(canvas, data: Array):
|
||||
.do_action(canvas, data)
|
||||
|
||||
if canvas.get_pixel_v(data[0]) == data[2]:
|
||||
return
|
||||
var pixels = canvas.select_same_color(data[0].x, data[0].y)
|
||||
|
||||
for pixel in pixels:
|
||||
if pixel in action_data.undo.cells:
|
||||
continue
|
||||
|
||||
if canvas.is_alpha_locked() and canvas.get_pixel_v(pixel) == Color.transparent:
|
||||
continue
|
||||
|
||||
action_data.undo.colors.append(canvas.get_pixel_v(pixel))
|
||||
action_data.undo.cells.append(pixel)
|
||||
|
||||
canvas.set_pixel_v(pixel, data[2])
|
||||
|
||||
action_data.redo.cells.append(pixel)
|
||||
action_data.redo.colors.append(data[2])
|
||||
|
||||
|
||||
func commit_action(canvas):
|
||||
var cells = action_data.preview.cells
|
||||
var colors = action_data.preview.colors
|
||||
return []
|
||||
|
||||
|
||||
func undo_action(canvas):
|
||||
var cells = action_data.undo.cells
|
||||
var colors = action_data.undo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
func redo_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
|
82
addons/Godoxel/actions/Cut.gd
Normal file
82
addons/Godoxel/actions/Cut.gd
Normal file
@ -0,0 +1,82 @@
|
||||
extends GEAction
|
||||
class_name GECut
|
||||
|
||||
const selection_color = Color(0.8, 0.8, 0.8, 0.5)
|
||||
var mouse_start_pos = null
|
||||
var mouse_end_pos = null
|
||||
|
||||
|
||||
func can_commit() -> bool:
|
||||
return false #ugly way of handling a cut
|
||||
|
||||
|
||||
func do_action(canvas, data: Array):
|
||||
.do_action(canvas, data)
|
||||
|
||||
if mouse_start_pos == null:
|
||||
mouse_start_pos = data[0]
|
||||
mouse_end_pos = data[0]
|
||||
|
||||
action_data.preview.cells.clear()
|
||||
action_data.preview.colors.clear()
|
||||
canvas.clear_preview_layer()
|
||||
|
||||
var p = mouse_start_pos
|
||||
var s = mouse_end_pos - mouse_start_pos
|
||||
|
||||
var pixels = GEUtils.get_pixels_in_line(p, p + Vector2(s.x, 0))
|
||||
pixels += GEUtils.get_pixels_in_line(p, p + Vector2(0, s.y))
|
||||
pixels += GEUtils.get_pixels_in_line(p + s, p + s + Vector2(0, -s.y))
|
||||
pixels += GEUtils.get_pixels_in_line(p + s, p + s + Vector2(-s.x, 0))
|
||||
|
||||
for pixel in pixels:
|
||||
canvas.set_preview_pixel_v(pixel, selection_color)
|
||||
action_data.preview.cells.append(pixel)
|
||||
action_data.preview.colors.append(selection_color)
|
||||
|
||||
|
||||
func commit_action(canvas):
|
||||
canvas.clear_preview_layer()
|
||||
var p = mouse_start_pos
|
||||
var s = mouse_end_pos - mouse_start_pos
|
||||
|
||||
for x in range(abs(s.x)+1):
|
||||
for y in range(abs(s.y)+1):
|
||||
var px = x
|
||||
var py = y
|
||||
if s.x < 0:
|
||||
px *= -1
|
||||
if s.y < 0:
|
||||
py *= -1
|
||||
|
||||
var pos = p + Vector2(px, py)
|
||||
var color = canvas.get_pixel(pos.x, pos.y)
|
||||
|
||||
if color == null or color.a == 0.0:
|
||||
continue
|
||||
|
||||
action_data.redo.cells.append(pos)
|
||||
action_data.redo.colors.append(canvas.get_pixel_v(pos))
|
||||
|
||||
canvas.set_pixel_v(pos, Color.transparent)
|
||||
|
||||
action_data.undo.cells.append(pos)
|
||||
action_data.undo.colors.append(Color.transparent)
|
||||
return []
|
||||
|
||||
|
||||
func undo_action(canvas):
|
||||
var cells = action_data.undo.cells
|
||||
var colors = action_data.undo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
func redo_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
|
55
addons/Godoxel/actions/Darken.gd
Normal file
55
addons/Godoxel/actions/Darken.gd
Normal file
@ -0,0 +1,55 @@
|
||||
extends GEAction
|
||||
class_name GEDarken
|
||||
|
||||
const dark_factor = 0.1
|
||||
|
||||
|
||||
func do_action(canvas, data: Array):
|
||||
.do_action(canvas, data)
|
||||
|
||||
var pixels = GEUtils.get_pixels_in_line(data[0], data[1])
|
||||
for pixel in pixels:
|
||||
if canvas.get_pixel_v(pixel) == null:
|
||||
continue
|
||||
|
||||
if canvas.is_alpha_locked() and canvas.get_pixel_v(pixel) == Color.transparent:
|
||||
continue
|
||||
|
||||
if pixel in action_data.undo.cells:
|
||||
var darkened_color = canvas.get_pixel_v(pixel).darkened(dark_factor)
|
||||
canvas.set_pixel_v(pixel, darkened_color)
|
||||
|
||||
action_data.redo.cells.append(pixel)
|
||||
action_data.redo.colors.append(darkened_color)
|
||||
continue
|
||||
|
||||
action_data.undo.colors.append(canvas.get_pixel_v(pixel))
|
||||
action_data.undo.cells.append(pixel)
|
||||
var darkened_color = canvas.get_pixel_v(pixel).darkened(dark_factor)
|
||||
canvas.set_pixel_v(pixel, darkened_color)
|
||||
|
||||
action_data.redo.cells.append(pixel)
|
||||
action_data.redo.colors.append(darkened_color)
|
||||
|
||||
|
||||
func commit_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
return []
|
||||
|
||||
|
||||
func undo_action(canvas):
|
||||
var cells = action_data.undo.cells
|
||||
var colors = action_data.undo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
func redo_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
|
59
addons/Godoxel/actions/Line.gd
Normal file
59
addons/Godoxel/actions/Line.gd
Normal file
@ -0,0 +1,59 @@
|
||||
extends GEAction
|
||||
class_name GELine
|
||||
|
||||
|
||||
var mouse_start_pos = null
|
||||
|
||||
func do_action(canvas, data: Array):
|
||||
.do_action(canvas, data)
|
||||
|
||||
if mouse_start_pos == null:
|
||||
mouse_start_pos = data[0]
|
||||
|
||||
action_data.preview.cells.clear()
|
||||
action_data.preview.colors.clear()
|
||||
canvas.clear_preview_layer()
|
||||
|
||||
var pixels = GEUtils.get_pixels_in_line(data[0], mouse_start_pos)
|
||||
for pixel in pixels:
|
||||
if canvas.is_alpha_locked() and canvas.get_pixel_v(pixel) == Color.transparent:
|
||||
continue
|
||||
|
||||
canvas.set_preview_pixel_v(pixel, data[2])
|
||||
action_data.preview.cells.append(pixel)
|
||||
action_data.preview.colors.append(data[2])
|
||||
|
||||
|
||||
func commit_action(canvas):
|
||||
canvas.clear_preview_layer()
|
||||
var cells = action_data.preview.cells
|
||||
var colors = action_data.preview.colors
|
||||
for idx in range(cells.size()):
|
||||
if canvas.get_pixel_v(cells[idx]) == null:
|
||||
continue
|
||||
action_data.undo.cells.append(cells[idx])
|
||||
action_data.undo.colors.append(canvas.get_pixel_v(cells[idx]))
|
||||
|
||||
canvas.set_pixel_v(cells[idx], colors[idx])
|
||||
|
||||
action_data.redo.cells.append(cells[idx])
|
||||
action_data.redo.colors.append(colors[idx])
|
||||
mouse_start_pos = null
|
||||
return []
|
||||
|
||||
|
||||
func undo_action(canvas):
|
||||
var cells = action_data.undo.cells
|
||||
var colors = action_data.undo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
func redo_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
|
44
addons/Godoxel/actions/MultiLine.gd
Normal file
44
addons/Godoxel/actions/MultiLine.gd
Normal file
@ -0,0 +1,44 @@
|
||||
extends GEAction
|
||||
class_name GEMultiLine
|
||||
|
||||
|
||||
func can_commit() -> bool:
|
||||
return false
|
||||
|
||||
|
||||
func update_action(canvas, data: Array):
|
||||
.update_action(canvas, data)
|
||||
|
||||
var pixels = GEUtils.get_pixels_in_line(data[0], data[1])
|
||||
for pixel in pixels:
|
||||
if pixel in action_data.undo.cells or canvas.get_pixel_v(pixel) == null or canvas.is_alpha_locked():
|
||||
continue
|
||||
action_data.undo.colors.append(canvas.get_pixel_v(pixel))
|
||||
action_data.undo.cells.append(pixel)
|
||||
canvas.set_pixel_v(pixel, data[2])
|
||||
|
||||
action_data.redo.cells.append(pixel)
|
||||
action_data.redo.colors.append(data[2])
|
||||
|
||||
|
||||
func commit_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
return []
|
||||
|
||||
|
||||
func undo_action(canvas):
|
||||
var cells = action_data.undo.cells
|
||||
var colors = action_data.undo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
func redo_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
|
59
addons/Godoxel/actions/PasteCut.gd
Normal file
59
addons/Godoxel/actions/PasteCut.gd
Normal file
@ -0,0 +1,59 @@
|
||||
extends GEAction
|
||||
class_name GEPasteCut
|
||||
|
||||
|
||||
#data[2] = selection_pos
|
||||
#data[3] = selection_color
|
||||
#data[4] = cut pos
|
||||
#data[5] = cut size
|
||||
func do_action(canvas, data: Array):
|
||||
.do_action(canvas, data)
|
||||
|
||||
for pixel_pos in GEUtils.get_pixels_in_line(data[0], data[1]):
|
||||
for idx in range(data[2].size()):
|
||||
var pixel = data[2][idx]
|
||||
var color = data[3][idx]
|
||||
pixel -= data[4] + data[5] / 2
|
||||
pixel += pixel_pos
|
||||
|
||||
if canvas.get_pixel_v(pixel) == null:
|
||||
continue
|
||||
|
||||
if canvas.is_alpha_locked() and canvas.get_pixel_v(pixel) == Color.transparent:
|
||||
continue
|
||||
|
||||
var found = action_data.redo.cells.find(pixel)
|
||||
if found == -1:
|
||||
action_data.redo.cells.append(pixel)
|
||||
action_data.redo.colors.append(color)
|
||||
else:
|
||||
action_data.redo.colors[found] = color
|
||||
|
||||
found = action_data.undo.cells.find(pixel)
|
||||
if found == -1:
|
||||
action_data.undo.colors.append(canvas.get_pixel_v(pixel))
|
||||
action_data.undo.cells.append(pixel)
|
||||
|
||||
canvas.set_pixel_v(pixel, color)
|
||||
|
||||
|
||||
func commit_action(canvas):
|
||||
canvas.clear_preview_layer()
|
||||
return []
|
||||
|
||||
|
||||
func undo_action(canvas):
|
||||
var cells = action_data.undo.cells
|
||||
var colors = action_data.undo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
func redo_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
|
43
addons/Godoxel/actions/Pencil.gd
Normal file
43
addons/Godoxel/actions/Pencil.gd
Normal file
@ -0,0 +1,43 @@
|
||||
extends GEAction
|
||||
class_name GEPencil
|
||||
|
||||
|
||||
func do_action(canvas, data: Array):
|
||||
.do_action(canvas, data)
|
||||
|
||||
var pixels = GEUtils.get_pixels_in_line(data[0], data[1])
|
||||
for pixel in pixels:
|
||||
for p in get_points(canvas, pixel):
|
||||
_set_pixel(canvas, p, data[2])
|
||||
|
||||
|
||||
func _set_pixel(canvas, pixel, color):
|
||||
action_data.undo.colors.append(canvas.get_pixel_v(pixel))
|
||||
action_data.undo.cells.append(pixel)
|
||||
canvas.set_pixel_v(pixel, color)
|
||||
|
||||
action_data.redo.cells.append(pixel)
|
||||
action_data.redo.colors.append(color)
|
||||
|
||||
|
||||
func commit_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
return []
|
||||
|
||||
|
||||
func undo_action(canvas):
|
||||
var cells = action_data.undo.cells
|
||||
var colors = action_data.undo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
func redo_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
|
58
addons/Godoxel/actions/Rainbow.gd
Normal file
58
addons/Godoxel/actions/Rainbow.gd
Normal file
@ -0,0 +1,58 @@
|
||||
extends GEAction
|
||||
class_name GERainbow
|
||||
|
||||
|
||||
func do_action(canvas, data: Array):
|
||||
.do_action(canvas, data)
|
||||
|
||||
var pixels = GEUtils.get_pixels_in_line(data[0], data[1])
|
||||
for pixel in pixels:
|
||||
if canvas.get_pixel_v(pixel) == null:
|
||||
continue
|
||||
|
||||
if canvas.is_alpha_locked() and canvas.get_pixel_v(pixel) == Color.transparent:
|
||||
continue
|
||||
|
||||
if pixel in action_data.undo.cells:
|
||||
var color = GEUtils.random_color()
|
||||
canvas.set_pixel_v(pixel, color)
|
||||
|
||||
var idx = action_data.redo.cells.find(pixel)
|
||||
action_data.redo.cells.remove(idx)
|
||||
action_data.redo.colors.remove(idx)
|
||||
|
||||
action_data.redo.cells.append(pixel)
|
||||
action_data.redo.colors.append(color)
|
||||
continue
|
||||
|
||||
action_data.undo.colors.append(canvas.get_pixel_v(pixel))
|
||||
action_data.undo.cells.append(pixel)
|
||||
|
||||
var color = GEUtils.random_color()
|
||||
canvas.set_pixel_v(pixel, color)
|
||||
|
||||
action_data.redo.cells.append(pixel)
|
||||
action_data.redo.colors.append(color)
|
||||
|
||||
|
||||
func commit_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
return []
|
||||
|
||||
|
||||
func undo_action(canvas):
|
||||
var cells = action_data.undo.cells
|
||||
var colors = action_data.undo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
func redo_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
|
69
addons/Godoxel/actions/Rect.gd
Normal file
69
addons/Godoxel/actions/Rect.gd
Normal file
@ -0,0 +1,69 @@
|
||||
extends GEAction
|
||||
class_name GERect
|
||||
|
||||
|
||||
var mouse_start_pos = null
|
||||
|
||||
func do_action(canvas, data: Array):
|
||||
.do_action(canvas, data)
|
||||
|
||||
if mouse_start_pos == null:
|
||||
mouse_start_pos = data[0]
|
||||
#print("init:", mouse_start_pos)
|
||||
|
||||
|
||||
action_data.undo.cells.clear()
|
||||
action_data.undo.colors.clear()
|
||||
action_data.preview.cells.clear()
|
||||
action_data.preview.colors.clear()
|
||||
canvas.clear_preview_layer()
|
||||
|
||||
var p = mouse_start_pos
|
||||
var s = data[0] - mouse_start_pos
|
||||
var pixels = GEUtils.get_pixels_in_line(p, p + Vector2(s.x, 0))
|
||||
pixels += GEUtils.get_pixels_in_line(p, p + Vector2(0, s.y))
|
||||
pixels += GEUtils.get_pixels_in_line(p + s, p + s + Vector2(0, -s.y))
|
||||
pixels += GEUtils.get_pixels_in_line(p + s, p + s + Vector2(-s.x, 0))
|
||||
|
||||
for pixel in pixels:
|
||||
if canvas.get_pixel_v(pixel) == null:
|
||||
continue
|
||||
|
||||
if canvas.is_alpha_locked() and canvas.get_pixel_v(pixel) == Color.transparent:
|
||||
continue
|
||||
|
||||
canvas.set_preview_pixel_v(pixel, data[2])
|
||||
action_data.undo.cells.append(pixel)
|
||||
action_data.undo.colors.append(canvas.get_pixel_v(pixel))
|
||||
action_data.preview.cells.append(pixel)
|
||||
action_data.preview.colors.append(data[2])
|
||||
|
||||
|
||||
func commit_action(canvas):
|
||||
canvas.clear_preview_layer()
|
||||
var cells = action_data.preview.cells
|
||||
var colors = action_data.preview.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas.set_pixel_v(cells[idx], colors[idx])
|
||||
|
||||
action_data.redo.cells.append(cells[idx])
|
||||
action_data.redo.colors.append(colors[idx])
|
||||
mouse_start_pos = null
|
||||
return []
|
||||
|
||||
|
||||
func undo_action(canvas):
|
||||
var cells = action_data.undo.cells
|
||||
var colors = action_data.undo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
func redo_action(canvas):
|
||||
var cells = action_data.redo.cells
|
||||
var colors = action_data.redo.colors
|
||||
for idx in range(cells.size()):
|
||||
canvas._set_pixel_v(action_data.layer, cells[idx], colors[idx])
|
||||
|
||||
|
||||
|
BIN
addons/Godoxel/assets/BrushCircle.png
(Stored with Git LFS)
Normal file
BIN
addons/Godoxel/assets/BrushCircle.png
(Stored with Git LFS)
Normal file
Binary file not shown.
35
addons/Godoxel/assets/BrushCircle.png.import
Normal file
35
addons/Godoxel/assets/BrushCircle.png.import
Normal file
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/BrushCircle.png-dd250909fee7964ffc38f7e4fcfe9c07.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/BrushCircle.png"
|
||||
dest_files=[ "res://.import/BrushCircle.png-dd250909fee7964ffc38f7e4fcfe9c07.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=true
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
addons/Godoxel/assets/BrushCircle_Hovered.png
(Stored with Git LFS)
Normal file
BIN
addons/Godoxel/assets/BrushCircle_Hovered.png
(Stored with Git LFS)
Normal file
Binary file not shown.
35
addons/Godoxel/assets/BrushCircle_Hovered.png.import
Normal file
35
addons/Godoxel/assets/BrushCircle_Hovered.png.import
Normal file
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/BrushCircle_Hovered.png-ae1a4d835af51e8a293b71d6a241b71c.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/BrushCircle_Hovered.png"
|
||||
dest_files=[ "res://.import/BrushCircle_Hovered.png-ae1a4d835af51e8a293b71d6a241b71c.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=true
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
addons/Godoxel/assets/BrushHLine.png
(Stored with Git LFS)
Normal file
BIN
addons/Godoxel/assets/BrushHLine.png
(Stored with Git LFS)
Normal file
Binary file not shown.
35
addons/Godoxel/assets/BrushHLine.png.import
Normal file
35
addons/Godoxel/assets/BrushHLine.png.import
Normal file
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/BrushHLine.png-9182ec8ac804af16d356bf911782e299.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/BrushHLine.png"
|
||||
dest_files=[ "res://.import/BrushHLine.png-9182ec8ac804af16d356bf911782e299.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=true
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
addons/Godoxel/assets/BrushHLine_Hovered.png
(Stored with Git LFS)
Normal file
BIN
addons/Godoxel/assets/BrushHLine_Hovered.png
(Stored with Git LFS)
Normal file
Binary file not shown.
35
addons/Godoxel/assets/BrushHLine_Hovered.png.import
Normal file
35
addons/Godoxel/assets/BrushHLine_Hovered.png.import
Normal file
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/BrushHLine_Hovered.png-e51d5f3c1628c510a225057f3ed60d5a.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/BrushHLine_Hovered.png"
|
||||
dest_files=[ "res://.import/BrushHLine_Hovered.png-e51d5f3c1628c510a225057f3ed60d5a.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=true
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
addons/Godoxel/assets/BrushRect.png
(Stored with Git LFS)
Normal file
BIN
addons/Godoxel/assets/BrushRect.png
(Stored with Git LFS)
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user