mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-01-18 17:19:50 +00:00
Implement 3D layers (#840)
* Implement 3D layers
* Remove unneeded files
* Fix bug where a single hidden layer would ignore all of the layers on top when exporting
* Fix pxo loading
* Remove junk nodes from 3DShapeEdit
Seems like they were created when I copied from the old 3D Options.tscn panel to the new 3D Shape Edit tool.
* Make light gizmos half the size, and hide gizmos when rotating
* Fix crash when using the 3D shape edit tool on a group layer
* Remove unneeded code in Canvas.gd
* Add torus in the Cel3DObject.Type enumerator
Torus isn't currently supported in Godot 3.5, but it is in 3.6 and 4.0, so this is just future-proofing. May break compatibility with .pxo files that were exported with 3D layers before this change.
* Toggle 3D object visibility
* Change texts and some variable names
* Fill translation strings
* Fix crash on group blending, and make the code in Export.blend_layers() more general
* Fix errors when attempting to draw on a 3D cel
Can occur when multiple cels are selected, some of them 3D and some of them pixel
* Make scene properties and objects be per-cel instead of per-layer
Breaks compatibility with previous .pxo files that had 3D layers. Also introduces serialize() and deserialize() methods to BaseCel
* Use if not layer is get_script() in GroupLayer.blend_children()
* Flip the condition in GroupLayer.blend_children()
* Fix bug where locked/invisible layers could get drawn
Regression from c2f6bf0f3f
* Move gizmo code to 3DShapeEdit's draw_start(), move some undo/redo logic to 3DShapeEdit
* Move all of the undo/redo code to 3DShapeEdit, simplify code in Cel3D
* Store Cel3D image data to pxo, for easy usage by external software
This makes importing projects with 3D layers to other software, such as Godot using godot_pixelorama_importer easier.
* Make the linter happy
* Fix bug where the previously selected object would remain selected when it got removed with undo
This commit is contained in:
parent
5f290ae343
commit
91aea32864
|
@ -483,6 +483,10 @@ msgstr ""
|
|||
msgid "Group layer:"
|
||||
msgstr ""
|
||||
|
||||
#. A type of layer. A 3D layer contains data of 3D objects that are rasterized automatically by Pixelorama.
|
||||
msgid "3D layer:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Direction:"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1703,10 +1707,20 @@ msgstr ""
|
|||
msgid "Layers"
|
||||
msgstr ""
|
||||
|
||||
#. Hint tooltip of the create new layer button, found on the left side of the timeline.
|
||||
msgid "Create a new layer"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create a new group layer"
|
||||
#. One of the options of the create new layer button.
|
||||
msgid "Add Pixel Layer"
|
||||
msgstr ""
|
||||
|
||||
#. One of the options of the create new layer button.
|
||||
msgid "Add Group Layer"
|
||||
msgstr ""
|
||||
|
||||
#. One of the options of the create new layer button.
|
||||
msgid "Add 3D Layer"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove current layer"
|
||||
|
@ -2236,3 +2250,174 @@ msgid "Locked size\n\n"
|
|||
"When enabled using the tool on the canvas will only move the cropping rectangle.\n\n"
|
||||
"When disabled using the tool on the canvas will draw the rectangle."
|
||||
msgstr ""
|
||||
|
||||
#. A tool used in 3D layers, that edits 3D objects.
|
||||
msgid "3D Shape Edit"
|
||||
msgstr ""
|
||||
|
||||
#. Used in 3D layers. A type of 3D object.
|
||||
msgid "Box"
|
||||
msgstr ""
|
||||
|
||||
#. Used in 3D layers. A type of 3D object.
|
||||
msgid "Sphere"
|
||||
msgstr ""
|
||||
|
||||
#. Used in 3D layers. A type of 3D object.
|
||||
msgid "Capsule"
|
||||
msgstr ""
|
||||
|
||||
#. Used in 3D layers. A type of 3D object.
|
||||
msgid "Cylinder"
|
||||
msgstr ""
|
||||
|
||||
#. Used in 3D layers. A type of 3D object.
|
||||
msgid "Prism"
|
||||
msgstr ""
|
||||
|
||||
#. Used in 3D layers. A type of 3D object.
|
||||
msgid "Torus"
|
||||
msgstr ""
|
||||
|
||||
#. Used in 3D layers. A type of 3D object.
|
||||
msgid "Plane"
|
||||
msgstr ""
|
||||
|
||||
#. Used in 3D layers. A type of light.
|
||||
msgid "Directional light"
|
||||
msgstr ""
|
||||
|
||||
#. Used in 3D layers. A type of light.
|
||||
msgid "Spotlight"
|
||||
msgstr ""
|
||||
|
||||
#. Used in 3D layers. A type of light.
|
||||
msgid "Point light"
|
||||
msgstr ""
|
||||
|
||||
#. Used in 3D layers. A type of 3D object.
|
||||
msgid "Custom model"
|
||||
msgstr ""
|
||||
|
||||
msgid "Selected object:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add new object"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove object"
|
||||
msgstr ""
|
||||
|
||||
msgid "Camera"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool. Refers to camera projection mode.
|
||||
msgid "Projection:"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool. One of the modes of camera projection.
|
||||
msgid "Perspective"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool. One of the modes of camera projection.
|
||||
msgid "Orthogonal"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool. One of the modes of camera projection.
|
||||
msgid "Frustum"
|
||||
msgstr ""
|
||||
|
||||
msgid "Rotation:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Scale:"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool.
|
||||
msgid "Environment"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool, under the Environment category.
|
||||
msgid "Ambient color:"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool, under the Environment category.
|
||||
msgid "Ambient color energy:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Visible:"
|
||||
msgstr ""
|
||||
|
||||
#. Refers to the transformation options of an object, such as its position, rotation and scale. For technical details, see https://docs.godotengine.org/en/stable/tutorials/math/matrices_and_transforms.html
|
||||
msgid "Transform"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool. A category with mesh-related options.
|
||||
msgid "Mesh"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool, under the Mesh category if a Prism is selected. Refers to the displacement of the upper edge along the X axis.
|
||||
msgid "Left to right:"
|
||||
msgstr ""
|
||||
|
||||
#. Radius of a circle/spherical object.
|
||||
msgid "Radius:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Height:"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool, under the Mesh category if a spherical object is selected.
|
||||
msgid "Radial segments:"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool, under the Mesh category if a spherical object is selected. Refers to the number of segments along the height of the sphere.
|
||||
msgid "Rings:"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool, under the Mesh category if a Sphere is selected. If true, a hemisphere is created rather than a full sphere.
|
||||
msgid "Is hemisphere:"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool, under the Mesh category if a Cylinder is selected.
|
||||
msgid "Top radius:"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool, under the Mesh category if a Cylinder is selected.
|
||||
msgid "Bottom radius:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Text:"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool, under the Mesh category if a Text object is selected. Refers to the size of one pixel's width on the text to scale it in 3D.
|
||||
msgid "Pixel size:"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool, under the Mesh category if a Text object is selected. Step (in pixels) used to approximate Bézier curves.
|
||||
msgid "Curve step:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Horizontal alignment:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Left"
|
||||
msgstr ""
|
||||
|
||||
msgid "Right"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool, under the Light category if a light is selected. Refers to the energy of the light. The more energy, the brighter it shines.
|
||||
msgid "Energy:"
|
||||
msgstr ""
|
||||
|
||||
#. Found in the tool options of the 3D Shape Edit tool, under the Light category if a light is selected. If true, the light's effect is reversed, darkening areas and casting bright shadows.
|
||||
msgid "Negative:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Shadow:"
|
||||
msgstr ""
|
||||
|
||||
#. Refers to the range of something, like the range of a spotlight.
|
||||
msgid "Range:"
|
||||
msgstr ""
|
||||
|
|
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)
|
||||
|
|
|
@ -383,10 +383,10 @@ func blend_layers(
|
|||
else:
|
||||
var layer: BaseLayer = project.layers[export_layers - 2]
|
||||
var layer_image := Image.new()
|
||||
if layer is PixelLayer:
|
||||
layer_image.copy_from(frame.cels[export_layers - 2].image)
|
||||
elif layer is GroupLayer:
|
||||
if layer is GroupLayer:
|
||||
layer_image.copy_from(layer.blend_children(frame, Vector2.ZERO))
|
||||
else:
|
||||
layer_image.copy_from(frame.cels[export_layers - 2].get_image())
|
||||
image.blend_rect(layer_image, Rect2(Vector2.ZERO, project.size), origin)
|
||||
|
||||
|
||||
|
@ -396,20 +396,25 @@ 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():
|
||||
layer_i += 1
|
||||
continue
|
||||
if cel is GroupCel:
|
||||
layer_i += 1
|
||||
continue
|
||||
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,21 @@ 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 key == "id" or key == "type":
|
||||
dict[key] = int(dict[key])
|
||||
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 or "center_offset" in key: # Convert a String to a Vector3
|
||||
dict[key] = str2var("Vector3" + dict[key])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -5,8 +5,8 @@ extends Reference
|
|||
|
||||
signal texture_changed
|
||||
|
||||
var opacity: float
|
||||
var image_texture: ImageTexture
|
||||
var opacity := 1.0
|
||||
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,6 +51,14 @@ func update_texture() -> void:
|
|||
return
|
||||
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return {"opacity": opacity}
|
||||
|
||||
|
||||
func deserialize(dict: Dictionary) -> void:
|
||||
opacity = dict["opacity"]
|
||||
|
||||
|
||||
func save_image_data_to_pxo(_file: File) -> void:
|
||||
return
|
||||
|
||||
|
@ -55,5 +67,9 @@ func load_image_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
|
||||
|
||||
|
|
203
src/Classes/Cel3D.gd
Normal file
203
src/Classes/Cel3D.gd
Normal file
|
@ -0,0 +1,203 @@
|
|||
class_name Cel3D
|
||||
extends BaseCel
|
||||
|
||||
signal selected_object(object)
|
||||
signal scene_property_changed
|
||||
signal objects_changed
|
||||
|
||||
var size: Vector2
|
||||
var viewport: Viewport
|
||||
var parent_node: Spatial
|
||||
var camera: Camera
|
||||
var scene_properties := {}
|
||||
# Key = Cel3DObject's id, Value = Dictionary containing the properties of the Cel3DObject
|
||||
var object_properties := {}
|
||||
var selected: Cel3DObject = null setget _set_selected
|
||||
var current_object_id := 0 # Its value never decreases
|
||||
|
||||
|
||||
func _init(_size: Vector2, from_pxo := false, _object_prop := {}, _scene_prop := {}) -> void:
|
||||
size = _size
|
||||
object_properties = _object_prop
|
||||
scene_properties = _scene_prop
|
||||
if scene_properties.empty():
|
||||
var camera_transform := Transform()
|
||||
camera_transform.origin = Vector3(0, 0, 3)
|
||||
scene_properties = {
|
||||
"camera_transform": camera_transform,
|
||||
"camera_projection": Camera.PROJECTION_PERSPECTIVE,
|
||||
"ambient_light_color": Color.black,
|
||||
"ambient_light_energy": 1,
|
||||
}
|
||||
_add_nodes()
|
||||
if not from_pxo:
|
||||
if object_properties.empty():
|
||||
var transform := Transform()
|
||||
transform.origin = Vector3(-2.5, 0, 0)
|
||||
object_properties[0] = {"type": Cel3DObject.Type.DIR_LIGHT, "transform": transform}
|
||||
_add_object_node(0)
|
||||
object_properties[1] = {"type": Cel3DObject.Type.BOX}
|
||||
_add_object_node(1)
|
||||
current_object_id = object_properties.size()
|
||||
|
||||
|
||||
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_scene_properties()
|
||||
viewport.add_child(camera)
|
||||
viewport.add_child(parent_node)
|
||||
Global.canvas.add_child(viewport)
|
||||
for object in object_properties:
|
||||
_add_object_node(object)
|
||||
|
||||
image_texture = viewport.get_texture()
|
||||
|
||||
|
||||
func _get_image_texture() -> Texture:
|
||||
if not is_instance_valid(viewport):
|
||||
_add_nodes()
|
||||
return image_texture
|
||||
|
||||
|
||||
func serialize_scene_properties() -> Dictionary:
|
||||
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_scene_properties() -> void:
|
||||
camera.transform = scene_properties["camera_transform"]
|
||||
camera.projection = scene_properties["camera_projection"]
|
||||
viewport.world.environment.ambient_light_color = scene_properties["ambient_light_color"]
|
||||
viewport.world.environment.ambient_light_energy = scene_properties["ambient_light_energy"]
|
||||
|
||||
|
||||
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 _scene_property_changed() -> void: # Called by undo/redo
|
||||
deserialize_scene_properties()
|
||||
emit_signal("scene_property_changed")
|
||||
|
||||
|
||||
func _add_object_node(id: int) -> void:
|
||||
var node3d := Cel3DObject.new()
|
||||
node3d.id = id
|
||||
node3d.cel = self
|
||||
parent_node.add_child(node3d)
|
||||
node3d.type = object_properties[id]["type"]
|
||||
if object_properties.has(id):
|
||||
if object_properties[id].has("id"):
|
||||
node3d.deserialize(object_properties[id])
|
||||
else:
|
||||
if object_properties[id].has("transform"):
|
||||
node3d.transform = object_properties[id]["transform"]
|
||||
if object_properties[id].has("file_path"):
|
||||
node3d.file_path = object_properties[id]["file_path"]
|
||||
object_properties[id] = node3d.serialize()
|
||||
emit_signal("objects_changed")
|
||||
|
||||
|
||||
func _remove_object_node(id: int) -> void: # Called by undo/redo
|
||||
var object := get_object_from_id(id)
|
||||
if is_instance_valid(object):
|
||||
if selected == object:
|
||||
self.selected = null
|
||||
object.queue_free()
|
||||
emit_signal("objects_changed")
|
||||
|
||||
|
||||
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 serialize() -> Dictionary:
|
||||
var dict := .serialize()
|
||||
dict["scene_properties"] = scene_properties
|
||||
dict["object_properties"] = object_properties
|
||||
return dict
|
||||
|
||||
|
||||
func deserialize(dict: Dictionary) -> void:
|
||||
.deserialize(dict)
|
||||
scene_properties = dict["scene_properties"]
|
||||
var objects_copy = dict["object_properties"]
|
||||
for object in objects_copy:
|
||||
if typeof(object) != TYPE_STRING:
|
||||
return
|
||||
Global.convert_dictionary_values(objects_copy[object])
|
||||
object_properties[int(object)] = objects_copy[object]
|
||||
current_object_id = object_properties.size()
|
||||
Global.convert_dictionary_values(scene_properties)
|
||||
deserialize_scene_properties()
|
||||
for object in object_properties:
|
||||
_add_object_node(object)
|
||||
|
||||
|
||||
func on_remove() -> void:
|
||||
if is_instance_valid(viewport):
|
||||
viewport.queue_free()
|
||||
|
||||
|
||||
func save_image_data_to_pxo(file: File) -> void:
|
||||
file.store_buffer(get_image().get_data())
|
||||
|
||||
|
||||
func load_image_data_from_pxo(file: File, project_size: Vector2) -> void:
|
||||
# Don't do anything with it, just read it so that the file can move on
|
||||
file.get_buffer(project_size.x * project_size.y * 4)
|
||||
|
||||
|
||||
func instantiate_cel_button() -> Node:
|
||||
return Global.pixel_cel_button_node.instance()
|
327
src/Classes/Cel3DObject.gd
Normal file
327
src/Classes/Cel3DObject.gd
Normal file
|
@ -0,0 +1,327 @@
|
|||
class_name Cel3DObject
|
||||
extends Spatial
|
||||
|
||||
signal property_changed
|
||||
|
||||
enum Type {
|
||||
BOX,
|
||||
SPHERE,
|
||||
CAPSULE,
|
||||
CYLINDER,
|
||||
PRISM,
|
||||
TORUS,
|
||||
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 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")
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -18,10 +18,13 @@ func blend_children(frame: Frame, origin := Vector2.ZERO) -> Image:
|
|||
for layer in children:
|
||||
if not layer.is_visible_in_hierarchy():
|
||||
continue
|
||||
if layer is PixelLayer:
|
||||
var cel: PixelCel = frame.cels[layer.index]
|
||||
# Checks if layer is GroupLayer, cannot define this due to cyclic reference error
|
||||
if layer is get_script():
|
||||
image.blend_rect(layer.blend_children(frame, origin), blend_rect, origin)
|
||||
else:
|
||||
var cel: BaseCel = frame.cels[layer.index]
|
||||
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:
|
||||
|
@ -33,8 +36,6 @@ func blend_children(frame: Frame, origin := Vector2.ZERO) -> Image:
|
|||
)
|
||||
cel_image.unlock()
|
||||
image.blend_rect(cel_image, blend_rect, origin)
|
||||
else: # Only if layer is GroupLayer, cannot define this due to cyclic reference error
|
||||
image.blend_rect(layer.blend_children(frame, origin), blend_rect, origin)
|
||||
return image
|
||||
|
||||
|
||||
|
@ -42,8 +43,8 @@ func blend_children(frame: Frame, origin := Vector2.ZERO) -> Image:
|
|||
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
var data = .serialize()
|
||||
data["type"] = Global.LayerTypes.GROUP
|
||||
var data := .serialize()
|
||||
data["type"] = get_layer_type()
|
||||
data["expanded"] = expanded
|
||||
return data
|
||||
|
||||
|
@ -53,6 +54,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
|
||||
|
|
32
src/Classes/Layer3D.gd
Normal file
32
src/Classes/Layer3D.gd
Normal file
|
@ -0,0 +1,32 @@
|
|||
class_name Layer3D
|
||||
extends BaseLayer
|
||||
|
||||
|
||||
func _init(_project, _name := "") -> void:
|
||||
project = _project
|
||||
name = _name
|
||||
|
||||
|
||||
# Overridden Methods:
|
||||
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
var dict = .serialize()
|
||||
dict["type"] = get_layer_type()
|
||||
return dict
|
||||
|
||||
|
||||
func get_layer_type() -> int:
|
||||
return Global.LayerTypes.THREE_D
|
||||
|
||||
|
||||
func new_empty_cel() -> BaseCel:
|
||||
return Cel3D.new(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
|
|
@ -70,5 +70,4 @@ func load_image_data_from_pxo(file: File, project_size: Vector2) -> void:
|
|||
|
||||
|
||||
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)
|
||||
|
@ -296,7 +300,8 @@ func serialize() -> Dictionary:
|
|||
for frame in frames:
|
||||
var cel_data := []
|
||||
for cel in frame.cels:
|
||||
cel_data.append({"opacity": cel.opacity, "metadata": _serialize_metadata(cel)})
|
||||
cel_data.append(cel.serialize())
|
||||
cel_data[-1]["metadata"] = _serialize_metadata(cel)
|
||||
|
||||
frame_data.append(
|
||||
{"cels": cel_data, "duration": frame.duration, "metadata": _serialize_metadata(frame)}
|
||||
|
@ -356,6 +361,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 := []
|
||||
|
@ -363,9 +377,12 @@ func deserialize(dict: Dictionary) -> void:
|
|||
for cel in frame.cels:
|
||||
match int(dict.layers[cel_i].get("type", Global.LayerTypes.PIXEL)):
|
||||
Global.LayerTypes.PIXEL:
|
||||
cels.append(PixelCel.new(Image.new(), cel.opacity))
|
||||
cels.append(PixelCel.new(Image.new()))
|
||||
Global.LayerTypes.GROUP:
|
||||
cels.append(GroupCel.new(cel.opacity))
|
||||
cels.append(GroupCel.new())
|
||||
Global.LayerTypes.THREE_D:
|
||||
cels.append(Cel3D.new(size, true))
|
||||
cels[cel_i].deserialize(cel)
|
||||
_deserialize_metadata(cels[cel_i], cel)
|
||||
cel_i += 1
|
||||
var duration := 1.0
|
||||
|
@ -379,12 +396,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 +564,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 +692,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 +774,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:
|
||||
|
|
449
src/Tools/3DShapeEdit.gd
Normal file
449
src/Tools/3DShapeEdit.gd
Normal file
|
@ -0,0 +1,449 @@
|
|||
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
|
||||
var _object_names := {
|
||||
Cel3DObject.Type.BOX: "Box",
|
||||
Cel3DObject.Type.SPHERE: "Sphere",
|
||||
Cel3DObject.Type.CAPSULE: "Capsule",
|
||||
Cel3DObject.Type.CYLINDER: "Cylinder",
|
||||
Cel3DObject.Type.PRISM: "Prism",
|
||||
Cel3DObject.Type.TORUS: "Torus",
|
||||
Cel3DObject.Type.PLANE: "Plane",
|
||||
Cel3DObject.Type.TEXT: "Text",
|
||||
Cel3DObject.Type.DIR_LIGHT: "Directional light",
|
||||
Cel3DObject.Type.SPOT_LIGHT: "Spotlight",
|
||||
Cel3DObject.Type.OMNI_LIGHT: "Point light",
|
||||
Cel3DObject.Type.IMPORTED: "Custom model",
|
||||
}
|
||||
|
||||
onready var object_option_button := $"%ObjectOptionButton" as OptionButton
|
||||
onready var new_object_menu_button := $"%NewObjectMenuButton" as MenuButton
|
||||
onready var remove_object_button := $"%RemoveObject" as Button
|
||||
onready var cel_options := $"%CelOptions" 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 cel_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 := {
|
||||
"visible": $"%VisibleCheckBox",
|
||||
"translation": $"%ObjectPosition",
|
||||
"rotation_degrees": $"%ObjectRotation",
|
||||
"scale": $"%ObjectScale",
|
||||
"node3d_type:mesh:size": $"%MeshSize",
|
||||
"node3d_type:mesh:sizev2": $"%MeshSizeV2",
|
||||
"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()
|
||||
for object in _object_names:
|
||||
if object == Cel3DObject.Type.TORUS: # Remove when Godot 3.6 or 4.0 is used
|
||||
continue
|
||||
new_object_popup.add_item(_object_names[object], object)
|
||||
new_object_popup.connect("id_pressed", self, "_new_object_popup_id_pressed")
|
||||
for prop in cel_properties:
|
||||
var node: Control = cel_properties[prop]
|
||||
if node is ValueSliderV3:
|
||||
node.connect("value_changed", self, "_cel_property_vector3_changed", [prop])
|
||||
elif node is Range:
|
||||
node.connect("value_changed", self, "_cel_property_value_changed", [prop])
|
||||
elif node is OptionButton:
|
||||
node.connect("item_selected", self, "_cel_property_item_selected", [prop])
|
||||
elif node is ColorPickerButton:
|
||||
node.connect("color_changed", self, "_cel_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:
|
||||
var project: Project = Global.current_project
|
||||
if not project.get_current_cel() is Cel3D:
|
||||
return
|
||||
if not project.layers[project.current_layer].can_layer_get_drawn():
|
||||
return
|
||||
var found_cel := false
|
||||
for frame_layer in project.selected_cels:
|
||||
if _cel == project.frames[frame_layer[0]].cels[frame_layer[1]]:
|
||||
found_cel = true
|
||||
if not found_cel:
|
||||
return
|
||||
|
||||
if is_instance_valid(_cel.selected):
|
||||
# Needs canvas.current_pixel, because draw_start()'s position is floored
|
||||
_cel.selected.applying_gizmos = Global.canvas.gizmos_3d.get_hovering_gizmo(
|
||||
Global.canvas.current_pixel
|
||||
)
|
||||
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:
|
||||
if not Global.current_project.get_current_cel() is Cel3D:
|
||||
return
|
||||
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:
|
||||
if not Global.current_project.get_current_cel() is Cel3D:
|
||||
return
|
||||
_dragging = false
|
||||
if is_instance_valid(_cel.selected) and _has_been_dragged:
|
||||
_cel.selected.applying_gizmos = Cel3DObject.Gizmos.NONE
|
||||
_object_property_changed(_cel.selected)
|
||||
_has_been_dragged = false
|
||||
|
||||
|
||||
func cursor_move(position: Vector2) -> void:
|
||||
.cursor_move(position)
|
||||
if not Global.current_project.get_current_cel() is Cel3D:
|
||||
return
|
||||
# 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
|
||||
if not _cel.is_connected("scene_property_changed", self, "_set_cel_node_values"):
|
||||
_cel.connect("scene_property_changed", self, "_set_cel_node_values")
|
||||
_cel.connect("objects_changed", self, "_fill_object_option_button")
|
||||
_cel.connect("selected_object", self, "_selected_object")
|
||||
cel_options.visible = true
|
||||
object_options.visible = false
|
||||
_set_cel_node_values()
|
||||
_fill_object_option_button()
|
||||
|
||||
|
||||
func _new_object_popup_id_pressed(id: int) -> void:
|
||||
if id == Cel3DObject.Type.IMPORTED:
|
||||
load_model_dialog.popup_centered()
|
||||
Global.dialog_open(true)
|
||||
else:
|
||||
_add_object(id)
|
||||
|
||||
|
||||
func _add_object(type: int, file_path := "") -> void:
|
||||
var dict := {"type": type, "file_path": file_path}
|
||||
var new_objects := _cel.object_properties.duplicate()
|
||||
new_objects[_cel.current_object_id] = dict
|
||||
var undo_redo: UndoRedo = Global.current_project.undo_redo
|
||||
undo_redo.create_action("Add 3D object")
|
||||
undo_redo.add_do_property(_cel, "object_properties", new_objects)
|
||||
undo_redo.add_undo_property(_cel, "object_properties", _cel.object_properties)
|
||||
undo_redo.add_do_method(_cel, "_add_object_node", _cel.current_object_id)
|
||||
undo_redo.add_undo_method(_cel, "_remove_object_node", _cel.current_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()
|
||||
_cel.current_object_id += 1
|
||||
|
||||
|
||||
func _on_RemoveObject_pressed() -> void:
|
||||
if is_instance_valid(_cel.selected):
|
||||
var new_objects := _cel.object_properties.duplicate()
|
||||
new_objects.erase(_cel.selected.id)
|
||||
var undo_redo: UndoRedo = Global.current_project.undo_redo
|
||||
undo_redo.create_action("Remove 3D object")
|
||||
undo_redo.add_do_property(_cel, "object_properties", new_objects)
|
||||
undo_redo.add_undo_property(_cel, "object_properties", _cel.object_properties)
|
||||
undo_redo.add_do_method(_cel, "_remove_object_node", _cel.selected.id)
|
||||
undo_redo.add_undo_method(_cel, "_add_object_node", _cel.selected.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()
|
||||
_cel.selected = null
|
||||
|
||||
|
||||
func _object_property_changed(object: Cel3DObject) -> void:
|
||||
var undo_redo: UndoRedo = Global.current_project.undo_redo
|
||||
var new_properties := _cel.object_properties.duplicate()
|
||||
new_properties[object.id] = object.serialize()
|
||||
undo_redo.create_action("Change object transform")
|
||||
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(_cel, "_update_objects_transform", object.id)
|
||||
undo_redo.add_undo_method(_cel, "_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 _selected_object(object: Cel3DObject) -> void:
|
||||
if is_instance_valid(object):
|
||||
cel_options.visible = false
|
||||
object_options.visible = true
|
||||
remove_object_button.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 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
|
||||
if node.get_index() > 0:
|
||||
var prev_node: Control = node.get_parent().get_child(node.get_index() - 1)
|
||||
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:
|
||||
cel_options.visible = true
|
||||
object_options.visible = false
|
||||
remove_object_button.disabled = true
|
||||
object_option_button.select(0)
|
||||
|
||||
|
||||
func _set_cel_node_values() -> void:
|
||||
_can_start_timer = false
|
||||
_set_node_values(_cel, cel_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 not is_instance_valid(to_edit):
|
||||
return
|
||||
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 _cel_property_vector3_changed(value: Vector3, prop: String) -> void:
|
||||
_set_value_from_node(_cel, value, prop)
|
||||
_value_handle_change()
|
||||
Global.canvas.gizmos_3d.update()
|
||||
|
||||
|
||||
func _cel_property_value_changed(value: float, prop: String) -> void:
|
||||
_set_value_from_node(_cel, value, prop)
|
||||
_value_handle_change()
|
||||
Global.canvas.gizmos_3d.update()
|
||||
|
||||
|
||||
func _cel_property_item_selected(value: int, prop: String) -> void:
|
||||
_set_value_from_node(_cel, value, prop)
|
||||
_value_handle_change()
|
||||
Global.canvas.gizmos_3d.update()
|
||||
|
||||
|
||||
func _cel_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
|
||||
object_option_button.clear()
|
||||
object_option_button.add_item("None", 0)
|
||||
for id in _cel.object_properties:
|
||||
var item_name: String = _object_names[_cel.object_properties[id]["type"]]
|
||||
object_option_button.add_item(item_name, id + 1)
|
||||
|
||||
|
||||
func _on_UndoRedoTimer_timeout() -> void:
|
||||
if is_instance_valid(_cel.selected):
|
||||
_object_property_changed(_cel.selected)
|
||||
else:
|
||||
var undo_redo: UndoRedo = Global.current_project.undo_redo
|
||||
undo_redo.create_action("Change 3D layer properties")
|
||||
undo_redo.add_do_property(_cel, "scene_properties", _cel.serialize_scene_properties())
|
||||
undo_redo.add_undo_property(_cel, "scene_properties", _cel.scene_properties)
|
||||
undo_redo.add_do_method(_cel, "_scene_property_changed")
|
||||
undo_redo.add_undo_method(_cel, "_scene_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 _on_LoadModelDialog_files_selected(paths: PoolStringArray) -> void:
|
||||
for path in paths:
|
||||
_add_object(Cel3DObject.Type.IMPORTED, path)
|
||||
|
||||
|
||||
func _on_LoadModelDialog_popup_hide() -> void:
|
||||
Global.dialog_open(false)
|
822
src/Tools/3DShapeEdit.tscn
Normal file
822
src/Tools/3DShapeEdit.tscn
Normal file
|
@ -0,0 +1,822 @@
|
|||
[gd_scene load_steps=10 format=2]
|
||||
|
||||
[ext_resource path="res://src/Tools/BaseTool.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://src/Tools/3DShapeEdit.gd" type="Script" id=2]
|
||||
[ext_resource path="res://src/UI/Nodes/ValueSliderV2.tscn" type="PackedScene" id=3]
|
||||
[ext_resource path="res://src/UI/Nodes/ValueSlider.tscn" type="PackedScene" id=4]
|
||||
[ext_resource path="res://src/UI/Nodes/ValueSlider.gd" type="Script" id=5]
|
||||
[ext_resource path="res://src/UI/Nodes/CollapsibleContainer.gd" type="Script" id=6]
|
||||
[ext_resource path="res://src/UI/Nodes/ValueSliderV3.tscn" type="PackedScene" id=7]
|
||||
|
||||
[sub_resource type="InputEventAction" id=33]
|
||||
action = "delete"
|
||||
|
||||
[sub_resource type="ShortCut" id=34]
|
||||
shortcut = SubResource( 33 )
|
||||
|
||||
[node name="3DShapeEdit" instance=ExtResource( 1 )]
|
||||
script = ExtResource( 2 )
|
||||
|
||||
[node name="ColorRect" parent="." index="0"]
|
||||
margin_right = 223.0
|
||||
|
||||
[node name="Label" parent="." index="1"]
|
||||
margin_right = 223.0
|
||||
|
||||
[node name="HandleObjects" type="GridContainer" parent="." index="2"]
|
||||
margin_top = 26.0
|
||||
margin_right = 223.0
|
||||
margin_bottom = 70.0
|
||||
columns = 2
|
||||
|
||||
[node name="Label" type="Label" parent="HandleObjects" index="0"]
|
||||
margin_top = 3.0
|
||||
margin_right = 111.0
|
||||
margin_bottom = 17.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Selected object:"
|
||||
|
||||
[node name="ObjectOptionButton" type="OptionButton" parent="HandleObjects" index="1"]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 115.0
|
||||
margin_right = 223.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="HandleObjects" index="2"]
|
||||
unique_name_in_owner = true
|
||||
margin_top = 24.0
|
||||
margin_right = 111.0
|
||||
margin_bottom = 44.0
|
||||
focus_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Add new object"
|
||||
align = 0
|
||||
|
||||
[node name="RemoveObject" type="Button" parent="HandleObjects" index="3"]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 115.0
|
||||
margin_top = 24.0
|
||||
margin_right = 223.0
|
||||
margin_bottom = 44.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
disabled = true
|
||||
shortcut = SubResource( 34 )
|
||||
text = "Remove object"
|
||||
|
||||
[node name="CelOptions" type="VBoxContainer" parent="." index="3"]
|
||||
unique_name_in_owner = true
|
||||
margin_top = 74.0
|
||||
margin_right = 223.0
|
||||
margin_bottom = 226.0
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="CameraOptions" type="VBoxContainer" parent="CelOptions" index="0"]
|
||||
margin_right = 223.0
|
||||
margin_bottom = 128.0
|
||||
theme_type_variation = "CollapsibleContainer"
|
||||
script = ExtResource( 6 )
|
||||
text = "Camera"
|
||||
visible_content = true
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="CelOptions/CameraOptions" index="1"]
|
||||
margin_top = 24.0
|
||||
margin_right = 223.0
|
||||
margin_bottom = 128.0
|
||||
columns = 2
|
||||
|
||||
[node name="ProjectionLabel" type="Label" parent="CelOptions/CameraOptions/GridContainer" index="0"]
|
||||
margin_top = 3.0
|
||||
margin_right = 110.0
|
||||
margin_bottom = 17.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Projection:"
|
||||
|
||||
[node name="ProjectionOptionButton" type="OptionButton" parent="CelOptions/CameraOptions/GridContainer" index="1"]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 114.0
|
||||
margin_right = 223.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="CelOptions/CameraOptions/GridContainer" index="2"]
|
||||
margin_top = 24.0
|
||||
margin_right = 110.0
|
||||
margin_bottom = 38.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 0
|
||||
text = "Rotation:"
|
||||
|
||||
[node name="CameraRotation" parent="CelOptions/CameraOptions/GridContainer" index="3" instance=ExtResource( 7 )]
|
||||
unique_name_in_owner = true
|
||||
margin_left = 114.0
|
||||
margin_top = 24.0
|
||||
margin_right = 223.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="CelOptions" index="1"]
|
||||
margin_top = 132.0
|
||||
margin_right = 223.0
|
||||
margin_bottom = 152.0
|
||||
theme_type_variation = "CollapsibleContainer"
|
||||
script = ExtResource( 6 )
|
||||
text = "Environment"
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="CelOptions/EnvironmentOptions" index="1"]
|
||||
visible = false
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 48.0
|
||||
columns = 2
|
||||
|
||||
[node name="AmbientColorLabel" type="Label" parent="CelOptions/EnvironmentOptions/GridContainer" index="0"]
|
||||
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="CelOptions/EnvironmentOptions/GridContainer" index="1"]
|
||||
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="CelOptions/EnvironmentOptions/GridContainer" index="2"]
|
||||
margin_top = 29.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 43.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Ambient color energy:"
|
||||
|
||||
[node name="AmbientEnergy" parent="CelOptions/EnvironmentOptions/GridContainer" index="3" instance=ExtResource( 4 )]
|
||||
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="." index="4"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
margin_top = 408.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 800.0
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="GlobalOptions" type="GridContainer" parent="ObjectOptions" index="0"]
|
||||
margin_right = 40.0
|
||||
margin_bottom = 40.0
|
||||
columns = 2
|
||||
|
||||
[node name="VisibleLabel" type="Label" parent="ObjectOptions/GlobalOptions" index="0"]
|
||||
margin_right = 40.0
|
||||
margin_bottom = 14.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Visible:"
|
||||
|
||||
[node name="VisibleCheckBox" type="CheckBox" parent="ObjectOptions/GlobalOptions" index="1"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 71.0
|
||||
margin_bottom = 24.0
|
||||
mouse_default_cursor_shape = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "On"
|
||||
|
||||
[node name="TransformOptions" type="VBoxContainer" parent="ObjectOptions" index="1"]
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 272.0
|
||||
theme_type_variation = "CollapsibleContainer"
|
||||
script = ExtResource( 6 )
|
||||
text = "Transform"
|
||||
visible_content = true
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="ObjectOptions/TransformOptions" index="1"]
|
||||
margin_top = 24.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 272.0
|
||||
columns = 2
|
||||
|
||||
[node name="PositionLabel" type="Label" parent="ObjectOptions/TransformOptions/GridContainer" index="0"]
|
||||
margin_right = 631.0
|
||||
margin_bottom = 80.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
text = "Position:"
|
||||
|
||||
[node name="ObjectPosition" parent="ObjectOptions/TransformOptions/GridContainer" index="1" instance=ExtResource( 7 )]
|
||||
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="ObjectOptions/TransformOptions/GridContainer" index="2"]
|
||||
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="ObjectOptions/TransformOptions/GridContainer" index="3" instance=ExtResource( 7 )]
|
||||
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="ObjectOptions/TransformOptions/GridContainer" index="4"]
|
||||
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="ObjectOptions/TransformOptions/GridContainer" index="5" instance=ExtResource( 7 )]
|
||||
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="ObjectOptions" index="2"]
|
||||
unique_name_in_owner = true
|
||||
margin_top = 276.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 296.0
|
||||
theme_type_variation = "CollapsibleContainer"
|
||||
script = ExtResource( 6 )
|
||||
text = "Mesh"
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="ObjectOptions/MeshOptions" index="1"]
|
||||
visible = false
|
||||
margin_top = 24.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 104.0
|
||||
columns = 2
|
||||
|
||||
[node name="MeshSizeLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="0"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Size:"
|
||||
|
||||
[node name="MeshSize" parent="ObjectOptions/MeshOptions/GridContainer" index="1" instance=ExtResource( 7 )]
|
||||
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="ObjectOptions/MeshOptions/GridContainer" index="2"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Size:"
|
||||
|
||||
[node name="MeshSizeV2" parent="ObjectOptions/MeshOptions/GridContainer" index="3" 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="MeshLeftToRightLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="4"]
|
||||
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="ObjectOptions/MeshOptions/GridContainer" index="5"]
|
||||
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( 5 )
|
||||
|
||||
[node name="MeshRadiusLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="6"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Radius:"
|
||||
|
||||
[node name="MeshRadius" type="TextureProgress" parent="ObjectOptions/MeshOptions/GridContainer" index="7"]
|
||||
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( 5 )
|
||||
|
||||
[node name="MeshHeightLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="8"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Height:"
|
||||
|
||||
[node name="MeshHeight" type="TextureProgress" parent="ObjectOptions/MeshOptions/GridContainer" index="9"]
|
||||
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( 5 )
|
||||
|
||||
[node name="MeshRadialSegmentsLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="10"]
|
||||
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="ObjectOptions/MeshOptions/GridContainer" index="11"]
|
||||
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( 5 )
|
||||
|
||||
[node name="MeshRingsLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="12"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Rings:"
|
||||
|
||||
[node name="MeshRings" type="TextureProgress" parent="ObjectOptions/MeshOptions/GridContainer" index="13"]
|
||||
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( 5 )
|
||||
|
||||
[node name="MeshIsHemisphereLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="14"]
|
||||
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="ObjectOptions/MeshOptions/GridContainer" index="15"]
|
||||
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="ObjectOptions/MeshOptions/GridContainer" index="16"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Height:"
|
||||
|
||||
[node name="MeshMidHeight" type="TextureProgress" parent="ObjectOptions/MeshOptions/GridContainer" index="17"]
|
||||
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( 5 )
|
||||
|
||||
[node name="MeshTopRadiusLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="18"]
|
||||
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="ObjectOptions/MeshOptions/GridContainer" index="19"]
|
||||
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( 5 )
|
||||
|
||||
[node name="MeshBottomRadiusLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="20"]
|
||||
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="ObjectOptions/MeshOptions/GridContainer" index="21"]
|
||||
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( 5 )
|
||||
|
||||
[node name="MeshTextLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="22"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Text:"
|
||||
|
||||
[node name="MeshText" type="LineEdit" parent="ObjectOptions/MeshOptions/GridContainer" index="23"]
|
||||
unique_name_in_owner = true
|
||||
margin_right = 58.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="MeshPixelSizeLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="24"]
|
||||
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="ObjectOptions/MeshOptions/GridContainer" index="25"]
|
||||
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( 5 )
|
||||
snap_step = 0.01
|
||||
|
||||
[node name="MeshCurveStepLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="26"]
|
||||
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="ObjectOptions/MeshOptions/GridContainer" index="27"]
|
||||
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( 5 )
|
||||
|
||||
[node name="MeshHorizontalAlignmentLabel" type="Label" parent="ObjectOptions/MeshOptions/GridContainer" index="28"]
|
||||
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="ObjectOptions/MeshOptions/GridContainer" index="29"]
|
||||
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="ObjectOptions" index="3"]
|
||||
unique_name_in_owner = true
|
||||
margin_top = 276.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 296.0
|
||||
theme_type_variation = "CollapsibleContainer"
|
||||
script = ExtResource( 6 )
|
||||
text = "Light"
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="ObjectOptions/LightOptions" index="1"]
|
||||
visible = false
|
||||
margin_top = 24.0
|
||||
margin_right = 1266.0
|
||||
margin_bottom = 104.0
|
||||
columns = 2
|
||||
|
||||
[node name="LightColorLabel" type="Label" parent="ObjectOptions/LightOptions/GridContainer" index="0"]
|
||||
margin_top = -499.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = -485.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Color:"
|
||||
|
||||
[node name="LightColor" type="ColorPickerButton" parent="ObjectOptions/LightOptions/GridContainer" index="1"]
|
||||
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="ObjectOptions/LightOptions/GridContainer" index="2"]
|
||||
margin_top = -473.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = -459.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Energy:"
|
||||
|
||||
[node name="LightEnergy" parent="ObjectOptions/LightOptions/GridContainer" index="3" instance=ExtResource( 4 )]
|
||||
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="ObjectOptions/LightOptions/GridContainer" index="4"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Negative:"
|
||||
|
||||
[node name="LightNegative" type="CheckBox" parent="ObjectOptions/LightOptions/GridContainer" index="5"]
|
||||
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="ObjectOptions/LightOptions/GridContainer" index="6"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Shadow:"
|
||||
|
||||
[node name="ShadowEnabled" type="CheckBox" parent="ObjectOptions/LightOptions/GridContainer" index="7"]
|
||||
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="ObjectOptions/LightOptions/GridContainer" index="8"]
|
||||
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="ObjectOptions/LightOptions/GridContainer" index="9"]
|
||||
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="ObjectOptions/LightOptions/GridContainer" index="10"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Range:"
|
||||
|
||||
[node name="OmniRange" type="TextureProgress" parent="ObjectOptions/LightOptions/GridContainer" index="11"]
|
||||
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( 5 )
|
||||
|
||||
[node name="SpotRangeLabel" type="Label" parent="ObjectOptions/LightOptions/GridContainer" index="12"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Range:"
|
||||
|
||||
[node name="SpotRange" type="TextureProgress" parent="ObjectOptions/LightOptions/GridContainer" index="13"]
|
||||
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( 5 )
|
||||
|
||||
[node name="SpotAngleLabel" type="Label" parent="ObjectOptions/LightOptions/GridContainer" index="14"]
|
||||
margin_top = 33.0
|
||||
margin_right = 631.0
|
||||
margin_bottom = 47.0
|
||||
size_flags_horizontal = 3
|
||||
text = "Angle:"
|
||||
|
||||
[node name="SpotAngle" type="TextureProgress" parent="ObjectOptions/LightOptions/GridContainer" index="15"]
|
||||
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( 5 )
|
||||
|
||||
[node name="UndoRedoTimer" type="Timer" parent="." index="5"]
|
||||
wait_time = 0.2
|
||||
one_shot = true
|
||||
|
||||
[node name="LoadModelDialog" type="FileDialog" parent="." index="6"]
|
||||
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="HandleObjects/ObjectOptionButton" to="." method="_on_ObjectOptionButton_item_selected"]
|
||||
[connection signal="pressed" from="HandleObjects/RemoveObject" to="." method="_on_RemoveObject_pressed"]
|
||||
[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"]
|
|
@ -260,8 +260,10 @@ func _get_selected_draw_images() -> Array: # Array of Images
|
|||
var project: Project = Global.current_project
|
||||
for cel_index in project.selected_cels:
|
||||
var cel: BaseCel = project.frames[cel_index[0]].cels[cel_index[1]]
|
||||
if not cel is PixelCel:
|
||||
continue
|
||||
if project.layers[cel_index[1]].can_layer_get_drawn():
|
||||
images.append(cel.image)
|
||||
images.append(cel.get_image())
|
||||
return images
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 )
|
||||
|
|
240
src/UI/Canvas/Gizmos3D.gd
Normal file
240
src/UI/Canvas/Gizmos3D.gd
Normal file
|
@ -0,0 +1,240 @@
|
|||
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()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
set_process_input(false)
|
||||
Global.connect("cel_changed", self, "_cel_changed")
|
||||
Global.camera.connect("zoom_changed", self, "update")
|
||||
|
||||
|
||||
func get_hovering_gizmo(pos: Vector2) -> int:
|
||||
var draw_scale := Global.camera.zoom * 10
|
||||
pos -= gizmos_origin
|
||||
# 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]):
|
||||
return Cel3DObject.Gizmos.X_POS
|
||||
elif Geometry.point_is_inside_triangle(pos, gizmo_pos_y[0], gizmo_pos_y[1], gizmo_pos_y[2]):
|
||||
return Cel3DObject.Gizmos.Y_POS
|
||||
elif Geometry.point_is_inside_triangle(pos, gizmo_pos_z[0], gizmo_pos_z[1], gizmo_pos_z[2]):
|
||||
return Cel3DObject.Gizmos.Z_POS
|
||||
elif Geometry.is_point_in_circle(pos, proj_right_local_scale, SCALE_CIRCLE_RADIUS):
|
||||
return Cel3DObject.Gizmos.X_SCALE
|
||||
elif Geometry.is_point_in_circle(pos, proj_up_local_scale, SCALE_CIRCLE_RADIUS):
|
||||
return Cel3DObject.Gizmos.Y_SCALE
|
||||
elif Geometry.is_point_in_circle(pos, proj_back_local_scale, SCALE_CIRCLE_RADIUS):
|
||||
return Cel3DObject.Gizmos.Z_SCALE
|
||||
elif Geometry.is_point_in_polygon(pos, rot_x_offset):
|
||||
return Cel3DObject.Gizmos.X_ROT
|
||||
elif Geometry.is_point_in_polygon(pos, rot_y_offset):
|
||||
return Cel3DObject.Gizmos.Y_ROT
|
||||
elif Geometry.is_point_in_polygon(pos, rot_z_offset):
|
||||
return Cel3DObject.Gizmos.Z_ROT
|
||||
return Cel3DObject.Gizmos.NONE
|
||||
|
||||
|
||||
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 / 4)
|
||||
draw_texture(texture, -center)
|
||||
draw_set_transform(pos, 0, draw_scale / 2)
|
||||
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)
|
||||
if object.applying_gizmos == Cel3DObject.Gizmos.X_ROT:
|
||||
draw_line(gizmos_origin, Global.canvas.current_pixel, Color.red)
|
||||
continue
|
||||
elif object.applying_gizmos == Cel3DObject.Gizmos.Y_ROT:
|
||||
draw_line(gizmos_origin, Global.canvas.current_pixel, Color.green)
|
||||
continue
|
||||
elif object.applying_gizmos == Cel3DObject.Gizmos.Z_ROT:
|
||||
draw_line(gizmos_origin, Global.canvas.current_pixel, Color.blue)
|
||||
continue
|
||||
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], [])
|
|
@ -654,7 +654,7 @@ func _get_selected_draw_images() -> Array: # Array of Image(s)
|
|||
if not cel is PixelCel:
|
||||
continue
|
||||
if project.layers[cel_index[1]].can_layer_get_drawn():
|
||||
images.append(cel.image)
|
||||
images.append(cel.get_image())
|
||||
return images
|
||||
|
||||
|
||||
|
|
|
@ -202,6 +202,8 @@ func create_layer_list() -> void:
|
|||
var layer_name := tr("Pixel layer:")
|
||||
if layer is GroupLayer:
|
||||
layer_name = tr("Group layer:")
|
||||
elif layer is Layer3D:
|
||||
layer_name = tr("3D layer:")
|
||||
layer_name += " %s" % layer.get_layer_path()
|
||||
layers_option_button.add_item(layer_name)
|
||||
|
||||
|
|
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"]
|
|
@ -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.size, false, src_cel.object_properties, src_cel.scene_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 = {}
|
||||
|
@ -561,6 +569,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:
|
||||
|
@ -614,7 +624,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.size, false, src_cel.object_properties, src_cel.scene_properties
|
||||
)
|
||||
else:
|
||||
new_cel = src_cel.get_script().new()
|
||||
|
||||
if src_cel.link_set == null:
|
||||
new_cel.set_content(src_cel.copy_content())
|
||||
|
@ -741,7 +757,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 := []
|
||||
|
||||
|
@ -752,7 +768,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…
Reference in a new issue