First commit!

This commit is contained in:
Flairieve 2019-08-04 02:57:42 -05:00
parent 0a82366f7b
commit 5168ed8193
29 changed files with 1347 additions and 0 deletions

14
.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
\exports/
\.import/
addons/scene_notes/
addons/todo/
scene-notes\.ini
todo\.cache\.ini
todo\.config\.ini
export_presets\.cfg

View File

@ -1 +1,17 @@
GraphicsEditor GraphicsEditor
Made by Flairieve!
This plugin was built in Godot v3.1 and is currently in alpha so it doesn't work fully and it's not finished!
Right now it can create basic images in a 100x100 resolution.
It is using a chunk system to keep rendering optmized!
Stuff that's still WIP and are not finished:
The bucket tool lags and the selection tool doesn't work at all!
The way in which the program saves temporary data doesn't do it in chunks yet!
There is no custom color selection tool yet so the color picker doesn't work on it.
All chunks are always rendered which causes lag when there is more chunks.
There is no way to change the resolution of the image.
Images can only save in the PNG format.
You can't zoom in and out on the image! Either a Camera2D can be used or every control node can be scaled!

View File

@ -0,0 +1,8 @@
extends Node
func _ready():
print("test")
pass
func draw_pixel():
print("Drawed pixel!")

View File

@ -0,0 +1,23 @@
tool
extends Control
export var color = Color()
func _ready():
pass
func _draw():
draw_outline_box(self.rect_size, color, 3)
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)
func _process(delta):
update()

View File

