This commit is contained in:
Relintai 2021-02-07 15:15:13 +01:00
parent ac137ca62f
commit 32648325e0
90 changed files with 5971 additions and 1 deletions

View File

@ -1,3 +1,6 @@
*
!module_manager
!module_manager/**
!module_manager/**
!Godoxel
!Godoxel/**
!addon_versions

View 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

View 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

View 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()

View 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])

View File

@ -0,0 +1,9 @@
extends RichTextLabel
tool
func _ready():
pass
func display_text(text):
self.text = text

View 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

File diff suppressed because one or more lines are too long

View 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.

View 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

View 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

View 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)

View 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

View 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

View 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

View 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

View 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 )

View File

@ -0,0 +1,2 @@
tool
extends Control

View 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

View 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()

View 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)

View 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()

View 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"]

View 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)]

View 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])

View File

@ -0,0 +1,6 @@
extends ViewportContainer
tool
func _ready():
get_child(0).size = rect_size

View 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()

View 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 )

View 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

View 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])

View 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])

View 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])

View 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])

View 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])

View 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])

View 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])

View 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])

View 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])

View 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])

View 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])

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

View 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

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

View 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

View 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.

View 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

View 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()

View 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 )

View File

@ -0,0 +1,7 @@
[plugin]
name="Godoxel - Pixel Image Editor"
description=""
author=""
version=""
script="plugin.gd"

View 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")

View File

@ -0,0 +1,3 @@
Godotxel 6e3b61c2887e51c767d0c2417e0b293a35f904ac from https://github.com/aaronfranke/GraphicsEditor

View File

@ -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