Added godoxel from https://github.com/aaronfranke/GraphicsEditor .
5
game/addons/.gitignore
vendored
@ -1,3 +1,6 @@
|
||||
*
|
||||
!module_manager
|
||||
!module_manager/**
|
||||
!module_manager/**
|
||||
!Godoxel
|
||||
!Godoxel/**
|
||||
!addon_versions
|
||||
|
106
game/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
|
||||
|
||||
|
461
game/addons/Godoxel/Canvas.gd
Normal file
@ -0,0 +1,461 @@
|
||||
extends Control
|
||||
class_name GECanvas
|
||||
tool
|
||||
|
||||
export var pixel_size: int = 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 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 _enter_tree():
|
||||
#-------------------------------
|
||||
# Set nodes
|
||||
#-------------------------------
|
||||
canvas = find_node("Canvas")
|
||||
grid = find_node("Grid")
|
||||
big_grid = find_node("BigGrid")
|
||||
canvas_layers = find_node("CanvasLayers")
|
||||
|
||||
#-------------------------------
|
||||
# setup layers and canvas
|
||||
#-------------------------------
|
||||
connect("mouse_entered", self, "_on_mouse_entered")
|
||||
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
|
||||
|
||||
active_layer = add_new_layer("Layer1")
|
||||
preview_layer = add_new_layer("Preview")
|
||||
tool_layer = add_new_layer("Tool")
|
||||
|
||||
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():
|
||||
for layer in layers:
|
||||
layer.update_texture()
|
||||
|
||||
preview_layer.update_texture()
|
||||
tool_layer.update_texture()
|
||||
|
||||
|
||||
func resize(width: int, height: int):
|
||||
if width < 0:
|
||||
width = 1
|
||||
if height < 0:
|
||||
height = 1
|
||||
|
||||
set_canvas_width(width)
|
||||
set_canvas_height(height)
|
||||
|
||||
preview_layer.resize(width, height)
|
||||
tool_layer.resize(width, height)
|
||||
for layer in layers:
|
||||
layer.resize(width, height)
|
||||
|
||||
|
||||
#-------------------------------
|
||||
# Export
|
||||
#-------------------------------
|
||||
|
||||
func set_pixel_size(size: int):
|
||||
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:
|
||||
var rect = Rect2(999999, 999999, -999999, -999999)
|
||||
|
||||
preview_layer.image.get_used_rect()
|
||||
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)
|
||||
|
||||
|
||||
func get_active_layer():
|
||||
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 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 layers:
|
||||
if layer == preview_layer or layer == active_layer or layer == tool_layer:
|
||||
continue
|
||||
active_layer = layer
|
||||
break
|
||||
layers.erase(del_layer)
|
||||
return active_layer
|
||||
|
||||
|
||||
func add_new_layer(layer_name: String):
|
||||
for layer in layers:
|
||||
if layer.name == layer_name:
|
||||
return
|
||||
var layer = GELayer.new()
|
||||
layer.name = layer_name
|
||||
|
||||
if layer_name == "Preview":
|
||||
layer.create($PreviewLayer, canvas_width, canvas_height)
|
||||
elif layer_name == "Tool":
|
||||
layer.create($ToolPreviewLayer, canvas_width, canvas_height)
|
||||
else:
|
||||
var texture_rect = TextureRect.new()
|
||||
texture_rect.name = layer_name
|
||||
canvas_layers.add_child(texture_rect, true)
|
||||
texture_rect.expand = true
|
||||
texture_rect.anchor_right = 1
|
||||
texture_rect.anchor_bottom = 1
|
||||
texture_rect.margin_right = 0
|
||||
texture_rect.margin_bottom = 0
|
||||
texture_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
layer.create(texture_rect, canvas_width, canvas_height)
|
||||
layers.append(layer)
|
||||
|
||||
return layer
|
||||
|
||||
|
||||
func duplicate_layer(layer_name: String, new_layer_name: String):
|
||||
for layer in layers:
|
||||
if layer.name == new_layer_name:
|
||||
return
|
||||
|
||||
var dup_layer :GELayer = find_layer_by_name(layer_name)
|
||||
var layer :GELayer = add_new_layer(new_layer_name)
|
||||
layer.image.copy_from(dup_layer.image)
|
||||
return layer
|
||||
|
||||
|
||||
func toggle_layer_visibility(layer_name: String):
|
||||
for layer in layers:
|
||||
if layer.name == layer_name:
|
||||
layer.visible = not layer.visible
|
||||
|
||||
|
||||
func find_layer_by_name(layer_name: String):
|
||||
for layer in 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 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)
|
||||
canvas_layers.move_child(layer, new_idx)
|
||||
|
||||
|
||||
func move_layer_back(layer_name: String):
|
||||
var layer = find_layer_by_name(layer_name).texture_rect_ref
|
||||
canvas_layers.move_child(layer, layer.get_index() + 1)
|
||||
|
||||
|
||||
func select_layer(layer_name: String):
|
||||
active_layer = find_layer_by_name(layer_name)
|
||||
|
||||
|
||||
#-------------------------------
|
||||
# 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
game/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, 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
game/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
game/addons/Godoxel/DebugTextDisplay.gd
Normal file
@ -0,0 +1,9 @@
|
||||
extends RichTextLabel
|
||||
tool
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
|
||||
func display_text(text):
|
||||
self.text = text
|
832
game/addons/Godoxel/Editor.gd
Normal file
@ -0,0 +1,832 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
enum Tools {
|
||||
PAINT,
|
||||
BRUSH,
|
||||
BUCKET,
|
||||
RAINBOW,
|
||||
LINE,
|
||||
RECT,
|
||||
DARKEN,
|
||||
BRIGHTEN
|
||||
COLORPICKER,
|
||||
CUT,
|
||||
PASTECUT,
|
||||
}
|
||||
|
||||
# Keyboard shortcuts
|
||||
const K_UNDO = KEY_Z
|
||||
const K_REDO = KEY_Y
|
||||
const K_PENCIL = KEY_Q
|
||||
const K_BRUSH = KEY_W
|
||||
const K_BUCKET = KEY_F
|
||||
const K_RAINBOW = KEY_R
|
||||
const K_LINE = KEY_L
|
||||
const K_DARK = KEY_D
|
||||
const K_BRIGHT = KEY_B
|
||||
const K_CUT = KEY_C
|
||||
const K_PICK = KEY_P
|
||||
|
||||
|
||||
var layer_buttons: Control
|
||||
var paint_canvas_container_node
|
||||
var paint_canvas: GECanvas
|
||||
var canvas_background: TextureRect
|
||||
var grids_node
|
||||
var colors_grid
|
||||
var selected_color = Color(1, 1, 1, 1) setget set_selected_color
|
||||
var util = preload("res://addons/Godoxel/Util.gd")
|
||||
var textinfo
|
||||
var allow_drawing = true
|
||||
|
||||
var mouse_in_region = false
|
||||
var mouse_on_top = false
|
||||
|
||||
|
||||
var _middle_mouse_pressed_pos = null
|
||||
var _middle_mouse_pressed_start_pos = null
|
||||
var _left_mouse_pressed_start_pos = Vector2()
|
||||
var _previous_tool
|
||||
var brush_mode
|
||||
|
||||
var _layer_button_ref = {}
|
||||
|
||||
var _total_added_layers = 1
|
||||
|
||||
var selected_brush_prefab = 0
|
||||
var _last_drawn_pixel = Vector2.ZERO
|
||||
var _last_preview_draw_cell_pos = Vector2.ZERO
|
||||
|
||||
var _selection_cells = []
|
||||
var _selection_colors = []
|
||||
|
||||
var _cut_pos = Vector2.ZERO
|
||||
var _cut_size = Vector2.ZERO
|
||||
|
||||
var _actions_history = [] # for undo
|
||||
var _redo_history = []
|
||||
var _current_action
|
||||
|
||||
var _last_mouse_pos_canvas_area = Vector2.ZERO
|
||||
|
||||
var _picked_color = false
|
||||
|
||||
var mouse_position = Vector2()
|
||||
var canvas_position = Vector2()
|
||||
var canvas_mouse_position = Vector2()
|
||||
var cell_mouse_position = Vector2()
|
||||
var cell_color = Color()
|
||||
|
||||
var last_mouse_position = Vector2()
|
||||
var last_canvas_position = Vector2()
|
||||
var last_canvas_mouse_position = Vector2()
|
||||
var last_cell_mouse_position = Vector2()
|
||||
var last_cell_color = Color()
|
||||
|
||||
const current_layer_highlight = Color(0.354706, 0.497302, 0.769531)
|
||||
const other_layer_highlight = Color(0.180392, 0.176471, 0.176471)
|
||||
const locked_layer_highlight = Color(0.098039, 0.094118, 0.094118)
|
||||
|
||||
var big_grid_pixels = 4 # 1 grid-box is big_grid_pixels big
|
||||
|
||||
|
||||
|
||||
func _ready():
|
||||
#--------------------
|
||||
#Setup nodes
|
||||
#--------------------
|
||||
|
||||
paint_canvas_container_node = find_node("PaintCanvasContainer")
|
||||
textinfo = find_node("DebugTextDisplay")
|
||||
selected_color = find_node("ColorPicker").color
|
||||
colors_grid = find_node("Colors")
|
||||
paint_canvas = paint_canvas_container_node.find_node("Canvas")
|
||||
layer_buttons = find_node("LayerButtons")
|
||||
canvas_background = find_node("CanvasBackground")
|
||||
|
||||
set_process(true)
|
||||
|
||||
#--------------------
|
||||
#connect nodes
|
||||
#--------------------
|
||||
if not colors_grid.is_connected("color_change_request", self, "change_color"):
|
||||
colors_grid.connect("color_change_request", self, "change_color")
|
||||
|
||||
if not is_connected("visibility_changed", self, "_on_Editor_visibility_changed"):
|
||||
connect("visibility_changed", self, "_on_Editor_visibility_changed")
|
||||
|
||||
find_node("CanvasBackground").material.set_shader_param(
|
||||
"pixel_size", 8 * pow(0.5, big_grid_pixels)/paint_canvas.pixel_size)
|
||||
|
||||
# ready
|
||||
|
||||
set_brush(Tools.PAINT)
|
||||
_layer_button_ref[layer_buttons.get_child(0).name] = layer_buttons.get_child(0) #ugly
|
||||
_connect_layer_buttons()
|
||||
highlight_layer(paint_canvas.get_active_layer().name)
|
||||
|
||||
find_node("BrushSizeLabel").text = str(int(find_node("BrushSize").value))
|
||||
|
||||
paint_canvas.update()
|
||||
|
||||
|
||||
func _input(event):
|
||||
if is_any_menu_open():
|
||||
return
|
||||
if not is_visible_in_tree():
|
||||
return
|
||||
if paint_canvas_container_node == null or paint_canvas == null:
|
||||
return
|
||||
|
||||
if event is InputEventKey and event.is_pressed() and not event.is_echo():
|
||||
_handle_shortcuts(event.scancode)
|
||||
|
||||
if is_mouse_in_canvas():
|
||||
_handle_zoom(event)
|
||||
|
||||
if paint_canvas.is_active_layer_locked():
|
||||
return
|
||||
|
||||
if brush_mode == Tools.CUT:
|
||||
if event is InputEventMouseButton:
|
||||
if event.button_index == BUTTON_LEFT:
|
||||
if not event.pressed:
|
||||
commit_action()
|
||||
|
||||
if (paint_canvas.mouse_in_region and paint_canvas.mouse_on_top):
|
||||
if event is InputEventMouseButton:
|
||||
match brush_mode:
|
||||
Tools.BUCKET:
|
||||
if event.button_index == BUTTON_LEFT:
|
||||
if event.pressed:
|
||||
if _current_action == null:
|
||||
_current_action = get_action()
|
||||
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
|
||||
|
||||
Tools.COLORPICKER:
|
||||
if event.button_index == BUTTON_LEFT:
|
||||
if event.pressed:
|
||||
if paint_canvas.get_pixel(cell_mouse_position.x, cell_mouse_position.y).a == 0:
|
||||
return
|
||||
selected_color = paint_canvas.get_pixel(cell_mouse_position.x, cell_mouse_position.y)
|
||||
_picked_color = true
|
||||
find_node("Colors").add_color_prefab(selected_color)
|
||||
elif _picked_color:
|
||||
set_brush(_previous_tool)
|
||||
elif event.button_index == BUTTON_RIGHT:
|
||||
if event.pressed:
|
||||
set_brush(_previous_tool)
|
||||
|
||||
Tools.PASTECUT:
|
||||
if event.button_index == BUTTON_RIGHT:
|
||||
if event.pressed:
|
||||
commit_action()
|
||||
set_brush(Tools.PAINT)
|
||||
|
||||
|
||||
func _process(delta):
|
||||
if not is_visible_in_tree():
|
||||
return
|
||||
if paint_canvas_container_node == null or paint_canvas == null:
|
||||
return
|
||||
if is_any_menu_open():
|
||||
return
|
||||
|
||||
if is_mouse_in_canvas():
|
||||
_handle_scroll()
|
||||
|
||||
#Update commonly used variables
|
||||
var grid_size = paint_canvas.pixel_size
|
||||
mouse_position = get_global_mouse_position() #paint_canvas.get_local_mouse_position()
|
||||
canvas_position = paint_canvas.rect_global_position
|
||||
canvas_mouse_position = Vector2(mouse_position.x - canvas_position.x, mouse_position.y - canvas_position.y)
|
||||
if is_mouse_in_canvas():
|
||||
cell_mouse_position = Vector2(
|
||||
floor(canvas_mouse_position.x / grid_size),
|
||||
floor(canvas_mouse_position.y / grid_size))
|
||||
cell_color = paint_canvas.get_pixel(cell_mouse_position.x, cell_mouse_position.y)
|
||||
update_text_info()
|
||||
|
||||
# if not is_mouse_in_canvas():
|
||||
# paint_canvas.tool_layer.clear()
|
||||
# paint_canvas.update()
|
||||
# paint_canvas.tool_layer.update_texture()
|
||||
# else:
|
||||
if is_mouse_in_canvas():
|
||||
if not paint_canvas.is_active_layer_locked():
|
||||
if is_position_in_canvas(get_global_mouse_position()) or \
|
||||
is_position_in_canvas(_last_mouse_pos_canvas_area):
|
||||
brush_process()
|
||||
else:
|
||||
print(cell_mouse_position, " not in ", paint_canvas_container_node.rect_size)
|
||||
print("not in canvas")
|
||||
|
||||
_draw_tool_brush()
|
||||
|
||||
#Update last variables with the current variables
|
||||
last_mouse_position = mouse_position
|
||||
last_canvas_position = canvas_position
|
||||
last_canvas_mouse_position = canvas_mouse_position
|
||||
last_cell_mouse_position = cell_mouse_position
|
||||
last_cell_color = cell_color
|
||||
_last_mouse_pos_canvas_area = get_global_mouse_position() #paint_canvas_container_node.get_local_mouse_position()
|
||||
|
||||
|
||||
func _handle_shortcuts(scancode):
|
||||
match scancode:
|
||||
K_UNDO:
|
||||
undo_action()
|
||||
|
||||
K_REDO:
|
||||
redo_action()
|
||||
|
||||
K_PENCIL:
|
||||
set_brush(Tools.PAINT)
|
||||
|
||||
K_BRUSH:
|
||||
set_brush(Tools.BRUSH)
|
||||
|
||||
K_BUCKET:
|
||||
set_brush(Tools.BUCKET)
|
||||
|
||||
K_RAINBOW:
|
||||
set_brush(Tools.RAINBOW)
|
||||
|
||||
K_LINE:
|
||||
set_brush(Tools.LINE)
|
||||
|
||||
K_DARK:
|
||||
set_brush(Tools.DARKEN)
|
||||
|
||||
K_BRIGHT:
|
||||
set_brush(Tools.BRIGHTEN)
|
||||
|
||||
K_CUT:
|
||||
set_brush(Tools.CUT)
|
||||
|
||||
K_PICK:
|
||||
set_brush(Tools.COLORPICKER)
|
||||
|
||||
|
||||
func _draw_tool_brush():
|
||||
paint_canvas.tool_layer.clear()
|
||||
|
||||
match brush_mode:
|
||||
Tools.PASTECUT:
|
||||
for idx in range(_selection_cells.size()):
|
||||
var pixel = _selection_cells[idx]
|
||||
# if pixel.x < 0 or pixel.y < 0:
|
||||
# print(pixel)
|
||||
var color = _selection_colors[idx]
|
||||
pixel -= _cut_pos + _cut_size / 2
|
||||
pixel += cell_mouse_position
|
||||
paint_canvas._set_pixel_v(paint_canvas.tool_layer, pixel, color)
|
||||
Tools.BRUSH:
|
||||
var pixels = BrushPrefabs.get_brush(selected_brush_prefab, find_node("BrushSize").value)
|
||||
for pixel in pixels:
|
||||
|
||||
paint_canvas._set_pixel(paint_canvas.tool_layer,
|
||||
cell_mouse_position.x + pixel.x, cell_mouse_position.y + pixel.y, selected_color)
|
||||
|
||||
Tools.RAINBOW:
|
||||
paint_canvas._set_pixel(paint_canvas.tool_layer,
|
||||
cell_mouse_position.x, cell_mouse_position.y, Color(0.46875, 0.446777, 0.446777, 0.196078))
|
||||
|
||||
Tools.COLORPICKER:
|
||||
paint_canvas._set_pixel(paint_canvas.tool_layer,
|
||||
cell_mouse_position.x, cell_mouse_position.y, Color(0.866667, 0.847059, 0.847059, 0.196078))
|
||||
_:
|
||||
paint_canvas._set_pixel(paint_canvas.tool_layer,
|
||||
cell_mouse_position.x, cell_mouse_position.y, selected_color)
|
||||
|
||||
paint_canvas.update()
|
||||
#TODO add here brush prefab drawing
|
||||
paint_canvas.tool_layer.update_texture()
|
||||
|
||||
|
||||
func _handle_scroll():
|
||||
if Input.is_mouse_button_pressed(BUTTON_MIDDLE):
|
||||
if _middle_mouse_pressed_start_pos == null:
|
||||
_middle_mouse_pressed_start_pos = paint_canvas.rect_position
|
||||
_middle_mouse_pressed_pos = get_global_mouse_position()
|
||||
|
||||
paint_canvas.rect_position = _middle_mouse_pressed_start_pos
|
||||
paint_canvas.rect_position += get_global_mouse_position() - _middle_mouse_pressed_pos
|
||||
|
||||
elif _middle_mouse_pressed_start_pos != null:
|
||||
_middle_mouse_pressed_start_pos = null
|
||||
|
||||
|
||||
const max_zoom_out = 1
|
||||
const max_zoom_in = 50
|
||||
|
||||
func _handle_zoom(event):
|
||||
if not event is InputEventMouseButton:
|
||||
return
|
||||
if event.is_pressed():
|
||||
if event.button_index == BUTTON_WHEEL_UP:
|
||||
var px = min(paint_canvas.pixel_size * 2, max_zoom_in)
|
||||
if px == paint_canvas.pixel_size:
|
||||
return
|
||||
paint_canvas.set_pixel_size(px)
|
||||
find_node("CanvasBackground").material.set_shader_param(
|
||||
"pixel_size", 8 * pow(0.5, big_grid_pixels)/paint_canvas.pixel_size)
|
||||
paint_canvas.rect_position -= paint_canvas.get_local_mouse_position()
|
||||
paint_canvas.rect_position.x = clamp(paint_canvas.rect_position.x, -paint_canvas.rect_size.x * 0.8, rect_size.x)
|
||||
paint_canvas.rect_position.y = clamp(paint_canvas.rect_position.y, -paint_canvas.rect_size.y * 0.8, rect_size.y)
|
||||
elif event.button_index == BUTTON_WHEEL_DOWN:
|
||||
var px = max(paint_canvas.pixel_size / 2.0, max_zoom_out)
|
||||
if px == paint_canvas.pixel_size:
|
||||
return
|
||||
paint_canvas.set_pixel_size(px)
|
||||
find_node("CanvasBackground").material.set_shader_param(
|
||||
# 4 2 1
|
||||
"pixel_size", 8 * pow(0.5, big_grid_pixels)/paint_canvas.pixel_size)
|
||||
paint_canvas.rect_position += paint_canvas.get_local_mouse_position() / 2
|
||||
paint_canvas.rect_position.x = clamp(paint_canvas.rect_position.x, -paint_canvas.rect_size.x * 0.8, rect_size.x)
|
||||
paint_canvas.rect_position.y = clamp(paint_canvas.rect_position.y, -paint_canvas.rect_size.y * 0.8, rect_size.y)
|
||||
|
||||
|
||||
func _handle_cut():
|
||||
if Input.is_mouse_button_pressed(BUTTON_RIGHT):
|
||||
paint_canvas.clear_preview_layer()
|
||||
set_brush(_previous_tool)
|
||||
return
|
||||
|
||||
if Input.is_mouse_button_pressed(BUTTON_LEFT):
|
||||
for pixel_pos in GEUtils.get_pixels_in_line(cell_mouse_position, last_cell_mouse_position):
|
||||
for idx in range(_selection_cells.size()):
|
||||
var pixel = _selection_cells[idx]
|
||||
var color = _selection_colors[idx]
|
||||
pixel -= _cut_pos + _cut_size / 2
|
||||
pixel += pixel_pos
|
||||
paint_canvas.set_pixel_v(pixel, color)
|
||||
else:
|
||||
if _last_preview_draw_cell_pos == cell_mouse_position:
|
||||
return
|
||||
paint_canvas.clear_preview_layer()
|
||||
for idx in range(_selection_cells.size()):
|
||||
var pixel = _selection_cells[idx]
|
||||
var color = _selection_colors[idx]
|
||||
pixel -= _cut_pos + _cut_size / 2
|
||||
pixel += cell_mouse_position
|
||||
paint_canvas.set_preview_pixel_v(pixel, color)
|
||||
_last_preview_draw_cell_pos = cell_mouse_position
|
||||
|
||||
|
||||
func brush_process():
|
||||
if Input.is_mouse_button_pressed(BUTTON_LEFT):
|
||||
if _current_action == null:
|
||||
_current_action = get_action()
|
||||
if brush_mode == Tools.COLORPICKER:
|
||||
_current_action = null
|
||||
|
||||
match brush_mode:
|
||||
Tools.PAINT:
|
||||
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
|
||||
Tools.BRUSH:
|
||||
do_action([cell_mouse_position, last_cell_mouse_position, selected_color,
|
||||
selected_brush_prefab, find_node("BrushSize").value])
|
||||
Tools.LINE:
|
||||
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
|
||||
Tools.RECT:
|
||||
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
|
||||
Tools.DARKEN:
|
||||
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
|
||||
Tools.BRIGHTEN:
|
||||
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
|
||||
Tools.COLORPICKER:
|
||||
pass
|
||||
Tools.CUT:
|
||||
do_action([cell_mouse_position, last_cell_mouse_position, selected_color])
|
||||
Tools.PASTECUT:
|
||||
do_action([cell_mouse_position, last_cell_mouse_position,
|
||||
_selection_cells, _selection_colors,
|
||||
_cut_pos, _cut_size])
|
||||
Tools.RAINBOW:
|
||||
do_action([cell_mouse_position, last_cell_mouse_position])
|
||||
paint_canvas.update()
|
||||
|
||||
elif Input.is_mouse_button_pressed(BUTTON_RIGHT):
|
||||
paint_canvas.update()
|
||||
if _current_action == null:
|
||||
_current_action = get_action()
|
||||
|
||||
match brush_mode:
|
||||
Tools.PAINT:
|
||||
do_action([cell_mouse_position, last_cell_mouse_position, Color.transparent])
|
||||
Tools.BRUSH:
|
||||
do_action([cell_mouse_position, last_cell_mouse_position, Color.transparent,
|
||||
selected_brush_prefab, find_node("BrushSize").value])
|
||||
else:
|
||||
if _current_action and _current_action.can_commit():
|
||||
commit_action()
|
||||
paint_canvas.update()
|
||||
|
||||
|
||||
func update_text_info():
|
||||
var text = ""
|
||||
|
||||
var cell_color_text = cell_color
|
||||
cell_color_text = Color(0, 0, 0, 0)
|
||||
|
||||
text += \
|
||||
str("FPS %s\t" + \
|
||||
"Mouse Position %s\t" + \
|
||||
"Canvas Mouse Position %s \t" + \
|
||||
"Canvas Position %s\t\n" + \
|
||||
"Cell Position %s \t" + \
|
||||
"Cell Color %s\t") % [
|
||||
str(Engine.get_frames_per_second()),
|
||||
str(mouse_position),
|
||||
str(canvas_mouse_position),
|
||||
str(canvas_position),
|
||||
str(cell_mouse_position),
|
||||
str(cell_color_text),
|
||||
]
|
||||
|
||||
find_node("DebugTextDisplay").display_text(text)
|
||||
|
||||
|
||||
func _on_Save_pressed():
|
||||
get_node("SaveFileDialog").show()
|
||||
|
||||
|
||||
|
||||
#---------------------------------------
|
||||
# Actions
|
||||
#---------------------------------------
|
||||
|
||||
|
||||
func do_action(data: Array):
|
||||
if _current_action == null:
|
||||
#print("clear redo")
|
||||
_redo_history.clear()
|
||||
_current_action.do_action(paint_canvas, data)
|
||||
|
||||
|
||||
func commit_action():
|
||||
if not _current_action:
|
||||
return
|
||||
|
||||
#print("commit action")
|
||||
var commit_data = _current_action.commit_action(paint_canvas)
|
||||
var action = get_action()
|
||||
action.action_data = _current_action.action_data.duplicate(true)
|
||||
_actions_history.push_back(action)
|
||||
_redo_history.clear()
|
||||
|
||||
match brush_mode:
|
||||
Tools.CUT:
|
||||
_cut_pos = _current_action.mouse_start_pos
|
||||
_cut_size = _current_action.mouse_end_pos - _current_action.mouse_start_pos
|
||||
_selection_cells = _current_action.action_data.redo.cells.duplicate()
|
||||
_selection_colors = _current_action.action_data.redo.colors.duplicate()
|
||||
set_brush(Tools.PASTECUT)
|
||||
_:
|
||||
_current_action = null
|
||||
|
||||
|
||||
func redo_action():
|
||||
if _redo_history.empty():
|
||||
print("nothing to redo")
|
||||
return
|
||||
var action = _redo_history.pop_back()
|
||||
if not action:
|
||||
return
|
||||
_actions_history.append(action)
|
||||
action.redo_action(paint_canvas)
|
||||
paint_canvas.update()
|
||||
#print("redo action")
|
||||
|
||||
|
||||
func undo_action():
|
||||
var action = _actions_history.pop_back()
|
||||
if not action:
|
||||
return
|
||||
_redo_history.append(action)
|
||||
action.undo_action(paint_canvas)
|
||||
update()
|
||||
paint_canvas.update()
|
||||
#print("undo action")
|
||||
|
||||
|
||||
func get_action():
|
||||
match brush_mode:
|
||||
Tools.PAINT:
|
||||
return GEPencil.new()
|
||||
Tools.BRUSH:
|
||||
return GEBrush.new()
|
||||
Tools.LINE:
|
||||
return GELine.new()
|
||||
Tools.RAINBOW:
|
||||
return GERainbow.new()
|
||||
Tools.BUCKET:
|
||||
return GEBucket.new()
|
||||
Tools.RECT:
|
||||
return GERect.new()
|
||||
Tools.DARKEN:
|
||||
return GEDarken.new()
|
||||
Tools.BRIGHTEN:
|
||||
return GEBrighten.new()
|
||||
Tools.CUT:
|
||||
return GECut.new()
|
||||
Tools.PASTECUT:
|
||||
return GEPasteCut.new()
|
||||
_:
|
||||
#print("no tool!")
|
||||
return null
|
||||
|
||||
|
||||
############################################
|
||||
# Brushes
|
||||
############################################
|
||||
|
||||
|
||||
func set_selected_color(color):
|
||||
selected_color = color
|
||||
|
||||
|
||||
func set_brush(new_mode):
|
||||
if brush_mode == new_mode:
|
||||
return
|
||||
_previous_tool = brush_mode
|
||||
brush_mode = new_mode
|
||||
|
||||
_current_action = get_action()
|
||||
|
||||
match _previous_tool:
|
||||
Tools.CUT:
|
||||
paint_canvas.clear_preview_layer()
|
||||
Tools.PASTECUT:
|
||||
_selection_cells.clear()
|
||||
_selection_colors.clear()
|
||||
Tools.BUCKET:
|
||||
_current_action = null
|
||||
#print("Selected: ", Tools.keys()[brush_mode])
|
||||
|
||||
|
||||
func change_color(new_color):
|
||||
if new_color.a == 0:
|
||||
return
|
||||
selected_color = new_color
|
||||
find_node("ColorPicker").color = selected_color
|
||||
|
||||
|
||||
func _on_ColorPicker_color_changed(color):
|
||||
selected_color = color
|
||||
|
||||
|
||||
func _on_PaintTool_pressed():
|
||||
set_brush(Tools.PAINT)
|
||||
|
||||
|
||||
func _on_BucketTool_pressed():
|
||||
set_brush(Tools.BUCKET)
|
||||
|
||||
|
||||
func _on_RainbowTool_pressed():
|
||||
set_brush(Tools.RAINBOW)
|
||||
|
||||
|
||||
func _on_BrushTool_pressed():
|
||||
set_brush(Tools.BRUSH)
|
||||
|
||||
|
||||
func _on_LineTool_pressed():
|
||||
set_brush(Tools.LINE)
|
||||
|
||||
|
||||
func _on_RectTool_pressed():
|
||||
set_brush(Tools.RECT)
|
||||
|
||||
|
||||
func _on_DarkenTool_pressed():
|
||||
set_brush(Tools.DARKEN)
|
||||
|
||||
|
||||
func _on_BrightenTool_pressed():
|
||||
set_brush(Tools.BRIGHTEN)
|
||||
|
||||
|
||||
func _on_ColorPickerTool_pressed():
|
||||
set_brush(Tools.COLORPICKER)
|
||||
|
||||
|
||||
func _on_CutTool_pressed():
|
||||
set_brush(Tools.CUT)
|
||||
|
||||
|
||||
func _on_Editor_visibility_changed():
|
||||
pause_mode = not visible
|
||||
|
||||
|
||||
|
||||
############################################
|
||||
# Layer
|
||||
############################################
|
||||
|
||||
func highlight_layer(layer_name: String):
|
||||
for button in layer_buttons.get_children():
|
||||
if paint_canvas.find_layer_by_name(button.name).locked:
|
||||
button.get("custom_styles/panel").set("bg_color", locked_layer_highlight)
|
||||
elif button.name == layer_name:
|
||||
button.get("custom_styles/panel").set("bg_color", current_layer_highlight)
|
||||
else:
|
||||
button.get("custom_styles/panel").set("bg_color", other_layer_highlight)
|
||||
|
||||
|
||||
func toggle_layer_visibility(button, layer_name: String):
|
||||
#print("toggling: ", layer_name)
|
||||
paint_canvas.toggle_layer_visibility(layer_name)
|
||||
|
||||
|
||||
func select_layer(layer_name: String):
|
||||
#print("select layer: ", layer_name)
|
||||
paint_canvas.select_layer(layer_name)
|
||||
highlight_layer(layer_name)
|
||||
|
||||
|
||||
func lock_layer(button, layer_name: String):
|
||||
paint_canvas.toggle_lock_layer(layer_name)
|
||||
highlight_layer(paint_canvas.get_active_layer().name)
|
||||
|
||||
|
||||
func add_new_layer():
|
||||
var new_layer_button = layer_buttons.get_child(0).duplicate()
|
||||
new_layer_button.set("custom_styles/panel", layer_buttons.get_child(0).get("custom_styles/panel").duplicate())
|
||||
layer_buttons.add_child_below_node(
|
||||
layer_buttons.get_child(layer_buttons.get_child_count() - 1), new_layer_button, true)
|
||||
_total_added_layers += 1
|
||||
new_layer_button.find_node("Select").text = "Layer " + str(_total_added_layers)
|
||||
_layer_button_ref[new_layer_button.name] = new_layer_button
|
||||
_connect_layer_buttons()
|
||||
|
||||
var layer: GELayer = paint_canvas.add_new_layer(new_layer_button.name)
|
||||
|
||||
highlight_layer(paint_canvas.get_active_layer().name)
|
||||
#print("added layer: ", layer.name)
|
||||
return layer
|
||||
|
||||
|
||||
func remove_active_layer():
|
||||
if layer_buttons.get_child_count() <= 1:
|
||||
return
|
||||
var layer_name = paint_canvas.active_layer.name
|
||||
paint_canvas.remove_layer(layer_name)
|
||||
layer_buttons.remove_child(_layer_button_ref[layer_name])
|
||||
_layer_button_ref[layer_name].queue_free()
|
||||
_layer_button_ref.erase(layer_name)
|
||||
|
||||
highlight_layer(paint_canvas.get_active_layer().name)
|
||||
|
||||
|
||||
func duplicate_active_layer():
|
||||
var new_layer_button = layer_buttons.get_child(0).duplicate()
|
||||
new_layer_button.set("custom_styles/panel", layer_buttons.get_child(0).get("custom_styles/panel").duplicate())
|
||||
layer_buttons.add_child_below_node(
|
||||
layer_buttons.get_child(layer_buttons.get_child_count() - 1), new_layer_button, true)
|
||||
|
||||
_total_added_layers += 1 # for keeping track...
|
||||
new_layer_button.find_node("Select").text = "Layer " + str(_total_added_layers)
|
||||
|
||||
var new_layer = paint_canvas.duplicate_layer(paint_canvas.active_layer.name, new_layer_button.name)
|
||||
new_layer.update_texture()
|
||||
_layer_button_ref[new_layer.name] = new_layer_button
|
||||
|
||||
new_layer_button.find_node("Select").connect("pressed", self, "select_layer", [new_layer_button.name])
|
||||
new_layer_button.find_node("Visible").connect("pressed", self, "toggle_layer_visibility",
|
||||
[new_layer_button.find_node("Visible"), new_layer_button.name])
|
||||
new_layer_button.find_node("Up").connect("pressed", self, "move_down", [new_layer_button])
|
||||
new_layer_button.find_node("Down").connect("pressed", self, "move_up", [new_layer_button])
|
||||
new_layer_button.find_node("Lock").connect("pressed", self, "lock_layer", [new_layer_button, new_layer_button.name])
|
||||
|
||||
# update highlight
|
||||
highlight_layer(paint_canvas.get_active_layer().name)
|
||||
#print("added layer: ", new_layer.name, " (total:", layer_buttons.get_child_count(), ")")
|
||||
|
||||
|
||||
func move_up(layer_btn):
|
||||
var new_idx = min(layer_btn.get_index() + 1, layer_buttons.get_child_count())
|
||||
#print("move_up: ", layer_btn.name, " from ", layer_btn.get_index(), " to ", new_idx)
|
||||
layer_buttons.move_child(layer_btn, new_idx)
|
||||
paint_canvas.move_layer_back(layer_btn.name)
|
||||
|
||||
|
||||
func move_down(layer_btn):
|
||||
var new_idx = max(layer_btn.get_index() - 1, 0)
|
||||
#print("move_down: ", layer_btn.name, " from ", layer_btn.get_index(), " to ", new_idx)
|
||||
layer_buttons.move_child(layer_btn, new_idx)
|
||||
paint_canvas.move_layer_forward(layer_btn.name)
|
||||
|
||||
|
||||
func _connect_layer_buttons():
|
||||
for layer_btn in layer_buttons.get_children():
|
||||
if layer_btn.find_node("Select").is_connected("pressed", self, "select_layer"):
|
||||
continue
|
||||
layer_btn.find_node("Select").connect("pressed", self, "select_layer", [layer_btn.name])
|
||||
layer_btn.find_node("Visible").connect("pressed", self, "toggle_layer_visibility",
|
||||
[layer_btn.find_node("Visible"), layer_btn.name])
|
||||
layer_btn.find_node("Up").connect("pressed", self, "move_down", [layer_btn])
|
||||
layer_btn.find_node("Down").connect("pressed", self, "move_up", [layer_btn])
|
||||
layer_btn.find_node("Lock").connect("pressed", self, "lock_layer",
|
||||
[layer_btn, layer_btn.name])
|
||||
|
||||
|
||||
func _on_Button_pressed():
|
||||
add_new_layer()
|
||||
|
||||
|
||||
func _on_PaintCanvasContainer_mouse_entered():
|
||||
if mouse_on_top == true:
|
||||
return
|
||||
mouse_on_top = true
|
||||
paint_canvas.tool_layer.clear()
|
||||
paint_canvas.update()
|
||||
paint_canvas.tool_layer.update_texture()
|
||||
|
||||
|
||||
func _on_PaintCanvasContainer_mouse_exited():
|
||||
if mouse_on_top == false:
|
||||
return
|
||||
mouse_on_top = false
|
||||
paint_canvas.tool_layer.clear()
|
||||
paint_canvas.update()
|
||||
paint_canvas.tool_layer.update_texture()
|
||||
|
||||
|
||||
func _on_ColorPicker_popup_closed():
|
||||
find_node("Colors").add_color_prefab(find_node("ColorPicker").color)
|
||||
|
||||
|
||||
############################################
|
||||
# MISC
|
||||
############################################
|
||||
|
||||
func is_position_in_canvas(pos):
|
||||
if Rect2(paint_canvas_container_node.rect_global_position,
|
||||
paint_canvas_container_node.rect_global_position + paint_canvas_container_node.rect_size).has_point(pos):
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func is_mouse_in_canvas() -> bool:
|
||||
if is_position_in_canvas(get_global_mouse_position()):
|
||||
return true #mouse_on_top # check if mouse is inside canvas
|
||||
else:
|
||||
return false
|
||||
|
||||
|
||||
func is_any_menu_open() -> bool:
|
||||
return $ChangeCanvasSize.visible or \
|
||||
$ChangeGridSizeDialog.visible or \
|
||||
$Settings.visible or \
|
||||
$LoadFileDialog.visible or \
|
||||
$SaveFileDialog.visible or \
|
||||
find_node("Navbar").is_any_menu_open()
|
||||
|
||||
|
||||
|
||||
func _on_LockAlpha_pressed():
|
||||
var checked = find_node("LockAlpha").pressed
|
||||
paint_canvas.active_layer.toggle_alpha_locked()
|
||||
for i in range(find_node("Layer").get_popup().get_item_count()):
|
||||
if find_node("Layer").get_popup().get_item_text(i) == "Toggle Alpha Locked":
|
||||
find_node("Layer").get_popup().set_item_checked(i, not find_node("Layer").get_popup().is_item_checked(i))
|
||||
|
||||
|
||||
func _on_BrushRect_pressed():
|
||||
if brush_mode != Tools.BRUSH:
|
||||
set_brush(Tools.BRUSH)
|
||||
selected_brush_prefab = BrushPrefabs.Type.RECT
|
||||
|
||||
|
||||
func _on_BrushCircle_pressed():
|
||||
if brush_mode != Tools.BRUSH:
|
||||
set_brush(Tools.BRUSH)
|
||||
selected_brush_prefab = BrushPrefabs.Type.CIRCLE
|
||||
|
||||
|
||||
func _on_BrushVLine_pressed():
|
||||
if brush_mode != Tools.BRUSH:
|
||||
set_brush(Tools.BRUSH)
|
||||
selected_brush_prefab = BrushPrefabs.Type.V_LINE
|
||||
|
||||
|
||||
func _on_BrushHLine_pressed():
|
||||
if brush_mode != Tools.BRUSH:
|
||||
set_brush(Tools.BRUSH)
|
||||
selected_brush_prefab = BrushPrefabs.Type.H_LINE
|
||||
|
||||
|
||||
func _on_BrushSize_value_changed(value: float):
|
||||
find_node("BrushSizeLabel").text = str(int(value))
|
||||
|
||||
|
||||
func _on_XSymmetry_pressed():
|
||||
paint_canvas.symmetry_x = not paint_canvas.symmetry_x
|
||||
|
||||
|
||||
func _on_YSymmetry_pressed():
|
||||
paint_canvas.symmetry_y = not paint_canvas.symmetry_y
|
1279
game/addons/Godoxel/Editor.tscn
Normal file
22
game/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.
|
98
game/addons/Godoxel/Layer.gd
Normal file
@ -0,0 +1,98 @@
|
||||
extends Reference
|
||||
class_name GELayer
|
||||
|
||||
|
||||
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
|
||||
|
||||
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.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):
|
||||
visible = vis
|
||||
texture_rect_ref.visible = visible
|
||||
|
||||
|
||||
func toggle_lock():
|
||||
locked = not locked
|
||||
|
||||
|
||||
func toggle_alpha_locked():
|
||||
alpha_locked = not alpha_locked
|
||||
|
110
game/addons/Godoxel/LayerButton.tscn
Normal file
@ -0,0 +1,110 @@
|
||||
[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]
|
||||
bg_color = Color( 0.180392, 0.176471, 0.176471, 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
game/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)
|
||||
|
||||
|
106
game/addons/Godoxel/Navbar.gd
Normal file
@ -0,0 +1,106 @@
|
||||
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)
|
||||
"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":
|
||||
owner.get_node("SaveFileDialog").show()
|
||||
"Load":
|
||||
owner.get_node("LoadFileDialog").show()
|
||||
"New":
|
||||
owner.get_node("ConfirmationDialog").show()
|
||||
|
||||
|
||||
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_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()
|
||||
"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)
|
||||
|
||||
|
||||
func is_any_menu_open() -> bool:
|
||||
for child in $Buttons.get_children():
|
||||
if child.get_popup().visible:
|
||||
return true
|
||||
return false
|
||||
|
489
game/addons/Godoxel/PaintCanvas.gd
Normal file
@ -0,0 +1,489 @@
|
||||
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):
|
||||
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
|
||||
#so that we don't have to call these functions
|
||||
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
|
30
game/addons/Godoxel/PaintCanvas.tscn
Normal file
@ -0,0 +1,30 @@
|
||||
[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( 64, 37 )
|
||||
|
||||
[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
game/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
game/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
game/addons/Godoxel/PaintCanvasContainer.gd
Normal file
@ -0,0 +1,2 @@
|
||||
tool
|
||||
extends Control
|
17
game/addons/Godoxel/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Godoxel (image Editor) v0.1
|
||||
###### (Godot-Pixel Image Editor)
|
||||
|
||||
|
||||
Godoxel is an image editor for Godot, that can be used inside the editor.
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
* Has basic pixel editor functionality
|
||||
* Save/Load
|
||||
* Multiple layers (can be locked/hidden)
|
||||
* Tools: Pencil, lighten/darken, rectangle/line, fill, ...
|
||||
|
||||
---
|
||||
###### For feature proposals or occuring problems, please write an issue or message me on Discord cobrapitz#2872
|
70
game/addons/Godoxel/SaveFileDialog.gd
Normal file
@ -0,0 +1,70 @@
|
||||
tool
|
||||
extends FileDialog
|
||||
|
||||
var canvas
|
||||
|
||||
var file_path = ""
|
||||
|
||||
|
||||
func _enter_tree():
|
||||
canvas = get_parent().find_node("Canvas")
|
||||
|
||||
|
||||
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")
|
||||
|
||||
|
||||
func _on_SaveFileDialog_file_selected(path):
|
||||
#print("selected file: ", path)
|
||||
file_path = path
|
||||
save_file()
|
||||
|
||||
|
||||
# warning-ignore:unused_argument
|
||||
func _on_LineEdit_text_entered(text):
|
||||
return
|
||||
# print("text entered: ", text)
|
||||
|
||||
|
||||
func _on_SaveFileDialog_confirmed():
|
||||
return
|
||||
# print("confirmed: ", current_path)
|
||||
|
||||
|
||||
func save_file():
|
||||
var image = Image.new()
|
||||
image.create(canvas.canvas_width, canvas.canvas_height, true, Image.FORMAT_RGBA8)
|
||||
image.lock()
|
||||
|
||||
for layer in canvas.layers:
|
||||
var idx = 0
|
||||
if not layer.visible:
|
||||
continue
|
||||
for x in range(layer.layer_width):
|
||||
for y in range(layer.layer_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, image_color.blend(color))
|
||||
else:
|
||||
image.set_pixel(x, y, color)
|
||||
image.unlock()
|
||||
|
||||
var dir = Directory.new()
|
||||
if dir.file_exists(file_path):
|
||||
dir.remove(file_path)
|
||||
|
||||
image.save_png(file_path)
|
||||
|
||||
|
||||
func _on_SaveFileDialog_about_to_show():
|
||||
invalidate()
|
||||
|
||||
|
||||
func _on_SaveFileDialog_visibility_changed():
|
||||
invalidate()
|
24
game/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
game/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
game/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
game/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
game/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
game/addons/Godoxel/ViewportContainer.gd
Normal file
@ -0,0 +1,6 @@
|
||||
extends ViewportContainer
|
||||
tool
|
||||
|
||||
func _ready():
|
||||
get_child(0).size = rect_size
|
||||
|
41
game/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
game/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
game/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
game/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
game/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
game/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
game/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
game/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
game/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
game/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
game/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
game/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
game/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
game/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
game/addons/Godoxel/assets/BrushCircle.png
Normal file
After Width: | Height: | Size: 195 B |
34
game/addons/Godoxel/assets/BrushCircle.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/BrushCircle_Hovered.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
34
game/addons/Godoxel/assets/BrushCircle_Hovered.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/BrushHLine.png
Normal file
After Width: | Height: | Size: 127 B |
34
game/addons/Godoxel/assets/BrushHLine.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/BrushHLine_Hovered.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
34
game/addons/Godoxel/assets/BrushHLine_Hovered.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/BrushRect.png
Normal file
After Width: | Height: | Size: 128 B |
34
game/addons/Godoxel/assets/BrushRect.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/BrushRect.png-2b2d0ae4889c1fbc5c7bee7ae5515663.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/BrushRect.png"
|
||||
dest_files=[ "res://.import/BrushRect.png-2b2d0ae4889c1fbc5c7bee7ae5515663.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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/BrushRect_Hovered.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
34
game/addons/Godoxel/assets/BrushRect_Hovered.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/BrushRect_Hovered.png-b09066b673d6082ce887a03a19f17977.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/BrushRect_Hovered.png"
|
||||
dest_files=[ "res://.import/BrushRect_Hovered.png-b09066b673d6082ce887a03a19f17977.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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/BrushVLine.png
Normal file
After Width: | Height: | Size: 129 B |
34
game/addons/Godoxel/assets/BrushVLine.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/BrushVLine.png-022220d888fe2fe2f8a081bcca62b4b2.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/BrushVLine.png"
|
||||
dest_files=[ "res://.import/BrushVLine.png-022220d888fe2fe2f8a081bcca62b4b2.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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/BrushVLine_Hovered.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
34
game/addons/Godoxel/assets/BrushVLine_Hovered.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/BrushVLine_Hovered.png-104e29757699756f1b44bd32a622df2c.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/BrushVLine_Hovered.png"
|
||||
dest_files=[ "res://.import/BrushVLine_Hovered.png-104e29757699756f1b44bd32a622df2c.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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/Godoxel_Preview.png
Normal file
After Width: | Height: | Size: 45 KiB |
34
game/addons/Godoxel/assets/Godoxel_Preview.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/Godoxel_Preview.png-e30103581d3fc0ed2a2c92cdf72b5c70.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/Godoxel_Preview.png"
|
||||
dest_files=[ "res://.import/Godoxel_Preview.png-e30103581d3fc0ed2a2c92cdf72b5c70.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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/arrow_down.png
Normal file
After Width: | Height: | Size: 562 B |
34
game/addons/Godoxel/assets/arrow_down.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/arrow_down.png-d2bd93428c0bc172a28a43c55aac576e.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/arrow_down.png"
|
||||
dest_files=[ "res://.import/arrow_down.png-d2bd93428c0bc172a28a43c55aac576e.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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/arrow_up.png
Normal file
After Width: | Height: | Size: 565 B |
34
game/addons/Godoxel/assets/arrow_up.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/arrow_up.png-2598e148d1b795a628ce80a4fd5cf401.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/arrow_up.png"
|
||||
dest_files=[ "res://.import/arrow_up.png-2598e148d1b795a628ce80a4fd5cf401.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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
@ -0,0 +1,124 @@
|
||||
Bitstream Vera Fonts Copyright
|
||||
|
||||
The fonts have a generous copyright, allowing derivative works (as
|
||||
long as "Bitstream" or "Vera" are not in the names), and full
|
||||
redistribution (so long as they are not *sold* by themselves). They
|
||||
can be be bundled, redistributed and sold with any software.
|
||||
|
||||
The fonts are distributed under the following copyright:
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
|
||||
Vera is a trademark of Bitstream, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the fonts accompanying this license ("Fonts") and associated
|
||||
documentation files (the "Font Software"), to reproduce and distribute
|
||||
the Font Software, including without limitation the rights to use,
|
||||
copy, merge, publish, distribute, and/or sell copies of the Font
|
||||
Software, and to permit persons to whom the Font Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright and trademark notices and this permission notice
|
||||
shall be included in all copies of one or more of the Font Software
|
||||
typefaces.
|
||||
|
||||
The Font Software may be modified, altered, or added to, and in
|
||||
particular the designs of glyphs or characters in the Fonts may be
|
||||
modified and additional glyphs or characters may be added to the
|
||||
Fonts, only if the fonts are renamed to names not containing either
|
||||
the words "Bitstream" or the word "Vera".
|
||||
|
||||
This License becomes null and void to the extent applicable to Fonts
|
||||
or Font Software that has been modified and is distributed under the
|
||||
"Bitstream Vera" names.
|
||||
|
||||
The Font Software may be sold as part of a larger software package but
|
||||
no copy of one or more of the Font Software typefaces may be sold by
|
||||
itself.
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
|
||||
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
|
||||
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the names of Gnome, the Gnome
|
||||
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||
otherwise to promote the sale, use or other dealings in this Font
|
||||
Software without prior written authorization from the Gnome Foundation
|
||||
or Bitstream Inc., respectively. For further information, contact:
|
||||
fonts at gnome dot org.
|
||||
|
||||
Copyright FAQ
|
||||
=============
|
||||
|
||||
1. I don't understand the resale restriction... What gives?
|
||||
|
||||
Bitstream is giving away these fonts, but wishes to ensure its
|
||||
competitors can't just drop the fonts as is into a font sale system
|
||||
and sell them as is. It seems fair that if Bitstream can't make money
|
||||
from the Bitstream Vera fonts, their competitors should not be able to
|
||||
do so either. You can sell the fonts as part of any software package,
|
||||
however.
|
||||
|
||||
2. I want to package these fonts separately for distribution and
|
||||
sale as part of a larger software package or system. Can I do so?
|
||||
|
||||
Yes. A RPM or Debian package is a "larger software package" to begin
|
||||
with, and you aren't selling them independently by themselves.
|
||||
See 1. above.
|
||||
|
||||
3. Are derivative works allowed?
|
||||
Yes!
|
||||
|
||||
4. Can I change or add to the font(s)?
|
||||
Yes, but you must change the name(s) of the font(s).
|
||||
|
||||
5. Under what terms are derivative works allowed?
|
||||
|
||||
You must change the name(s) of the fonts. This is to ensure the
|
||||
quality of the fonts, both to protect Bitstream and Gnome. We want to
|
||||
ensure that if an application has opened a font specifically of these
|
||||
names, it gets what it expects (though of course, using fontconfig,
|
||||
substitutions could still could have occurred during font
|
||||
opening). You must include the Bitstream copyright. Additional
|
||||
copyrights can be added, as per copyright law. Happy Font Hacking!
|
||||
|
||||
6. If I have improvements for Bitstream Vera, is it possible they might get
|
||||
adopted in future versions?
|
||||
|
||||
Yes. The contract between the Gnome Foundation and Bitstream has
|
||||
provisions for working with Bitstream to ensure quality additions to
|
||||
the Bitstream Vera font family. Please contact us if you have such
|
||||
additions. Note, that in general, we will want such additions for the
|
||||
entire family, not just a single font, and that you'll have to keep
|
||||
both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
|
||||
glyphs to the font, they must be stylistically in keeping with Vera's
|
||||
design. Vera cannot become a "ransom note" font. Jim Lyles will be
|
||||
providing a document describing the design elements used in Vera, as a
|
||||
guide and aid for people interested in contributing to Vera.
|
||||
|
||||
7. I want to sell a software package that uses these fonts: Can I do so?
|
||||
|
||||
Sure. Bundle the fonts with your software and sell your software
|
||||
with the fonts. That is the intent of the copyright.
|
||||
|
||||
8. If applications have built the names "Bitstream Vera" into them,
|
||||
can I override this somehow to use fonts of my choosing?
|
||||
|
||||
This depends on exact details of the software. Most open source
|
||||
systems and software (e.g., Gnome, KDE, etc.) are now converting to
|
||||
use fontconfig (see www.fontconfig.org) to handle font configuration,
|
||||
selection and substitution; it has provisions for overriding font
|
||||
names and subsituting alternatives. An example is provided by the
|
||||
supplied local.conf file, which chooses the family Bitstream Vera for
|
||||
"sans", "serif" and "monospace". Other software (e.g., the XFree86
|
||||
core server) has other mechanisms for font substitution.
|
||||
|
@ -0,0 +1,11 @@
|
||||
Contained herin is the Bitstream Vera font family.
|
||||
|
||||
The Copyright information is found in the COPYRIGHT.TXT file (along
|
||||
with being incoporated into the fonts themselves).
|
||||
|
||||
The releases notes are found in the file "RELEASENOTES.TXT".
|
||||
|
||||
We hope you enjoy Vera!
|
||||
|
||||
Bitstream, Inc.
|
||||
The Gnome Project
|
@ -0,0 +1,162 @@
|
||||
Bitstream Vera Fonts - April 16, 2003
|
||||
=====================================
|
||||
|
||||
The version number of these fonts is 1.10 to distinguish them from the
|
||||
beta test fonts.
|
||||
|
||||
Note that the Vera copyright is incorporated in the fonts themselves.
|
||||
The License field in the fonts contains the copyright license as it
|
||||
appears below. The TrueType copyright field is not large enough to
|
||||
contain the full license, so the license is incorporated (as you might
|
||||
think if you thought about it) into the license field, which
|
||||
unfortunately can be obscure to find. (In pfaedit, see: Element->Font
|
||||
Info->TTFNames->License).
|
||||
|
||||
Our apologies for it taking longer to complete the fonts than planned.
|
||||
Beta testers requested a tighter line spacing (less leading) and Jim
|
||||
Lyles redesigned Vera's accents to bring its line spacing to more
|
||||
typical of other fonts. This took additional time and effort. Our
|
||||
thanks to Jim for this effort above and beyond the call of duty.
|
||||
|
||||
There are four monospace and sans faces (normal, oblique, bold, bold
|
||||
oblique) and two serif faces (normal and bold). Fontconfig/Xft2 (see
|
||||
www.fontconfig.org) can artificially oblique the serif faces for you:
|
||||
this loses hinting and distorts the faces slightly, but is visibly
|
||||
different than normal and bold, and reasonably pleasing.
|
||||
|
||||
On systems with fontconfig 2.0 or 2.1 installed, making your sans,
|
||||
serif and monospace fonts default to these fonts is very easy. Just
|
||||
drop the file local.conf into your /etc/fonts directory. This will
|
||||
make the Bitstream fonts your default fonts for all applications using
|
||||
fontconfig (if sans, serif, or monospace names are used, as they often
|
||||
are as default values in many desktops). The XML in local.conf may
|
||||
need modification to enable subpixel decimation, if appropriate,
|
||||
however, the commented out phrase does so for XFree86 4.3, in the case
|
||||
that the server does not have sufficient information to identify the
|
||||
use of a flat panel. Fontconfig 2.2 adds Vera to the list of font
|
||||
families and will, by default use it as the default sans, serif and
|
||||
monospace fonts.
|
||||
|
||||
During the testing of the final Vera fonts, we learned that screen
|
||||
fonts in general are only typically hinted to work correctly at
|
||||
integer pixel sizes. Vera is coded internally for integer sizes only.
|
||||
We need to investigate further to see if there are commonly used fonts
|
||||
that are hinted to be rounded but are not rounded to integer sizes due
|
||||
to oversights in their coding.
|
||||
|
||||
Most fonts work best at 8 pixels and below if anti-aliased only, as
|
||||
the amount of work required to hint well at smaller and smaller sizes
|
||||
becomes astronomical. GASP tables are typically used to control
|
||||
whether hinting is used or not, but Freetype/Xft does not currently
|
||||
support GASP tables (which are present in Vera).
|
||||
|
||||
To mitigate this problem, both for Vera and other fonts, there will be
|
||||
(very shortly) a new fontconfig 2.2 release that will, by default not
|
||||
apply hints if the size is below 8 pixels. if you should have a font
|
||||
that in fact has been hinted more agressively, you can use fontconfig
|
||||
to note this exception. We believe this should improve many hinted
|
||||
fonts in addition to Vera, though implemeting GASP support is likely
|
||||
the right long term solution.
|
||||
|
||||
Font rendering in Gnome or KDE is the combination of algorithms in
|
||||
Xft2 and Freetype, along with hinting in the fonts themselves. It is
|
||||
vital to have sufficient information to disentangle problems that you
|
||||
may observe.
|
||||
|
||||
Note that having your font rendering system set up correctly is vital
|
||||
to proper judgement of problems of the fonts:
|
||||
|
||||
* Freetype may or may not be configured to in ways that may
|
||||
implement execution of possibly patented (in some parts of the world)
|
||||
TrueType hinting algorithms, particularly at small sizes. Best
|
||||
results are obtained while using these algorithms.
|
||||
|
||||
* The freetype autohinter (used when the possibly patented
|
||||
algorithms are not used) continues to improve with each release. If
|
||||
you are using the autohinter, please ensure you are using an up to
|
||||
date version of freetype before reporting problems.
|
||||
|
||||
* Please identify what version of freetype you are using in any
|
||||
bug reports, and how your freetype is configured.
|
||||
|
||||
* Make sure you are not using the freetype version included in
|
||||
XFree86 4.3, as it has bugs that significantly degrade most fonts,
|
||||
including Vera. if you build XFree86 4.3 from source yourself, you may
|
||||
have installed this broken version without intending it (as I
|
||||
did). Vera was verified with the recently released Freetype 2.1.4. On
|
||||
many systems, 'ldd" can be used to see which freetype shared library
|
||||
is actually being used.
|
||||
|
||||
* Xft/X Render does not (yet) implement gamma correction. This
|
||||
causes significant problems rendering white text on a black background
|
||||
(causing partial pixels to be insufficiently shaded) if the gamma of
|
||||
your monitor has not been compensated for, and minor problems with
|
||||
black text on a while background. The program "xgamma" can be used to
|
||||
set a gamma correction value in the X server's color pallette. Most
|
||||
monitors have a gamma near 2.
|
||||
|
||||
* Note that the Vera family uses minimal delta hinting. Your
|
||||
results on other systems when not used anti-aliased may not be
|
||||
entirely satisfying. We are primarily interested in reports of
|
||||
problems on open source systems implementing Xft2/fontconfig/freetype
|
||||
(which implements antialiasing and hinting adjustements, and
|
||||
sophisticated subpixel decimation on flatpanels). Also, the
|
||||
algorithms used by Xft2 adjust the hints to integer widths and the
|
||||
results are crisper on open source systems than on Windows or
|
||||
MacIntosh.
|
||||
|
||||
* Your fontconfig may (probably does) predate the release of
|
||||
fontconfig 2.2, and you may see artifacts not present when the font is
|
||||
used at very small sizes with hinting enabled. "vc-list -V" can be
|
||||
used to see what version you have installed.
|
||||
|
||||
We believe and hope that these fonts will resolve the problems
|
||||
reported during beta test. The largest change is the reduction of
|
||||
leading (interline spacing), which had annoyed a number of people, and
|
||||
reduced Vera's utility for some applcations. The Vera monospace font
|
||||
should also now make '0' and 'O' and '1' and 'l' more clearly
|
||||
distinguishable.
|
||||
|
||||
The version of these fonts is version 1.10. Fontconfig should be
|
||||
choosing the new version of the fonts if both the released fonts and
|
||||
beta test fonts are installed (though please discard them: they have
|
||||
names of form tt20[1-12]gn.ttf). Note that older versions of
|
||||
fontconfig sometimes did not rebuild their cache correctly when new
|
||||
fonts are installed: please upgrade to fontconfig 2.2. "fc-cache -f"
|
||||
can be used to force rebuilding fontconfig's cache files.
|
||||
|
||||
If you note problems, please send them to fonts at gnome dot org, with
|
||||
exactly which face and size and unicode point you observe the problem
|
||||
at. The xfd utility from XFree86 CVS may be useful for this (e.g. "xfd
|
||||
-fa sans"). A possibly more useful program to examine fonts at a
|
||||
variety of sizes is the "waterfall" program found in Keith Packard's
|
||||
CVS.
|
||||
|
||||
$ cvs -d :pserver:anoncvs@keithp.com:/local/src/CVS login
|
||||
Logging in to :pserver:anoncvs@keithp.com:2401/local/src/CVS
|
||||
CVS password: <hit return>
|
||||
$ cvs -d :pserver:anoncvs@keithp.com:/local/src/CVS co waterfall
|
||||
$ cd waterfall
|
||||
$ xmkmf -a
|
||||
$ make
|
||||
# make install
|
||||
# make install.man
|
||||
|
||||
Again, please make sure you are running an up-to-date freetype, and
|
||||
that you are only examining integer sizes.
|
||||
|
||||
Reporting Problems
|
||||
==================
|
||||
|
||||
Please send problem reports to fonts at gnome org, with the following
|
||||
information:
|
||||
|
||||
1. Version of Freetype, Xft2 and fontconfig
|
||||
2. Whether TT hinting is being used, or the autohinter
|
||||
3. Application being used
|
||||
4. Character/Unicode code point that has problems (if applicable)
|
||||
5. Version of which operating system
|
||||
6. Please include a screenshot, when possible.
|
||||
|
||||
Please check the fonts list archives before reporting problems to cut
|
||||
down on duplication.
|
@ -0,0 +1,8 @@
|
||||
[InternetShortcut]
|
||||
URL=http://www.all-free-download.com/
|
||||
IDList=
|
||||
HotKey=0
|
||||
IconFile=C:\WINDOWS\system32\SHELL32.dll
|
||||
IconIndex=23
|
||||
[{000214A0-0000-0000-C000-000000000046}]
|
||||
Prop3=19,2
|
BIN
game/addons/Godoxel/assets/grid.png
Normal file
After Width: | Height: | Size: 148 B |
34
game/addons/Godoxel/assets/grid.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/grid.png-e3d637acacdb891e09f422df261dbd1e.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/grid.png"
|
||||
dest_files=[ "res://.import/grid.png-e3d637acacdb891e09f422df261dbd1e.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=false
|
||||
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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/lock_layer.png
Normal file
After Width: | Height: | Size: 251 B |
34
game/addons/Godoxel/assets/lock_layer.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/lock_layer.png-076954b389746de9e13c853ed5d9ba59.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/lock_layer.png"
|
||||
dest_files=[ "res://.import/lock_layer.png-076954b389746de9e13c853ed5d9ba59.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=false
|
||||
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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/lock_layer_1.png
Normal file
After Width: | Height: | Size: 242 B |
34
game/addons/Godoxel/assets/lock_layer_1.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/lock_layer_1.png-4848d5f2cd0f48c68b880712b6b38776.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/lock_layer_1.png"
|
||||
dest_files=[ "res://.import/lock_layer_1.png-4848d5f2cd0f48c68b880712b6b38776.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=false
|
||||
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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/minidotta.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
34
game/addons/Godoxel/assets/minidotta.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/minidotta.png-adac81df344972ef82e2499656aa288e.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/minidotta.png"
|
||||
dest_files=[ "res://.import/minidotta.png-adac81df344972ef82e2499656aa288e.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=false
|
||||
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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/minidotta_invis.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
34
game/addons/Godoxel/assets/minidotta_invis.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/minidotta_invis.png-5232a113bb226997ae55212b2aa90bd4.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/minidotta_invis.png"
|
||||
dest_files=[ "res://.import/minidotta_invis.png-5232a113bb226997ae55212b2aa90bd4.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=false
|
||||
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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
game/addons/Godoxel/assets/unlock_layer.png
Normal file
After Width: | Height: | Size: 240 B |
34
game/addons/Godoxel/assets/unlock_layer.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/unlock_layer.png-ae7c97a04fb889522c7c466fdc9dd8f6.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/Godoxel/assets/unlock_layer.png"
|
||||
dest_files=[ "res://.import/unlock_layer.png-ae7c97a04fb889522c7c466fdc9dd8f6.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
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
28
game/addons/Godoxel/dialogs/ChangeGridSizeDialog.gd
Normal file
@ -0,0 +1,28 @@
|
||||
extends AcceptDialog
|
||||
tool
|
||||
|
||||
func _ready():
|
||||
yield(owner, "ready")
|
||||
find_node("GridValue").value = owner.paint_canvas.grid_size
|
||||
find_node("BigGridValue").value = owner.paint_canvas.big_grid_size
|
||||
|
||||
|
||||
func _on_ChangeGridSizeDialog_confirmed():
|
||||
var grid_size = find_node("GridValue").value
|
||||
var big_grid_size = find_node("BigGridValue").value
|
||||
owner.paint_canvas.grid_size = grid_size
|
||||
owner.paint_canvas.big_grid_size = big_grid_size
|
||||
|
||||
|
||||
func _on_GridValue_value_changed(value):
|
||||
var grid_size = value
|
||||
owner.paint_canvas.grid_size = grid_size
|
||||
|
||||
|
||||
func _on_BigGridValue_value_changed(value):
|
||||
var big_grid_size = value
|
||||
owner.paint_canvas.big_grid_size = big_grid_size
|
||||
|
||||
|
||||
func _on_ChangeGridSizeDialog_visibility_changed():
|
||||
pass # Replace with function body.
|
20
game/addons/Godoxel/dialogs/ConfirmationDialog.gd
Normal file
@ -0,0 +1,20 @@
|
||||
extends ConfirmationDialog
|
||||
tool
|
||||
|
||||
func _ready():
|
||||
yield(owner, "ready")
|
||||
find_node("Width").value = owner.paint_canvas.canvas_width
|
||||
find_node("Height").value = owner.paint_canvas.canvas_height
|
||||
|
||||
|
||||
func _on_ConfirmationDialog_confirmed():
|
||||
var width = find_node("Width").value
|
||||
var height = find_node("Height").value
|
||||
print("change canvas size: ", width, " ", height)
|
||||
owner.paint_canvas.resize(width, height)
|
||||
|
||||
|
||||
func _on_ChangeCanvasSize_visibility_changed():
|
||||
if visible:
|
||||
find_node("Width").value = owner.paint_canvas.canvas_width
|
||||
find_node("Height").value = owner.paint_canvas.canvas_height
|
70
game/addons/Godoxel/dialogs/LoadFileDialog.gd
Normal file
@ -0,0 +1,70 @@
|
||||
tool
|
||||
extends FileDialog
|
||||
|
||||
|
||||
var canvas: GECanvas
|
||||
|
||||
var file_path = ""
|
||||
|
||||
|
||||
func _ready():
|
||||
get_line_edit().connect("text_entered", self, "_on_LineEdit_text_entered")
|
||||
invalidate()
|
||||
clear_filters()
|
||||
add_filter("*.png ; PNG Images")
|
||||
|
||||
|
||||
func _on_LineEdit_text_entered(_text):
|
||||
return
|
||||
# print(_text)
|
||||
#load_img()
|
||||
# print("hsadfasd")
|
||||
|
||||
|
||||
func _on_LoadFileDialog_file_selected(path):
|
||||
file_path = path
|
||||
#print("1ere")
|
||||
load_img()
|
||||
|
||||
|
||||
func _on_LoadFileDialog_confirmed():
|
||||
return
|
||||
#print("ere")
|
||||
#load_img()
|
||||
|
||||
|
||||
func load_img():
|
||||
var image = Image.new()
|
||||
if image.load(file_path) != OK:
|
||||
print("couldn't load image!")
|
||||
return
|
||||
|
||||
var image_data = image.get_data()
|
||||
var layer: GELayer = owner.add_new_layer()
|
||||
|
||||
var width = image.get_width()
|
||||
var height = image.get_height()
|
||||
|
||||
if owner.paint_canvas.canvas_width < width:
|
||||
owner.paint_canvas.resize(width, owner.paint_canvas.canvas_height)
|
||||
|
||||
if owner.paint_canvas.canvas_height < height:
|
||||
owner.paint_canvas.resize(owner.paint_canvas.canvas_width, height)
|
||||
|
||||
for i in range(image_data.size() / 4):
|
||||
var color = Color(image_data[i*4] / 255.0, image_data[i*4+1] / 255.0, image_data[i*4+2] / 255.0, image_data[i*4+3] / 255.0)
|
||||
var pos = GEUtils.to_2D(i, image.get_width())
|
||||
if pos.x > layer.layer_width:
|
||||
continue
|
||||
|
||||
layer.set_pixel(pos.x, pos.y, color)
|
||||
layer.update_texture()
|
||||
|
||||
|
||||
|
||||
func _on_LoadFileDialog_about_to_show():
|
||||
invalidate()
|
||||
|
||||
|
||||
func _on_LoadFileDialog_visibility_changed():
|
||||
invalidate()
|
22
game/addons/Godoxel/dialogs/LoadFileDialog.tscn
Normal file
@ -0,0 +1,22 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[sub_resource type="GDScript" id=1]
|
||||
script/source = "extends ConfirmationDialog
|
||||
|
||||
func _ready():
|
||||
get_ok().connect(\"pressed\", self, \"hide\")
|
||||
get_cancel().connect(\"pressed\", self, \"hide\")
|
||||
|
||||
|
||||
|
||||
"
|
||||
|
||||
[node name="LoadFileDialog" type="FileDialog"]
|
||||
margin_right = 604.0
|
||||
margin_bottom = 367.0
|
||||
window_title = "Open a File"
|
||||
mode = 0
|
||||
access = 2
|
||||
current_dir = "/Projects/BitBucket/GraphicsEditor"
|
||||
current_path = "/Projects/BitBucket/GraphicsEditor/"
|
||||
script = SubResource( 1 )
|
7
game/addons/Godoxel/plugin.cfg
Normal file
@ -0,0 +1,7 @@
|
||||
[plugin]
|
||||
|
||||
name="Godoxel - Pixel Image Editor"
|
||||
description=""
|
||||
author=""
|
||||
version=""
|
||||
script="plugin.gd"
|
37
game/addons/Godoxel/plugin.gd
Normal file
@ -0,0 +1,37 @@
|
||||
tool
|
||||
extends EditorPlugin
|
||||
|
||||
var editor_scene = load("res://addons/Godoxel/Editor.tscn").instance()
|
||||
|
||||
func _enter_tree():
|
||||
editor_scene.name = "Editor"
|
||||
if get_editor_interface().get_editor_viewport().has_node("Editor"):
|
||||
var n = get_editor_interface().get_editor_viewport().get_node("Editor")
|
||||
n.name = "EditorDel"
|
||||
n.queue_free()
|
||||
get_editor_interface().get_editor_viewport().add_child(editor_scene, true)
|
||||
editor_scene.owner = get_editor_interface().get_editor_viewport()
|
||||
make_visible(false)
|
||||
|
||||
|
||||
func _exit_tree():
|
||||
if editor_scene:
|
||||
editor_scene.queue_free()
|
||||
|
||||
|
||||
func has_main_screen():
|
||||
return true
|
||||
|
||||
|
||||
func make_visible(visible):
|
||||
if editor_scene:
|
||||
editor_scene.visible = visible
|
||||
|
||||
|
||||
func get_plugin_name():
|
||||
return "Godoxel"
|
||||
|
||||
|
||||
func get_plugin_icon():
|
||||
# Must return some kind of Texture for the icon.
|
||||
return get_editor_interface().get_base_control().get_icon("CanvasModulate", "EditorIcons")
|
3
game/addons/addon_versions
Normal file
@ -0,0 +1,3 @@
|
||||
Godotxel 6e3b61c2887e51c767d0c2417e0b293a35f904ac from https://github.com/aaronfranke/GraphicsEditor
|
||||
|
||||
|
@ -14,6 +14,16 @@ _global_script_classes=[ {
|
||||
"language": "GDScript",
|
||||
"path": "res://scripts/auras/aura_script.gd"
|
||||
}, {
|
||||
"base": "EditorSpatialGizmo",
|
||||
"class": "BoneSpatialGizmo",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/gdpose/bonegizmo.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "BrushPrefabs",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/BrushPrefabs.gd"
|
||||
}, {
|
||||
"base": "Spatial",
|
||||
"class": "CharacterSkeketonAttachPoint",
|
||||
"language": "GDScript",
|
||||
@ -39,6 +49,81 @@ _global_script_classes=[ {
|
||||
"language": "GDScript",
|
||||
"path": "res://voxelman/debug/CubicVoxelMesher.gd"
|
||||
}, {
|
||||
"base": "Node",
|
||||
"class": "GEAction",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/actions/Action.gd"
|
||||
}, {
|
||||
"base": "GEAction",
|
||||
"class": "GEBrighten",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/actions/Brighten.gd"
|
||||
}, {
|
||||
"base": "GEAction",
|
||||
"class": "GEBrush",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/actions/Brush.gd"
|
||||
}, {
|
||||
"base": "GEAction",
|
||||
"class": "GEBucket",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/actions/Bucket.gd"
|
||||
}, {
|
||||
"base": "Control",
|
||||
"class": "GECanvas",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/Canvas.gd"
|
||||
}, {
|
||||
"base": "GEAction",
|
||||
"class": "GECut",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/actions/Cut.gd"
|
||||
}, {
|
||||
"base": "GEAction",
|
||||
"class": "GEDarken",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/actions/Darken.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "GELayer",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/Layer.gd"
|
||||
}, {
|
||||
"base": "GEAction",
|
||||
"class": "GELine",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/actions/Line.gd"
|
||||
}, {
|
||||
"base": "GEAction",
|
||||
"class": "GEMultiLine",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/actions/MultiLine.gd"
|
||||
}, {
|
||||
"base": "GEAction",
|
||||
"class": "GEPasteCut",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/actions/PasteCut.gd"
|
||||
}, {
|
||||
"base": "GEAction",
|
||||
"class": "GEPencil",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/actions/Pencil.gd"
|
||||
}, {
|
||||
"base": "GEAction",
|
||||
"class": "GERainbow",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/actions/Rainbow.gd"
|
||||
}, {
|
||||
"base": "GEAction",
|
||||
"class": "GERect",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/actions/Rect.gd"
|
||||
}, {
|
||||
"base": "Node",
|
||||
"class": "GEUtils",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/Godoxel/Util.gd"
|
||||
}, {
|
||||
"base": "Resource",
|
||||
"class": "GameModule",
|
||||
"language": "GDScript",
|
||||
@ -141,11 +226,28 @@ _global_script_classes=[ {
|
||||
} ]
|
||||
_global_script_class_icons={
|
||||
"AuraGD": "",
|
||||
"BoneSpatialGizmo": "",
|
||||
"BrushPrefabs": "",
|
||||
"CharacterSkeketonAttachPoint": "",
|
||||
"DisplayPlayerGD": "",
|
||||
"EntityAIGD": "",
|
||||
"EntityDataGD": "",
|
||||
"GDCubicVoxelMesher": "",
|
||||
"GEAction": "",
|
||||
"GEBrighten": "",
|
||||
"GEBrush": "",
|
||||
"GEBucket": "",
|
||||
"GECanvas": "",
|
||||
"GECut": "",
|
||||
"GEDarken": "",
|
||||
"GELayer": "",
|
||||
"GELine": "",
|
||||
"GEMultiLine": "",
|
||||
"GEPasteCut": "",
|
||||
"GEPencil": "",
|
||||
"GERainbow": "",
|
||||
"GERect": "",
|
||||
"GEUtils": "",
|
||||
"GameModule": "",
|
||||
"HealthResource": "",
|
||||
"ItemTemplateGD": "",
|
||||
@ -207,6 +309,10 @@ window/size/viewport_scale=100
|
||||
window/size/ui_scale=1.2
|
||||
window/size/ui_scale_touch=1.0
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
enabled=PoolStringArray( "Godoxel" )
|
||||
|
||||
[ess]
|
||||
|
||||
level/auto_learn_spells=false
|
||||
|