@ -0,0 +1,219 @@
tool
extends Control
onready var paint_canvas_container_node = get_node("PaintCanvasContainer")
onready var paint_canvas_node = paint_canvas_container_node.get_node("PaintCanvas")
onready var grids_node = paint_canvas_node.get_node("Grids")
var selected_color = Color(1, 1, 1, 1)
var util = preload("res://addons/graphics_editor/Util.gd")
onready var textinfo = get_node("BottomPanel/TextInfo")
onready var grid_size = paint_canvas_node.grid_size
onready var region_size = paint_canvas_node.region_size
var allow_drawing = true
func _ready():
#---------------------------
#Setup the info bottom panel
#---------------------------
add_text_info_variables()
#--------------------
#Setup the layer tree
#--------------------
setup_layer_tree()
#------------------
#Setup visual grids
#------------------
for i in grids_node.get_children():
i.rect_size = Vector2(paint_canvas_node.canvas_size.x * grid_size, paint_canvas_node.canvas_size.y * grid_size)
grids_node.get_node("VisualGrid").size = grid_size
grids_node.get_node("VisualGrid2").size = grid_size * region_size
#-----------------------------------
#Setup canvas node size and position
#-----------------------------------
paint_canvas_node.rect_size = Vector2(paint_canvas_node.canvas_size.x * grid_size, paint_canvas_node.canvas_size.y * grid_size)
paint_canvas_node.rect_min_size = Vector2(paint_canvas_node.canvas_size.x * grid_size, paint_canvas_node.canvas_size.y * grid_size)
#------------------------------------------------------------
#Set the selected color to what the color picker has selected
#------------------------------------------------------------
selected_color = get_node("ToolMenu/Buttons/ColorPicker").color
#----------------------------------------------------------------
#Setup is done so we can now allow the user to draw on the canvas
#----------------------------------------------------------------
paint_canvas_node.can_draw = true
func setup_layer_tree():
var tree = get_node("ToolMenu/Layers/Tree")
var root = tree.create_item()
tree.set_hide_root(true)
var child1 = tree.create_item(root)
child1.set_text(0, "Layers")
child1.set_editable(0, true)
for i in paint_canvas_node.layers:
var layer_name = i.name
var subchild = tree.create_item(child1)
subchild.set_text(0, layer_name)
subchild.set_editable(0, true)
func _thread_process():
pass
var brush_mode = "paint"
var mouse_position = Vector2()
var canvas_position = Vector2()
var canvas_mouse_position = Vector2()
var cell_mouse_position = Vector2()
var cell_region_position = Vector2()
var cell_position_in_region = 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()
# warning-ignore:unused_argument
func _process(delta):
#It's a lot more easier to just keep updating the variables in here than just have a bunch of local variables
#in every update function and make it very messy
if paint_canvas_node == null:
set_process(false)
return
#Update commonly used variables
grid_size = paint_canvas_node.grid_size
region_size = paint_canvas_node.region_size
mouse_position = get_local_mouse_position()
canvas_position = paint_canvas_node.rect_position + paint_canvas_container_node.rect_position
canvas_mouse_position = Vector2(mouse_position.x - canvas_position.x, mouse_position.y - canvas_position.y)
cell_mouse_position = Vector2(floor(canvas_mouse_position.x / grid_size), floor(canvas_mouse_position.y / grid_size))
cell_region_position = Vector2(floor(cell_mouse_position.x / region_size), floor(cell_mouse_position.y / region_size))
cell_position_in_region = paint_canvas_node.get_region_from_cell(cell_mouse_position.x, cell_mouse_position.y)
cell_color = paint_canvas_node.get_pixel_cell_color(cell_mouse_position.x, cell_mouse_position.y)
#Process the brush drawing stuff
if paint_canvas_container_node.mouse_in_region and paint_canvas_container_node.mouse_on_top:
brush_process()
#Render the highlighting stuff
update()
#Canvas Shift Moving
if not mouse_position == last_mouse_position:
if paint_canvas_container_node.has_focus():
if Input.is_key_pressed(KEY_SHIFT):
if Input.is_mouse_button_pressed(BUTTON_LEFT):
var relative = mouse_position - last_mouse_position
paint_canvas_node.rect_position += relative
allow_drawing = false
else:
allow_drawing = true
#Update text info
update_text_info()
#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
var currently_selecting = false
func _draw():
if paint_canvas_node == null:
return
if paint_canvas_container_node.mouse_in_region and paint_canvas_node.mouse_in_region:
#draw cell_mouse_position
if paint_canvas_node.cell_in_canvas_region(cell_mouse_position.x, cell_mouse_position.y):
draw_rect(Rect2(Vector2((cell_mouse_position.x * grid_size) + canvas_position.x, (cell_mouse_position.y * grid_size) + canvas_position.y), Vector2(grid_size, grid_size)), Color(0.8, 0.8, 0.8, 0.8), true)
func draw_outline_box(pos, size, color, width):
#Top line
draw_line(Vector2(0 + 1 + pos.x, 0 + pos.y), Vector2(pos.x + size.x, 0 + pos.y), color, width)
#Left line
draw_line(Vector2(0 + 1 + pos.x, 0 + pos.y), Vector2(0 + pos.x, pos.y + size.y), color, width)
#Bottom line
draw_line(Vector2(0 + 1 + pos.x, pos.y + size.y), Vector2(pos.x + size.x, pos.y + size.y), color, width)
#Right line
draw_line(Vector2(pos.x + size.x, 0 + pos.y), Vector2(pos.x + size.x, pos.y + size.y), color, width)
func pool_vector2_array_append_new_value(vec2array, vec2):
for i in vec2array:
if i == vec2:
return
vec2array.append(vec2)
func custom_rect_size_brush(x, y, color, size):
for cx in range(x, x + size):
for cy in range(y, y + size):
paint_canvas_node.set_pixel_cell(cx, cy, color)
pass
func brush_process():
if Input.is_mouse_button_pressed(BUTTON_LEFT):
if allow_drawing:
if brush_mode == "paint":
paint_canvas_node.set_pixels_from_line(cell_mouse_position, last_cell_mouse_position, selected_color)
if brush_mode == "bucket":
paint_canvas_node.flood_fill(cell_mouse_position.x, cell_mouse_position.y, cell_color, selected_color)
if brush_mode == "rainbow":
paint_canvas_node.set_random_pixels_from_line(cell_mouse_position, last_cell_mouse_position)
elif Input.is_mouse_button_pressed(BUTTON_RIGHT):
if allow_drawing:
if brush_mode == "paint" or brush_mode == "rainbow":
paint_canvas_node.set_pixels_from_line(cell_mouse_position, last_cell_mouse_position, Color(0, 0, 0, 0))
if brush_mode == "bucket":
paint_canvas_node.flood_fill(cell_mouse_position.x, cell_mouse_position.y, cell_color, Color(0, 0, 0, 0))
func add_text_info_variables():
textinfo.add_text_info("FPS")
textinfo.add_text_info("Mouse Position")
textinfo.add_text_info("Canvas Mouse Position")
textinfo.add_text_info("Canvas Position")
textinfo.add_text_info("Cell Position")
var cell_color_texture_rect = ColorRect.new()
cell_color_texture_rect.name = "Cell Color"
cell_color_texture_rect.rect_size = Vector2(14, 14)
cell_color_texture_rect.rect_position.x = 120
textinfo.add_text_info("Cell Color", cell_color_texture_rect)
textinfo.add_text_info("Cell Region")
textinfo.add_text_info("Cell Position in Region")
func update_text_info():
textinfo.update_text_info("FPS", Engine.get_frames_per_second())
textinfo.update_text_info("Mouse Position", mouse_position)
textinfo.update_text_info("Canvas Mouse Position", canvas_mouse_position)
textinfo.update_text_info("Canvas Position", canvas_position)
textinfo.update_text_info("Cell Position", cell_mouse_position)
var cell_color_text = cell_color
if paint_canvas_node.mouse_in_region and paint_canvas_container_node.mouse_on_top:
if Input.is_mouse_button_pressed(BUTTON_LEFT) or Input.is_mouse_button_pressed(BUTTON_RIGHT):
if paint_canvas_node.last_pixel.size() > 0:
cell_color_text = paint_canvas_node.last_pixel[2]
if cell_color_text == null:
cell_color_text = Color(0, 0, 0, 0)
textinfo.update_text_info("Cell Color", cell_color_text, "Cell Color", "color", cell_color_text)
textinfo.update_text_info("Cell Region", cell_region_position)
textinfo.update_text_info("Cell Position in Region", cell_position_in_region)
func _on_PaintTool_pressed():
brush_mode = "paint"
func _on_BucketTool_pressed():
brush_mode = "bucket"
func _on_ColorPicker_color_changed(color):
selected_color = color
func _on_Save_pressed():
get_node("SaveFileDialog").show()
func _on_RainbowTool_pressed():
brush_mode = "rainbow"

