mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-02-22 13:33:13 +00:00
Implement 3D layers
This commit is contained in:
parent
5c9f0d8c18
commit
e0caee3458
43 changed files with 4978 additions and 104 deletions
1
assets/graphics/gizmos/directional_light.svg
Normal file
1
assets/graphics/gizmos/directional_light.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v3h2v-3zm-2.5352 2.0508-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm7.0703 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-3.5352 1.9492c-1.6569 0-3 1.3432-3 3s1.3431 3 3 3 3-1.3432 3-3-1.3431-3-3-3zm-7 2v2h3v-2zm11 0v2h3v-2zm-7.5352 3.1211-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm7.0703 0-1.4141 1.4141 1.4141 1.4141 1.4141-1.4141zm-4.5352 1.8789v3h2v-3z" fill="#fc7f7f" fill-opacity=".99608"/></svg>
|
After Width: | Height: | Size: 498 B |
35
assets/graphics/gizmos/directional_light.svg.import
Normal file
35
assets/graphics/gizmos/directional_light.svg.import
Normal file
|
@ -0,0 +1,35 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/directional_light.svg-093cdb9a72dee590271da014181a4680.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/graphics/gizmos/directional_light.svg"
|
||||
dest_files=[ "res://.import/directional_light.svg-093cdb9a72dee590271da014181a4680.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
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=false
|
||||
svg/scale=1.0
|
1
assets/graphics/gizmos/omni_light.svg
Normal file
1
assets/graphics/gizmos/omni_light.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1a5 5 0 0 0 -5 5 5 5 0 0 0 3 4.5762v2.4238h4v-2.4199a5 5 0 0 0 3-4.5801 5 5 0 0 0 -5-5zm0 2a3 3 0 0 1 3 3 3 3 0 0 1 -3 3 3 3 0 0 1 -3-3 3 3 0 0 1 3-3zm-1 11v1h2v-1z" fill="#fc7f7f" fill-opacity=".99608"/></svg>
|
After Width: | Height: | Size: 306 B |
35
assets/graphics/gizmos/omni_light.svg.import
Normal file
35
assets/graphics/gizmos/omni_light.svg.import
Normal file
|
@ -0,0 +1,35 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/omni_light.svg-b0faa945d45257c6c9fecd256ed51eee.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/graphics/gizmos/omni_light.svg"
|
||||
dest_files=[ "res://.import/omni_light.svg-b0faa945d45257c6c9fecd256ed51eee.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
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=false
|
||||
svg/scale=1.0
|
1
assets/graphics/gizmos/spot_light.svg
Normal file
1
assets/graphics/gizmos/spot_light.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1a1 1 0 0 0 -1 1v3.6934c-1.7861.86608-3 2.4605-3 4.3066h4a2 2 0 0 0 2 2 2 2 0 0 0 2-2h4c0-1.8462-1.2139-3.4406-3-4.3066v-3.6934a1 1 0 0 0 -1-1zm-1.0977 9.6348-1.7324 1 1 1.7305 1.7324-1zm6.1953 0-1 1.7305 1.7324 1 1-1.7305zm-4.0977 2.3652v2h2v-2z" fill="#fc7f7f" fill-opacity=".99608"/></svg>
|
After Width: | Height: | Size: 388 B |
35
assets/graphics/gizmos/spot_light.svg.import
Normal file
35
assets/graphics/gizmos/spot_light.svg.import
Normal file
|
@ -0,0 +1,35 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/spot_light.svg-d309a6f5345413a6c8b9afd3cfe72f29.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/graphics/gizmos/spot_light.svg"
|
||||
dest_files=[ "res://.import/spot_light.svg-d309a6f5345413a6c8b9afd3cfe72f29.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
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=false
|
||||
svg/scale=1.0
|
BIN
assets/graphics/tools/3dshapeedit.png
Normal file
BIN
assets/graphics/tools/3dshapeedit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 175 B |
35
assets/graphics/tools/3dshapeedit.png.import
Normal file
35
assets/graphics/tools/3dshapeedit.png.import
Normal file
|
@ -0,0 +1,35 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/3dshapeedit.png-2d2aa73fafc7df7f7df84b5c0db6c1a6.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/graphics/tools/3dshapeedit.png"
|
||||
dest_files=[ "res://.import/3dshapeedit.png-2d2aa73fafc7df7f7df84b5c0db6c1a6.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
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=false
|
||||
svg/scale=1.0
|
BIN
assets/graphics/tools/cursors/3dshapeedit.png
Normal file
BIN
assets/graphics/tools/cursors/3dshapeedit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 196 B |
35
assets/graphics/tools/cursors/3dshapeedit.png.import
Normal file
35
assets/graphics/tools/cursors/3dshapeedit.png.import
Normal file
|
@ -0,0 +1,35 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/3dshapeedit.png-cca253df77476289c578814dc8af5d80.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/graphics/tools/cursors/3dshapeedit.png"
|
||||
dest_files=[ "res://.import/3dshapeedit.png-cca253df77476289c578814dc8af5d80.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
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=false
|
||||
svg/scale=1.0
|
|
@ -74,6 +74,16 @@ _global_script_classes=[ {
|
|||
"language": "GDScript",
|
||||
"path": "res://src/UI/Canvas/Canvas.gd"
|
||||
}, {
|
||||
"base": "BaseCel",
|
||||
"class": "Cel3D",
|
||||
"language": "GDScript",
|
||||
"path": "res://src/Classes/Cel3D.gd"
|
||||
}, {
|
||||
"base": "Spatial",
|
||||
"class": "Cel3DObject",
|
||||
"language": "GDScript",
|
||||
"path": "res://src/Classes/Cel3DObject.gd"
|
||||
}, {
|
||||
"base": "VBoxContainer",
|
||||
"class": "CollapsibleContainer",
|
||||
"language": "GDScript",
|
||||
|
@ -124,6 +134,11 @@ _global_script_classes=[ {
|
|||
"language": "GDScript",
|
||||
"path": "res://src/Classes/ImageEffect.gd"
|
||||
}, {
|
||||
"base": "BaseLayer",
|
||||
"class": "Layer3D",
|
||||
"language": "GDScript",
|
||||
"path": "res://src/Classes/Layer3D.gd"
|
||||
}, {
|
||||
"base": "Button",
|
||||
"class": "LayerButton",
|
||||
"language": "GDScript",
|
||||
|
@ -139,6 +154,11 @@ _global_script_classes=[ {
|
|||
"language": "GDScript",
|
||||
"path": "res://src/UI/Nodes/NotificationLabel.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "ObjParse",
|
||||
"language": "GDScript",
|
||||
"path": "res://src/Classes/ObjParse.gd"
|
||||
}, {
|
||||
"base": "Resource",
|
||||
"class": "Palette",
|
||||
"language": "GDScript",
|
||||
|
@ -238,6 +258,11 @@ _global_script_classes=[ {
|
|||
"class": "ValueSliderV2",
|
||||
"language": "GDScript",
|
||||
"path": "res://src/UI/Nodes/ValueSliderV2.gd"
|
||||
}, {
|
||||
"base": "HBoxContainer",
|
||||
"class": "ValueSliderV3",
|
||||
"language": "GDScript",
|
||||
"path": "res://src/UI/Nodes/ValueSliderV3.gd"
|
||||
} ]
|
||||
_global_script_class_icons={
|
||||
"AImgIOAPNGExporter": "",
|
||||
|
@ -253,6 +278,8 @@ _global_script_class_icons={
|
|||
"BaseTool": "",
|
||||
"Brushes": "",
|
||||
"Canvas": "",
|
||||
"Cel3D": "",
|
||||
"Cel3DObject": "",
|
||||
"CollapsibleContainer": "",
|
||||
"CropRect": "",
|
||||
"Drawer": "",
|
||||
|
@ -263,9 +290,11 @@ _global_script_class_icons={
|
|||
"GroupLayer": "",
|
||||
"Guide": "",
|
||||
"ImageEffect": "",
|
||||
"Layer3D": "",
|
||||
"LayerButton": "",
|
||||
"MaxMinEdit": "",
|
||||
"NotificationLabel": "",
|
||||
"ObjParse": "",
|
||||
"Palette": "",
|
||||
"PaletteColor": "",
|
||||
"PaletteGrid": "",
|
||||
|
@ -285,7 +314,8 @@ _global_script_class_icons={
|
|||
"SymmetryGuide": "",
|
||||
"Tiles": "",
|
||||
"ValueSlider": "",
|
||||
"ValueSliderV2": ""
|
||||
"ValueSliderV2": "",
|
||||
"ValueSliderV3": ""
|
||||
}
|
||||
|
||||
[application]
|
||||
|
|
|
@ -432,7 +432,7 @@ func scale_image(width: int, height: int, interpolation: int) -> void:
|
|||
|
||||
for f in Global.current_project.frames:
|
||||
for i in range(f.cels.size() - 1, -1, -1):
|
||||
if f.cels[i] is GroupCel:
|
||||
if not f.cels[i] is PixelCel:
|
||||
continue
|
||||
var sprite := Image.new()
|
||||
sprite.copy_from(f.cels[i].image)
|
||||
|
|
|
@ -387,6 +387,8 @@ func blend_layers(
|
|||
layer_image.copy_from(frame.cels[export_layers - 2].image)
|
||||
elif layer is GroupLayer:
|
||||
layer_image.copy_from(layer.blend_children(frame, Vector2.ZERO))
|
||||
elif layer is Layer3D:
|
||||
layer_image.copy_from(frame.cels[export_layers - 2].get_image())
|
||||
image.blend_rect(layer_image, Rect2(Vector2.ZERO, project.size), origin)
|
||||
|
||||
|
||||
|
@ -396,20 +398,23 @@ func blend_all_layers(
|
|||
) -> void:
|
||||
var layer_i := 0
|
||||
for cel in frame.cels:
|
||||
if project.layers[layer_i].is_visible_in_hierarchy() and cel is PixelCel:
|
||||
var cel_image := Image.new()
|
||||
cel_image.copy_from(cel.image)
|
||||
if cel.opacity < 1: # If we have cel transparency
|
||||
cel_image.lock()
|
||||
for xx in cel_image.get_size().x:
|
||||
for yy in cel_image.get_size().y:
|
||||
var pixel_color := cel_image.get_pixel(xx, yy)
|
||||
var alpha: float = pixel_color.a * cel.opacity
|
||||
cel_image.set_pixel(
|
||||
xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha)
|
||||
)
|
||||
cel_image.unlock()
|
||||
image.blend_rect(cel_image, Rect2(Vector2.ZERO, project.size), origin)
|
||||
if not project.layers[layer_i].is_visible_in_hierarchy():
|
||||
return
|
||||
if cel is GroupCel:
|
||||
return
|
||||
var cel_image := Image.new()
|
||||
cel_image.copy_from(cel.get_image())
|
||||
if cel.opacity < 1: # If we have cel transparency
|
||||
cel_image.lock()
|
||||
for xx in cel_image.get_size().x:
|
||||
for yy in cel_image.get_size().y:
|
||||
var pixel_color := cel_image.get_pixel(xx, yy)
|
||||
var alpha: float = pixel_color.a * cel.opacity
|
||||
cel_image.set_pixel(
|
||||
xx, yy, Color(pixel_color.r, pixel_color.g, pixel_color.b, alpha)
|
||||
)
|
||||
cel_image.unlock()
|
||||
image.blend_rect(cel_image, Rect2(Vector2.ZERO, project.size), origin)
|
||||
layer_i += 1
|
||||
|
||||
|
||||
|
@ -421,13 +426,13 @@ func blend_selected_cels(
|
|||
var test_array := [project.current_frame, cel_ind]
|
||||
if not test_array in project.selected_cels:
|
||||
continue
|
||||
if not frame.cels[cel_ind] is PixelCel:
|
||||
if frame.cels[cel_ind] is GroupCel:
|
||||
continue
|
||||
if not project.layers[cel_ind].is_visible_in_hierarchy():
|
||||
continue
|
||||
var cel: PixelCel = frame.cels[cel_ind]
|
||||
var cel: BaseCel = frame.cels[cel_ind]
|
||||
var cel_image := Image.new()
|
||||
cel_image.copy_from(cel.image)
|
||||
cel_image.copy_from(cel.get_image())
|
||||
if cel.opacity < 1: # If we have cel transparency
|
||||
cel_image.lock()
|
||||
for xx in cel_image.get_size().x:
|
||||
|
|
|
@ -274,10 +274,11 @@ class ToolAPI:
|
|||
shortcut: String,
|
||||
scene: PackedScene,
|
||||
extra_hint := "",
|
||||
extra_shortucts := []
|
||||
extra_shortucts := [],
|
||||
layer_types: PoolIntArray = []
|
||||
) -> void:
|
||||
var tool_class := Tools.Tool.new(
|
||||
tool_name, display_name, shortcut, scene, extra_hint, extra_shortucts
|
||||
tool_name, display_name, shortcut, scene, layer_types, extra_hint, extra_shortucts
|
||||
)
|
||||
Tools.tools[tool_name] = tool_class
|
||||
Tools.add_tool_button(tool_class)
|
||||
|
|
|
@ -3,7 +3,7 @@ extends Node
|
|||
signal project_changed
|
||||
signal cel_changed
|
||||
|
||||
enum LayerTypes { PIXEL, GROUP }
|
||||
enum LayerTypes { PIXEL, GROUP, THREE_D }
|
||||
enum GridTypes { CARTESIAN, ISOMETRIC, ALL }
|
||||
enum ColorFrom { THEME, CUSTOM }
|
||||
enum ButtonSize { SMALL, BIG }
|
||||
|
@ -159,6 +159,7 @@ var crop_left := 0
|
|||
var crop_right := 0
|
||||
|
||||
# Nodes
|
||||
var base_layer_button_node: PackedScene = preload("res://src/UI/Timeline/BaseLayerButton.tscn")
|
||||
var pixel_layer_button_node: PackedScene = preload("res://src/UI/Timeline/PixelLayerButton.tscn")
|
||||
var group_layer_button_node: PackedScene = preload("res://src/UI/Timeline/GroupLayerButton.tscn")
|
||||
var pixel_cel_button_node: PackedScene = preload("res://src/UI/Timeline/PixelCelButton.tscn")
|
||||
|
@ -479,7 +480,10 @@ func undo_or_redo(
|
|||
for i in project.frames.size():
|
||||
for j in project.layers.size():
|
||||
var current_cel: BaseCel = project.frames[i].cels[j]
|
||||
current_cel.image_texture.create_from_image(current_cel.get_image(), 0)
|
||||
if current_cel is Cel3D:
|
||||
current_cel.size_changed(project.size)
|
||||
else:
|
||||
current_cel.image_texture.create_from_image(current_cel.get_image(), 0)
|
||||
canvas.camera_zoom()
|
||||
canvas.grid.update()
|
||||
canvas.pixel_grid.update()
|
||||
|
@ -578,3 +582,19 @@ func update_hint_tooltips() -> void:
|
|||
var first_key: InputEventKey = Keychain.action_get_first_key(event_type.action)
|
||||
hint = first_key.as_text() if first_key else "None"
|
||||
tip.hint_tooltip = tr(ui_tooltips[tip]) % hint
|
||||
|
||||
|
||||
# Used in case some of the values in a dictionary are Strings, when they should be something else
|
||||
func convert_dictionary_values(dict: Dictionary) -> void:
|
||||
for key in dict:
|
||||
if typeof(dict[key]) != TYPE_STRING:
|
||||
continue
|
||||
if "transform" in key: # Convert a String to a Transform
|
||||
var transform_string: String = dict[key].replace(" - ", ", ")
|
||||
dict[key] = str2var("Transform(" + transform_string + ")")
|
||||
elif "color" in key: # Convert a String to a Color
|
||||
dict[key] = str2var("Color(" + dict[key] + ")")
|
||||
elif "v2" in key: # Convert a String to a Vector2
|
||||
dict[key] = str2var("Vector2" + dict[key])
|
||||
elif "size" in key: # Convert a String to a Vector3
|
||||
dict[key] = str2var("Vector3" + dict[key])
|
||||
|
|
|
@ -150,7 +150,7 @@ func open_pxo_file(path: String, untitled_backup: bool = false, replace_empty: b
|
|||
new_project.deserialize(dict.result)
|
||||
for frame in new_project.frames:
|
||||
for cel in frame.cels:
|
||||
cel.load_image_data_from_pxo(file, new_project.size)
|
||||
cel.load_cel_data_from_pxo(file, new_project.size)
|
||||
|
||||
if dict.result.has("brushes"):
|
||||
for brush in dict.result.brushes:
|
||||
|
@ -401,7 +401,7 @@ func save_pxo_file(
|
|||
file.store_line(to_save)
|
||||
for frame in project.frames:
|
||||
for cel in frame.cels:
|
||||
cel.save_image_data_to_pxo(file)
|
||||
cel.save_cel_data_to_pxo(file)
|
||||
for brush in project.brushes:
|
||||
file.store_buffer(brush.get_data())
|
||||
if project.tiles.has_mask:
|
||||
|
|
|
@ -45,6 +45,7 @@ var tools := {
|
|||
"Polygonal Selection",
|
||||
"polygon_select",
|
||||
preload("res://src/Tools/SelectionTools/PolygonSelect.tscn"),
|
||||
[],
|
||||
"Double-click to connect the last point to the starting point"
|
||||
),
|
||||
"ColorSelect":
|
||||
|
@ -75,7 +76,10 @@ var tools := {
|
|||
"paint_selection",
|
||||
preload("res://src/Tools/SelectionTools/PaintSelect.tscn")
|
||||
),
|
||||
"Move": Tool.new("Move", "Move", "move", preload("res://src/Tools/Move.tscn")),
|
||||
"Move":
|
||||
Tool.new(
|
||||
"Move", "Move", "move", preload("res://src/Tools/Move.tscn"), [Global.LayerTypes.PIXEL]
|
||||
),
|
||||
"Zoom": Tool.new("Zoom", "Zoom", "zoom", preload("res://src/Tools/Zoom.tscn")),
|
||||
"Pan": Tool.new("Pan", "Pan", "pan", preload("res://src/Tools/Pan.tscn")),
|
||||
"ColorPicker":
|
||||
|
@ -84,16 +88,20 @@ var tools := {
|
|||
"Color Picker",
|
||||
"colorpicker",
|
||||
preload("res://src/Tools/ColorPicker.tscn"),
|
||||
[],
|
||||
"Select a color from a pixel of the sprite"
|
||||
),
|
||||
"Crop":
|
||||
Tool.new("Crop", "Crop", "crop", preload("res://src/Tools/CropTool.tscn"), "Resize the canvas"),
|
||||
Tool.new(
|
||||
"Crop", "Crop", "crop", preload("res://src/Tools/CropTool.tscn"), [], "Resize the canvas"
|
||||
),
|
||||
"Pencil":
|
||||
Tool.new(
|
||||
"Pencil",
|
||||
"Pencil",
|
||||
"pencil",
|
||||
preload("res://src/Tools/Pencil.tscn"),
|
||||
[Global.LayerTypes.PIXEL],
|
||||
"Hold %s to make a line",
|
||||
["draw_create_line"]
|
||||
),
|
||||
|
@ -103,18 +111,33 @@ var tools := {
|
|||
"Eraser",
|
||||
"eraser",
|
||||
preload("res://src/Tools/Eraser.tscn"),
|
||||
[Global.LayerTypes.PIXEL],
|
||||
"Hold %s to make a line",
|
||||
["draw_create_line"]
|
||||
),
|
||||
"Bucket": Tool.new("Bucket", "Bucket", "fill", preload("res://src/Tools/Bucket.tscn")),
|
||||
"Bucket":
|
||||
Tool.new(
|
||||
"Bucket",
|
||||
"Bucket",
|
||||
"fill",
|
||||
preload("res://src/Tools/Bucket.tscn"),
|
||||
[Global.LayerTypes.PIXEL]
|
||||
),
|
||||
"Shading":
|
||||
Tool.new("Shading", "Shading Tool", "shading", preload("res://src/Tools/Shading.tscn")),
|
||||
Tool.new(
|
||||
"Shading",
|
||||
"Shading Tool",
|
||||
"shading",
|
||||
preload("res://src/Tools/Shading.tscn"),
|
||||
[Global.LayerTypes.PIXEL]
|
||||
),
|
||||
"LineTool":
|
||||
Tool.new(
|
||||
"LineTool",
|
||||
"Line Tool",
|
||||
"linetool",
|
||||
preload("res://src/Tools/LineTool.tscn"),
|
||||
[Global.LayerTypes.PIXEL],
|
||||
"""Hold %s to snap the angle of the line
|
||||
Hold %s to center the shape on the click origin
|
||||
Hold %s to displace the shape's origin""",
|
||||
|
@ -126,6 +149,7 @@ Hold %s to displace the shape's origin""",
|
|||
"Rectangle Tool",
|
||||
"rectangletool",
|
||||
preload("res://src/Tools/RectangleTool.tscn"),
|
||||
[Global.LayerTypes.PIXEL],
|
||||
"""Hold %s to create a 1:1 shape
|
||||
Hold %s to center the shape on the click origin
|
||||
Hold %s to displace the shape's origin""",
|
||||
|
@ -137,16 +161,34 @@ Hold %s to displace the shape's origin""",
|
|||
"Ellipse Tool",
|
||||
"ellipsetool",
|
||||
preload("res://src/Tools/EllipseTool.tscn"),
|
||||
[Global.LayerTypes.PIXEL],
|
||||
"""Hold %s to create a 1:1 shape
|
||||
Hold %s to center the shape on the click origin
|
||||
Hold %s to displace the shape's origin""",
|
||||
["shape_perfect", "shape_center", "shape_displace"]
|
||||
),
|
||||
"3DShapeEdit":
|
||||
Tool.new(
|
||||
"3DShapeEdit",
|
||||
"3D Shape Edit",
|
||||
"3dshapeedit",
|
||||
preload("res://src/Tools/3DShapeEdit.tscn"),
|
||||
[Global.LayerTypes.THREE_D]
|
||||
),
|
||||
}
|
||||
|
||||
var _tool_button_scene: PackedScene = preload("res://src/Tools/ToolButton.tscn")
|
||||
var _slots := {}
|
||||
var _panels := {}
|
||||
var _curr_layer_type: int = Global.LayerTypes.PIXEL
|
||||
var _left_tools_per_layer_type := {
|
||||
Global.LayerTypes.PIXEL: "Pencil",
|
||||
Global.LayerTypes.THREE_D: "3DShapeEdit",
|
||||
}
|
||||
var _right_tools_per_layer_type := {
|
||||
Global.LayerTypes.PIXEL: "Eraser",
|
||||
Global.LayerTypes.THREE_D: "Pan",
|
||||
}
|
||||
var _tool_buttons: Node
|
||||
var _active_button := -1
|
||||
var _last_position := Vector2.INF
|
||||
|
@ -161,6 +203,7 @@ class Tool:
|
|||
var shortcut := ""
|
||||
var extra_hint := ""
|
||||
var extra_shortcuts := [] # Array of String(s)
|
||||
var layer_types: PoolIntArray = []
|
||||
var button_node: BaseButton
|
||||
|
||||
func _init(
|
||||
|
@ -168,6 +211,7 @@ class Tool:
|
|||
_display_name: String,
|
||||
_shortcut: String,
|
||||
_scene: PackedScene,
|
||||
_layer_types: PoolIntArray = [],
|
||||
_extra_hint := "",
|
||||
_extra_shortucts := []
|
||||
) -> void:
|
||||
|
@ -175,6 +219,7 @@ class Tool:
|
|||
display_name = _display_name
|
||||
shortcut = _shortcut
|
||||
scene = _scene
|
||||
layer_types = _layer_types
|
||||
extra_hint = _extra_hint
|
||||
extra_shortcuts = _extra_shortucts
|
||||
icon = load("res://assets/graphics/tools/%s.png" % name.to_lower())
|
||||
|
@ -234,6 +279,7 @@ class Slot:
|
|||
|
||||
|
||||
func _ready() -> void:
|
||||
Global.connect("cel_changed", self, "_cel_changed")
|
||||
_tool_buttons = Global.control.find_node("ToolButtons")
|
||||
for t in tools:
|
||||
add_tool_button(tools[t])
|
||||
|
@ -248,15 +294,19 @@ func _ready() -> void:
|
|||
_panels[BUTTON_LEFT] = Global.control.find_node("LeftPanelContainer", true, false)
|
||||
_panels[BUTTON_RIGHT] = Global.control.find_node("RightPanelContainer", true, false)
|
||||
|
||||
var default_left_tool: String = _left_tools_per_layer_type[0]
|
||||
var default_right_tool: String = _right_tools_per_layer_type[0]
|
||||
var tool_name: String = Global.config_cache.get_value(
|
||||
_slots[BUTTON_LEFT].kname, "tool", "Pencil"
|
||||
_slots[BUTTON_LEFT].kname, "tool", default_left_tool
|
||||
)
|
||||
if not tool_name in tools:
|
||||
tool_name = "Pencil"
|
||||
if not tool_name in tools or not _is_tool_available(Global.LayerTypes.PIXEL, tools[tool_name]):
|
||||
tool_name = default_left_tool
|
||||
set_tool(tool_name, BUTTON_LEFT)
|
||||
tool_name = Global.config_cache.get_value(_slots[BUTTON_RIGHT].kname, "tool", "Eraser")
|
||||
if not tool_name in tools:
|
||||
tool_name = "Eraser"
|
||||
tool_name = Global.config_cache.get_value(
|
||||
_slots[BUTTON_RIGHT].kname, "tool", default_right_tool
|
||||
)
|
||||
if not tool_name in tools or not _is_tool_available(Global.LayerTypes.PIXEL, tools[tool_name]):
|
||||
tool_name = default_right_tool
|
||||
set_tool(tool_name, BUTTON_RIGHT)
|
||||
update_tool_buttons()
|
||||
|
||||
|
@ -273,6 +323,9 @@ func _ready() -> void:
|
|||
color_value = Global.config_cache.get_value(_slots[BUTTON_RIGHT].kname, "color", Color.white)
|
||||
assign_color(color_value, BUTTON_RIGHT, false)
|
||||
update_tool_cursors()
|
||||
var layer: BaseLayer = Global.current_project.layers[Global.current_project.current_layer]
|
||||
var layer_type := layer.get_layer_type()
|
||||
_show_relevant_tools(layer_type)
|
||||
|
||||
|
||||
func add_tool_button(t: Tool) -> void:
|
||||
|
@ -293,7 +346,7 @@ func remove_tool(t: Tool) -> void:
|
|||
|
||||
|
||||
func set_tool(name: String, button: int) -> void:
|
||||
var slot = _slots[button]
|
||||
var slot: Slot = _slots[button]
|
||||
var panel: Node = _panels[button]
|
||||
var node: Node = tools[name].scene.instance()
|
||||
if button == BUTTON_LEFT: # As guides are only moved with left mouse
|
||||
|
@ -307,9 +360,16 @@ func set_tool(name: String, button: int) -> void:
|
|||
slot.button = button
|
||||
panel.add_child(slot.tool_node)
|
||||
|
||||
if _curr_layer_type == Global.LayerTypes.GROUP:
|
||||
return
|
||||
if button == BUTTON_LEFT:
|
||||
_left_tools_per_layer_type[_curr_layer_type] = name
|
||||
elif button == BUTTON_RIGHT:
|
||||
_right_tools_per_layer_type[_curr_layer_type] = name
|
||||
|
||||
|
||||
func assign_tool(name: String, button: int) -> void:
|
||||
var slot = _slots[button]
|
||||
var slot: Slot = _slots[button]
|
||||
var panel: Node = _panels[button]
|
||||
|
||||
if slot.tool_node != null:
|
||||
|
@ -463,3 +523,33 @@ func get_alpha_dynamic(strength := 1.0) -> float:
|
|||
elif dynamics_alpha == Dynamics.VELOCITY:
|
||||
strength *= lerp(alpha_min, alpha_max, mouse_velocity)
|
||||
return strength
|
||||
|
||||
|
||||
func _cel_changed() -> void:
|
||||
var layer: BaseLayer = Global.current_project.layers[Global.current_project.current_layer]
|
||||
var layer_type := layer.get_layer_type()
|
||||
# Do not make any changes when its the same type of layer, or a group layer
|
||||
if layer_type == _curr_layer_type or layer_type == Global.LayerTypes.GROUP:
|
||||
return
|
||||
_show_relevant_tools(layer_type)
|
||||
|
||||
|
||||
func _show_relevant_tools(layer_type: int) -> void:
|
||||
# Hide tools that are not available in the current layer type
|
||||
for button in _tool_buttons.get_children():
|
||||
var tool_name: String = button.name
|
||||
var t: Tool = tools[tool_name]
|
||||
var hide_tool := _is_tool_available(layer_type, t)
|
||||
button.visible = hide_tool
|
||||
|
||||
# Assign new tools if the layer type has changed
|
||||
_curr_layer_type = layer_type
|
||||
var new_tool_name: String = _left_tools_per_layer_type[layer_type]
|
||||
assign_tool(new_tool_name, BUTTON_LEFT)
|
||||
|
||||
new_tool_name = _right_tools_per_layer_type[layer_type]
|
||||
assign_tool(new_tool_name, BUTTON_RIGHT)
|
||||
|
||||
|
||||
func _is_tool_available(layer_type: int, t: Tool) -> bool:
|
||||
return t.layer_types.empty() or layer_type in t.layer_types
|
||||
|
|
|
@ -6,7 +6,7 @@ extends Reference
|
|||
signal texture_changed
|
||||
|
||||
var opacity: float
|
||||
var image_texture: ImageTexture
|
||||
var image_texture: Texture setget , _get_image_texture
|
||||
# If the cel is linked a ref to the link set Dictionary this cel is in, or null if not linked:
|
||||
var link_set = null # { "cels": Array, "hue": float } or null
|
||||
var transformed_content: Image # Used in transformations (moving, scaling etc with selections)
|
||||
|
@ -14,6 +14,10 @@ var transformed_content: Image # Used in transformations (moving, scaling etc w
|
|||
# Methods to Override:
|
||||
|
||||
|
||||
func _get_image_texture() -> Texture:
|
||||
return image_texture
|
||||
|
||||
|
||||
# The content methods deal with the unique content of each cel type. For example, an Image for
|
||||
# PixelLayers, or a Dictionary of settings for a procedural layer type, and null for Groups.
|
||||
# Can be used for linking/unlinking cels, copying, and deleting content
|
||||
|
@ -47,13 +51,17 @@ func update_texture() -> void:
|
|||
return
|
||||
|
||||
|
||||
func save_image_data_to_pxo(_file: File) -> void:
|
||||
func save_cel_data_to_pxo(_file: File) -> void:
|
||||
return
|
||||
|
||||
|
||||
func load_image_data_from_pxo(_file: File, _project_size: Vector2) -> void:
|
||||
func load_cel_data_from_pxo(_file: File, _project_size: Vector2) -> void:
|
||||
return
|
||||
|
||||
|
||||
func on_remove() -> void:
|
||||
pass
|
||||
|
||||
|
||||
func instantiate_cel_button() -> Node:
|
||||
return null
|
||||
|
|
|
@ -164,6 +164,10 @@ func deserialize(dict: Dictionary) -> void:
|
|||
cel_link_sets.append(link_set)
|
||||
|
||||
|
||||
func get_layer_type() -> int:
|
||||
return -1
|
||||
|
||||
|
||||
func new_empty_cel() -> BaseCel:
|
||||
return null
|
||||
|
||||
|
|
186
src/Classes/Cel3D.gd
Normal file
186
src/Classes/Cel3D.gd
Normal file
|
@ -0,0 +1,186 @@
|
|||
class_name Cel3D
|
||||
extends BaseCel
|
||||
|
||||
signal selected_object(object)
|
||||
|
||||
var layer
|
||||
var size: Vector2
|
||||
var viewport: Viewport
|
||||
var parent_node: Spatial
|
||||
var camera: Camera
|
||||
# Key = Cel3DObject's name, Value = Dictionary containing the properties of the Cel3DObject
|
||||
var object_properties := {}
|
||||
var selected: Cel3DObject = null setget _set_selected
|
||||
|
||||
|
||||
func _init(_layer, _size: Vector2, from_pxo := false, _object_properties := {}) -> void:
|
||||
layer = _layer
|
||||
size = _size
|
||||
object_properties = _object_properties
|
||||
opacity = 1.0
|
||||
if not from_pxo:
|
||||
_add_nodes()
|
||||
|
||||
|
||||
func _add_nodes() -> void:
|
||||
viewport = Viewport.new()
|
||||
viewport.size = size
|
||||
viewport.own_world = true
|
||||
viewport.transparent_bg = true
|
||||
viewport.render_target_v_flip = true
|
||||
var world := World.new()
|
||||
var environment := Environment.new()
|
||||
world.environment = environment
|
||||
viewport.world = world
|
||||
parent_node = Spatial.new()
|
||||
camera = Camera.new()
|
||||
camera.current = true
|
||||
deserialize_layer_properties()
|
||||
viewport.add_child(camera)
|
||||
viewport.add_child(parent_node)
|
||||
Global.canvas.add_child(viewport)
|
||||
|
||||
if object_properties.empty():
|
||||
for id in layer.objects:
|
||||
add_object(id)
|
||||
|
||||
else:
|
||||
var objects_duplicate := object_properties.duplicate()
|
||||
for id in objects_duplicate:
|
||||
var properties: Dictionary = object_properties[id]
|
||||
Global.convert_dictionary_values(properties)
|
||||
var node3d := Cel3DObject.new()
|
||||
node3d.cel = self
|
||||
node3d.connect("property_finished_changing", self, "_object_property_changed", [node3d])
|
||||
parent_node.add_child(node3d)
|
||||
node3d.deserialize(properties)
|
||||
object_properties.erase(id)
|
||||
object_properties[node3d.id] = properties
|
||||
|
||||
image_texture = viewport.get_texture()
|
||||
|
||||
|
||||
func _get_image_texture() -> Texture:
|
||||
if not is_instance_valid(viewport):
|
||||
_add_nodes()
|
||||
return image_texture
|
||||
|
||||
|
||||
func serialize_layer_properties() -> Dictionary: # To layer
|
||||
if not is_instance_valid(camera):
|
||||
return {}
|
||||
return {
|
||||
"camera_transform": camera.transform,
|
||||
"camera_projection": camera.projection,
|
||||
"ambient_light_color": viewport.world.environment.ambient_light_color,
|
||||
"ambient_light_energy": viewport.world.environment.ambient_light_energy
|
||||
}
|
||||
|
||||
|
||||
func deserialize_layer_properties() -> void: # From layer
|
||||
camera.transform = layer.properties["camera_transform"]
|
||||
camera.projection = layer.properties["camera_projection"]
|
||||
viewport.world.environment.ambient_light_color = layer.properties["ambient_light_color"]
|
||||
viewport.world.environment.ambient_light_energy = layer.properties["ambient_light_energy"]
|
||||
|
||||
|
||||
func _object_property_changed(object: Cel3DObject) -> void:
|
||||
var undo_redo: UndoRedo = layer.project.undo_redo
|
||||
var new_properties := object_properties.duplicate()
|
||||
new_properties[object.id] = object.serialize()
|
||||
undo_redo.create_action("Change object transform")
|
||||
undo_redo.add_do_property(self, "object_properties", new_properties)
|
||||
undo_redo.add_undo_property(self, "object_properties", object_properties)
|
||||
undo_redo.add_do_method(self, "_update_objects_transform", object.id)
|
||||
undo_redo.add_undo_method(self, "_update_objects_transform", object.id)
|
||||
undo_redo.add_do_method(Global, "undo_or_redo", false)
|
||||
undo_redo.add_undo_method(Global, "undo_or_redo", true)
|
||||
undo_redo.commit_action()
|
||||
|
||||
|
||||
func _update_objects_transform(id: int) -> void: # Called by undo/redo
|
||||
var properties: Dictionary = object_properties[id]
|
||||
var object := get_object_from_id(id)
|
||||
if not object:
|
||||
print("Object with id %s not found" % id)
|
||||
return
|
||||
object.deserialize(properties)
|
||||
|
||||
|
||||
func get_object_from_id(id: int) -> Cel3DObject:
|
||||
for child in parent_node.get_children():
|
||||
if not child is Cel3DObject:
|
||||
continue
|
||||
if child.id == id:
|
||||
return child
|
||||
return null
|
||||
|
||||
|
||||
func size_changed(new_size: Vector2) -> void:
|
||||
size = new_size
|
||||
viewport.size = size
|
||||
image_texture = viewport.get_texture()
|
||||
|
||||
|
||||
func add_object(id: int) -> void:
|
||||
var node3d := Cel3DObject.new()
|
||||
node3d.id = id
|
||||
node3d.cel = self
|
||||
node3d.connect("property_finished_changing", self, "_object_property_changed", [node3d])
|
||||
parent_node.add_child(node3d)
|
||||
node3d.type = layer.objects[id]
|
||||
if id == 0: # Directional light
|
||||
node3d.translation = Vector3(-2.5, 0, 0)
|
||||
node3d.rotate_y(-PI / 4)
|
||||
if object_properties.has(node3d.id) and object_properties[node3d.id].has("id"):
|
||||
node3d.deserialize(object_properties[node3d.id])
|
||||
else:
|
||||
if object_properties.has(node3d.id) and object_properties[node3d.id].has("file_path"):
|
||||
node3d.file_path = object_properties[node3d.id]["file_path"]
|
||||
object_properties[node3d.id] = node3d.serialize()
|
||||
|
||||
|
||||
func remove_object(id: int) -> void:
|
||||
var object := get_object_from_id(id)
|
||||
if is_instance_valid(object):
|
||||
object.queue_free()
|
||||
|
||||
|
||||
func _set_selected(value: Cel3DObject) -> void:
|
||||
if value == selected:
|
||||
return
|
||||
if is_instance_valid(selected): # Unselect previous object if we selected something else
|
||||
selected.unselect()
|
||||
selected = value
|
||||
if is_instance_valid(selected): # Select new object
|
||||
selected.select()
|
||||
emit_signal("selected_object", value)
|
||||
|
||||
|
||||
# Overridden methods
|
||||
|
||||
|
||||
func get_image() -> Image:
|
||||
return viewport.get_texture().get_data()
|
||||
|
||||
|
||||
func save_cel_data_to_pxo(file: File) -> void:
|
||||
file.store_line(JSON.print(object_properties))
|
||||
|
||||
|
||||
func load_cel_data_from_pxo(file: File, _project_size: Vector2) -> void:
|
||||
var dict := JSON.parse(file.get_line())
|
||||
if dict.error != OK:
|
||||
print("Error while parsing a Cel3D. %s" % dict.error_string)
|
||||
return
|
||||
object_properties = dict.result
|
||||
_add_nodes()
|
||||
|
||||
|
||||
func on_remove() -> void:
|
||||
if is_instance_valid(viewport):
|
||||
viewport.queue_free()
|
||||
|
||||
|
||||
func instantiate_cel_button() -> Node:
|
||||
return Global.pixel_cel_button_node.instance()
|
337
src/Classes/Cel3DObject.gd
Normal file
337
src/Classes/Cel3DObject.gd
Normal file
|
@ -0,0 +1,337 @@
|
|||
class_name Cel3DObject
|
||||
extends Spatial
|
||||
|
||||
signal property_changed
|
||||
signal property_finished_changing
|
||||
|
||||
enum Type {
|
||||
BOX,
|
||||
SPHERE,
|
||||
CAPSULE,
|
||||
CYLINDER,
|
||||
PRISM,
|
||||
PLANE,
|
||||
TEXT,
|
||||
DIR_LIGHT,
|
||||
SPOT_LIGHT,
|
||||
OMNI_LIGHT,
|
||||
IMPORTED
|
||||
}
|
||||
enum Gizmos { NONE, X_POS, Y_POS, Z_POS, X_ROT, Y_ROT, Z_ROT, X_SCALE, Y_SCALE, Z_SCALE }
|
||||
|
||||
var cel
|
||||
var id := -1
|
||||
var type: int = Type.BOX setget _set_type
|
||||
var selected := false
|
||||
var hovered := false
|
||||
var box_shape: BoxShape
|
||||
var camera: Camera
|
||||
var file_path := "" setget _set_file_path # Only useful for Type.IMPORTED
|
||||
var applying_gizmos: int = Gizmos.NONE
|
||||
var node3d_type: VisualInstance
|
||||
|
||||
var dir_light_texture := preload("res://assets/graphics/gizmos/directional_light.svg")
|
||||
var spot_light_texture := preload("res://assets/graphics/gizmos/spot_light.svg")
|
||||
var omni_light_texture := preload("res://assets/graphics/gizmos/omni_light.svg")
|
||||
|
||||
onready var gizmos_3d: Node2D = Global.canvas.gizmos_3d
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
camera = get_viewport().get_camera()
|
||||
var static_body := StaticBody.new()
|
||||
var collision_shape := CollisionShape.new()
|
||||
box_shape = BoxShape.new()
|
||||
box_shape.extents = scale
|
||||
collision_shape.shape = box_shape
|
||||
static_body.add_child(collision_shape)
|
||||
add_child(static_body)
|
||||
|
||||
|
||||
func find_cel() -> bool:
|
||||
var project = Global.current_project
|
||||
return cel == project.frames[project.current_frame].cels[project.current_layer]
|
||||
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
var dict := {
|
||||
"id": id, "type": type, "transform": transform, "visible": visible, "file_path": file_path
|
||||
}
|
||||
if _is_mesh():
|
||||
var mesh: Mesh = node3d_type.mesh
|
||||
match type:
|
||||
Type.BOX:
|
||||
dict["mesh_size"] = mesh.size
|
||||
Type.PLANE:
|
||||
dict["mesh_sizev2"] = mesh.size
|
||||
dict["mesh_center_offset"] = mesh.center_offset
|
||||
Type.PRISM:
|
||||
dict["mesh_size"] = mesh.size
|
||||
dict["mesh_left_to_right"] = mesh.left_to_right
|
||||
Type.SPHERE:
|
||||
dict["mesh_radius"] = mesh.radius
|
||||
dict["mesh_height"] = mesh.height
|
||||
dict["mesh_radial_segments"] = mesh.radial_segments
|
||||
dict["mesh_rings"] = mesh.rings
|
||||
dict["mesh_is_hemisphere"] = mesh.is_hemisphere
|
||||
Type.CAPSULE:
|
||||
dict["mesh_radius"] = mesh.radius
|
||||
dict["mesh_mid_height"] = mesh.mid_height
|
||||
dict["mesh_radial_segments"] = mesh.radial_segments
|
||||
dict["mesh_rings"] = mesh.rings
|
||||
Type.CYLINDER:
|
||||
dict["mesh_bottom_radius"] = mesh.bottom_radius
|
||||
dict["mesh_top_radius"] = mesh.top_radius
|
||||
dict["mesh_height"] = mesh.height
|
||||
dict["mesh_radial_segments"] = mesh.radial_segments
|
||||
dict["mesh_rings"] = mesh.rings
|
||||
Type.TEXT:
|
||||
dict["mesh_text"] = mesh.text
|
||||
dict["mesh_pixel_size"] = mesh.pixel_size
|
||||
dict["mesh_curve_step"] = mesh.curve_step
|
||||
dict["mesh_horizontal_alignment"] = mesh.horizontal_alignment
|
||||
else:
|
||||
dict["light_color"] = node3d_type.light_color
|
||||
dict["light_energy"] = node3d_type.light_energy
|
||||
dict["light_negative"] = node3d_type.light_negative
|
||||
dict["shadow_enabled"] = node3d_type.shadow_enabled
|
||||
dict["shadow_color"] = node3d_type.shadow_color
|
||||
match type:
|
||||
Type.OMNI_LIGHT:
|
||||
dict["omni_range"] = node3d_type.omni_range
|
||||
Type.SPOT_LIGHT:
|
||||
dict["spot_range"] = node3d_type.spot_range
|
||||
dict["spot_angle"] = node3d_type.spot_angle
|
||||
return dict
|
||||
|
||||
|
||||
func deserialize(dict: Dictionary) -> void:
|
||||
id = dict["id"]
|
||||
self.type = dict["type"]
|
||||
transform = dict["transform"]
|
||||
visible = dict["visible"]
|
||||
self.file_path = dict["file_path"]
|
||||
if _is_mesh():
|
||||
var mesh: Mesh = node3d_type.mesh
|
||||
match type:
|
||||
Type.BOX:
|
||||
mesh.size = dict["mesh_size"]
|
||||
Type.PLANE:
|
||||
mesh.size = dict["mesh_sizev2"]
|
||||
mesh.center_offset = dict["mesh_center_offset"]
|
||||
Type.PRISM:
|
||||
mesh.size = dict["mesh_size"]
|
||||
mesh.left_to_right = dict["mesh_left_to_right"]
|
||||
Type.SPHERE:
|
||||
mesh.radius = dict["mesh_radius"]
|
||||
mesh.height = dict["mesh_height"]
|
||||
mesh.radial_segments = dict["mesh_radial_segments"]
|
||||
mesh.rings = dict["mesh_rings"]
|
||||
mesh.is_hemisphere = dict["mesh_is_hemisphere"]
|
||||
Type.CAPSULE:
|
||||
mesh.radius = dict["mesh_radius"]
|
||||
mesh.mid_height = dict["mesh_mid_height"]
|
||||
mesh.radial_segments = dict["mesh_radial_segments"]
|
||||
mesh.rings = dict["mesh_rings"]
|
||||
Type.CYLINDER:
|
||||
mesh.bottom_radius = dict["mesh_bottom_radius"]
|
||||
mesh.top_radius = dict["mesh_top_radius"]
|
||||
mesh.height = dict["mesh_height"]
|
||||
mesh.radial_segments = dict["mesh_radial_segments"]
|
||||
mesh.rings = dict["mesh_rings"]
|
||||
Type.TEXT:
|
||||
mesh.text = dict["mesh_text"]
|
||||
mesh.pixel_size = dict["mesh_pixel_size"]
|
||||
mesh.curve_step = dict["mesh_curve_step"]
|
||||
mesh.horizontal_alignment = dict["mesh_horizontal_alignment"]
|
||||
else:
|
||||
node3d_type.light_color = dict["light_color"]
|
||||
node3d_type.light_energy = dict["light_energy"]
|
||||
node3d_type.light_negative = dict["light_negative"]
|
||||
node3d_type.shadow_enabled = dict["shadow_enabled"]
|
||||
node3d_type.shadow_color = dict["shadow_color"]
|
||||
match type:
|
||||
Type.OMNI_LIGHT:
|
||||
node3d_type.omni_range = dict["omni_range"]
|
||||
Type.SPOT_LIGHT:
|
||||
node3d_type.spot_range = dict["spot_range"]
|
||||
node3d_type.spot_angle = dict["spot_angle"]
|
||||
change_property()
|
||||
|
||||
|
||||
func _is_mesh() -> bool:
|
||||
return node3d_type is MeshInstance
|
||||
|
||||
|
||||
func _set_type(value: int) -> void:
|
||||
type = value
|
||||
if is_instance_valid(node3d_type):
|
||||
node3d_type.queue_free()
|
||||
match type:
|
||||
Type.BOX:
|
||||
node3d_type = MeshInstance.new()
|
||||
node3d_type.mesh = CubeMesh.new()
|
||||
Type.SPHERE:
|
||||
node3d_type = MeshInstance.new()
|
||||
node3d_type.mesh = SphereMesh.new()
|
||||
Type.CAPSULE:
|
||||
node3d_type = MeshInstance.new()
|
||||
node3d_type.mesh = CapsuleMesh.new()
|
||||
Type.CYLINDER:
|
||||
node3d_type = MeshInstance.new()
|
||||
node3d_type.mesh = CylinderMesh.new()
|
||||
Type.PRISM:
|
||||
node3d_type = MeshInstance.new()
|
||||
node3d_type.mesh = PrismMesh.new()
|
||||
Type.PLANE:
|
||||
node3d_type = MeshInstance.new()
|
||||
node3d_type.mesh = PlaneMesh.new()
|
||||
Type.TEXT:
|
||||
node3d_type = MeshInstance.new()
|
||||
var mesh := TextMesh.new()
|
||||
mesh.font = Global.control.theme.default_font
|
||||
mesh.text = "Sample"
|
||||
node3d_type.mesh = mesh
|
||||
Type.DIR_LIGHT:
|
||||
node3d_type = DirectionalLight.new()
|
||||
gizmos_3d.add_always_visible(self, dir_light_texture)
|
||||
Type.SPOT_LIGHT:
|
||||
node3d_type = SpotLight.new()
|
||||
gizmos_3d.add_always_visible(self, spot_light_texture)
|
||||
Type.OMNI_LIGHT:
|
||||
node3d_type = OmniLight.new()
|
||||
gizmos_3d.add_always_visible(self, omni_light_texture)
|
||||
Type.IMPORTED:
|
||||
node3d_type = MeshInstance.new()
|
||||
var mesh: Mesh
|
||||
if not file_path.empty():
|
||||
mesh = ObjParse.load_obj(file_path)
|
||||
node3d_type.mesh = mesh
|
||||
add_child(node3d_type)
|
||||
|
||||
|
||||
func _set_file_path(value: String) -> void:
|
||||
file_path = value
|
||||
if file_path.empty():
|
||||
return
|
||||
if type == Type.IMPORTED:
|
||||
node3d_type.mesh = ObjParse.load_obj(file_path)
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_EXIT_TREE:
|
||||
unselect()
|
||||
gizmos_3d.remove_always_visible(self)
|
||||
|
||||
|
||||
func select() -> void:
|
||||
selected = true
|
||||
gizmos_3d.get_points(camera, self)
|
||||
|
||||
|
||||
func unselect() -> void:
|
||||
selected = false
|
||||
gizmos_3d.clear_points(self)
|
||||
|
||||
|
||||
func hover() -> void:
|
||||
if hovered:
|
||||
return
|
||||
hovered = true
|
||||
if selected:
|
||||
return
|
||||
gizmos_3d.get_points(camera, self)
|
||||
|
||||
|
||||
func unhover() -> void:
|
||||
if not hovered:
|
||||
return
|
||||
hovered = false
|
||||
if selected:
|
||||
return
|
||||
gizmos_3d.clear_points(self)
|
||||
|
||||
|
||||
func delete() -> void:
|
||||
cel.layer.remove_object(id)
|
||||
queue_free()
|
||||
|
||||
|
||||
func change_transform(a: Vector3, b: Vector3) -> void:
|
||||
var diff := a - b
|
||||
match applying_gizmos:
|
||||
Gizmos.X_POS:
|
||||
move_axis(diff, transform.basis.x)
|
||||
Gizmos.Y_POS:
|
||||
move_axis(diff, transform.basis.y)
|
||||
Gizmos.Z_POS:
|
||||
move_axis(diff, transform.basis.z)
|
||||
Gizmos.X_ROT:
|
||||
change_rotation(a, b, transform.basis.x)
|
||||
Gizmos.Y_ROT:
|
||||
change_rotation(a, b, transform.basis.y)
|
||||
Gizmos.Z_ROT:
|
||||
change_rotation(a, b, transform.basis.z)
|
||||
Gizmos.X_SCALE:
|
||||
change_scale(diff, transform.basis.x, Vector3.RIGHT)
|
||||
Gizmos.Y_SCALE:
|
||||
change_scale(diff, transform.basis.y, Vector3.UP)
|
||||
Gizmos.Z_SCALE:
|
||||
change_scale(diff, transform.basis.z, Vector3.BACK)
|
||||
_:
|
||||
move(diff)
|
||||
|
||||
|
||||
func move(position: Vector3) -> void:
|
||||
translation += position
|
||||
change_property()
|
||||
|
||||
|
||||
func move_axis(diff: Vector3, axis: Vector3) -> void:
|
||||
# Move the object in the direction it is facing, and restrict mouse movement in that axis
|
||||
var axis_v2 := Vector2(axis.x, axis.y).normalized()
|
||||
if axis_v2 == Vector2.ZERO:
|
||||
axis_v2 = Vector2(axis.y, axis.z).normalized()
|
||||
var diff_v2 := Vector2(diff.x, diff.y).normalized()
|
||||
translation += axis * axis_v2.dot(diff_v2) * diff.length()
|
||||
change_property()
|
||||
|
||||
|
||||
func change_rotation(a: Vector3, b: Vector3, axis: Vector3) -> void:
|
||||
var a_local := a - translation
|
||||
var a_local_v2 := Vector2(a_local.x, a_local.y)
|
||||
var b_local := b - translation
|
||||
var b_local_v2 := Vector2(b_local.x, b_local.y)
|
||||
var angle := b_local_v2.angle_to(a_local_v2)
|
||||
# Rotate the object around a basis axis, instead of a fixed axis, such as
|
||||
# Vector3.RIGHT, Vector3.UP or Vector3.BACK
|
||||
rotate(axis.normalized(), angle)
|
||||
rotation.x = wrapf(rotation.x, -PI, PI)
|
||||
rotation.y = wrapf(rotation.y, -PI, PI)
|
||||
rotation.z = wrapf(rotation.z, -PI, PI)
|
||||
change_property()
|
||||
|
||||
|
||||
func change_scale(diff: Vector3, axis: Vector3, dir: Vector3) -> void:
|
||||
# Scale the object in the direction it is facing, and restrict mouse movement in that axis
|
||||
var axis_v2 := Vector2(axis.x, axis.y).normalized()
|
||||
if axis_v2 == Vector2.ZERO:
|
||||
axis_v2 = Vector2(axis.y, axis.z).normalized()
|
||||
var diff_v2 := Vector2(diff.x, diff.y).normalized()
|
||||
scale += dir * axis_v2.dot(diff_v2) * diff.length()
|
||||
change_property()
|
||||
|
||||
|
||||
func change_property() -> void:
|
||||
if selected:
|
||||
select()
|
||||
else:
|
||||
# Check is needed in case this runs before _ready(), and thus onready variables
|
||||
if is_instance_valid(gizmos_3d):
|
||||
gizmos_3d.update()
|
||||
emit_signal("property_changed")
|
||||
|
||||
|
||||
func finish_changing_property() -> void:
|
||||
select()
|
||||
emit_signal("property_finished_changing")
|
92
src/Classes/Cel3DParent.gd
Normal file
92
src/Classes/Cel3DParent.gd
Normal file
|
@ -0,0 +1,92 @@
|
|||
#class_name Cel3DParent
|
||||
#extends Spatial
|
||||
#
|
||||
#signal selected_object(object)
|
||||
#
|
||||
#var cel #: Cel3D
|
||||
#var hovering: Cel3DObject = null
|
||||
#var selected: Cel3DObject = null setget _set_selected
|
||||
#var dragging := false
|
||||
#var has_been_dragged := false
|
||||
#var prev_mouse_pos := Vector2.ZERO
|
||||
#
|
||||
#onready var camera := get_viewport().get_camera()
|
||||
#
|
||||
#
|
||||
#func _ready() -> void:
|
||||
# Global.connect("cel_changed", self, "_cel_changed")
|
||||
#
|
||||
#
|
||||
#func _inputo(event: InputEvent) -> void:
|
||||
# if event.is_action_pressed("delete") and is_instance_valid(selected):
|
||||
# selected.delete()
|
||||
# self.selected = null
|
||||
# if not event is InputEventMouse:
|
||||
# return
|
||||
# if not cel.layer.can_layer_get_drawn():
|
||||
# return
|
||||
# var found_cel := false
|
||||
# for frame_layer in Global.current_project.selected_cels:
|
||||
# if cel == Global.current_project.frames[frame_layer[0]].cels[frame_layer[1]]:
|
||||
# found_cel = true
|
||||
# if not found_cel:
|
||||
# return
|
||||
# var mouse_pos: Vector2 = event.position
|
||||
# if event is InputEventMouseButton:
|
||||
# if event.button_index == BUTTON_LEFT and event.pressed == true:
|
||||
# if is_instance_valid(hovering):
|
||||
# self.selected = hovering
|
||||
# dragging = true
|
||||
# prev_mouse_pos = mouse_pos
|
||||
# else:
|
||||
# # We're not hovering
|
||||
# if is_instance_valid(selected):
|
||||
# # If we're not clicking on a gizmo, unselect
|
||||
# if selected.applying_gizmos == Cel3DObject.Gizmos.NONE:
|
||||
# self.selected = null
|
||||
# else:
|
||||
# dragging = true
|
||||
# prev_mouse_pos = mouse_pos
|
||||
# elif event.button_index == BUTTON_LEFT and event.pressed == false:
|
||||
# dragging = false
|
||||
# if is_instance_valid(selected) and has_been_dragged:
|
||||
# selected.finish_changing_property()
|
||||
# has_been_dragged = false
|
||||
#
|
||||
# var ray_from := camera.project_ray_origin(mouse_pos)
|
||||
# var ray_to := ray_from + camera.project_ray_normal(mouse_pos) * 20
|
||||
# var space_state := get_world().direct_space_state
|
||||
# var selection := space_state.intersect_ray(ray_from, ray_to)
|
||||
#
|
||||
# if dragging and event is InputEventMouseMotion:
|
||||
# has_been_dragged = true
|
||||
# var proj_mouse_pos := camera.project_position(mouse_pos, camera.translation.z)
|
||||
# var proj_prev_mouse_pos := camera.project_position(prev_mouse_pos, camera.translation.z)
|
||||
# selected.change_transform(proj_mouse_pos, proj_prev_mouse_pos)
|
||||
# prev_mouse_pos = mouse_pos
|
||||
#
|
||||
# # Hover logic
|
||||
# if selection.empty():
|
||||
# if is_instance_valid(hovering):
|
||||
# hovering.unhover()
|
||||
# hovering = null
|
||||
# else:
|
||||
# if is_instance_valid(hovering):
|
||||
# hovering.unhover()
|
||||
# hovering = selection["collider"].get_parent()
|
||||
# hovering.hover()
|
||||
#
|
||||
#
|
||||
#func _set_selected(value: Cel3DObject) -> void:
|
||||
# if value == selected:
|
||||
# return
|
||||
# if is_instance_valid(selected): # Unselect previous object if we selected something else
|
||||
# selected.unselect()
|
||||
# selected = value
|
||||
# if is_instance_valid(selected): # Select new object
|
||||
# selected.select()
|
||||
# emit_signal("selected_object", value)
|
||||
#
|
||||
#
|
||||
#func _cel_changed() -> void:
|
||||
# self.selected = null
|
|
@ -10,7 +10,7 @@ func _init(_opacity := 1.0) -> void:
|
|||
|
||||
|
||||
func get_image() -> Image:
|
||||
var image = Image.new()
|
||||
var image := Image.new()
|
||||
image.create(
|
||||
Global.current_project.size.x, Global.current_project.size.y, false, Image.FORMAT_RGBA8
|
||||
)
|
||||
|
|
|
@ -43,7 +43,7 @@ func blend_children(frame: Frame, origin := Vector2.ZERO) -> Image:
|
|||
|
||||
func serialize() -> Dictionary:
|
||||
var data = .serialize()
|
||||
data["type"] = Global.LayerTypes.GROUP
|
||||
data["type"] = get_layer_type()
|
||||
data["expanded"] = expanded
|
||||
return data
|
||||
|
||||
|
@ -53,6 +53,10 @@ func deserialize(dict: Dictionary) -> void:
|
|||
expanded = dict.expanded
|
||||
|
||||
|
||||
func get_layer_type() -> int:
|
||||
return Global.LayerTypes.GROUP
|
||||
|
||||
|
||||
func new_empty_cel() -> BaseCel:
|
||||
return GroupCel.new()
|
||||
|
||||
|
|
|
@ -69,7 +69,9 @@ func _confirmed() -> void:
|
|||
for cel_index in project.selected_cels:
|
||||
if !project.layers[cel_index[1]].can_layer_get_drawn():
|
||||
continue
|
||||
var cel: PixelCel = project.frames[cel_index[0]].cels[cel_index[1]]
|
||||
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
|
||||
if not cel is PixelCel:
|
||||
continue
|
||||
var cel_image: Image = cel.image
|
||||
commit_action(cel_image)
|
||||
_commit_undo("Draw", undo_data, project)
|
||||
|
@ -78,6 +80,9 @@ func _confirmed() -> void:
|
|||
var undo_data := _get_undo_data(project)
|
||||
var i := 0
|
||||
for cel in project.frames[project.current_frame].cels:
|
||||
if not cel is PixelCel:
|
||||
i += 1
|
||||
continue
|
||||
if project.layers[i].can_layer_get_drawn():
|
||||
commit_action(cel.image)
|
||||
i += 1
|
||||
|
@ -88,6 +93,9 @@ func _confirmed() -> void:
|
|||
for frame in project.frames:
|
||||
var i := 0
|
||||
for cel in frame.cels:
|
||||
if not cel is PixelCel:
|
||||
i += 1
|
||||
continue
|
||||
if project.layers[i].can_layer_get_drawn():
|
||||
commit_action(cel.image)
|
||||
i += 1
|
||||
|
@ -99,6 +107,9 @@ func _confirmed() -> void:
|
|||
for frame in _project.frames:
|
||||
var i := 0
|
||||
for cel in frame.cels:
|
||||
if not cel is PixelCel:
|
||||
i += 1
|
||||
continue
|
||||
if _project.layers[i].can_layer_get_drawn():
|
||||
commit_action(cel.image, _project)
|
||||
i += 1
|
||||
|
|
142
src/Classes/Layer3D.gd
Normal file
142
src/Classes/Layer3D.gd
Normal file
|
@ -0,0 +1,142 @@
|
|||
class_name Layer3D
|
||||
extends BaseLayer
|
||||
|
||||
signal property_changed
|
||||
signal objects_changed
|
||||
|
||||
var properties := {} # Key = property name, Value = property
|
||||
var objects := {} # Key = id, Value = Cel3DObject.Type
|
||||
var current_object_id := 0 # Its value never decreases
|
||||
|
||||
|
||||
func _init(_project, _name := "") -> void:
|
||||
project = _project
|
||||
name = _name
|
||||
var camera_transform := Transform()
|
||||
camera_transform.origin = Vector3(0, 0, 3)
|
||||
properties = {
|
||||
"camera_transform": camera_transform,
|
||||
"camera_projection": Camera.PROJECTION_PERSPECTIVE,
|
||||
"ambient_light_color": Color.black,
|
||||
"ambient_light_energy": 1,
|
||||
}
|
||||
add_object(Cel3DObject.Type.DIR_LIGHT, false)
|
||||
add_object(Cel3DObject.Type.BOX, false)
|
||||
|
||||
|
||||
func add_object(type: int, undoredo := true, file_path := "") -> void:
|
||||
if undoredo:
|
||||
var id := current_object_id
|
||||
var new_objects := objects.duplicate()
|
||||
new_objects[id] = type
|
||||
var undo_redo: UndoRedo = project.undo_redo
|
||||
undo_redo.create_action("Add 3D object")
|
||||
undo_redo.add_do_property(self, "objects", new_objects)
|
||||
undo_redo.add_undo_property(self, "objects", objects)
|
||||
for frame in project.frames:
|
||||
var cel: Cel3D = frame.cels[index]
|
||||
var new_properties := cel.object_properties.duplicate()
|
||||
new_properties[id] = {"file_path": file_path}
|
||||
undo_redo.add_do_property(cel, "object_properties", new_properties)
|
||||
undo_redo.add_undo_property(cel, "object_properties", cel.object_properties)
|
||||
undo_redo.add_do_method(self, "add_object_in_cels", id)
|
||||
undo_redo.add_undo_method(self, "remove_object_from_cels", id)
|
||||
undo_redo.add_do_method(Global, "undo_or_redo", false)
|
||||
undo_redo.add_undo_method(Global, "undo_or_redo", true)
|
||||
undo_redo.commit_action()
|
||||
else:
|
||||
objects[current_object_id] = type
|
||||
|
||||
current_object_id += 1
|
||||
|
||||
|
||||
func add_object_in_cels(id: int) -> void:
|
||||
for frame in project.frames:
|
||||
var cel: Cel3D = frame.cels[index]
|
||||
cel.add_object(id)
|
||||
emit_signal("objects_changed")
|
||||
|
||||
|
||||
func remove_object(id: int) -> void:
|
||||
var new_objects := objects.duplicate()
|
||||
new_objects.erase(id)
|
||||
var undo_redo: UndoRedo = project.undo_redo
|
||||
undo_redo.create_action("Remove 3D object")
|
||||
undo_redo.add_do_property(self, "objects", new_objects)
|
||||
undo_redo.add_undo_property(self, "objects", objects)
|
||||
# Store object_properties in undoredo memory to keep previous transforms
|
||||
for frame in project.frames:
|
||||
var cel: Cel3D = frame.cels[index]
|
||||
var new_properties := cel.object_properties.duplicate()
|
||||
new_properties.erase(id)
|
||||
undo_redo.add_do_property(cel, "object_properties", new_properties)
|
||||
undo_redo.add_undo_property(cel, "object_properties", cel.object_properties)
|
||||
undo_redo.add_do_method(self, "remove_object_from_cels", id)
|
||||
undo_redo.add_undo_method(self, "add_object_in_cels", id)
|
||||
undo_redo.add_do_method(Global, "undo_or_redo", false)
|
||||
undo_redo.add_undo_method(Global, "undo_or_redo", true)
|
||||
undo_redo.commit_action()
|
||||
|
||||
|
||||
func remove_object_from_cels(id: int) -> void:
|
||||
for frame in project.frames:
|
||||
var cel: Cel3D = frame.cels[index]
|
||||
cel.remove_object(id)
|
||||
emit_signal("objects_changed")
|
||||
|
||||
|
||||
func change_properties(new_properties: Dictionary) -> void:
|
||||
var undo_redo: UndoRedo = project.undo_redo
|
||||
undo_redo.create_action("Change 3D layer properties")
|
||||
undo_redo.add_do_property(self, "properties", new_properties)
|
||||
undo_redo.add_undo_property(self, "properties", properties)
|
||||
for frame in project.frames:
|
||||
var cel: Cel3D = frame.cels[index]
|
||||
undo_redo.add_do_method(cel, "deserialize_layer_properties")
|
||||
undo_redo.add_undo_method(cel, "deserialize_layer_properties")
|
||||
undo_redo.add_do_method(self, "_property_changed")
|
||||
undo_redo.add_undo_method(self, "_property_changed")
|
||||
undo_redo.add_do_method(Global, "undo_or_redo", false)
|
||||
undo_redo.add_undo_method(Global, "undo_or_redo", true)
|
||||
undo_redo.commit_action()
|
||||
|
||||
|
||||
func _property_changed() -> void:
|
||||
emit_signal("property_changed")
|
||||
|
||||
|
||||
# Overridden Methods:
|
||||
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
var dict = .serialize()
|
||||
dict["type"] = get_layer_type()
|
||||
dict["properties"] = properties
|
||||
dict["objects"] = objects
|
||||
# dict["new_cels_linked"] = new_cels_linked
|
||||
return dict
|
||||
|
||||
|
||||
func deserialize(dict: Dictionary) -> void:
|
||||
.deserialize(dict)
|
||||
properties = dict["properties"]
|
||||
objects = dict["objects"]
|
||||
# new_cels_linked = dict.new_cels_linked
|
||||
current_object_id = objects.size()
|
||||
Global.convert_dictionary_values(properties)
|
||||
|
||||
|
||||
func get_layer_type() -> int:
|
||||
return Global.LayerTypes.THREE_D
|
||||
|
||||
|
||||
func new_empty_cel() -> BaseCel:
|
||||
return Cel3D.new(self, project.size)
|
||||
|
||||
|
||||
func can_layer_get_drawn() -> bool:
|
||||
return is_visible_in_hierarchy() && !is_locked_in_hierarchy()
|
||||
|
||||
|
||||
func instantiate_layer_button() -> Node:
|
||||
return Global.base_layer_button_node.instance()
|
295
src/Classes/ObjParse.gd
Normal file
295
src/Classes/ObjParse.gd
Normal file
|
@ -0,0 +1,295 @@
|
|||
class_name ObjParse
|
||||
|
||||
const DEBUG := false
|
||||
|
||||
# Obj parser made by Ezcha, updated by Deakcor
|
||||
# Created on 7/11/2018
|
||||
# https://ezcha.net
|
||||
# https://github.com/Ezcha/gd-obj
|
||||
# MIT License
|
||||
# https://github.com/Ezcha/gd-obj/blob/master/LICENSE
|
||||
|
||||
# Returns an array of materials from a MTL file
|
||||
|
||||
# Public methods
|
||||
|
||||
|
||||
# Create mesh from obj and mtl paths
|
||||
static func load_obj(obj_path: String, mtl_path: String = "") -> Mesh:
|
||||
if mtl_path == "":
|
||||
mtl_path = search_mtl_path(obj_path)
|
||||
var obj := get_data(obj_path)
|
||||
var mats := {}
|
||||
if mtl_path != "":
|
||||
mats = _create_mtl(get_data(mtl_path), get_mtl_tex(mtl_path))
|
||||
return _create_obj(obj, mats) if obj and mats else null
|
||||
|
||||
|
||||
# Create mesh from obj, materials. Materials should be {"matname":data}
|
||||
static func load_obj_from_buffer(obj_data: String, materials: Dictionary) -> Mesh:
|
||||
return _create_obj(obj_data, materials)
|
||||
|
||||
|
||||
# Create materials
|
||||
static func load_mtl_from_buffer(mtl_data: String, textures: Dictionary) -> Dictionary:
|
||||
return _create_mtl(mtl_data, textures)
|
||||
|
||||
|
||||
# Get data from file path
|
||||
static func get_data(path: String) -> String:
|
||||
if path != "":
|
||||
var file := File.new()
|
||||
var err := file.open(path, File.READ)
|
||||
if err == OK:
|
||||
var res := file.get_as_text()
|
||||
file.close()
|
||||
return res
|
||||
return ""
|
||||
|
||||
|
||||
# Get textures from mtl path (return {"tex_path":data})
|
||||
static func get_mtl_tex(mtl_path: String) -> Dictionary:
|
||||
var file_paths := get_mtl_tex_paths(mtl_path)
|
||||
var textures := {}
|
||||
for k in file_paths:
|
||||
textures[k] = _get_image(mtl_path, k).save_png_to_buffer()
|
||||
return textures
|
||||
|
||||
|
||||
# Get textures paths from mtl path
|
||||
static func get_mtl_tex_paths(mtl_path: String) -> Array:
|
||||
var file := File.new()
|
||||
var err := file.open(mtl_path, File.READ)
|
||||
var paths := []
|
||||
if err == OK:
|
||||
var lines := file.get_as_text().split("\n", false)
|
||||
file.close()
|
||||
for line in lines:
|
||||
var parts = line.split(" ", false, 1)
|
||||
if parts[0] in ["map_Kd", "map_Ks", "map_Ka"]:
|
||||
if !parts[1] in paths:
|
||||
paths.push_back(parts[1])
|
||||
return paths
|
||||
|
||||
|
||||
# Try to find mtl path from obj path
|
||||
static func search_mtl_path(obj_path: String) -> String:
|
||||
var mtl_path := obj_path.get_base_dir().plus_file(
|
||||
obj_path.get_file().rsplit(".", false, 1)[0] + ".mtl"
|
||||
)
|
||||
var dir: Directory = Directory.new()
|
||||
if !dir.file_exists(mtl_path):
|
||||
mtl_path = obj_path.get_base_dir().plus_file(obj_path.get_file() + ".mtl")
|
||||
if !dir.file_exists(mtl_path):
|
||||
return ""
|
||||
return mtl_path
|
||||
|
||||
|
||||
# Private methods
|
||||
|
||||
|
||||
static func _create_mtl(obj: String, textures: Dictionary) -> Dictionary:
|
||||
var mats := {}
|
||||
var current_mat: SpatialMaterial = null
|
||||
|
||||
var lines := obj.split("\n", false)
|
||||
for line in lines:
|
||||
var parts = line.split(" ", false)
|
||||
match parts[0]:
|
||||
"#":
|
||||
# Comment
|
||||
#print("Comment: "+line)
|
||||
pass
|
||||
"newmtl":
|
||||
# Create a new material
|
||||
if DEBUG:
|
||||
print("Adding new material " + parts[1])
|
||||
current_mat = SpatialMaterial.new()
|
||||
mats[parts[1]] = current_mat
|
||||
"Ka":
|
||||
# Ambient color
|
||||
#current_mat.albedo_color = Color(float(parts[1]), float(parts[2]), float(parts[3]))
|
||||
pass
|
||||
"Kd":
|
||||
# Diffuse color
|
||||
current_mat.albedo_color = Color(float(parts[1]), float(parts[2]), float(parts[3]))
|
||||
if DEBUG:
|
||||
print("Setting material color " + str(current_mat.albedo_color))
|
||||
_:
|
||||
if parts[0] in ["map_Kd", "map_Ks", "map_Ka"]:
|
||||
var path = line.split(" ", false, 1)[1]
|
||||
if textures.has(path):
|
||||
current_mat.albedo_texture = _create_texture(textures[path])
|
||||
return mats
|
||||
|
||||
|
||||
static func _get_image(mtl_filepath: String, tex_filename: String) -> Image:
|
||||
if DEBUG:
|
||||
print(" Debug: Mapping texture file " + tex_filename)
|
||||
var texfilepath := tex_filename
|
||||
if tex_filename.is_rel_path():
|
||||
texfilepath = mtl_filepath.get_base_dir().plus_file(tex_filename)
|
||||
var filetype := texfilepath.get_extension()
|
||||
if DEBUG:
|
||||
print(" Debug: texture file path: " + texfilepath + " of type " + filetype)
|
||||
|
||||
var img: Image = Image.new()
|
||||
img.load(texfilepath)
|
||||
return img
|
||||
|
||||
|
||||
static func _create_texture(data: PoolByteArray) -> ImageTexture:
|
||||
var img: Image = Image.new()
|
||||
var tex: ImageTexture = ImageTexture.new()
|
||||
img.load_png_from_buffer(data)
|
||||
tex.create_from_image(img)
|
||||
return tex
|
||||
|
||||
|
||||
static func _create_obj(obj: String, mats: Dictionary) -> Mesh:
|
||||
# Setup
|
||||
var mesh := ArrayMesh.new()
|
||||
var vertices := PoolVector3Array()
|
||||
var normals := PoolVector3Array()
|
||||
var uvs := PoolVector2Array()
|
||||
var faces := {}
|
||||
|
||||
var mat_name := "default"
|
||||
var count_mtl := 0
|
||||
|
||||
# Parse
|
||||
var lines := obj.split("\n", false)
|
||||
for line in lines:
|
||||
var parts = line.split(" ", false)
|
||||
match parts[0]:
|
||||
"#":
|
||||
# Comment
|
||||
#print("Comment: "+line)
|
||||
pass
|
||||
"v":
|
||||
# Vertex
|
||||
var n_v = Vector3(float(parts[1]), float(parts[2]), float(parts[3]))
|
||||
vertices.append(n_v)
|
||||
"vn":
|
||||
# Normal
|
||||
var n_vn = Vector3(float(parts[1]), float(parts[2]), float(parts[3]))
|
||||
normals.append(n_vn)
|
||||
"vt":
|
||||
# UV
|
||||
var n_uv = Vector2(float(parts[1]), 1 - float(parts[2]))
|
||||
uvs.append(n_uv)
|
||||
"usemtl":
|
||||
# Material group
|
||||
count_mtl += 1
|
||||
mat_name = parts[1]
|
||||
if not faces.has(mat_name):
|
||||
var mats_keys := mats.keys()
|
||||
if !mats.has(mat_name):
|
||||
if mats_keys.size() > count_mtl:
|
||||
mat_name = mats_keys[count_mtl]
|
||||
faces[mat_name] = []
|
||||
"f":
|
||||
if not faces.has(mat_name):
|
||||
var mats_keys := mats.keys()
|
||||
if mats_keys.size() > count_mtl:
|
||||
mat_name = mats_keys[count_mtl]
|
||||
faces[mat_name] = []
|
||||
# Face
|
||||
if parts.size() == 4:
|
||||
# Tri
|
||||
var face = {"v": [], "vt": [], "vn": []}
|
||||
for map in parts:
|
||||
var vertices_index = map.split("/")
|
||||
if str(vertices_index[0]) != "f":
|
||||
face["v"].append(int(vertices_index[0]) - 1)
|
||||
face["vt"].append(int(vertices_index[1]) - 1)
|
||||
if vertices_index.size() > 2:
|
||||
face["vn"].append(int(vertices_index[2]) - 1)
|
||||
if faces.has(mat_name):
|
||||
faces[mat_name].append(face)
|
||||
elif parts.size() > 4:
|
||||
# Triangulate
|
||||
var points = []
|
||||
for map in parts:
|
||||
var vertices_index = map.split("/")
|
||||
if str(vertices_index[0]) != "f":
|
||||
var point = []
|
||||
point.append(int(vertices_index[0]) - 1)
|
||||
point.append(int(vertices_index[1]) - 1)
|
||||
if vertices_index.size() > 2:
|
||||
point.append(int(vertices_index[2]) - 1)
|
||||
points.append(point)
|
||||
for i in points.size():
|
||||
if i != 0:
|
||||
var face = {"v": [], "vt": [], "vn": []}
|
||||
var point0 = points[0]
|
||||
var point1 = points[i]
|
||||
var point2 = points[i - 1]
|
||||
face["v"].append(point0[0])
|
||||
face["v"].append(point2[0])
|
||||
face["v"].append(point1[0])
|
||||
face["vt"].append(point0[1])
|
||||
face["vt"].append(point2[1])
|
||||
face["vt"].append(point1[1])
|
||||
if point0.size() > 2:
|
||||
face["vn"].append(point0[2])
|
||||
if point2.size() > 2:
|
||||
face["vn"].append(point2[2])
|
||||
if point1.size() > 2:
|
||||
face["vn"].append(point1[2])
|
||||
faces[mat_name].append(face)
|
||||
|
||||
# Make tri
|
||||
for matgroup in faces.keys():
|
||||
if DEBUG:
|
||||
print(
|
||||
(
|
||||
"Creating surface for matgroup "
|
||||
+ matgroup
|
||||
+ " with "
|
||||
+ str(faces[matgroup].size())
|
||||
+ " faces"
|
||||
)
|
||||
)
|
||||
|
||||
# Mesh Assembler
|
||||
var st = SurfaceTool.new()
|
||||
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||
if !mats.has(matgroup):
|
||||
mats[matgroup] = SpatialMaterial.new()
|
||||
st.set_material(mats[matgroup])
|
||||
for face in faces[matgroup]:
|
||||
if face["v"].size() == 3:
|
||||
# Vertices
|
||||
var fan_v = PoolVector3Array()
|
||||
fan_v.append(vertices[face["v"][0]])
|
||||
fan_v.append(vertices[face["v"][2]])
|
||||
fan_v.append(vertices[face["v"][1]])
|
||||
|
||||
# Normals
|
||||
var fan_vn = PoolVector3Array()
|
||||
if face["vn"].size() > 0:
|
||||
fan_vn.append(normals[face["vn"][0]])
|
||||
fan_vn.append(normals[face["vn"][2]])
|
||||
fan_vn.append(normals[face["vn"][1]])
|
||||
|
||||
# Textures
|
||||
var fan_vt = PoolVector2Array()
|
||||
if face["vt"].size() > 0:
|
||||
for k in [0, 2, 1]:
|
||||
var f = face["vt"][k]
|
||||
if f > -1:
|
||||
var uv = uvs[f]
|
||||
fan_vt.append(uv)
|
||||
|
||||
st.add_triangle_fan(fan_v, fan_vt, PoolColorArray(), PoolVector2Array(), fan_vn, [])
|
||||
mesh = st.commit(mesh)
|
||||
for k in mesh.get_surface_count():
|
||||
var mat = mesh.surface_get_material(k)
|
||||
mat_name = ""
|
||||
for m in mats:
|
||||
if mats[m] == mat:
|
||||
mat_name = m
|
||||
mesh.surface_set_name(k, mat_name)
|
||||
# Finish
|
||||
return mesh
|
|
@ -59,16 +59,15 @@ func update_texture() -> void:
|
|||
.update_texture()
|
||||
|
||||
|
||||
func save_image_data_to_pxo(file: File) -> void:
|
||||
func save_cel_data_to_pxo(file: File) -> void:
|
||||
file.store_buffer(image.get_data())
|
||||
|
||||
|
||||
func load_image_data_from_pxo(file: File, project_size: Vector2) -> void:
|
||||
func load_cel_data_from_pxo(file: File, project_size: Vector2) -> void:
|
||||
var buffer := file.get_buffer(project_size.x * project_size.y * 4)
|
||||
image.create_from_data(project_size.x, project_size.y, false, Image.FORMAT_RGBA8, buffer)
|
||||
image_changed(image)
|
||||
|
||||
|
||||
func instantiate_cel_button() -> Node:
|
||||
var cel_button = Global.pixel_cel_button_node.instance()
|
||||
return cel_button
|
||||
return Global.pixel_cel_button_node.instance()
|
||||
|
|
|
@ -12,8 +12,8 @@ func _init(_project, _name := "") -> void:
|
|||
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
var dict = .serialize()
|
||||
dict["type"] = Global.LayerTypes.PIXEL
|
||||
var dict := .serialize()
|
||||
dict["type"] = get_layer_type()
|
||||
dict["new_cels_linked"] = new_cels_linked
|
||||
return dict
|
||||
|
||||
|
@ -23,6 +23,10 @@ func deserialize(dict: Dictionary) -> void:
|
|||
new_cels_linked = dict.new_cels_linked
|
||||
|
||||
|
||||
func get_layer_type() -> int:
|
||||
return Global.LayerTypes.PIXEL
|
||||
|
||||
|
||||
func new_empty_cel() -> BaseCel:
|
||||
var image := Image.new()
|
||||
image.create(project.size.x, project.size.y, false, Image.FORMAT_RGBA8)
|
||||
|
|
|
@ -100,6 +100,10 @@ func remove() -> void:
|
|||
c.queue_free()
|
||||
for guide in guides:
|
||||
guide.queue_free()
|
||||
for frame in frames:
|
||||
for l in layers.size():
|
||||
var cel: BaseCel = frame.cels[l]
|
||||
cel.on_remove()
|
||||
# Prevents memory leak (due to the layers' project reference stopping ref counting from freeing)
|
||||
layers.clear()
|
||||
Global.projects.erase(self)
|
||||
|
@ -356,6 +360,15 @@ func deserialize(dict: Dictionary) -> void:
|
|||
if dict.has("save_path"):
|
||||
OpenSave.current_save_paths[Global.projects.find(self)] = dict.save_path
|
||||
if dict.has("frames") and dict.has("layers"):
|
||||
for saved_layer in dict.layers:
|
||||
match int(saved_layer.get("type", Global.LayerTypes.PIXEL)):
|
||||
Global.LayerTypes.PIXEL:
|
||||
layers.append(PixelLayer.new(self))
|
||||
Global.LayerTypes.GROUP:
|
||||
layers.append(GroupLayer.new(self))
|
||||
Global.LayerTypes.THREE_D:
|
||||
layers.append(Layer3D.new(self))
|
||||
|
||||
var frame_i := 0
|
||||
for frame in dict.frames:
|
||||
var cels := []
|
||||
|
@ -366,6 +379,8 @@ func deserialize(dict: Dictionary) -> void:
|
|||
cels.append(PixelCel.new(Image.new(), cel.opacity))
|
||||
Global.LayerTypes.GROUP:
|
||||
cels.append(GroupCel.new(cel.opacity))
|
||||
Global.LayerTypes.THREE_D:
|
||||
cels.append(Cel3D.new(layers[cel_i], size, true))
|
||||
_deserialize_metadata(cels[cel_i], cel)
|
||||
cel_i += 1
|
||||
var duration := 1.0
|
||||
|
@ -379,12 +394,6 @@ func deserialize(dict: Dictionary) -> void:
|
|||
frames.append(frame_class)
|
||||
frame_i += 1
|
||||
|
||||
for saved_layer in dict.layers:
|
||||
match int(saved_layer.get("type", Global.LayerTypes.PIXEL)):
|
||||
Global.LayerTypes.PIXEL:
|
||||
layers.append(PixelLayer.new(self))
|
||||
Global.LayerTypes.GROUP:
|
||||
layers.append(GroupLayer.new(self))
|
||||
# Parent references to other layers are created when deserializing
|
||||
# a layer, so loop again after creating them:
|
||||
for layer_i in dict.layers.size():
|
||||
|
@ -553,6 +562,7 @@ func toggle_layer_buttons() -> void:
|
|||
current_layer == child_count
|
||||
or layers[current_layer] is GroupLayer
|
||||
or layers[current_layer - 1] is GroupLayer
|
||||
or layers[current_layer - 1] is Layer3D
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -680,6 +690,7 @@ func remove_frames(indices: Array) -> void: # indices should be in ascending or
|
|||
# For each linked cel in the frame, update its layer's cel_link_sets
|
||||
for l in layers.size():
|
||||
var cel: BaseCel = frames[indices[i] - i].cels[l]
|
||||
cel.on_remove()
|
||||
if cel.link_set != null:
|
||||
cel.link_set["cels"].erase(cel)
|
||||
if cel.link_set["cels"].empty():
|
||||
|
@ -761,6 +772,7 @@ func remove_layers(indices: Array) -> void:
|
|||
# With each removed index, future indices need to be lowered, so subtract by i
|
||||
layers.remove(indices[i] - i)
|
||||
for frame in frames:
|
||||
frame.cels[indices[i] - i].on_remove()
|
||||
frame.cels.remove(indices[i] - i)
|
||||
Global.animation_timeline.project_layer_removed(indices[i] - i)
|
||||
# Update the layer indices and layer/cel buttons:
|
||||
|
|
381
src/Tools/3DShapeEdit.gd
Normal file
381
src/Tools/3DShapeEdit.gd
Normal file
|
@ -0,0 +1,381 @@
|
|||
extends BaseTool
|
||||
|
||||
var cel: Cel3D
|
||||
var can_start_timer := true
|
||||
var _hovering: Cel3DObject = null
|
||||
var _dragging := false
|
||||
var _has_been_dragged := false
|
||||
var _prev_mouse_pos := Vector2.ZERO
|
||||
|
||||
onready var object_option_button := $"%ObjectOptionButton" as OptionButton
|
||||
onready var new_object_menu_button := $"%NewObjectMenuButton" as MenuButton
|
||||
onready var remove_object := $"%RemoveObject" as Button
|
||||
onready var layer_options := $"%LayerOptions" as Container
|
||||
onready var object_options := $"%ObjectOptions" as Container
|
||||
onready var mesh_options := $"%MeshOptions" as VBoxContainer
|
||||
onready var light_options := $"%LightOptions" as VBoxContainer
|
||||
onready var undo_redo_timer := $UndoRedoTimer as Timer
|
||||
onready var load_model_dialog := $LoadModelDialog as FileDialog
|
||||
|
||||
onready var layer_properties := {
|
||||
"camera:projection": $"%ProjectionOptionButton",
|
||||
"camera:rotation_degrees": $"%CameraRotation",
|
||||
"viewport:world:environment:ambient_light_color": $"%AmbientColorPickerButton",
|
||||
"viewport:world:environment:ambient_light_energy": $"%AmbientEnergy",
|
||||
}
|
||||
|
||||
onready var object_properties := {
|
||||
"translation": $"%ObjectPosition",
|
||||
"rotation_degrees": $"%ObjectRotation",
|
||||
"scale": $"%ObjectScale",
|
||||
"node3d_type:mesh:size": $"%MeshSize",
|
||||
"node3d_type:mesh:sizev2": $"%MeshSizeV2",
|
||||
"node3d_type:mesh:center_offset": $"%MeshCenterOffset",
|
||||
"node3d_type:mesh:left_to_right": $"%MeshLeftToRight",
|
||||
"node3d_type:mesh:radius": $"%MeshRadius",
|
||||
"node3d_type:mesh:height": $"%MeshHeight",
|
||||
"node3d_type:mesh:radial_segments": $"%MeshRadialSegments",
|
||||
"node3d_type:mesh:rings": $"%MeshRings",
|
||||
"node3d_type:mesh:is_hemisphere": $"%MeshIsHemisphere",
|
||||
"node3d_type:mesh:mid_height": $"%MeshMidHeight",
|
||||
"node3d_type:mesh:top_radius": $"%MeshTopRadius",
|
||||
"node3d_type:mesh:bottom_radius": $"%MeshBottomRadius",
|
||||
"node3d_type:mesh:text": $"%MeshText",
|
||||
"node3d_type:mesh:pixel_size": $"%MeshPixelSize",
|
||||
"node3d_type:mesh:curve_step": $"%MeshCurveStep",
|
||||
"node3d_type:mesh:horizontal_alignment": $"%MeshHorizontalAlignment",
|
||||
"node3d_type:light_color": $"%LightColor",
|
||||
"node3d_type:light_energy": $"%LightEnergy",
|
||||
"node3d_type:light_negative": $"%LightNegative",
|
||||
"node3d_type:shadow_enabled": $"%ShadowEnabled",
|
||||
"node3d_type:shadow_color": $"%ShadowColor",
|
||||
"node3d_type:omni_range": $"%OmniRange",
|
||||
"node3d_type:spot_range": $"%SpotRange",
|
||||
"node3d_type:spot_angle": $"%SpotAngle",
|
||||
}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
Global.connect("cel_changed", self, "_cel_changed")
|
||||
_cel_changed()
|
||||
var new_object_popup := new_object_menu_button.get_popup()
|
||||
new_object_popup.add_item("Box")
|
||||
new_object_popup.add_item("Sphere")
|
||||
new_object_popup.add_item("Capsule")
|
||||
new_object_popup.add_item("Cylinder")
|
||||
new_object_popup.add_item("Prism")
|
||||
new_object_popup.add_item("Plane")
|
||||
new_object_popup.add_item("Text")
|
||||
new_object_popup.add_item("Directional light")
|
||||
new_object_popup.add_item("Spotlight")
|
||||
new_object_popup.add_item("Omnidirectional (point) light")
|
||||
new_object_popup.add_item("Load model from file")
|
||||
new_object_popup.connect("id_pressed", self, "_add_new_object")
|
||||
for prop in layer_properties:
|
||||
var node: Control = layer_properties[prop]
|
||||
if node is ValueSliderV3:
|
||||
node.connect("value_changed", self, "_layer_property_vector3_changed", [prop])
|
||||
elif node is Range:
|
||||
node.connect("value_changed", self, "_layer_property_value_changed", [prop])
|
||||
elif node is OptionButton:
|
||||
node.connect("item_selected", self, "_layer_property_item_selected", [prop])
|
||||
elif node is ColorPickerButton:
|
||||
node.connect("color_changed", self, "_layer_property_color_changed", [prop])
|
||||
for prop in object_properties:
|
||||
var node: Control = object_properties[prop]
|
||||
if node is ValueSliderV3:
|
||||
node.connect("value_changed", self, "_object_property_vector3_changed", [prop])
|
||||
elif node is ValueSliderV2:
|
||||
var property_path: String = prop
|
||||
if property_path.ends_with("v2"):
|
||||
property_path = property_path.replace("v2", "")
|
||||
node.connect("value_changed", self, "_object_property_vector2_changed", [property_path])
|
||||
elif node is Range:
|
||||
node.connect("value_changed", self, "_object_property_value_changed", [prop])
|
||||
elif node is OptionButton:
|
||||
node.connect("item_selected", self, "_object_property_item_selected", [prop])
|
||||
elif node is ColorPickerButton:
|
||||
node.connect("color_changed", self, "_object_property_color_changed", [prop])
|
||||
elif node is CheckBox:
|
||||
node.connect("toggled", self, "_object_property_toggled", [prop])
|
||||
elif node is LineEdit:
|
||||
node.connect("text_changed", self, "_object_property_text_changed", [prop])
|
||||
|
||||
|
||||
func draw_start(position: Vector2) -> void:
|
||||
if not cel.layer.can_layer_get_drawn():
|
||||
return
|
||||
var found_cel := false
|
||||
for frame_layer in Global.current_project.selected_cels:
|
||||
if cel == Global.current_project.frames[frame_layer[0]].cels[frame_layer[1]]:
|
||||
found_cel = true
|
||||
if not found_cel:
|
||||
return
|
||||
|
||||
if is_instance_valid(_hovering):
|
||||
cel.selected = _hovering
|
||||
_dragging = true
|
||||
_prev_mouse_pos = position
|
||||
else:
|
||||
# We're not hovering
|
||||
if is_instance_valid(cel.selected):
|
||||
# If we're not clicking on a gizmo, unselect
|
||||
if cel.selected.applying_gizmos == Cel3DObject.Gizmos.NONE:
|
||||
cel.selected = null
|
||||
else:
|
||||
_dragging = true
|
||||
_prev_mouse_pos = position
|
||||
|
||||
|
||||
func draw_move(position: Vector2) -> void:
|
||||
var camera: Camera = cel.camera
|
||||
if _dragging:
|
||||
_has_been_dragged = true
|
||||
var proj_mouse_pos := camera.project_position(position, camera.translation.z)
|
||||
var proj_prev_mouse_pos := camera.project_position(_prev_mouse_pos, camera.translation.z)
|
||||
cel.selected.change_transform(proj_mouse_pos, proj_prev_mouse_pos)
|
||||
_prev_mouse_pos = position
|
||||
|
||||
|
||||
func draw_end(_position: Vector2) -> void:
|
||||
_dragging = false
|
||||
if is_instance_valid(cel.selected) and _has_been_dragged:
|
||||
cel.selected.finish_changing_property()
|
||||
_has_been_dragged = false
|
||||
|
||||
|
||||
func cursor_move(position: Vector2) -> void:
|
||||
.cursor_move(position)
|
||||
# Hover logic
|
||||
var camera: Camera = cel.camera
|
||||
var ray_from := camera.project_ray_origin(position)
|
||||
var ray_to := ray_from + camera.project_ray_normal(position) * 20
|
||||
var space_state := camera.get_world().direct_space_state
|
||||
var selection := space_state.intersect_ray(ray_from, ray_to)
|
||||
if selection.empty():
|
||||
if is_instance_valid(_hovering):
|
||||
_hovering.unhover()
|
||||
_hovering = null
|
||||
else:
|
||||
if is_instance_valid(_hovering):
|
||||
_hovering.unhover()
|
||||
_hovering = selection["collider"].get_parent()
|
||||
_hovering.hover()
|
||||
|
||||
|
||||
func _on_ObjectOptionButton_item_selected(index: int) -> void:
|
||||
if not cel is Cel3D:
|
||||
return
|
||||
var id := object_option_button.get_item_id(index) - 1
|
||||
var object := cel.get_object_from_id(id)
|
||||
if not is_instance_valid(object):
|
||||
cel.selected = null
|
||||
return
|
||||
cel.selected = object
|
||||
|
||||
|
||||
func _cel_changed() -> void:
|
||||
if not Global.current_project.get_current_cel() is Cel3D:
|
||||
get_child(0).visible = false # Just to ensure that the content of the tool is hidden
|
||||
return
|
||||
get_child(0).visible = true
|
||||
cel = Global.current_project.get_current_cel()
|
||||
cel.selected = null
|
||||
var layer: Layer3D = cel.layer
|
||||
if not layer.is_connected("property_changed", self, "_set_layer_node_values"):
|
||||
layer.connect("property_changed", self, "_set_layer_node_values")
|
||||
layer.connect("objects_changed", self, "_fill_object_option_button")
|
||||
if not cel.is_connected("selected_object", self, "_selected_object"):
|
||||
cel.connect("selected_object", self, "_selected_object")
|
||||
layer_options.visible = true
|
||||
object_options.visible = false
|
||||
_set_layer_node_values()
|
||||
_fill_object_option_button()
|
||||
|
||||
|
||||
func _add_new_object(id: int) -> void:
|
||||
if id == Cel3DObject.Type.IMPORTED:
|
||||
load_model_dialog.popup_centered()
|
||||
Global.dialog_open(true)
|
||||
else:
|
||||
cel.layer.add_object(id)
|
||||
|
||||
|
||||
func _on_RemoveObject_pressed() -> void:
|
||||
if is_instance_valid(cel.selected):
|
||||
cel.selected.delete()
|
||||
cel.selected = null
|
||||
|
||||
|
||||
func _selected_object(object: Cel3DObject) -> void:
|
||||
if is_instance_valid(object):
|
||||
layer_options.visible = false
|
||||
object_options.visible = true
|
||||
remove_object.disabled = false
|
||||
for prop in object_properties: # Hide irrelevant nodes
|
||||
var node: Control = object_properties[prop]
|
||||
var property_path: String = prop
|
||||
if property_path.ends_with("v2"):
|
||||
property_path = property_path.replace("v2", "")
|
||||
var prev_node: Control = node.get_parent().get_child(node.get_index() - 1)
|
||||
var property = object.get_indexed(property_path)
|
||||
var property_exists: bool = property != null
|
||||
# Differentiate between the mesh size of a box/prism (Vector3) and a plane (Vector2)
|
||||
if node is ValueSliderV3 and typeof(property) != TYPE_VECTOR3:
|
||||
property_exists = false
|
||||
elif node is ValueSliderV2 and typeof(property) != TYPE_VECTOR2:
|
||||
property_exists = false
|
||||
prev_node.visible = property_exists
|
||||
node.visible = property_exists
|
||||
mesh_options.visible = object.node3d_type is MeshInstance
|
||||
light_options.visible = object.node3d_type is Light
|
||||
_set_object_node_values()
|
||||
if not object.is_connected("property_changed", self, "_set_object_node_values"):
|
||||
object.connect("property_changed", self, "_set_object_node_values")
|
||||
object_option_button.select(object_option_button.get_item_index(object.id + 1))
|
||||
else:
|
||||
layer_options.visible = true
|
||||
object_options.visible = false
|
||||
remove_object.disabled = true
|
||||
object_option_button.select(0)
|
||||
|
||||
|
||||
func _set_layer_node_values() -> void:
|
||||
can_start_timer = false
|
||||
_set_node_values(cel, layer_properties)
|
||||
can_start_timer = true
|
||||
|
||||
|
||||
func _set_object_node_values() -> void:
|
||||
var object: Cel3DObject = cel.selected
|
||||
if not is_instance_valid(object):
|
||||
return
|
||||
can_start_timer = false
|
||||
_set_node_values(object, object_properties)
|
||||
can_start_timer = true
|
||||
|
||||
|
||||
func _set_node_values(to_edit: Object, properties: Dictionary) -> void:
|
||||
for prop in properties:
|
||||
var property_path: String = prop
|
||||
if property_path.ends_with("v2"):
|
||||
property_path = property_path.replace("v2", "")
|
||||
var value = to_edit.get_indexed(property_path)
|
||||
if value == null:
|
||||
continue
|
||||
if "scale" in prop:
|
||||
value *= 100
|
||||
var node: Control = properties[prop]
|
||||
if node is Range or node is ValueSliderV3 or node is ValueSliderV2:
|
||||
if typeof(node.value) != typeof(value) and typeof(value) != TYPE_INT:
|
||||
continue
|
||||
node.value = value
|
||||
elif node is OptionButton:
|
||||
node.selected = value
|
||||
elif node is ColorPickerButton:
|
||||
node.color = value
|
||||
elif node is CheckBox:
|
||||
node.pressed = value
|
||||
elif node is LineEdit:
|
||||
node.text = value
|
||||
|
||||
|
||||
func _set_value_from_node(to_edit: Object, value, prop: String) -> void:
|
||||
if "mesh_" in prop:
|
||||
prop = prop.replace("mesh_", "")
|
||||
to_edit = to_edit.node3d_type.mesh
|
||||
if "scale" in prop:
|
||||
value /= 100
|
||||
to_edit.set_indexed(prop, value)
|
||||
|
||||
|
||||
func _layer_property_vector3_changed(value: Vector3, prop: String) -> void:
|
||||
_set_value_from_node(cel, value, prop)
|
||||
_value_handle_change()
|
||||
Global.canvas.gizmos_3d.update()
|
||||
|
||||
|
||||
func _layer_property_value_changed(value: float, prop: String) -> void:
|
||||
_set_value_from_node(cel, value, prop)
|
||||
_value_handle_change()
|
||||
Global.canvas.gizmos_3d.update()
|
||||
|
||||
|
||||
func _layer_property_item_selected(value: int, prop: String) -> void:
|
||||
_set_value_from_node(cel, value, prop)
|
||||
_value_handle_change()
|
||||
Global.canvas.gizmos_3d.update()
|
||||
|
||||
|
||||
func _layer_property_color_changed(value: Color, prop: String) -> void:
|
||||
_set_value_from_node(cel, value, prop)
|
||||
_value_handle_change()
|
||||
Global.canvas.gizmos_3d.update()
|
||||
|
||||
|
||||
func _object_property_vector3_changed(value: Vector3, prop: String) -> void:
|
||||
_set_value_from_node(cel.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _object_property_vector2_changed(value: Vector2, prop: String) -> void:
|
||||
_set_value_from_node(cel.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _object_property_value_changed(value: float, prop: String) -> void:
|
||||
_set_value_from_node(cel.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _object_property_item_selected(value: int, prop: String) -> void:
|
||||
_set_value_from_node(cel.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _object_property_color_changed(value: Color, prop: String) -> void:
|
||||
_set_value_from_node(cel.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _object_property_toggled(value: bool, prop: String) -> void:
|
||||
_set_value_from_node(cel.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _object_property_text_changed(value: String, prop: String) -> void:
|
||||
_set_value_from_node(cel.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _value_handle_change() -> void:
|
||||
if can_start_timer:
|
||||
undo_redo_timer.start()
|
||||
|
||||
|
||||
func _fill_object_option_button() -> void:
|
||||
if not cel is Cel3D:
|
||||
return
|
||||
var layer: Layer3D = cel.layer
|
||||
object_option_button.clear()
|
||||
object_option_button.add_item("None", 0)
|
||||
for id in layer.objects:
|
||||
var item_name: String = Cel3DObject.Type.keys()[layer.objects[id]]
|
||||
object_option_button.add_item(item_name, id + 1)
|
||||
|
||||
|
||||
func _on_UndoRedoTimer_timeout() -> void:
|
||||
if is_instance_valid(cel.selected):
|
||||
cel.selected.finish_changing_property()
|
||||
else:
|
||||
var new_properties := cel.serialize_layer_properties()
|
||||
cel.layer.change_properties(new_properties)
|
||||
|
||||
|
||||
func _on_LoadModelDialog_files_selected(paths: PoolStringArray) -> void:
|
||||
for path in paths:
|
||||
cel.layer.add_object(Cel3DObject.Type.IMPORTED, true, path)
|
||||
|
||||
|
||||
func _on_LoadModelDialog_popup_hide() -> void:
|
||||
Global.dialog_open(false)
|
1358
src/Tools/3DShapeEdit.tscn
Normal file
1358
src/Tools/3DShapeEdit.tscn
Normal file
File diff suppressed because it is too large
Load diff
|
@ -261,7 +261,7 @@ func _get_selected_draw_images() -> Array: # Array of Images
|
|||
for cel_index in project.selected_cels:
|
||||
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
|
||||
if project.layers[cel_index[1]].can_layer_get_drawn():
|
||||
images.append(cel.image)
|
||||
images.append(cel.get_image())
|
||||
return images
|
||||
|
||||
|
||||
|
|
823
src/UI/3D Options.tscn
Normal file
823
src/UI/3D Options.tscn
Normal file
|
@ -0,0 +1,823 @@
|
|||
[gd_scene load_steps=7 format=2]
|
||||
|
||||
[ext_resource path="res://src/UI/Options3D.gd" type="Script" id=1]
|
||||
[ext_resource path="res://src/UI/Nodes/ValueSlider.tscn" type="PackedScene" id=2]
|
||||
[ext_resource path="res://src/UI/Nodes/ValueSliderV2.tscn" type="PackedScene" id=3]
|
||||
[ext_resource path="res://src/UI/Nodes/CollapsibleContainer.gd" type="Script" id=4]
|
||||
[ext_resource path="res://src/UI/Nodes/ValueSliderV3.tscn" type="PackedScene" id=5]
|
||||
[ext_resource path="res://src/UI/Nodes/ValueSlider.gd" type="Script" id=6]
|
||||
|
||||
[node name="3D Options" type="PanelContainer"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
margin_left = 7.0
|
||||
margin_top = 7.0
|
||||
margin_right = 1273.0
|
||||
margin_bottom = 713.0
|
||||
|
||||
[node name="HeaderHBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 14.0
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HeaderHBoxContainer"]
|
||||
margin_right = 73.0
|
||||
margin_bottom = 14.0
|
||||
theme_type_variation = "Header"
|
||||
text = "3D Options"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/HeaderHBoxContainer"]
|
||||
margin_left = 77.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 14.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="SelectedObjectontainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
margin_top = 18.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 38.0
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/SelectedObjectontainer"]
|
||||
margin_top = 3.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 17.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Selected object"
|
||||
|
||||
[node name="ObjectOptionButton" type="OptionButton" parent="VBoxContainer/SelectedObjectontainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 635.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "None"
|
||||
items = [ "None", null, false, 0, null ]
|
||||
selected = 0
|
||||
|
||||
[node name="NewObjectMenuButton" type="MenuButton" parent="VBoxContainer"]
|
||||
margin_top = 42.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 62.0
|
||||
mouse_default_cursor_shape = 2
|
||||
text = "Add new object"
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer"]
|
||||
margin_top = 66.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 706.0
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/ScrollContainer"]
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 640.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="LayerOptions" type="VBoxContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 640.0
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="CameraOptions" type="VBoxContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer/LayerOptions"]
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 128.0
|
||||
theme_type_variation = "CollapsibleContainer"
|
||||
script = ExtResource( 4 )
|
||||
text = "Camera"
|
||||
visible_content = true
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer/LayerOptions/CameraOptions"]
|
||||
margin_top = 24.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 128.0
|
||||
columns = 2
|
||||
|
||||
[node name="ProjectionLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/LayerOptions/CameraOptions/GridContainer"]
|
||||
margin_top = 3.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 17.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Projection"
|
||||
|
||||
[node name="ProjectionOptionButton" type="OptionButton" parent="VBoxContainer/ScrollContainer/VBoxContainer/LayerOptions/CameraOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 635.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 20.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Perspective"
|
||||
items = [ "Perspective", null, false, 0, null, "Orthogonal", null, false, 1, null, "Frustum", null, false, 2, null ]
|
||||
selected = 0
|
||||
|
||||
[node name="RotationLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/LayerOptions/CameraOptions/GridContainer"]
|
||||
margin_top = 24.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 38.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 0
|
||||
text = "Rotation"
|
||||
|
||||
[node name="CameraRotation" parent="VBoxContainer/ScrollContainer/VBoxContainer/LayerOptions/CameraOptions/GridContainer" instance=ExtResource( 5 )]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 635.0
|
||||
margin_top = 24.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 104.0
|
||||
size_flags_horizontal = 3
|
||||
min_value = Vector3( -180, -180, -180 )
|
||||
max_value = Vector3( 180, 180, 180 )
|
||||
step = 0.1
|
||||
suffix_x = "°"
|
||||
suffix_y = "°"
|
||||
suffix_z = "°"
|
||||
|
||||
[node name="EnvironmentOptions" type="VBoxContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer/LayerOptions"]
|
||||
margin_top = 132.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 152.0
|
||||
theme_type_variation = "CollapsibleContainer"
|
||||
script = ExtResource( 4 )
|
||||
text = "Environment"
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer/LayerOptions/EnvironmentOptions"]
|
||||
visible = false
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 48.0
|
||||
columns = 2
|
||||
|
||||
[node name="AmbientColorLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/LayerOptions/EnvironmentOptions/GridContainer"]
|
||||
margin_top = 3.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 17.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Ambient color"
|
||||
|
||||
[node name="AmbientColorPickerButton" type="ColorPickerButton" parent="VBoxContainer/ScrollContainer/VBoxContainer/LayerOptions/EnvironmentOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 635.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 20.0
|
||||
size_flags_horizontal = 3
|
||||
edit_alpha = false
|
||||
|
||||
[node name="AmbientEnergyLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/LayerOptions/EnvironmentOptions/GridContainer"]
|
||||
margin_top = 29.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 43.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Ambient color energy"
|
||||
|
||||
[node name="AmbientEnergy" parent="VBoxContainer/ScrollContainer/VBoxContainer/LayerOptions/EnvironmentOptions/GridContainer" instance=ExtResource( 2 )]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 635.0
|
||||
margin_top = 24.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 48.0
|
||||
max_value = 16.0
|
||||
step = 0.01
|
||||
value = 1.0
|
||||
allow_greater = true
|
||||
|
||||
[node name="ObjectOptions" type="VBoxContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
margin_top = 334.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 664.0
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="TransformOptions" type="VBoxContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions"]
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 272.0
|
||||
theme_type_variation = "CollapsibleContainer"
|
||||
script = ExtResource( 4 )
|
||||
text = "Transform"
|
||||
visible_content = true
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/TransformOptions"]
|
||||
margin_top = 24.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 272.0
|
||||
columns = 2
|
||||
|
||||
[node name="PositionLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/TransformOptions/GridContainer"]
|
||||
margin_right = 631.0
|
||||
margin_bottom = 80.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
text = "Position"
|
||||
|
||||
[node name="ObjectPosition" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/TransformOptions/GridContainer" instance=ExtResource( 5 )]
|
||||
unique_name_in_owner = true
|
||||
size_flags_horizontal = 3
|
||||
min_value = Vector3( -20, -20, -20 )
|
||||
max_value = Vector3( 20, 20, 20 )
|
||||
step = 0.01
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
suffix_x = "m"
|
||||
suffix_y = "m"
|
||||
suffix_z = "m"
|
||||
|
||||
[node name="RotationLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/TransformOptions/GridContainer"]
|
||||
margin_top = 84.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 164.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
text = "Rotation"
|
||||
|
||||
[node name="ObjectRotation" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/TransformOptions/GridContainer" instance=ExtResource( 5 )]
|
||||
unique_name_in_owner = true
|
||||
margin_top = -226.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = -146.0
|
||||
size_flags_horizontal = 3
|
||||
min_value = Vector3( -180, -180, -180 )
|
||||
max_value = Vector3( 180, 180, 180 )
|
||||
step = 0.1
|
||||
suffix_x = "°"
|
||||
suffix_y = "°"
|
||||
suffix_z = "°"
|
||||
|
||||
[node name="ScaleLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/TransformOptions/GridContainer"]
|
||||
margin_top = 168.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 248.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
text = "Scale"
|
||||
|
||||
[node name="ObjectScale" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/TransformOptions/GridContainer" instance=ExtResource( 5 )]
|
||||
unique_name_in_owner = true
|
||||
margin_bottom = 80.0
|
||||
size_flags_horizontal = 3
|
||||
value = Vector3( 100, 100, 100 )
|
||||
step = 0.01
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
show_ratio = true
|
||||
suffix_x = "%"
|
||||
suffix_y = "%"
|
||||
suffix_z = "%"
|
||||
|
||||
[node name="MeshOptions" type="VBoxContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions"]
|
||||
unique_name_in_owner = true
|
||||
margin_top = 276.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 296.0
|
||||
theme_type_variation = "CollapsibleContainer"
|
||||
script = ExtResource( 4 )
|
||||
text = "Mesh"
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions"]
|
||||
visible = false
|
||||
margin_top = 24.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 104.0
|
||||
columns = 2
|
||||
|
||||
[node name="MeshSizeLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Size"
|
||||
|
||||
[node name="MeshSize" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer" instance=ExtResource( 5 )]
|
||||
unique_name_in_owner = true
|
||||
size_flags_horizontal = 3
|
||||
max_value = Vector3( 10, 10, 10 )
|
||||
step = 0.01
|
||||
allow_greater = true
|
||||
show_ratio = true
|
||||
|
||||
[node name="MeshSizeLabel2" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Size"
|
||||
|
||||
[node name="MeshSizeV2" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer" instance=ExtResource( 3 )]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 52.0
|
||||
size_flags_horizontal = 3
|
||||
max_value = Vector2( 10, 10 )
|
||||
allow_greater = true
|
||||
show_ratio = true
|
||||
snap_step = 0.01
|
||||
|
||||
[node name="MeshCenterOffsetLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Center offset"
|
||||
|
||||
[node name="MeshCenterOffset" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer" instance=ExtResource( 5 )]
|
||||
unique_name_in_owner = true
|
||||
size_flags_horizontal = 3
|
||||
max_value = Vector3( 10, 10, 10 )
|
||||
step = 0.01
|
||||
allow_greater = true
|
||||
allow_lesser = true
|
||||
show_ratio = true
|
||||
|
||||
[node name="MeshLeftToRightLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Left to right"
|
||||
|
||||
[node name="MeshLeftToRight" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
min_value = -2.0
|
||||
max_value = 2.0
|
||||
step = 0.1
|
||||
value = 0.5
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="MeshRadiusLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Radius"
|
||||
|
||||
[node name="MeshRadius" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
min_value = 0.001
|
||||
max_value = 10.0
|
||||
step = 0.01
|
||||
value = 1.0
|
||||
allow_greater = true
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="MeshHeightLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Height"
|
||||
|
||||
[node name="MeshHeight" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
min_value = 0.001
|
||||
max_value = 10.0
|
||||
step = 0.01
|
||||
value = 2.0
|
||||
allow_greater = true
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="MeshRadialSegmentsLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Radial segments"
|
||||
|
||||
[node name="MeshRadialSegments" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
min_value = 4.0
|
||||
max_value = 640.0
|
||||
value = 64.0
|
||||
allow_greater = true
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="MeshRingsLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Rings"
|
||||
|
||||
[node name="MeshRings" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
min_value = 1.0
|
||||
max_value = 320.0
|
||||
value = 32.0
|
||||
allow_greater = true
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="MeshIsHemisphereLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Is hemisphere"
|
||||
|
||||
[node name="MeshIsHemisphere" type="CheckBox" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 24.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "On"
|
||||
|
||||
[node name="MeshMidHeightLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Mid height"
|
||||
|
||||
[node name="MeshMidHeight" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
min_value = 0.001
|
||||
max_value = 10.0
|
||||
step = 0.01
|
||||
value = 1.0
|
||||
allow_greater = true
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="MeshTopRadiusLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Top radius"
|
||||
|
||||
[node name="MeshTopRadius" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
max_value = 10.0
|
||||
step = 0.01
|
||||
value = 1.0
|
||||
allow_greater = true
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="MeshBottomRadiusLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Bottom radius"
|
||||
|
||||
[node name="MeshBottomRadius" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
max_value = 10.0
|
||||
step = 0.01
|
||||
value = 1.0
|
||||
allow_greater = true
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="MeshTextLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Text"
|
||||
|
||||
[node name="MeshText" type="LineEdit" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 58.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="MeshPixelSizeLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Pixel size"
|
||||
|
||||
[node name="MeshPixelSize" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
min_value = 0.001
|
||||
max_value = 10.0
|
||||
step = 0.001
|
||||
value = 0.01
|
||||
allow_greater = true
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
snap_step = 0.01
|
||||
|
||||
[node name="MeshCurveStepLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Curve step"
|
||||
|
||||
[node name="MeshCurveStep" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
min_value = 0.1
|
||||
max_value = 10.0
|
||||
step = 0.1
|
||||
value = 0.5
|
||||
allow_greater = true
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="MeshHorizontalAlignmentLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
margin_top = -583.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = -569.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Horizontal alignment"
|
||||
|
||||
[node name="MeshHorizontalAlignment" type="OptionButton" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/MeshOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 635.0
|
||||
margin_top = -586.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = -566.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Center"
|
||||
items = [ "Left", null, false, 0, null, "Center", null, false, 1, null, "Right", null, false, 2, null ]
|
||||
selected = 1
|
||||
|
||||
[node name="LightOptions" type="VBoxContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions"]
|
||||
unique_name_in_owner = true
|
||||
margin_top = 276.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 296.0
|
||||
theme_type_variation = "CollapsibleContainer"
|
||||
script = ExtResource( 4 )
|
||||
text = "Light"
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions"]
|
||||
visible = false
|
||||
margin_top = 24.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 104.0
|
||||
columns = 2
|
||||
|
||||
[node name="LightColorLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
margin_top = -499.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = -485.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Color"
|
||||
|
||||
[node name="LightColor" type="ColorPickerButton" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 635.0
|
||||
margin_top = -502.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = -482.0
|
||||
size_flags_horizontal = 3
|
||||
color = Color( 1, 1, 1, 1 )
|
||||
edit_alpha = false
|
||||
|
||||
[node name="LightEnergyLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
margin_top = -473.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = -459.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Energy"
|
||||
|
||||
[node name="LightEnergy" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer" instance=ExtResource( 2 )]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 635.0
|
||||
margin_top = -478.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = -454.0
|
||||
max_value = 16.0
|
||||
step = 0.01
|
||||
value = 1.0
|
||||
allow_greater = true
|
||||
|
||||
[node name="LightNegativeLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Negative"
|
||||
|
||||
[node name="LightNegative" type="CheckBox" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 47.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "On"
|
||||
|
||||
[node name="ShadowEnabledLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Shadow"
|
||||
|
||||
[node name="ShadowEnabled" type="CheckBox" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 47.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "On"
|
||||
|
||||
[node name="ShadowColorLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
margin_top = -499.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = -485.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Shadow color"
|
||||
|
||||
[node name="ShadowColor" type="ColorPickerButton" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 635.0
|
||||
margin_top = -502.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = -482.0
|
||||
size_flags_horizontal = 3
|
||||
edit_alpha = false
|
||||
|
||||
[node name="OmniRangeLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Range"
|
||||
|
||||
[node name="OmniRange" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
max_value = 4096.0
|
||||
step = 0.01
|
||||
value = 5.0
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="SpotRangeLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Range"
|
||||
|
||||
[node name="SpotRange" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
max_value = 4096.0
|
||||
step = 0.01
|
||||
value = 5.0
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="SpotAngleLabel" type="Label" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Angle"
|
||||
|
||||
[node name="SpotAngle" type="TextureProgress" parent="VBoxContainer/ScrollContainer/VBoxContainer/ObjectOptions/LightOptions/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
max_value = 180.0
|
||||
step = 0.01
|
||||
value = 45.0
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="UndoRedoTimer" type="Timer" parent="."]
|
||||
wait_time = 0.2
|
||||
one_shot = true
|
||||
|
||||
[node name="LoadModelDialog" type="FileDialog" parent="."]
|
||||
margin_top = 590.0
|
||||
margin_right = 515.0
|
||||
margin_bottom = 938.0
|
||||
rect_min_size = Vector2( 515, 348 )
|
||||
window_title = "Open File(s)"
|
||||
resizable = true
|
||||
mode = 1
|
||||
access = 2
|
||||
filters = PoolStringArray( "*.obj" )
|
||||
show_hidden_files = true
|
||||
|
||||
[connection signal="item_selected" from="VBoxContainer/SelectedObjectontainer/ObjectOptionButton" to="." method="_on_ObjectOptionButton_item_selected"]
|
||||
[connection signal="timeout" from="UndoRedoTimer" to="." method="_on_UndoRedoTimer_timeout"]
|
||||
[connection signal="files_selected" from="LoadModelDialog" to="." method="_on_LoadModelDialog_files_selected"]
|
||||
[connection signal="popup_hide" from="LoadModelDialog" to="." method="_on_LoadModelDialog_popup_hide"]
|
|
@ -18,6 +18,7 @@ onready var crop_rect: CropRect = $CropRect
|
|||
onready var indicators = $Indicators
|
||||
onready var previews = $Previews
|
||||
onready var mouse_guide_container = $MouseGuideContainer
|
||||
onready var gizmos_3d: Node2D = $Gizmos3D
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
@ -67,6 +68,14 @@ func _draw() -> void:
|
|||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if Global.current_project.get_current_cel() is Cel3D and Global.has_focus:
|
||||
for child in get_children():
|
||||
if not child is Viewport:
|
||||
continue
|
||||
var modified_event := event.duplicate()
|
||||
if event is InputEventMouse:
|
||||
modified_event.position = current_pixel.floor()
|
||||
child.input(modified_event)
|
||||
# Don't process anything below if the input isn't a mouse event, or Shift/Ctrl.
|
||||
# This decreases CPU/GPU usage slightly.
|
||||
var get_velocity := false
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=17 format=2]
|
||||
[gd_scene load_steps=18 format=2]
|
||||
|
||||
[ext_resource path="res://src/UI/Canvas/Canvas.gd" type="Script" id=1]
|
||||
[ext_resource path="res://src/UI/Canvas/Grid.gd" type="Script" id=2]
|
||||
|
@ -13,6 +13,7 @@
|
|||
[ext_resource path="res://src/UI/Canvas/MouseGuideContainer.tscn" type="PackedScene" id=11]
|
||||
[ext_resource path="res://src/UI/Canvas/OnionSkinning.gd" type="Script" id=12]
|
||||
[ext_resource path="res://src/UI/Canvas/CropRect.gd" type="Script" id=13]
|
||||
[ext_resource path="res://src/UI/Canvas/Gizmos3D.gd" type="Script" id=14]
|
||||
|
||||
[sub_resource type="CanvasItemMaterial" id=1]
|
||||
blend_mode = 4
|
||||
|
@ -80,3 +81,6 @@ script = ExtResource( 12 )
|
|||
script = ExtResource( 12 )
|
||||
|
||||
[node name="MouseGuideContainer" parent="." instance=ExtResource( 11 )]
|
||||
|
||||
[node name="Gizmos3D" type="Node2D" parent="."]
|
||||
script = ExtResource( 14 )
|
||||
|
|
261
src/UI/Canvas/Gizmos3D.gd
Normal file
261
src/UI/Canvas/Gizmos3D.gd
Normal file
|
@ -0,0 +1,261 @@
|
|||
extends Node2D
|
||||
|
||||
enum { X, Y, Z }
|
||||
|
||||
const ARROW_LENGTH := 14
|
||||
const LIGHT_ARROW_LENGTH := 25
|
||||
const GIZMO_WIDTH := 1.1
|
||||
const SCALE_CIRCLE_LENGTH := 8
|
||||
const SCALE_CIRCLE_RADIUS := 1
|
||||
const CHAR_SCALE := 0.16
|
||||
|
||||
var always_visible := {} # Key = Cel3DObject, Value = Texture
|
||||
var points_per_object := {} # Key = Cel3DObject, Value = PoolVector2Array
|
||||
var selected_color := Color.white
|
||||
var hovered_color := Color.gray
|
||||
|
||||
var gizmos_origin: Vector2
|
||||
var proj_right_local: Vector2
|
||||
var proj_up_local: Vector2
|
||||
var proj_back_local: Vector2
|
||||
# Same vectors as `proj_x_local`, but with a smaller length, for the rotation & scale gizmos
|
||||
var proj_right_local_scale: Vector2
|
||||
var proj_up_local_scale: Vector2
|
||||
var proj_back_local_scale: Vector2
|
||||
var gizmo_pos_x := PoolVector2Array()
|
||||
var gizmo_pos_y := PoolVector2Array()
|
||||
var gizmo_pos_z := PoolVector2Array()
|
||||
var gizmo_rot_x := PoolVector2Array()
|
||||
var gizmo_rot_y := PoolVector2Array()
|
||||
var gizmo_rot_z := PoolVector2Array()
|
||||
var is_rotating := -1
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
set_process_input(false)
|
||||
Global.connect("cel_changed", self, "_cel_changed")
|
||||
Global.camera.connect("zoom_changed", self, "update")
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if not event is InputEventMouseButton:
|
||||
return
|
||||
if points_per_object.empty():
|
||||
return
|
||||
if gizmo_rot_x.empty() or gizmo_pos_y.empty() or gizmo_pos_z.empty():
|
||||
return
|
||||
if not event.button_index == BUTTON_LEFT:
|
||||
return
|
||||
if not Global.current_project.get_current_cel().layer.can_layer_get_drawn():
|
||||
return
|
||||
var pos: Vector2 = Global.canvas.current_pixel - gizmos_origin
|
||||
var selected_obj := _find_selected_object()
|
||||
if not is_instance_valid(selected_obj):
|
||||
return
|
||||
if event.pressed:
|
||||
var draw_scale := Global.camera.zoom * 10
|
||||
# Scale the position based on the zoom, has the same effect as enlarging the shapes
|
||||
pos /= draw_scale
|
||||
# Inflate the rotation polylines by one to make them easier to click
|
||||
var rot_x_offset: PoolVector2Array = Geometry.offset_polyline_2d(gizmo_rot_x, 1)[0]
|
||||
var rot_y_offset: PoolVector2Array = Geometry.offset_polyline_2d(gizmo_rot_y, 1)[0]
|
||||
var rot_z_offset: PoolVector2Array = Geometry.offset_polyline_2d(gizmo_rot_z, 1)[0]
|
||||
|
||||
if Geometry.point_is_inside_triangle(pos, gizmo_pos_x[0], gizmo_pos_x[1], gizmo_pos_x[2]):
|
||||
selected_obj.applying_gizmos = Cel3DObject.Gizmos.X_POS
|
||||
elif Geometry.point_is_inside_triangle(pos, gizmo_pos_y[0], gizmo_pos_y[1], gizmo_pos_y[2]):
|
||||
selected_obj.applying_gizmos = Cel3DObject.Gizmos.Y_POS
|
||||
elif Geometry.point_is_inside_triangle(pos, gizmo_pos_z[0], gizmo_pos_z[1], gizmo_pos_z[2]):
|
||||
selected_obj.applying_gizmos = Cel3DObject.Gizmos.Z_POS
|
||||
elif Geometry.is_point_in_circle(pos, proj_right_local_scale, SCALE_CIRCLE_RADIUS):
|
||||
selected_obj.applying_gizmos = Cel3DObject.Gizmos.X_SCALE
|
||||
elif Geometry.is_point_in_circle(pos, proj_up_local_scale, SCALE_CIRCLE_RADIUS):
|
||||
selected_obj.applying_gizmos = Cel3DObject.Gizmos.Y_SCALE
|
||||
elif Geometry.is_point_in_circle(pos, proj_back_local_scale, SCALE_CIRCLE_RADIUS):
|
||||
selected_obj.applying_gizmos = Cel3DObject.Gizmos.Z_SCALE
|
||||
elif Geometry.is_point_in_polygon(pos, rot_x_offset):
|
||||
selected_obj.applying_gizmos = Cel3DObject.Gizmos.X_ROT
|
||||
is_rotating = X
|
||||
elif Geometry.is_point_in_polygon(pos, rot_y_offset):
|
||||
selected_obj.applying_gizmos = Cel3DObject.Gizmos.Y_ROT
|
||||
is_rotating = Y
|
||||
elif Geometry.is_point_in_polygon(pos, rot_z_offset):
|
||||
selected_obj.applying_gizmos = Cel3DObject.Gizmos.Z_ROT
|
||||
is_rotating = Z
|
||||
else:
|
||||
if selected_obj.applying_gizmos == Cel3DObject.Gizmos.NONE:
|
||||
return
|
||||
selected_obj.applying_gizmos = Cel3DObject.Gizmos.NONE
|
||||
is_rotating = -1
|
||||
update()
|
||||
|
||||
|
||||
func _cel_changed() -> void:
|
||||
update()
|
||||
set_process_input(Global.current_project.get_current_cel() is Cel3D)
|
||||
|
||||
|
||||
func _find_selected_object() -> Cel3DObject:
|
||||
for object in points_per_object:
|
||||
if is_instance_valid(object) and object.selected:
|
||||
return object
|
||||
return null
|
||||
|
||||
|
||||
func add_always_visible(object3d: Cel3DObject, texture: Texture) -> void:
|
||||
always_visible[object3d] = texture
|
||||
update()
|
||||
|
||||
|
||||
func remove_always_visible(object3d: Cel3DObject) -> void:
|
||||
always_visible.erase(object3d)
|
||||
update()
|
||||
|
||||
|
||||
func get_points(camera: Camera, object3d: Cel3DObject) -> void:
|
||||
var debug_mesh := object3d.box_shape.get_debug_mesh()
|
||||
var arrays := debug_mesh.surface_get_arrays(0)
|
||||
var points := PoolVector2Array()
|
||||
for vertex in arrays[ArrayMesh.ARRAY_VERTEX]:
|
||||
var x_vertex: Vector3 = object3d.transform.xform(vertex)
|
||||
var point := camera.unproject_position(x_vertex)
|
||||
points.append(point)
|
||||
points_per_object[object3d] = points
|
||||
if object3d.selected:
|
||||
gizmos_origin = camera.unproject_position(object3d.translation)
|
||||
|
||||
var right: Vector3 = object3d.translation + object3d.transform.basis.x
|
||||
var up: Vector3 = object3d.translation + object3d.transform.basis.y
|
||||
var back: Vector3 = object3d.translation + object3d.transform.basis.z
|
||||
|
||||
var proj_right: Vector2 = object3d.camera.unproject_position(right)
|
||||
var proj_up: Vector2 = object3d.camera.unproject_position(up)
|
||||
var proj_back: Vector2 = object3d.camera.unproject_position(back)
|
||||
|
||||
proj_right_local = proj_right - gizmos_origin
|
||||
proj_up_local = proj_up - gizmos_origin
|
||||
proj_back_local = proj_back - gizmos_origin
|
||||
|
||||
proj_right_local = _resize_vector(proj_right_local, ARROW_LENGTH)
|
||||
proj_up_local = _resize_vector(proj_up_local, ARROW_LENGTH)
|
||||
proj_back_local = _resize_vector(proj_back_local, ARROW_LENGTH)
|
||||
|
||||
proj_right_local_scale = _resize_vector(proj_right_local, SCALE_CIRCLE_LENGTH)
|
||||
proj_up_local_scale = _resize_vector(proj_up_local, SCALE_CIRCLE_LENGTH)
|
||||
proj_back_local_scale = _resize_vector(proj_back_local, SCALE_CIRCLE_LENGTH)
|
||||
|
||||
# Calculate position gizmos (arrows)
|
||||
gizmo_pos_x = _find_arrow(proj_right_local)
|
||||
gizmo_pos_y = _find_arrow(proj_up_local)
|
||||
gizmo_pos_z = _find_arrow(proj_back_local)
|
||||
# Calculate rotation gizmos
|
||||
gizmo_rot_x = _find_curve(proj_up_local, proj_back_local)
|
||||
gizmo_rot_y = _find_curve(proj_right_local, proj_back_local)
|
||||
gizmo_rot_z = _find_curve(proj_right_local, proj_up_local)
|
||||
|
||||
update()
|
||||
|
||||
|
||||
func clear_points(object3d: Cel3DObject) -> void:
|
||||
points_per_object.erase(object3d)
|
||||
update()
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
var draw_scale := Global.camera.zoom * 10
|
||||
for object in always_visible:
|
||||
if not always_visible[object]:
|
||||
continue
|
||||
if not object.find_cel():
|
||||
continue
|
||||
var texture: Texture = always_visible[object]
|
||||
var center := Vector2(8, 8)
|
||||
var pos: Vector2 = object.camera.unproject_position(object.translation)
|
||||
var back: Vector3 = object.translation - object.transform.basis.z
|
||||
var back_proj: Vector2 = object.camera.unproject_position(back) - pos
|
||||
back_proj = _resize_vector(back_proj, LIGHT_ARROW_LENGTH)
|
||||
draw_set_transform(pos, 0, draw_scale / 2)
|
||||
draw_texture(texture, -center)
|
||||
if object.type == Cel3DObject.Type.DIR_LIGHT:
|
||||
draw_line(Vector2.ZERO, back_proj, Color.white)
|
||||
var arrow := _find_arrow(back_proj)
|
||||
_draw_arrow(arrow, Color.white)
|
||||
draw_set_transform_matrix(Transform2D())
|
||||
|
||||
if points_per_object.empty():
|
||||
return
|
||||
for object in points_per_object:
|
||||
if not object.find_cel():
|
||||
if object.selected:
|
||||
object.unselect()
|
||||
continue
|
||||
var points: PoolVector2Array = points_per_object[object]
|
||||
if points.empty():
|
||||
continue
|
||||
if object.selected:
|
||||
# Draw bounding box outline
|
||||
draw_multiline(points, selected_color, 1.0, true)
|
||||
match is_rotating:
|
||||
X:
|
||||
draw_line(gizmos_origin, Global.canvas.current_pixel, Color.red)
|
||||
Y:
|
||||
draw_line(gizmos_origin, Global.canvas.current_pixel, Color.green)
|
||||
Z:
|
||||
draw_line(gizmos_origin, Global.canvas.current_pixel, Color.blue)
|
||||
|
||||
draw_set_transform(gizmos_origin, 0, draw_scale)
|
||||
# Draw position arrows
|
||||
draw_line(Vector2.ZERO, proj_right_local, Color.red)
|
||||
draw_line(Vector2.ZERO, proj_up_local, Color.green)
|
||||
draw_line(Vector2.ZERO, proj_back_local, Color.blue)
|
||||
_draw_arrow(gizmo_pos_x, Color.red)
|
||||
_draw_arrow(gizmo_pos_y, Color.green)
|
||||
_draw_arrow(gizmo_pos_z, Color.blue)
|
||||
|
||||
# Draw rotation curves
|
||||
draw_polyline(gizmo_rot_x, Color.red, GIZMO_WIDTH)
|
||||
draw_polyline(gizmo_rot_y, Color.green, GIZMO_WIDTH)
|
||||
draw_polyline(gizmo_rot_z, Color.blue, GIZMO_WIDTH)
|
||||
|
||||
# Draw scale circles
|
||||
draw_circle(proj_right_local_scale, SCALE_CIRCLE_RADIUS, Color.red)
|
||||
draw_circle(proj_up_local_scale, SCALE_CIRCLE_RADIUS, Color.green)
|
||||
draw_circle(proj_back_local_scale, SCALE_CIRCLE_RADIUS, Color.blue)
|
||||
|
||||
# Draw X, Y, Z characters on top of the scale circles
|
||||
var font: Font = Global.control.theme.default_font
|
||||
var font_height := font.get_height()
|
||||
var char_position := Vector2(-font_height, font_height) * CHAR_SCALE / 4 * draw_scale
|
||||
draw_set_transform(gizmos_origin + char_position, 0, draw_scale * CHAR_SCALE)
|
||||
draw_char(font, proj_right_local_scale / CHAR_SCALE, "X", "")
|
||||
draw_char(font, proj_up_local_scale / CHAR_SCALE, "Y", "")
|
||||
draw_char(font, proj_back_local_scale / CHAR_SCALE, "Z", "")
|
||||
draw_set_transform_matrix(Transform2D())
|
||||
elif object.hovered:
|
||||
draw_multiline(points, hovered_color, 1.0, true)
|
||||
|
||||
|
||||
func _resize_vector(v: Vector2, l: float) -> Vector2:
|
||||
return (v.normalized() * l).limit_length(v.length())
|
||||
|
||||
|
||||
func _find_curve(a: Vector2, b: Vector2) -> PoolVector2Array:
|
||||
var curve2d := Curve2D.new()
|
||||
curve2d.bake_interval = 1
|
||||
var control := b.linear_interpolate(a, 0.5)
|
||||
a = _resize_vector(a, SCALE_CIRCLE_LENGTH)
|
||||
b = _resize_vector(b, SCALE_CIRCLE_LENGTH)
|
||||
control = control.normalized() * sqrt(pow(a.length() / 4, 2) * 2) # Thank you Pythagoras
|
||||
curve2d.add_point(a, Vector2.ZERO, control)
|
||||
curve2d.add_point(b, control)
|
||||
return curve2d.get_baked_points()
|
||||
|
||||
|
||||
func _find_arrow(a: Vector2, tilt := 0.5) -> PoolVector2Array:
|
||||
var b := a + Vector2(-tilt, 1).rotated(a.angle() + PI / 2) * 2
|
||||
var c := a + Vector2(tilt, 1).rotated(a.angle() + PI / 2) * 2
|
||||
return PoolVector2Array([a, b, c])
|
||||
|
||||
|
||||
func _draw_arrow(triangle: PoolVector2Array, color: Color) -> void:
|
||||
draw_primitive(triangle, [color, color, color], [])
|
193
src/UI/Nodes/ValueSliderV3.gd
Normal file
193
src/UI/Nodes/ValueSliderV3.gd
Normal file
|
@ -0,0 +1,193 @@
|
|||
tool
|
||||
class_name ValueSliderV3
|
||||
extends HBoxContainer
|
||||
|
||||
signal value_changed(value)
|
||||
signal ratio_toggled(button_pressed)
|
||||
|
||||
export var editable := true setget _set_editable
|
||||
export var value := Vector3.ZERO setget _set_value
|
||||
export var min_value := Vector3.ZERO setget _set_min_value
|
||||
export var max_value := Vector3(100.0, 100.0, 100.0) setget _set_max_value
|
||||
export var step := 1.0 setget _set_step
|
||||
export var allow_greater := false setget _set_allow_greater
|
||||
export var allow_lesser := false setget _set_allow_lesser
|
||||
export var show_ratio := false setget _set_show_ratio
|
||||
export(int, 1, 2) var grid_columns := 1 setget _set_grid_columns
|
||||
export var slider_min_size := Vector2(32, 24) setget _set_slider_min_size
|
||||
export var snap_step := 1.0 setget _set_snap_step
|
||||
export var snap_by_default := false setget _set_snap_by_default
|
||||
export var prefix_x := "X:" setget _set_prefix_x
|
||||
export var prefix_y := "Y:" setget _set_prefix_y
|
||||
export var prefix_z := "Z:" setget _set_prefix_z
|
||||
export var suffix_x := "" setget _set_suffix_x
|
||||
export var suffix_y := "" setget _set_suffix_y
|
||||
export var suffix_z := "" setget _set_suffix_z
|
||||
|
||||
var ratio := Vector3.ONE
|
||||
var _locked_ratio := false
|
||||
var _can_emit_signal := true
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not Engine.editor_hint: # Pixelorama specific code
|
||||
$Ratio.modulate = Global.modulate_icon_color
|
||||
|
||||
|
||||
func get_sliders() -> Array:
|
||||
return [$GridContainer/X, $GridContainer/Y, $GridContainer/Z]
|
||||
|
||||
|
||||
func press_ratio_button(pressed: bool) -> void:
|
||||
$"%RatioButton".pressed = pressed
|
||||
|
||||
|
||||
# Greatest common divisor
|
||||
func _gcd(a: int, b: int) -> int:
|
||||
return a if b == 0 else _gcd(b, a % b)
|
||||
|
||||
|
||||
func _on_X_value_changed(val: float) -> void:
|
||||
value.x = val
|
||||
if _locked_ratio:
|
||||
self.value.y = max(min_value.y, (value.x / ratio.x) * ratio.y)
|
||||
self.value.z = max(min_value.z, (value.x / ratio.x) * ratio.z)
|
||||
if _can_emit_signal:
|
||||
emit_signal("value_changed", value)
|
||||
|
||||
|
||||
func _on_Y_value_changed(val: float) -> void:
|
||||
value.y = val
|
||||
if _locked_ratio:
|
||||
self.value.x = max(min_value.x, (value.y / ratio.y) * ratio.x)
|
||||
self.value.z = max(min_value.z, (value.y / ratio.y) * ratio.z)
|
||||
if _can_emit_signal:
|
||||
emit_signal("value_changed", value)
|
||||
|
||||
|
||||
func _on_Z_value_changed(val: float) -> void:
|
||||
value.z = val
|
||||
if _locked_ratio:
|
||||
self.value.x = max(min_value.x, (value.z / ratio.z) * ratio.x)
|
||||
self.value.y = max(min_value.y, (value.z / ratio.z) * ratio.y)
|
||||
if _can_emit_signal:
|
||||
emit_signal("value_changed", value)
|
||||
|
||||
|
||||
func _on_RatioButton_toggled(button_pressed: bool) -> void:
|
||||
_locked_ratio = button_pressed
|
||||
var divisor := _gcd(value.x, _gcd(value.y, value.z))
|
||||
if divisor == 0:
|
||||
ratio = Vector3.ONE
|
||||
else:
|
||||
ratio = value / divisor
|
||||
emit_signal("ratio_toggled", button_pressed)
|
||||
|
||||
|
||||
# Setters
|
||||
|
||||
|
||||
func _set_editable(val: bool) -> void:
|
||||
editable = val
|
||||
for slider in get_sliders():
|
||||
slider.editable = val
|
||||
$"%RatioButton".disabled = not val
|
||||
|
||||
|
||||
func _set_value(val: Vector3) -> void:
|
||||
value = val
|
||||
_can_emit_signal = false
|
||||
$GridContainer/X.value = value.x
|
||||
$GridContainer/Y.value = value.y
|
||||
$GridContainer/Z.value = value.z
|
||||
_can_emit_signal = true
|
||||
|
||||
|
||||
func _set_min_value(val: Vector3) -> void:
|
||||
min_value = val
|
||||
$GridContainer/X.min_value = val.x
|
||||
$GridContainer/Y.min_value = val.y
|
||||
$GridContainer/Z.min_value = val.z
|
||||
|
||||
|
||||
func _set_max_value(val: Vector3) -> void:
|
||||
max_value = val
|
||||
$GridContainer/X.max_value = val.x
|
||||
$GridContainer/Y.max_value = val.y
|
||||
$GridContainer/Z.max_value = val.z
|
||||
|
||||
|
||||
func _set_step(val: float) -> void:
|
||||
step = val
|
||||
for slider in get_sliders():
|
||||
slider.step = val
|
||||
|
||||
|
||||
func _set_allow_greater(val: bool) -> void:
|
||||
allow_greater = val
|
||||
for slider in get_sliders():
|
||||
slider.allow_greater = val
|
||||
|
||||
|
||||
func _set_allow_lesser(val: bool) -> void:
|
||||
allow_lesser = val
|
||||
for slider in get_sliders():
|
||||
slider.allow_lesser = val
|
||||
|
||||
|
||||
func _set_show_ratio(val: bool) -> void:
|
||||
show_ratio = val
|
||||
$Ratio.visible = val
|
||||
|
||||
|
||||
func _set_grid_columns(val: int) -> void:
|
||||
grid_columns = val
|
||||
$GridContainer.columns = val
|
||||
|
||||
|
||||
func _set_slider_min_size(val: Vector2) -> void:
|
||||
slider_min_size = val
|
||||
for slider in get_sliders():
|
||||
slider.rect_min_size = val
|
||||
|
||||
|
||||
func _set_snap_step(val: float) -> void:
|
||||
snap_step = val
|
||||
for slider in get_sliders():
|
||||
slider.snap_step = val
|
||||
|
||||
|
||||
func _set_snap_by_default(val: bool) -> void:
|
||||
snap_by_default = val
|
||||
for slider in get_sliders():
|
||||
slider.snap_by_default = val
|
||||
|
||||
|
||||
func _set_prefix_x(val: String) -> void:
|
||||
prefix_x = val
|
||||
$GridContainer/X.prefix = val
|
||||
|
||||
|
||||
func _set_prefix_y(val: String) -> void:
|
||||
prefix_y = val
|
||||
$GridContainer/Y.prefix = val
|
||||
|
||||
|
||||
func _set_prefix_z(val: String) -> void:
|
||||
prefix_z = val
|
||||
$GridContainer/Z.prefix = val
|
||||
|
||||
|
||||
func _set_suffix_x(val: String) -> void:
|
||||
suffix_x = val
|
||||
$GridContainer/X.suffix = val
|
||||
|
||||
|
||||
func _set_suffix_y(val: String) -> void:
|
||||
suffix_y = val
|
||||
$GridContainer/Y.suffix = val
|
||||
|
||||
|
||||
func _set_suffix_z(val: String) -> void:
|
||||
suffix_z = val
|
||||
$GridContainer/Z.suffix = val
|
101
src/UI/Nodes/ValueSliderV3.tscn
Normal file
101
src/UI/Nodes/ValueSliderV3.tscn
Normal file
|
@ -0,0 +1,101 @@
|
|||
[gd_scene load_steps=6 format=2]
|
||||
|
||||
[ext_resource path="res://src/UI/Nodes/ValueSlider.gd" type="Script" id=1]
|
||||
[ext_resource path="res://src/UI/Nodes/ValueSliderV3.gd" type="Script" id=2]
|
||||
[ext_resource path="res://assets/graphics/misc/lock_aspect_2.png" type="Texture" id=3]
|
||||
[ext_resource path="res://assets/graphics/misc/lock_aspect_guides.png" type="Texture" id=4]
|
||||
[ext_resource path="res://assets/graphics/misc/lock_aspect.png" type="Texture" id=5]
|
||||
|
||||
[node name="ValueSliderV3" type="HBoxContainer"]
|
||||
margin_right = 45.0
|
||||
margin_bottom = 52.0
|
||||
script = ExtResource( 2 )
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="."]
|
||||
margin_right = 45.0
|
||||
margin_bottom = 80.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="X" type="TextureProgress" parent="GridContainer"]
|
||||
margin_right = 45.0
|
||||
margin_bottom = 24.0
|
||||
rect_min_size = Vector2( 32, 24 )
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 1 )
|
||||
prefix = "X:"
|
||||
|
||||
[node name="Y" type="TextureProgress" parent="GridContainer"]
|
||||
margin_top = 28.0
|
||||
margin_right = 45.0
|
||||
margin_bottom = 52.0
|
||||
rect_min_size = Vector2( 32, 24 )
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 1 )
|
||||
prefix = "Y:"
|
||||
|
||||
[node name="Z" type="TextureProgress" parent="GridContainer"]
|
||||
margin_top = 56.0
|
||||
margin_right = 45.0
|
||||
margin_bottom = 80.0
|
||||
rect_min_size = Vector2( 32, 24 )
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_type_variation = "ValueSlider"
|
||||
nine_patch_stretch = true
|
||||
stretch_margin_left = 3
|
||||
stretch_margin_top = 3
|
||||
stretch_margin_right = 3
|
||||
stretch_margin_bottom = 3
|
||||
script = ExtResource( 1 )
|
||||
prefix = "Z:"
|
||||
|
||||
[node name="Ratio" type="Control" parent="."]
|
||||
visible = false
|
||||
margin_left = 36.0
|
||||
margin_right = 52.0
|
||||
margin_bottom = 80.0
|
||||
rect_min_size = Vector2( 16, 0 )
|
||||
|
||||
[node name="RatioGuides" type="NinePatchRect" parent="Ratio" groups=["UIButtons"]]
|
||||
anchor_bottom = 1.0
|
||||
margin_right = 9.0
|
||||
rect_min_size = Vector2( 9, 0 )
|
||||
texture = ExtResource( 4 )
|
||||
region_rect = Rect2( 0, 0, 9, 44 )
|
||||
patch_margin_top = 15
|
||||
patch_margin_bottom = 13
|
||||
|
||||
[node name="RatioButton" type="TextureButton" parent="Ratio" groups=["UIButtons"]]
|
||||
unique_name_in_owner = true
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -8.0
|
||||
margin_top = -8.0
|
||||
margin_right = 8.0
|
||||
margin_bottom = 8.0
|
||||
hint_tooltip = "Lock aspect ratio"
|
||||
mouse_default_cursor_shape = 2
|
||||
toggle_mode = true
|
||||
texture_normal = ExtResource( 3 )
|
||||
texture_pressed = ExtResource( 5 )
|
||||
|
||||
[connection signal="value_changed" from="GridContainer/X" to="." method="_on_X_value_changed"]
|
||||
[connection signal="value_changed" from="GridContainer/Y" to="." method="_on_Y_value_changed"]
|
||||
[connection signal="value_changed" from="GridContainer/Z" to="." method="_on_Z_value_changed"]
|
||||
[connection signal="toggled" from="Ratio/RatioButton" to="." method="_on_RatioButton_toggled"]
|
310
src/UI/Options3D.gd
Normal file
310
src/UI/Options3D.gd
Normal file
|
@ -0,0 +1,310 @@
|
|||
extends PanelContainer
|
||||
|
||||
var cel: Cel3D
|
||||
var can_start_timer := true
|
||||
|
||||
onready var object_option_button := $"%ObjectOptionButton" as OptionButton
|
||||
onready var new_object_menu_button: MenuButton = $VBoxContainer/NewObjectMenuButton
|
||||
onready var layer_options: Container = $"%LayerOptions"
|
||||
onready var object_options: Container = $"%ObjectOptions"
|
||||
onready var mesh_options: VBoxContainer = $"%MeshOptions"
|
||||
onready var light_options: VBoxContainer = $"%LightOptions"
|
||||
onready var undo_redo_timer: Timer = $UndoRedoTimer
|
||||
onready var load_model_dialog: FileDialog = $LoadModelDialog
|
||||
|
||||
onready var layer_properties := {
|
||||
"camera:projection": $"%ProjectionOptionButton",
|
||||
"camera:rotation_degrees": $"%CameraRotation",
|
||||
"viewport:world:environment:ambient_light_color": $"%AmbientColorPickerButton",
|
||||
"viewport:world:environment:ambient_light_energy": $"%AmbientEnergy",
|
||||
}
|
||||
|
||||
onready var object_properties := {
|
||||
"translation": $"%ObjectPosition",
|
||||
"rotation_degrees": $"%ObjectRotation",
|
||||
"scale": $"%ObjectScale",
|
||||
"node3d_type:mesh:size": $"%MeshSize",
|
||||
"node3d_type:mesh:sizev2": $"%MeshSizeV2",
|
||||
"node3d_type:mesh:center_offset": $"%MeshCenterOffset",
|
||||
"node3d_type:mesh:left_to_right": $"%MeshLeftToRight",
|
||||
"node3d_type:mesh:radius": $"%MeshRadius",
|
||||
"node3d_type:mesh:height": $"%MeshHeight",
|
||||
"node3d_type:mesh:radial_segments": $"%MeshRadialSegments",
|
||||
"node3d_type:mesh:rings": $"%MeshRings",
|
||||
"node3d_type:mesh:is_hemisphere": $"%MeshIsHemisphere",
|
||||
"node3d_type:mesh:mid_height": $"%MeshMidHeight",
|
||||
"node3d_type:mesh:top_radius": $"%MeshTopRadius",
|
||||
"node3d_type:mesh:bottom_radius": $"%MeshBottomRadius",
|
||||
"node3d_type:mesh:text": $"%MeshText",
|
||||
"node3d_type:mesh:pixel_size": $"%MeshPixelSize",
|
||||
"node3d_type:mesh:curve_step": $"%MeshCurveStep",
|
||||
"node3d_type:mesh:horizontal_alignment": $"%MeshHorizontalAlignment",
|
||||
"node3d_type:light_color": $"%LightColor",
|
||||
"node3d_type:light_energy": $"%LightEnergy",
|
||||
"node3d_type:light_negative": $"%LightNegative",
|
||||
"node3d_type:shadow_enabled": $"%ShadowEnabled",
|
||||
"node3d_type:shadow_color": $"%ShadowColor",
|
||||
"node3d_type:omni_range": $"%OmniRange",
|
||||
"node3d_type:spot_range": $"%SpotRange",
|
||||
"node3d_type:spot_angle": $"%SpotAngle",
|
||||
}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
Global.connect("cel_changed", self, "_cel_changed")
|
||||
var new_object_popup := new_object_menu_button.get_popup()
|
||||
new_object_popup.add_item("Box")
|
||||
new_object_popup.add_item("Sphere")
|
||||
new_object_popup.add_item("Capsule")
|
||||
new_object_popup.add_item("Cylinder")
|
||||
new_object_popup.add_item("Prism")
|
||||
new_object_popup.add_item("Plane")
|
||||
new_object_popup.add_item("Text")
|
||||
new_object_popup.add_item("Directional light")
|
||||
new_object_popup.add_item("Spotlight")
|
||||
new_object_popup.add_item("Omnidirectional (point) light")
|
||||
new_object_popup.add_item("Load model from file")
|
||||
new_object_popup.connect("id_pressed", self, "_add_new_object")
|
||||
for prop in layer_properties:
|
||||
var node: Control = layer_properties[prop]
|
||||
if node is ValueSliderV3:
|
||||
node.connect("value_changed", self, "_layer_property_vector3_changed", [prop])
|
||||
elif node is Range:
|
||||
node.connect("value_changed", self, "_layer_property_value_changed", [prop])
|
||||
elif node is OptionButton:
|
||||
node.connect("item_selected", self, "_layer_property_item_selected", [prop])
|
||||
elif node is ColorPickerButton:
|
||||
node.connect("color_changed", self, "_layer_property_color_changed", [prop])
|
||||
for prop in object_properties:
|
||||
var node: Control = object_properties[prop]
|
||||
if node is ValueSliderV3:
|
||||
node.connect("value_changed", self, "_object_property_vector3_changed", [prop])
|
||||
elif node is ValueSliderV2:
|
||||
var property_path: String = prop
|
||||
if property_path.ends_with("v2"):
|
||||
property_path = property_path.replace("v2", "")
|
||||
node.connect("value_changed", self, "_object_property_vector2_changed", [property_path])
|
||||
elif node is Range:
|
||||
node.connect("value_changed", self, "_object_property_value_changed", [prop])
|
||||
elif node is OptionButton:
|
||||
node.connect("item_selected", self, "_object_property_item_selected", [prop])
|
||||
elif node is ColorPickerButton:
|
||||
node.connect("color_changed", self, "_object_property_color_changed", [prop])
|
||||
elif node is CheckBox:
|
||||
node.connect("toggled", self, "_object_property_toggled", [prop])
|
||||
elif node is LineEdit:
|
||||
node.connect("text_changed", self, "_object_property_text_changed", [prop])
|
||||
|
||||
|
||||
func _on_ObjectOptionButton_item_selected(index: int) -> void:
|
||||
if not cel is Cel3D:
|
||||
return
|
||||
var id := object_option_button.get_item_id(index) - 1
|
||||
var parent: Cel3DParent = cel.parent_node
|
||||
var object := cel.get_object_from_id(id)
|
||||
if not is_instance_valid(object):
|
||||
parent.selected = null
|
||||
return
|
||||
parent.selected = object
|
||||
|
||||
|
||||
func _cel_changed() -> void:
|
||||
if not Global.current_project.get_current_cel() is Cel3D:
|
||||
get_child(0).visible = false
|
||||
return
|
||||
get_child(0).visible = true
|
||||
cel = Global.current_project.get_current_cel()
|
||||
var layer: Layer3D = cel.layer
|
||||
if not layer.is_connected("property_changed", self, "_set_layer_node_values"):
|
||||
layer.connect("property_changed", self, "_set_layer_node_values")
|
||||
layer.connect("objects_changed", self, "_fill_object_option_button")
|
||||
var parent: Cel3DParent = cel.parent_node
|
||||
if not is_instance_valid(parent):
|
||||
print("Parent not found")
|
||||
return
|
||||
if not parent.is_connected("selected_object", self, "_selected_object"):
|
||||
parent.connect("selected_object", self, "_selected_object")
|
||||
layer_options.visible = true
|
||||
object_options.visible = false
|
||||
_set_layer_node_values()
|
||||
_fill_object_option_button()
|
||||
|
||||
|
||||
func _add_new_object(id: int) -> void:
|
||||
if id == Cel3DObject.Type.IMPORTED:
|
||||
load_model_dialog.popup_centered()
|
||||
Global.dialog_open(true)
|
||||
else:
|
||||
cel.layer.add_object(id)
|
||||
|
||||
|
||||
func _selected_object(object: Cel3DObject) -> void:
|
||||
if is_instance_valid(object):
|
||||
layer_options.visible = false
|
||||
object_options.visible = true
|
||||
for prop in object_properties: # Hide irrelevant nodes
|
||||
var node: Control = object_properties[prop]
|
||||
var property_path: String = prop
|
||||
if property_path.ends_with("v2"):
|
||||
property_path = property_path.replace("v2", "")
|
||||
var prev_node: Control = node.get_parent().get_child(node.get_index() - 1)
|
||||
var property = object.get_indexed(property_path)
|
||||
var property_exists: bool = property != null
|
||||
# Differentiate between the mesh size of a box/prism (Vector3) and a plane (Vector2)
|
||||
if node is ValueSliderV3 and typeof(property) != TYPE_VECTOR3:
|
||||
property_exists = false
|
||||
elif node is ValueSliderV2 and typeof(property) != TYPE_VECTOR2:
|
||||
property_exists = false
|
||||
prev_node.visible = property_exists
|
||||
node.visible = property_exists
|
||||
mesh_options.visible = object.node3d_type is MeshInstance
|
||||
light_options.visible = object.node3d_type is Light
|
||||
_set_object_node_values()
|
||||
if not object.is_connected("property_changed", self, "_set_object_node_values"):
|
||||
object.connect("property_changed", self, "_set_object_node_values")
|
||||
object_option_button.select(object_option_button.get_item_index(object.id + 1))
|
||||
else:
|
||||
layer_options.visible = true
|
||||
object_options.visible = false
|
||||
object_option_button.select(0)
|
||||
|
||||
|
||||
func _set_layer_node_values() -> void:
|
||||
can_start_timer = false
|
||||
_set_node_values(cel, layer_properties)
|
||||
can_start_timer = true
|
||||
|
||||
|
||||
func _set_object_node_values() -> void:
|
||||
var object: Cel3DObject = cel.parent_node.selected
|
||||
if not is_instance_valid(object):
|
||||
return
|
||||
can_start_timer = false
|
||||
_set_node_values(object, object_properties)
|
||||
can_start_timer = true
|
||||
|
||||
|
||||
func _set_node_values(to_edit: Object, properties: Dictionary) -> void:
|
||||
for prop in properties:
|
||||
var property_path: String = prop
|
||||
if property_path.ends_with("v2"):
|
||||
property_path = property_path.replace("v2", "")
|
||||
var value = to_edit.get_indexed(property_path)
|
||||
if value == null:
|
||||
continue
|
||||
if "scale" in prop:
|
||||
value *= 100
|
||||
var node: Control = properties[prop]
|
||||
if node is Range or node is ValueSliderV3 or node is ValueSliderV2:
|
||||
if typeof(node.value) != typeof(value) and typeof(value) != TYPE_INT:
|
||||
continue
|
||||
node.value = value
|
||||
elif node is OptionButton:
|
||||
node.selected = value
|
||||
elif node is ColorPickerButton:
|
||||
node.color = value
|
||||
elif node is CheckBox:
|
||||
node.pressed = value
|
||||
elif node is LineEdit:
|
||||
node.text = value
|
||||
|
||||
|
||||
func _set_value_from_node(to_edit: Object, value, prop: String) -> void:
|
||||
if "mesh_" in prop:
|
||||
prop = prop.replace("mesh_", "")
|
||||
to_edit = to_edit.node3d_type.mesh
|
||||
if "scale" in prop:
|
||||
value /= 100
|
||||
to_edit.set_indexed(prop, value)
|
||||
|
||||
|
||||
func _layer_property_vector3_changed(value: Vector3, prop: String) -> void:
|
||||
_set_value_from_node(cel, value, prop)
|
||||
_value_handle_change()
|
||||
Global.canvas.gizmos_3d.update()
|
||||
|
||||
|
||||
func _layer_property_value_changed(value: float, prop: String) -> void:
|
||||
_set_value_from_node(cel, value, prop)
|
||||
_value_handle_change()
|
||||
Global.canvas.gizmos_3d.update()
|
||||
|
||||
|
||||
func _layer_property_item_selected(value: int, prop: String) -> void:
|
||||
_set_value_from_node(cel, value, prop)
|
||||
_value_handle_change()
|
||||
Global.canvas.gizmos_3d.update()
|
||||
|
||||
|
||||
func _layer_property_color_changed(value: Color, prop: String) -> void:
|
||||
_set_value_from_node(cel, value, prop)
|
||||
_value_handle_change()
|
||||
Global.canvas.gizmos_3d.update()
|
||||
|
||||
|
||||
func _object_property_vector3_changed(value: Vector3, prop: String) -> void:
|
||||
_set_value_from_node(cel.parent_node.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _object_property_vector2_changed(value: Vector2, prop: String) -> void:
|
||||
_set_value_from_node(cel.parent_node.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _object_property_value_changed(value: float, prop: String) -> void:
|
||||
_set_value_from_node(cel.parent_node.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _object_property_item_selected(value: int, prop: String) -> void:
|
||||
_set_value_from_node(cel.parent_node.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _object_property_color_changed(value: Color, prop: String) -> void:
|
||||
_set_value_from_node(cel.parent_node.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _object_property_toggled(value: bool, prop: String) -> void:
|
||||
_set_value_from_node(cel.parent_node.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _object_property_text_changed(value: String, prop: String) -> void:
|
||||
_set_value_from_node(cel.parent_node.selected, value, prop)
|
||||
_value_handle_change()
|
||||
|
||||
|
||||
func _value_handle_change() -> void:
|
||||
if can_start_timer:
|
||||
undo_redo_timer.start()
|
||||
|
||||
|
||||
func _fill_object_option_button() -> void:
|
||||
if not cel is Cel3D:
|
||||
return
|
||||
var layer: Layer3D = cel.layer
|
||||
object_option_button.clear()
|
||||
object_option_button.add_item("None", 0)
|
||||
for id in layer.objects:
|
||||
var item_name: String = Cel3DObject.Type.keys()[layer.objects[id]]
|
||||
object_option_button.add_item(item_name, id + 1)
|
||||
|
||||
|
||||
func _on_UndoRedoTimer_timeout() -> void:
|
||||
if is_instance_valid(cel.parent_node.selected):
|
||||
cel.parent_node.selected.finish_changing_property()
|
||||
else:
|
||||
var new_properties := cel.serialize_layer_properties()
|
||||
cel.layer.change_properties(new_properties)
|
||||
|
||||
|
||||
func _on_LoadModelDialog_files_selected(paths: PoolStringArray) -> void:
|
||||
for path in paths:
|
||||
cel.layer.add_object(Cel3DObject.Type.IMPORTED, true, path)
|
||||
|
||||
|
||||
func _on_LoadModelDialog_popup_hide() -> void:
|
||||
Global.dialog_open(false)
|
|
@ -17,6 +17,7 @@ var frame_button_node = preload("res://src/UI/Timeline/FrameButton.tscn")
|
|||
onready var old_scroll: int = 0 # The previous scroll state of $ScrollContainer
|
||||
onready var tag_spacer = find_node("TagSpacer")
|
||||
onready var start_spacer = find_node("StartSpacer")
|
||||
onready var add_layer_list: MenuButton = $"%AddLayerList"
|
||||
|
||||
onready var timeline_scroll: ScrollContainer = find_node("TimelineScroll")
|
||||
onready var frame_scroll_container: Control = find_node("FrameScrollContainer")
|
||||
|
@ -30,6 +31,7 @@ onready var drag_highlight: ColorRect = find_node("DragHighlight")
|
|||
|
||||
|
||||
func _ready() -> void:
|
||||
add_layer_list.get_popup().connect("id_pressed", self, "add_layer")
|
||||
frame_scroll_bar.connect("value_changed", self, "_frame_scroll_changed")
|
||||
Global.animation_timer.wait_time = 1 / Global.current_project.fps
|
||||
fps_spinbox.value = Global.current_project.fps
|
||||
|
@ -273,7 +275,13 @@ func copy_frames(indices := []) -> void:
|
|||
new_frame.duration = src_frame.duration
|
||||
for l in range(project.layers.size()):
|
||||
var src_cel: BaseCel = project.frames[f].cels[l] # Cel we're copying from, the source
|
||||
var new_cel: BaseCel = src_cel.get_script().new()
|
||||
var new_cel: BaseCel
|
||||
if src_cel is Cel3D:
|
||||
new_cel = src_cel.get_script().new(
|
||||
src_cel.layer, src_cel.size, false, src_cel.object_properties
|
||||
)
|
||||
else:
|
||||
new_cel = src_cel.get_script().new()
|
||||
if project.layers[l].new_cels_linked:
|
||||
if src_cel.link_set == null:
|
||||
src_cel.link_set = {}
|
||||
|
@ -565,6 +573,8 @@ func add_layer(type: int) -> void:
|
|||
l = PixelLayer.new(project)
|
||||
Global.LayerTypes.GROUP:
|
||||
l = GroupLayer.new(project)
|
||||
Global.LayerTypes.THREE_D:
|
||||
l = Layer3D.new(project)
|
||||
|
||||
var cels := []
|
||||
for f in project.frames:
|
||||
|
@ -618,7 +628,13 @@ func _on_CloneLayer_pressed() -> void:
|
|||
|
||||
for frame in project.frames:
|
||||
var src_cel: BaseCel = frame.cels[src_layer.index]
|
||||
var new_cel: BaseCel = src_cel.get_script().new()
|
||||
var new_cel: BaseCel
|
||||
if src_cel is Cel3D:
|
||||
new_cel = src_cel.get_script().new(
|
||||
src_cel.layer, src_cel.size, false, src_cel.object_properties
|
||||
)
|
||||
else:
|
||||
new_cel = src_cel.get_script().new()
|
||||
|
||||
if src_cel.link_set == null:
|
||||
new_cel.set_content(src_cel.copy_content())
|
||||
|
@ -745,7 +761,7 @@ func change_layer_order(up: bool) -> void:
|
|||
|
||||
func _on_MergeDownLayer_pressed() -> void:
|
||||
var project: Project = Global.current_project
|
||||
var top_layer: PixelLayer = project.layers[project.current_layer]
|
||||
var top_layer: BaseLayer = project.layers[project.current_layer]
|
||||
var bottom_layer: PixelLayer = project.layers[project.current_layer - 1]
|
||||
var top_cels := []
|
||||
|
||||
|
@ -756,7 +772,7 @@ func _on_MergeDownLayer_pressed() -> void:
|
|||
top_cels.append(frame.cels[top_layer.index]) # Store for undo purposes
|
||||
|
||||
var top_image := Image.new()
|
||||
top_image.copy_from(frame.cels[top_layer.index].image)
|
||||
top_image.copy_from(frame.cels[top_layer.index].get_image())
|
||||
|
||||
top_image.lock()
|
||||
if frame.cels[top_layer.index].opacity < 1: # If we have layer transparency
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
[ext_resource path="res://assets/graphics/layers/clone.png" type="Texture" id=7]
|
||||
[ext_resource path="res://assets/graphics/timeline/move_arrow.png" type="Texture" id=8]
|
||||
[ext_resource path="res://src/UI/Nodes/ValueSlider.tscn" type="PackedScene" id=9]
|
||||
[ext_resource path="res://assets/graphics/layers/group_new.png" type="Texture" id=10]
|
||||
[ext_resource path="res://assets/graphics/misc/value_arrow.svg" type="Texture" id=10]
|
||||
[ext_resource path="res://src/UI/Timeline/FrameScrollContainer.gd" type="Script" id=11]
|
||||
[ext_resource path="res://assets/graphics/timeline/new_frame.png" type="Texture" id=19]
|
||||
[ext_resource path="res://assets/graphics/timeline/remove_frame.png" type="Texture" id=20]
|
||||
|
@ -99,67 +99,66 @@ margin_bottom = 74.0
|
|||
size_flags_horizontal = 3
|
||||
|
||||
[node name="LayerTools" type="PanelContainer" parent="TimelineContainer/TimelineButtons"]
|
||||
margin_right = 222.0
|
||||
margin_right = 221.0
|
||||
margin_bottom = 74.0
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="TimelineContainer/TimelineButtons/LayerTools"]
|
||||
margin_left = 7.0
|
||||
margin_top = 7.0
|
||||
margin_right = 215.0
|
||||
margin_right = 214.0
|
||||
margin_bottom = 67.0
|
||||
|
||||
[node name="LayerButtons" type="HBoxContainer" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer"]
|
||||
margin_right = 208.0
|
||||
margin_right = 207.0
|
||||
margin_bottom = 22.0
|
||||
size_flags_vertical = 0
|
||||
custom_constants/separation = 9
|
||||
|
||||
[node name="AddLayer" type="Button" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons" groups=["UIButtons"]]
|
||||
margin_right = 22.0
|
||||
margin_right = 44.0
|
||||
margin_bottom = 22.0
|
||||
rect_min_size = Vector2( 22, 22 )
|
||||
rect_min_size = Vector2( 44, 22 )
|
||||
hint_tooltip = "Create a new layer"
|
||||
focus_mode = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons/AddLayer"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -11.0
|
||||
margin_top = -11.0
|
||||
margin_right = 11.0
|
||||
margin_right = 22.0
|
||||
margin_bottom = 11.0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
texture = ExtResource( 2 )
|
||||
|
||||
[node name="AddGroup" type="Button" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons" groups=["UIButtons"]]
|
||||
margin_left = 31.0
|
||||
margin_right = 53.0
|
||||
margin_bottom = 22.0
|
||||
rect_min_size = Vector2( 22, 22 )
|
||||
hint_tooltip = "Create a new group layer"
|
||||
focus_mode = 0
|
||||
[node name="AddLayerList" type="MenuButton" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons/AddLayer"]
|
||||
unique_name_in_owner = true
|
||||
anchor_left = 1.0
|
||||
anchor_top = 0.5
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -22.0
|
||||
margin_top = -10.0
|
||||
margin_bottom = 10.0
|
||||
rect_min_size = Vector2( 22, 0 )
|
||||
mouse_default_cursor_shape = 2
|
||||
items = [ "Add Pixel Layer", null, 0, false, false, 0, 0, null, "", false, "Add Group Layer", null, 0, false, false, 1, 0, null, "", false, "Add 3D Layer", null, 0, false, false, 2, 0, null, "", false ]
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons/AddGroup"]
|
||||
[node name="TextureRect" type="TextureRect" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons/AddLayer/AddLayerList"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -11.0
|
||||
margin_top = -11.0
|
||||
margin_right = 11.0
|
||||
margin_bottom = 11.0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
margin_left = -6.0
|
||||
margin_top = -6.0
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
texture = ExtResource( 10 )
|
||||
|
||||
[node name="RemoveLayer" type="Button" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons" groups=["UIButtons"]]
|
||||
margin_left = 62.0
|
||||
margin_right = 84.0
|
||||
margin_left = 53.0
|
||||
margin_right = 75.0
|
||||
margin_bottom = 22.0
|
||||
rect_min_size = Vector2( 22, 22 )
|
||||
hint_tooltip = "Remove current layer"
|
||||
|
@ -184,8 +183,8 @@ __meta__ = {
|
|||
}
|
||||
|
||||
[node name="MoveUpLayer" type="Button" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons" groups=["UIButtons"]]
|
||||
margin_left = 93.0
|
||||
margin_right = 115.0
|
||||
margin_left = 84.0
|
||||
margin_right = 106.0
|
||||
margin_bottom = 22.0
|
||||
rect_min_size = Vector2( 22, 22 )
|
||||
hint_tooltip = "Move up the current layer"
|
||||
|
@ -210,8 +209,8 @@ __meta__ = {
|
|||
}
|
||||
|
||||
[node name="MoveDownLayer" type="Button" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons" groups=["UIButtons"]]
|
||||
margin_left = 124.0
|
||||
margin_right = 146.0
|
||||
margin_left = 115.0
|
||||
margin_right = 137.0
|
||||
margin_bottom = 22.0
|
||||
rect_min_size = Vector2( 22, 22 )
|
||||
hint_tooltip = "Move down the current layer"
|
||||
|
@ -236,8 +235,8 @@ __meta__ = {
|
|||
}
|
||||
|
||||
[node name="CloneLayer" type="Button" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons" groups=["UIButtons"]]
|
||||
margin_left = 155.0
|
||||
margin_right = 177.0
|
||||
margin_left = 146.0
|
||||
margin_right = 168.0
|
||||
margin_bottom = 22.0
|
||||
rect_min_size = Vector2( 22, 22 )
|
||||
hint_tooltip = "Clone current layer"
|
||||
|
@ -261,8 +260,8 @@ __meta__ = {
|
|||
}
|
||||
|
||||
[node name="MergeDownLayer" type="Button" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons" groups=["UIButtons"]]
|
||||
margin_left = 186.0
|
||||
margin_right = 208.0
|
||||
margin_left = 177.0
|
||||
margin_right = 199.0
|
||||
margin_bottom = 22.0
|
||||
rect_min_size = Vector2( 22, 22 )
|
||||
hint_tooltip = "Merge current layer with the one below"
|
||||
|
@ -288,7 +287,7 @@ __meta__ = {
|
|||
|
||||
[node name="BlendingHBox" type="HBoxContainer" parent="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer"]
|
||||
margin_top = 36.0
|
||||
margin_right = 208.0
|
||||
margin_right = 207.0
|
||||
margin_bottom = 60.0
|
||||
size_flags_vertical = 10
|
||||
|
||||
|
@ -301,21 +300,21 @@ value = 100.0
|
|||
prefix = "Opacity:"
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="TimelineContainer/TimelineButtons"]
|
||||
margin_left = 226.0
|
||||
margin_left = 225.0
|
||||
margin_right = 902.0
|
||||
margin_bottom = 74.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="AnimationToolsScrollContainer" type="ScrollContainer" parent="TimelineContainer/TimelineButtons/VBoxContainer"]
|
||||
margin_right = 676.0
|
||||
margin_right = 677.0
|
||||
margin_bottom = 38.0
|
||||
size_flags_horizontal = 3
|
||||
theme = SubResource( 20 )
|
||||
scroll_vertical_enabled = false
|
||||
|
||||
[node name="AnimationTools" type="PanelContainer" parent="TimelineContainer/TimelineButtons/VBoxContainer/AnimationToolsScrollContainer"]
|
||||
margin_left = 156.0
|
||||
margin_right = 676.0
|
||||
margin_left = 157.0
|
||||
margin_right = 677.0
|
||||
margin_bottom = 38.0
|
||||
size_flags_horizontal = 10
|
||||
|
||||
|
@ -760,7 +759,7 @@ suffix = "FPS"
|
|||
|
||||
[node name="TagScroll" type="ScrollContainer" parent="TimelineContainer/TimelineButtons/VBoxContainer"]
|
||||
margin_top = 42.0
|
||||
margin_right = 676.0
|
||||
margin_right = 677.0
|
||||
margin_bottom = 74.0
|
||||
rect_min_size = Vector2( 0, 32 )
|
||||
mouse_filter = 2
|
||||
|
@ -769,7 +768,7 @@ theme = SubResource( 20 )
|
|||
scroll_vertical_enabled = false
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="TimelineContainer/TimelineButtons/VBoxContainer/TagScroll"]
|
||||
margin_right = 676.0
|
||||
margin_right = 677.0
|
||||
margin_bottom = 32.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
@ -779,7 +778,7 @@ custom_constants/separation = 0
|
|||
margin_bottom = 32.0
|
||||
|
||||
[node name="TagContainer" type="Control" parent="TimelineContainer/TimelineButtons/VBoxContainer/TagScroll/HBoxContainer"]
|
||||
margin_right = 676.0
|
||||
margin_right = 677.0
|
||||
margin_bottom = 32.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
|
@ -980,7 +979,6 @@ mouse_filter = 2
|
|||
color = Color( 0, 0.741176, 1, 0.501961 )
|
||||
|
||||
[connection signal="pressed" from="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons/AddLayer" to="." method="add_layer" binds= [ 0 ]]
|
||||
[connection signal="pressed" from="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons/AddGroup" to="." method="add_layer" binds= [ 1 ]]
|
||||
[connection signal="pressed" from="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons/RemoveLayer" to="." method="_on_RemoveLayer_pressed"]
|
||||
[connection signal="pressed" from="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons/MoveUpLayer" to="." method="change_layer_order" binds= [ true ]]
|
||||
[connection signal="pressed" from="TimelineContainer/TimelineButtons/LayerTools/VBoxContainer/LayerButtons/MoveDownLayer" to="." method="change_layer_order" binds= [ false ]]
|
||||
|
|
|
@ -11,6 +11,8 @@ func _input(event: InputEvent) -> void:
|
|||
return
|
||||
|
||||
for tool_name in Tools.tools: # Handle tool shortcuts
|
||||
if not get_node(tool_name).visible:
|
||||
continue
|
||||
var t: Tools.Tool = Tools.tools[tool_name]
|
||||
if InputMap.has_action("right_" + t.shortcut + "_tool"):
|
||||
if (
|
||||
|
|
Loading…
Add table
Reference in a new issue