View File

@ -0,0 +1,181 @@
[gd_scene load_steps=11 format=2]
[ext_resource path="res://addons/graphics_editor/Editor.gd" type="Script" id=1]
[ext_resource path="res://addons/graphics_editor/PaintCanvasContainer.gd" type="Script" id=2]
[ext_resource path="res://addons/graphics_editor/PaintCanvas.tscn" type="PackedScene" id=3]
[ext_resource path="res://addons/graphics_editor/VisualGrid.tscn" type="PackedScene" id=4]
[ext_resource path="res://addons/graphics_editor/CanvasOutline.gd" type="Script" id=5]
[ext_resource path="res://addons/graphics_editor/Navbar.gd" type="Script" id=6]
[ext_resource path="res://addons/graphics_editor/MenuButtonExtended.gd" type="Script" id=7]
[ext_resource path="res://addons/graphics_editor/TextInfo.gd" type="Script" id=8]
[ext_resource path="res://addons/graphics_editor/SaveFileDialog.gd" type="Script" id=9]
[ext_resource path="res://addons/graphics_editor/Settings.tscn" type="PackedScene" id=10]
[node name="Editor" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
rect_min_size = Vector2( 1024, 300 )
rect_clip_content = true
script = ExtResource( 1 )
[node name="PaintCanvasContainer" type="Control" parent="."]
show_behind_parent = true
anchor_right = 1.0
anchor_bottom = 1.0
margin_top = 20.0
margin_right = -114.0
margin_bottom = -40.0
focus_mode = 1
mouse_filter = 1
script = ExtResource( 2 )
__meta__ = {
"_edit_lock_": true
}
[node name="PaintCanvas" parent="PaintCanvasContainer" instance=ExtResource( 3 )]
anchor_right = 0.0
anchor_bottom = 0.0
margin_right = 1600.0
margin_bottom = 1600.0
rect_min_size = Vector2( 1600, 1600 )
[node name="Grids" type="Control" parent="PaintCanvasContainer/PaintCanvas"]
anchor_right = 1.0
anchor_bottom = 1.0
mouse_filter = 2
[node name="VisualGrid" parent="PaintCanvasContainer/PaintCanvas/Grids" instance=ExtResource( 4 )]
mouse_filter = 2
color = Color( 0.639216, 0.639216, 0.639216, 1 )
[node name="VisualGrid2" parent="PaintCanvasContainer/PaintCanvas/Grids" instance=ExtResource( 4 )]
mouse_filter = 2
color = Color( 1, 1, 1, 1 )
size = 160
[node name="CanvasOutline" type="Control" parent="PaintCanvasContainer/PaintCanvas"]
anchor_right = 1.0
anchor_bottom = 1.0
mouse_filter = 2
script = ExtResource( 5 )
color = Color( 0, 1, 0, 1 )
[node name="Navbar" type="Control" parent="."]
editor/display_folded = true
anchor_right = 1.0
margin_bottom = 20.0
script = ExtResource( 6 )
[node name="Panel" type="Panel" parent="Navbar"]
anchor_right = 1.0
anchor_bottom = 1.0
[node name="Buttons" type="Control" parent="Navbar"]
anchor_right = 1.0
anchor_bottom = 1.0
[node name="File" type="MenuButton" parent="Navbar/Buttons"]
margin_right = 90.0
margin_bottom = 20.0
text = "File"
flat = false
items = [ "Save", null, 0, false, false, -1, 0, null, "", false, "Quit", null, 0, false, false, -1, 0, null, "", false ]
switch_on_hover = true
script = ExtResource( 7 )
[node name="Editor" type="MenuButton" parent="Navbar/Buttons"]
margin_left = 90.0
margin_right = 180.0
margin_bottom = 20.0
text = "Editor"
flat = false
items = [ "Settings", null, 0, false, false, -1, 0, null, "", false, "Reset Canvas Position", null, 0, false, false, -1, 0, null, "", false, "Toggle Grid", null, 0, false, false, -1, 0, null, "", false ]
switch_on_hover = true
script = ExtResource( 7 )
[node name="ToolMenu" type="Control" parent="."]
anchor_left = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = -114.0
margin_top = 20.0
margin_bottom = -40.0
[node name="Buttons" type="Control" parent="ToolMenu"]
anchor_right = 1.0
margin_bottom = 310.0
[node name="PaintTool" type="Button" parent="ToolMenu/Buttons"]
anchor_right = 1.0
margin_bottom = 70.0
text = "Paint Tool"
[node name="BucketTool" type="Button" parent="ToolMenu/Buttons"]
anchor_right = 1.0
margin_top = 70.0
margin_bottom = 140.0
text = "Bucket Tool"
[node name="RainbowTool" type="Button" parent="ToolMenu/Buttons"]
anchor_right = 1.0
margin_top = 140.0
margin_bottom = 210.0
text = "Rainbow Tool"
[node name="ColorPicker" type="ColorPickerButton" parent="ToolMenu/Buttons"]
anchor_right = 1.0
margin_top = 210.0
margin_bottom = 240.0
[node name="Layers" type="Control" parent="ToolMenu"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_top = 310.0
[node name="Tree" type="Tree" parent="ToolMenu/Layers"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_top = -70.0
hide_root = true
[node name="BottomPanel" type="Panel" parent="."]
editor/display_folded = true
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
margin_top = -40.0
[node name="TextInfo" type="Control" parent="BottomPanel"]
script = ExtResource( 8 )
[node name="SaveFileDialog" type="FileDialog" parent="."]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -340.0
margin_top = -165.0
margin_right = 340.0
margin_bottom = 165.0
filters = PoolStringArray( "*.png ; PNG Images" )
script = ExtResource( 9 )
[node name="Settings" parent="." instance=ExtResource( 10 )]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -150.0
margin_top = -50.0
margin_right = 150.0
margin_bottom = 50.0
[connection signal="mouse_entered" from="PaintCanvasContainer" to="PaintCanvasContainer" method="_on_PaintCanvasContainer_mouse_entered"]
[connection signal="mouse_exited" from="PaintCanvasContainer" to="PaintCanvasContainer" method="_on_PaintCanvasContainer_mouse_exited"]
[connection signal="pressed" from="ToolMenu/Buttons/PaintTool" to="." method="_on_PaintTool_pressed"]
[connection signal="pressed" from="ToolMenu/Buttons/BucketTool" to="." method="_on_BucketTool_pressed"]
[connection signal="pressed" from="ToolMenu/Buttons/RainbowTool" to="." method="_on_RainbowTool_pressed"]
[connection signal="color_changed" from="ToolMenu/Buttons/ColorPicker" to="." method="_on_ColorPicker_color_changed"]
[connection signal="about_to_show" from="SaveFileDialog" to="SaveFileDialog" method="_on_SaveFileDialog_about_to_show"]
[connection signal="confirmed" from="SaveFileDialog" to="SaveFileDialog" method="_on_SaveFileDialog_confirmed"]
[connection signal="file_selected" from="SaveFileDialog" to="SaveFileDialog" method="_on_SaveFileDialog_file_selected"]
[connection signal="visibility_changed" from="SaveFileDialog" to="SaveFileDialog" method="_on_SaveFileDialog_visibility_changed"]

View File

@ -0,0 +1,11 @@
tool
extends MenuButton
var popup = self.get_popup()
signal item_pressed
func _ready():
popup.connect("id_pressed", self, "id_pressed")
func id_pressed(id):
emit_signal("item_pressed", self.name, popup.get_item_text(id))

View File

@ -0,0 +1,21 @@
tool
extends Control
func _ready():
for i in get_node("Buttons").get_children():
i.connect("item_pressed", self, "button_pressed")
func button_pressed(button_name, button_item):
if button_name == "File":
if button_item == "Save":
get_parent().get_node("SaveFileDialog").show()
if button_item == "Quit":
get_tree().quit()
elif button_name == "Editor":
if button_item == "Settings":
get_parent().get_node("Settings").show()
elif button_item == "Toggle Grid":
var grids_node = get_parent().get_node("PaintCanvasContainer/PaintCanvas/Grids")
grids_node.visible = !grids_node.visible
elif button_item == "Reset Canvas Position":
get_parent().paint_canvas_node.rect_position = Vector2(0, 0)

View File

@ -0,0 +1,379 @@
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(100, 100)
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 = [
{
"name": "Layer 1",
"data": [],
"chunks": []
}
]
var active_layer = "Layer 1"
var chunks = []
func get_layer_data(layer_name):
for layer in layers:
if layer.name == layer_name:
return layer.data
#return layer.chunks
func set_layer_data(layer_name, layer_data):
for layer in layers:
if layer.name == layer_name:
layer.data = layer_data
return
func remove_layer(layer_name):
for layer in layers:
if layer.name == layer_name:
layers[layer] = null
return
func add_layer(layer_name):
for layer in layers:
#TODO: Allow users to create layers with the same name, we can do this because of the indexes in the layer array
if layer.name == layer_name:
return false
layers.append({"name": layer_name, "data": []})
#layers.append({"name": layer_name, "chunks": []})
return true
var util = preload("res://addons/graphics_editor/Util.gd")
func _ready():
#canvas_image_node.rect_scale = Vector2(grid_size, grid_size)
#image.create(canvas_size.x, canvas_size.y, true, Image.FORMAT_RGBA8)
#image.lock()
#print(get_maxium_filled_chunks())
# for pixel_data in get_layer_data(active_layer):
# var cx = pixel_data[0]
# var cy = pixel_data[1]
# var ccolor = pixel_data[2]
# image.set_pixel(cx, cy, ccolor)
generate_chunks()
func _process(delta):
var mouse_position = get_local_mouse_position()
var rect = Rect2(Vector2(0, 0), self.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 x in maxium_chunk_size.x:
for y in maxium_chunk_size.y:
var paint_canvas_chunk = load("res://addons/graphics_editor/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 * (16 * 10), y * (16 * 10))
get_node("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 = get_node("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 not cell_in_canvas_region(x, y):
return null
for pixel in get_layer_data(active_layer):
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_data = get_layer_data(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 pixel[2] == color:
return
#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,22 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/graphics_editor/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 )
[node name="Chunks" type="Control" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
mouse_filter = 2
[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():
self.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():
self.visible = true
func _on_VisibilityNotifier2D_screen_exited():
self.visible = false

View File

@ -0,0 +1,12 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/graphics_editor/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,19 @@
tool
extends Control
var mouse_in_region
var mouse_on_top
func _ready():
pass
func _process(delta):
var mouse_position = get_local_mouse_position()
var rect = Rect2(Vector2(0, 0), self.rect_size)
mouse_in_region = rect.has_point(mouse_position)
func _on_PaintCanvasContainer_mouse_entered():
mouse_on_top = true
func _on_PaintCanvasContainer_mouse_exited():
mouse_on_top = false

View File

@ -0,0 +1,4 @@
extends "Brush.gd"
func _ready():
draw_pixel()

View File

@ -0,0 +1,51 @@
tool
extends FileDialog
onready var canvas = get_parent().get_node("PaintCanvasContainer/PaintCanvas")
var file_path = ""
func _ready():
# warning-ignore:return_value_discarded
get_line_edit().connect("text_entered", self, "_on_LineEdit_text_entered")
invalidate()
clear_filters()
add_filter("*.png ; PNG Images")
func _on_SaveFileDialog_file_selected(path):
file_path = path
# warning-ignore:unused_argument
func _on_LineEdit_text_entered(text):
save_file()
func _on_SaveFileDialog_confirmed():
save_file()
func save_file():
var image = Image.new()
image.create(canvas.canvas_size.x, canvas.canvas_size.y, true, Image.FORMAT_RGBA8)
image.lock()
for i in canvas.get_node("Chunks").get_children():
var chunk_name = i.name
var chunk_name_split = chunk_name.split("-")
var chunk_x = int(chunk_name_split[1])
var chunk_y = int(chunk_name_split[2])
var chunk_image = i.image.duplicate()
chunk_image.lock()
var chunk_image_size = chunk_image.get_size()
for x in chunk_image_size.x:
for y in chunk_image_size.y:
var pixel_color = chunk_image.get_pixel(x, y)
var global_cell_x = (chunk_x * canvas.region_size) + x
var global_cell_y = (chunk_y * canvas.region_size) + y
image.lock()
image.set_pixel(global_cell_x, global_cell_y, pixel_color)
image.unlock()
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,29 @@
tool
extends Control
onready var editor = get_parent()
onready var canvas_outline = get_parent().get_node("PaintCanvasContainer/PaintCanvas/CanvasOutline")
var start_time
var end_time
func _ready():
#start_time = OS.get_ticks_msec()
get_node("CanvasOutlineToggle/CheckButton").pressed = canvas_outline.visible
get_node("CanvasOutlineColor/ColorPickerButton").color = canvas_outline.color
func _process(delta):
# if get_parent().paint_canvas_node != null:
# canvas_outline = get_parent().paint_canvas_node.get_node("CanvasOutline")
# end_time = OS.get_ticks_msec()
# print("[Settings] Found Editor node in %s seconds!" % [(end_time - start_time) / float(1000)])
# set_process(false)
pass
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():
self.hide()

View File

@ -0,0 +1,62 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/graphics_editor/Settings.gd" type="Script" id=1]
[node name="Settings" type="WindowDialog"]
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 = self.get_child_count()
if not child_count <= 0:
last_text_info_child = self.get_children()[self.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)
self.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:
self.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,52 @@
tool
extends Node
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,9 @@
extends ViewportContainer
func _ready():
pass
func _notification(what):
if what == Control.NOTIFICATION_RESIZED:
get_node("Viewport").size = self.rect_size
get_node("Viewport/Node2D/Camera2D").position = Vector2(self.rect_size.x / 2, self.rect_size.y / 2)

View File

@ -0,0 +1,31 @@
tool
extends Control
export var color = Color()
export var size = 16
export var zoom = 0
export var offset = Vector2(0, 0)
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(self.rect_size.x / temp_size) + 0.01
var ceil_y = ceil(self.rect_size.y / temp_size) + 0.01
for i in ceil_y:
var start_x = Vector2(0, (i * temp_size) + wrap_offset.y)
var end_x = Vector2(self.rect_size.x, (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 + 0.01), self.rect_size.y)
draw_line(start_y, end_y, color, 1)
func _process(delta):
update()

View File

@ -0,0 +1,8 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://addons/graphics_editor/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,7 @@
[plugin]
name="GraphicsEditor"
description=""
author=""
version=""
script="plugin.gd"

View File

@ -0,0 +1,10 @@
tool
extends EditorPlugin
var editor_scene = load("res://addons/graphics_editor/Editor.tscn").instance()
func _enter_tree():
add_control_to_bottom_panel(editor_scene, "Graphics Editor")
func _exit_tree():
remove_control_from_bottom_panel(editor_scene)

7
default_env.tres Normal file
View File

@ -0,0 +1,7 @@
[gd_resource type="Environment" load_steps=2 format=2]
[sub_resource type="ProceduralSky" id=1]
[resource]
background_mode = 2
background_sky = SubResource( 1 )

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

34
icon.png.import Normal file
View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.png"
dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

28
project.godot Normal file
View File

@ -0,0 +1,28 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=4
_global_script_classes=[ ]
_global_script_class_icons={
}
[application]
config/name="GraphicsEditor"
run/main_scene="res://addons/graphics_editor/Editor.tscn"
config/icon="res://icon.png"
[editor_plugins]
enabled=PoolStringArray( "graphics_editor", "scene_notes", "todo" )
[rendering]
environment/default_environment="res://default_env.tres"