Adds missing data

This commit is contained in:
Michel 2025-02-03 19:17:20 +01:00
parent e6391d9fdd
commit 53cdcc3433
620 changed files with 47293 additions and 151 deletions

View file

@ -0,0 +1,86 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandAddBlock
extends CyclopsCommand
#Public data to set before activating command
var blocks_root_path:NodePath
#var origin:Vector3
var block_name:String
var bounds:AABB
var material_path:String
var uv_transform:Transform2D = Transform2D.IDENTITY
var collision_type:Collision.Type = Collision.Type.STATIC
var collision_layers:int = 1
var collision_mask:int = 1
#Private data
var block_path:NodePath
func _init():
command_name = "Add block"
func do_it():
var block:CyclopsBlock = preload("res://addons/cyclops_level_builder/nodes/cyclops_block.gd").new()
#var blocks_root:Node = builder.get_block_add_parent()
var block_parent:Node = builder.get_node(blocks_root_path)
block_parent.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()
block.name = block_name
block.collision_type = collision_type
block.collision_layer = collision_layers
block.collision_mask = collision_mask
var material_id:int = -1
if ResourceLoader.exists(material_path):
var mat = load(material_path)
if mat is Material:
material_id = 0
block.materials.append(mat)
#print("Block root %s" % block)
#print("Create bounds %s" % bounds)
#var parent_xform:Transform3D = node_global_transform(block_parent)
#var vol_xform:Transform3D = Transform3D(Basis(), -bounds.position)
var mesh:ConvexVolume = ConvexVolume.new()
mesh.init_block(bounds, uv_transform, material_id)
mesh.translate(-bounds.position)
block.mesh_vector_data = mesh.to_mesh_vector_data()
# block.block_data = mesh.to_convex_block_data()
block_path = block.get_path()
block.global_transform = Transform3D(Basis(), bounds.position)
# print("AddBlockCommand do_it() %s %s" % [block_inst_id, bounds])
func undo_it():
var block:CyclopsBlock = builder.get_node(block_path)
block.queue_free()
# print("AddBlockCommand undo_it()")

View file

@ -0,0 +1,115 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandAddCylinder
extends CyclopsCommand
#Public data to set before activating command
var blocks_root_path:NodePath
#var block_name:String
var block_name_prefix:String = "Block_"
var origin:Vector3
var axis_normal:Vector3
var height:float
var radius_inner:float
var radius_outer:float
var segments:int
var tube:bool = false
var material_path:String
var uv_transform:Transform2D = Transform2D.IDENTITY
var collision_type:Collision.Type = Collision.Type.STATIC
var collision_layers:int = 1
var collision_mask:int = 1
#Private data
var block_paths:Array[NodePath]
func _init():
command_name = "Add cylinder"
func create_block(blocks_root:Node, set_pivot_xform:Transform3D, mat:Material)->CyclopsBlock:
var block:CyclopsBlock = preload("res://addons/cyclops_level_builder/nodes/cyclops_block.gd").new()
blocks_root.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()
block.name = GeneralUtil.find_unique_name(blocks_root, block_name_prefix)
block.global_transform = set_pivot_xform.affine_inverse()
block.collision_type = collision_type
block.collision_layer = collision_layers
block.collision_mask = collision_mask
if mat:
block.materials.append(mat)
return block
func do_it():
# var blocks_root:CyclopsBlocks = builder.get_node(blocks_root_path)
var blocks_root:Node = builder.get_node(blocks_root_path)
var material:Material
var material_id:int = -1
if ResourceLoader.exists(material_path):
var mat = load(material_path)
if mat is Material:
material_id = 0
material = mat
var set_pivot_xform:Transform3D = Transform3D(Basis.IDENTITY, -origin)
if tube:
var bounding_points_inner:PackedVector3Array = MathUtil.create_circle_points(origin, axis_normal, radius_inner, segments)
var bounding_points_outer:PackedVector3Array = MathUtil.create_circle_points(origin, axis_normal, radius_outer, segments)
for p_idx0 in bounding_points_inner.size():
var p_idx1:int = wrap(p_idx0 + 1, 0, bounding_points_inner.size())
var block:CyclopsBlock = create_block(blocks_root, set_pivot_xform, material)
var mesh:ConvexVolume = ConvexVolume.new()
var base_points:PackedVector3Array = [bounding_points_inner[p_idx0], bounding_points_inner[p_idx1], bounding_points_outer[p_idx1], bounding_points_outer[p_idx0]]
mesh.init_prism(base_points, axis_normal * height, uv_transform, material_id)
mesh.transform(set_pivot_xform)
# block.block_data = mesh.to_convex_block_data()
block.mesh_vector_data = mesh.to_mesh_vector_data()
block_paths.append(block.get_path())
else:
var block:CyclopsBlock = create_block(blocks_root, set_pivot_xform, material)
var bounding_points:PackedVector3Array = MathUtil.create_circle_points(origin, axis_normal, radius_outer, segments)
var mesh:ConvexVolume = ConvexVolume.new()
mesh.init_prism(bounding_points, axis_normal * height, uv_transform, material_id)
mesh.transform(set_pivot_xform)
block.mesh_vector_data = mesh.to_mesh_vector_data()
block_paths.append(block.get_path())
func undo_it():
for path in block_paths:
var block:CyclopsBlock = builder.get_node(path)
block.queue_free()

View file

@ -0,0 +1,80 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandAddPrism
extends CyclopsCommand
@export var blocks_root_path:NodePath
@export var block_name:String
@export var base_polygon:PackedVector3Array
@export var extrude:Vector3
#var local_transform:Transform3D
@export var uv_transform:Transform2D
@export var material_path:String
@export var collision_type:Collision.Type = Collision.Type.STATIC
@export var collision_layers:int = 1
@export var collision_mask:int = 1
#Private
var block_path:NodePath
func _init():
command_name = "Add prism"
func do_it():
var block:CyclopsBlock = preload("res://addons/cyclops_level_builder/nodes/cyclops_block.gd").new()
var blocks_root:Node = builder.get_node(blocks_root_path)
blocks_root.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()
block.name = block_name
#block.transform = local_transform
block.collision_type = collision_type
block.collision_layer = collision_layers
block.collision_mask = collision_mask
var material_id:int = -1
if ResourceLoader.exists(material_path):
var mat = load(material_path)
if mat is Material:
material_id = 0
block.materials.append(mat)
var set_pivot_xform:Transform3D = Transform3D(Basis.IDENTITY, -base_polygon[0])
var mesh:ConvexVolume = ConvexVolume.new()
mesh.init_prism(base_polygon, extrude, uv_transform, material_id)
mesh.transform(set_pivot_xform)
block.mesh_vector_data = mesh.to_mesh_vector_data()
block_path = block.get_path()
block.global_transform = set_pivot_xform.affine_inverse()
# print("AddBlockCommand do_it() %s %s" % [block_inst_id, bounds])
func undo_it():
var block:CyclopsBlock = builder.get_node(block_path)
block.queue_free()
# print("AddBlockCommand undo_it()")

View file

@ -0,0 +1,139 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandAddStairs
extends CyclopsCommand
#var blocks_root_inst_id:int
var blocks_root_path:NodePath
var block_name_prefix:String
var floor_normal:Vector3
var drag_origin:Vector3
var base_drag_cur:Vector3
var block_drag_cur:Vector3
var step_height:float = .25
var step_depth:float = .5
var direction:int = 0
var uv_transform:Transform2D
var material_path:String
var collision_type:Collision.Type = Collision.Type.STATIC
var collision_layers:int = 1
var collision_mask:int = 1
#Private data
var block_paths:Array[NodePath]
func _init():
command_name = "Add stairs"
func create_block(blocks_root:Node, mat:Material)->CyclopsBlock:
var block:CyclopsBlock = preload("res://addons/cyclops_level_builder/nodes/cyclops_block.gd").new()
blocks_root.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()
block.name = GeneralUtil.find_unique_name(blocks_root, block_name_prefix)
block.collision_type = collision_type
block.collision_layer = collision_layers
block.collision_mask = collision_mask
if mat:
block.materials.append(mat)
return block
func do_it():
var blocks_root:Node = builder.get_node(blocks_root_path)
var material:Material
var material_id:int = -1
if ResourceLoader.exists(material_path):
var mat = load(material_path)
if mat is Material:
material_id = 0
material = mat
var tan_bi:Array[Vector3] = MathUtil.get_axis_aligned_tangent_and_binormal(floor_normal)
var u_normal:Vector3 = tan_bi[0]
var v_normal:Vector3 = tan_bi[1]
#Rotate ccw by 90 degree increments
match direction:
1:
var tmp:Vector3 = u_normal
u_normal = -v_normal
v_normal = tmp
2:
u_normal = -u_normal
v_normal = -v_normal
3:
var tmp:Vector3 = -u_normal
u_normal = v_normal
v_normal = tmp
var u_span:Vector3 = (base_drag_cur - drag_origin).project(u_normal)
var v_span:Vector3 = (base_drag_cur - drag_origin).project(v_normal)
var stairs_origin:Vector3 = drag_origin
if u_span.dot(u_normal) < 0:
stairs_origin += u_span
u_span = -u_span
if v_span.dot(v_normal) < 0:
stairs_origin += v_span
v_span = -v_span
#Stairs should ascend along v axis
var height_offset = block_drag_cur - base_drag_cur
if height_offset.dot(floor_normal) < 0:
return
var num_steps:int = min(v_span.length() / step_depth, height_offset.length() / step_height)
var max_height:float = floor(height_offset.length() / step_height) * step_height
var step_span:Vector3 = v_normal * step_depth
for i in num_steps:
var base_points:PackedVector3Array = [stairs_origin + step_span * i, \
stairs_origin + u_span + step_span * i, \
stairs_origin + u_span + step_span * (i + 1), \
stairs_origin + step_span * (i + 1)]
var pivot_xform:Transform3D = Transform3D(Basis.IDENTITY, -base_points[0])
var mesh:ConvexVolume = ConvexVolume.new()
mesh.init_prism(base_points, \
floor_normal * (max_height - step_height * i), \
uv_transform, material_id)
mesh.transform(pivot_xform)
var block:CyclopsBlock = create_block(blocks_root, material)
block.mesh_vector_data = mesh.to_mesh_vector_data()
block.global_transform = pivot_xform.affine_inverse()
block_paths.append(block.get_path())
func undo_it():
for path in block_paths:
var block:CyclopsBlock = builder.get_node(path)
block.queue_free()

View file

@ -0,0 +1,74 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandAddVertices
extends CyclopsCommand
#Public
var points_to_add:PackedVector3Array
var block_path:NodePath
#Private
var tracked_block_data:MeshVectorData
var selected_points:PackedVector3Array
func _init():
command_name = "Add vertices"
func do_it():
var block:CyclopsBlock = builder.get_node(block_path)
if !tracked_block_data:
var tracked_vol:ConvexVolume = block.control_mesh
tracked_block_data = tracked_vol.to_mesh_vector_data()
for v in tracked_vol.vertices:
if v.selected:
selected_points.append(v.point)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(tracked_block_data)
var point_list:PackedVector3Array = vol.get_points()
var local_points = block.global_transform.affine_inverse() * points_to_add
point_list.append_array(local_points)
var new_vol:ConvexVolume = ConvexVolume.new()
new_vol.init_from_points(point_list)
new_vol.copy_face_attributes(vol)
for v_idx in new_vol.vertices.size():
var v:ConvexVolume.VertexInfo = new_vol.vertices[v_idx]
if selected_points.has(v.point):
v.selected = true
block.mesh_vector_data = new_vol.to_mesh_vector_data()
func undo_it():
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = tracked_block_data

View file

@ -0,0 +1,106 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandClipBlock
extends CyclopsCommand
#Public data to set before activating command
var blocks_root_path:NodePath
var block_path:NodePath
var cut_plane:Plane
var uv_transform:Transform2D = Transform2D.IDENTITY
var material_path:String = ""
#Private
var block_sibling_name:String
var old_block_data:MeshVectorData
var old_mat_list:Array[Material]
var block_sibling_path:NodePath
func _init():
command_name = "Clip block"
func get_material_index(mat_list:Array[Material], path:String)->int:
if path.is_empty():
return -1
for i in mat_list.size():
var mat:Material = mat_list[i]
if mat != null && mat.resource_path == path:
return i
return -1
func do_it():
var blocks_root:Node = builder.get_node(blocks_root_path)
var block:CyclopsBlock = builder.get_node(block_path)
old_block_data = block.mesh_vector_data.duplicate()
old_mat_list = block.materials.duplicate()
var new_mat_list0:Array[Material] = old_mat_list.duplicate()
var cut_mat_idx = get_material_index(old_mat_list, material_path)
if cut_mat_idx == -1:
var mat = load(material_path)
if mat is Material:
cut_mat_idx = new_mat_list0.size()
new_mat_list0.append(mat)
var new_mat_list1:Array[Material] = new_mat_list0.duplicate()
#var cut_plane_reverse:Plane = Plane(-cut_plane.normal, cut_plane.get_center())
var w2l:Transform3D = block.global_transform.affine_inverse()
var cut_plane_local:Plane = w2l * cut_plane
var vol0:ConvexVolume = block.control_mesh.cut_with_plane(cut_plane_local, uv_transform, cut_mat_idx)
var vol1:ConvexVolume = block.control_mesh.cut_with_plane(MathUtil.flip_plane(cut_plane_local), uv_transform, cut_mat_idx)
#Set data of existing block
block.mesh_vector_data = vol0.to_mesh_vector_data()
block.materials = new_mat_list0
#Create second block
var block_sibling:CyclopsBlock = preload("../nodes/cyclops_block.gd").new()
blocks_root.add_child(block_sibling)
block_sibling.owner = builder.get_editor_interface().get_edited_scene_root()
block_sibling.name = block_sibling_name
block_sibling.global_transform = block.global_transform
#block_sibling.selected = block.selected
block_sibling_path = block_sibling.get_path()
block_sibling.mesh_vector_data = vol1.to_mesh_vector_data()
block_sibling.materials = new_mat_list1
func undo_it():
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = old_block_data
block.materials = old_mat_list.duplicate()
var block_sibling:CyclopsBlock = builder.get_node(block_sibling_path)
block_sibling.queue_free()

View file

@ -0,0 +1,82 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandDeleteBlocks
extends CyclopsCommand
#Public
var block_paths:Array[NodePath]
#Private
var tracked_blocks:Array[TrackedBlock]
func _init():
command_name = "Delete blocks"
func will_change_anything():
if !block_paths.is_empty():
return true
return false
func do_it():
#print("Delete do_it")
if tracked_blocks.is_empty():
var points:PackedVector3Array
for path in block_paths:
var block:CyclopsBlock = builder.get_node(path)
var tracker:TrackedBlock = TrackedBlock.new(block)
tracked_blocks.append(tracker)
#Delete source blocks
for block_path in block_paths:
var del_block:CyclopsBlock = builder.get_node(block_path)
del_block.get_parent().remove_child(del_block)
del_block.queue_free()
func undo_it():
#print("Delete undo_it")
for tracked in tracked_blocks:
var parent = builder.get_node(tracked.path_parent)
var block:CyclopsBlock = preload("../nodes/cyclops_block.gd").new()
block.mesh_vector_data = tracked.data
block.materials = tracked.materials
block.name = tracked.name
#block.selected = tracked.selected
block.collision_type = tracked.collision_type
block.collision_layer = tracked.collision_layers
block.collision_mask = tracked.collision_mask
parent.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()
block.global_transform = tracked.world_xform

View file

@ -0,0 +1,90 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandDuplicateBlocks
extends CyclopsCommand
#Public
var blocks_root_path:NodePath
var blocks_to_duplicate:Array[NodePath]
var move_offset:Vector3
var lock_uvs:bool
#Private
class BlockInfo extends RefCounted:
var new_block:CyclopsBlock
var source_data:MeshVectorData
var source_global_transform:Transform3D
func _init(new_block:CyclopsBlock, source_data:MeshVectorData, source_global_transform:Transform3D):
self.new_block = new_block
self.source_data = source_data
self.source_global_transform = source_global_transform
var added_blocks:Array[BlockInfo]
func will_change_anything():
return !added_blocks.is_empty()
func do_it():
if added_blocks.is_empty():
#Create new blocks
for block_path in blocks_to_duplicate:
var new_block:CyclopsBlock = preload("../nodes/cyclops_block.gd").new()
var source_block:CyclopsBlock = builder.get_node(block_path)
var blocks_root:Node = builder.get_node(blocks_root_path)
new_block.name = GeneralUtil.find_unique_name(blocks_root, source_block.name)
blocks_root.add_child(new_block)
new_block.owner = builder.get_editor_interface().get_edited_scene_root()
new_block.global_transform = source_block.global_transform
new_block.mesh_vector_data = source_block.mesh_vector_data.duplicate()
var info:BlockInfo = BlockInfo.new(new_block, source_block.mesh_vector_data, source_block.global_transform)
new_block.materials = source_block.materials
#new_block.selected = true
added_blocks.append(info)
for path in blocks_to_duplicate:
var block:CyclopsBlock = builder.get_node(path)
#block.selected = false
for info in added_blocks:
var new_xform:Transform3D = info.source_global_transform.translated(move_offset)
info.new_block.global_transform = new_xform
func undo_it():
for block in added_blocks:
block.new_block.queue_free()
added_blocks = []
for path in blocks_to_duplicate:
var block:CyclopsBlock = builder.get_node(path)
#block.selected = true

View file

@ -0,0 +1,152 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandIntersectBlock
extends CyclopsCommand
class NewBlockInfo extends RefCounted:
var data:MeshVectorData
var materials:Array[Material]
var path:NodePath
var xform:Transform3D
#var centroid:Vector3
#Public
var block_paths:Array[NodePath]
var main_block_path:NodePath
var block_name_prefix:String = "Block_"
#Private
var start_blocks:Array[TrackedBlock]
var main_block_cache:TrackedBlock
#var added_blocks:Array[NewBlockInfo]
var added_block:NewBlockInfo
func _init():
command_name = "Intersect blocks"
func restore_tracked_block(tracked:TrackedBlock)->CyclopsBlock:
var parent = builder.get_node(tracked.path_parent)
var block:CyclopsBlock = preload("../nodes/cyclops_block.gd").new()
block.mesh_vector_data = tracked.data
block.materials = tracked.materials
block.name = tracked.name
#block.selected = tracked.selected
block.global_transform = tracked.world_xform
block.collision_type = tracked.collision_type
block.collision_layer = tracked.collision_layers
block.collision_mask = tracked.collision_mask
parent.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()
if tracked.selected:
var selection:EditorSelection = builder.get_editor_interface().get_selection()
selection.add_node(block)
return block
func will_change_anything()->bool:
var main_block:CyclopsBlock = builder.get_node(main_block_path)
var main_vol:ConvexVolume = main_block.control_mesh
main_vol = main_vol.transformed(main_block.global_transform)
if block_paths.is_empty():
return false
for minuend_path in block_paths:
var minuend_block:CyclopsBlock = builder.get_node(minuend_path)
var minuend_vol:ConvexVolume = minuend_block.control_mesh
minuend_vol = minuend_vol.transformed(minuend_block.global_transform)
if minuend_vol.intersects_convex_volume(main_vol):
return true
return false
func do_it():
var main_block:CyclopsBlock = builder.get_node(main_block_path)
var snap_to_grid_util:SnapToGridUtil = CyclopsAutoload.calc_snap_to_grid_util()
if start_blocks.is_empty():
var main_vol:ConvexVolume = main_block.control_mesh
main_block_cache = TrackedBlock.new(main_block)
main_vol = main_vol.transformed(main_block.global_transform)
for path in block_paths:
var block:CyclopsBlock = builder.get_node(path)
var minuend_vol:ConvexVolume = block.control_mesh
minuend_vol = minuend_vol.transformed(block.global_transform)
if !minuend_vol.intersects_convex_volume(main_vol):
continue
var tracker:TrackedBlock = TrackedBlock.new(block)
start_blocks.append(tracker)
main_vol = minuend_vol.intersect(main_vol)
var block_info:NewBlockInfo = NewBlockInfo.new()
block_info.materials = main_block.materials
var xform_inv:Transform3D = main_block.global_transform.affine_inverse()
main_vol = main_vol.transformed(xform_inv)
block_info.data = main_vol.to_mesh_vector_data()
block_info.xform = main_block.global_transform
added_block = block_info
#Delete source blocks
for block_info in start_blocks:
var del_block:CyclopsBlock = builder.get_node(block_info.path)
del_block.queue_free()
main_block.queue_free()
#Create blocks
var block:CyclopsBlock = preload("../nodes/cyclops_block.gd").new()
var parent:Node = builder.get_node(start_blocks[0].path_parent)
parent.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()
block.name = GeneralUtil.find_unique_name(parent, block_name_prefix)
block.mesh_vector_data = added_block.data
block.materials = added_block.materials
block.global_transform = added_block.xform
added_block.path = block.get_path()
func undo_it():
#for info in added_blocks:
var added_block_node:CyclopsBlock = builder.get_node(added_block.path)
added_block_node.queue_free()
restore_tracked_block(main_block_cache)
for tracked in start_blocks:
restore_tracked_block(tracked)

View file

@ -0,0 +1,148 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandMergeBlocks
extends CyclopsCommand
#Public
var block_paths:Array[NodePath]
var block_name_prefix:String = "Block_"
#Private
var tracked_blocks:Array[TrackedBlock]
var merged_block_data:MeshVectorData
var merged_mat_list:Array[Material]
var merged_block_path:NodePath
var world_pivot:Vector3
func _init():
command_name = "Merge blocks"
func get_best_face(centroid:Vector3, ref_list:Array[NodePath])->Array:
var best_face:ConvexVolume.FaceInfo
var best_dist:float = INF
var best_block:CyclopsBlock
for block_path in ref_list:
var block:CyclopsBlock = builder.get_node(block_path)
var vol:ConvexVolume = block.control_mesh
for f in vol.faces:
var face_center:Vector3 = f.get_centroid()
var offset:float = centroid.distance_squared_to(face_center)
if offset < best_dist:
best_dist = offset
best_face = f
best_block = block
if best_face.material_id == -1:
return [best_face, null]
return [best_face, best_block.materials[best_face.material_id]]
func copy_face_attributes(target:ConvexVolume, ref_list:Array[NodePath])->Array[Material]:
var mat_list:Array[Material]
for f in target.faces:
var centroid:Vector3 = f.get_centroid()
var res:Array = get_best_face(centroid, ref_list)
var ref_face:ConvexVolume.FaceInfo = res[0]
var material:Material = res[1]
var mat_idx:int = -1
if material != null:
mat_idx = mat_list.find(material)
if mat_idx == -1:
mat_idx = mat_list.size()
mat_list.append(material)
f.material_id = mat_idx
f.uv_transform = ref_face.uv_transform
f.selected = ref_face.selected
return mat_list
func do_it():
if tracked_blocks.is_empty():
var points:PackedVector3Array
var first_block:CyclopsBlock = builder.get_node(block_paths[0])
world_pivot = first_block.global_transform.origin
for path in block_paths:
var block:CyclopsBlock = builder.get_node(path)
var tracker:TrackedBlock = TrackedBlock.new(block)
tracked_blocks.append(tracker)
var world_block:ConvexVolume = ConvexVolume.new()
world_block.init_from_mesh_vector_data(block.control_mesh.to_mesh_vector_data())
world_block.transform(block.global_transform)
points.append_array(world_block.get_points())
var merged_vol:ConvexVolume = ConvexVolume.new()
merged_vol.init_from_points(points)
merged_mat_list = copy_face_attributes(merged_vol, block_paths)
merged_vol.translate(-world_pivot)
merged_block_data = merged_vol.to_mesh_vector_data()
#Delete source blocks
for block_path in block_paths:
var del_block:CyclopsBlock = builder.get_node(block_path)
del_block.queue_free()
#Create block
var block:CyclopsBlock = preload("../nodes/cyclops_block.gd").new()
var parent:Node = builder.get_node(tracked_blocks[0].path_parent)
parent.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()
block.name = GeneralUtil.find_unique_name(parent, block_name_prefix)
block.mesh_vector_data = merged_block_data
block.materials = merged_mat_list
block.global_transform = Transform3D.IDENTITY.translated(world_pivot)
#block.materials
merged_block_path = block.get_path()
func undo_it():
# var blocks_root:CyclopsBlocks = builder.get_node(blocks_root_path)
var merged_block:CyclopsBlock = builder.get_node(merged_block_path)
merged_block.queue_free()
# for i in blocks_to_merge.size():
for tracked in tracked_blocks:
var parent = builder.get_node(tracked.path_parent)
var block:CyclopsBlock = preload("../nodes/cyclops_block.gd").new()
block.mesh_vector_data = tracked.data
block.materials = tracked.materials
block.name = tracked.name
#block.selected = tracked.selected
block.global_transform = tracked.world_xform
block.collision_type = tracked.collision_type
block.collision_layer = tracked.collision_layers
block.collision_mask = tracked.collision_mask
parent.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()

View file

@ -0,0 +1,142 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandMergeVertices
extends CyclopsCommand
class BlockVertexChanges extends RefCounted:
var block_path:NodePath
var vertex_indices:Array[int] = []
var tracked_block_data:MeshVectorData
#Private
var block_map:Dictionary = {}
#Public
var merge_point:Vector3
enum MergeType { POINT, CENTER, FIRST, LAST }
var merge_type:MergeType = MergeType.CENTER
func add_vertex(block_path:NodePath, index:int):
add_vertices(block_path, [index])
func add_vertices(block_path:NodePath, indices:Array[int]):
# print("adding vertex %s %s" % [block_path, indices])
var changes:BlockVertexChanges
if block_map.has(block_path):
changes = block_map[block_path]
else:
changes = BlockVertexChanges.new()
changes.block_path = block_path
var block:CyclopsBlock = builder.get_node(block_path)
changes.tracked_block_data = block.mesh_vector_data.duplicate()
block_map[block_path] = changes
for index in indices:
if !changes.vertex_indices.has(index):
changes.vertex_indices.append(index)
func _init():
command_name = "Move vertices"
func do_it():
# print("move verts do_it")
for block_path in block_map.keys():
var block:CyclopsBlock = builder.get_node(block_path)
var w2l:Transform3D = block.global_transform.affine_inverse()
var rec:BlockVertexChanges = block_map[block_path]
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
var selected_points:PackedVector3Array
var new_points:PackedVector3Array
for v_idx in vol.vertices.size():
if !rec.vertex_indices.has(v_idx):
var p:Vector3 = vol.vertices[v_idx].point
new_points.append(p)
var merge_point_local:Vector3
match merge_type:
MergeType.POINT:
merge_point_local = w2l * merge_point
pass
MergeType.CENTER:
var centroid:Vector3
var count:int = 0
for v_idx in vol.vertices.size():
if rec.vertex_indices.has(v_idx):
var p:Vector3 = vol.vertices[v_idx].point
centroid += p
count += 1
centroid /= count
merge_point_local = centroid
MergeType.FIRST:
merge_point_local = vol.vertices[rec.vertex_indices[0]].point
MergeType.LAST:
merge_point_local = vol.vertices[rec.vertex_indices[-1]].point
new_points.append(merge_point_local)
selected_points.append(merge_point_local)
var new_vol:ConvexVolume = ConvexVolume.new()
new_vol.init_from_points(new_points)
new_vol.copy_face_attributes(vol)
for v_idx in new_vol.vertices.size():
var v:ConvexVolume.VertexInfo = new_vol.vertices[v_idx]
# print ("vol point %s " % v.point)
if selected_points.has(v.point):
# print("set sel")
v.selected = true
block.mesh_vector_data = new_vol.to_mesh_vector_data()
func undo_it():
# print("move verts undo_it")
for block_path in block_map.keys():
var rec:BlockVertexChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = rec.tracked_block_data
func will_change_anything()->bool:
for path in block_map:
var rec:BlockVertexChanges = block_map[path]
match merge_type:
MergeType.POINT:
if rec.vertex_indices.size() >= 1:
return true
_:
if rec.vertex_indices.size() >= 2:
return true
return false

View file

@ -0,0 +1,150 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandMoveEdges
extends CyclopsCommand
class BlockEdgeChanges extends RefCounted:
var block_path:NodePath
var edge_indices:Array[int] = []
var tracked_block_data:MeshVectorData
#Public
var move_offset:Vector3 = Vector3.ZERO
#Private
var block_map:Dictionary = {}
func add_edge(block_path:NodePath, index:int):
add_edges(block_path, [index])
func add_edges(block_path:NodePath, indices:Array[int]):
var changes:BlockEdgeChanges
if block_map.has(block_path):
changes = block_map[block_path]
else:
changes = BlockEdgeChanges.new()
changes.block_path = block_path
var block:CyclopsBlock = builder.get_node(block_path)
changes.tracked_block_data = block.mesh_vector_data
block_map[block_path] = changes
for index in indices:
if !changes.edge_indices.has(index):
changes.edge_indices.append(index)
func _init():
command_name = "Move edges"
func do_it():
# print("cmd move edges- DO IT")
for block_path in block_map.keys():
# print("%s" % block_path)
var block:CyclopsBlock = builder.get_node(block_path)
var rec:BlockEdgeChanges = block_map[block_path]
# print("rec %s" % rec)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
# print("init done")
var w2l:Transform3D = block.global_transform.affine_inverse()
var move_offset_local = w2l.basis * move_offset
#print("move_offset ", move_offset)
#print("move_offset_local ", move_offset_local)
var vert_indices:PackedInt32Array
for edge_index in rec.edge_indices:
var e:ConvexVolume.EdgeInfo = vol.edges[edge_index]
if !vert_indices.has(e.start_index):
vert_indices.append(e.start_index)
if !vert_indices.has(e.end_index):
vert_indices.append(e.end_index)
for v_idx in vert_indices:
var v:ConvexVolume.VertexInfo = vol.vertices[v_idx]
v.point += move_offset_local
block.mesh_vector_data = vol.to_mesh_vector_data()
####
#var moved_vert_indices:PackedInt32Array
#var new_points:PackedVector3Array
#var new_sel_centroids:PackedVector3Array
#var moved_indices:Array[int] = []
#for edge_index in rec.edge_indices:
#var e:ConvexVolume.EdgeInfo = vol.edges[edge_index]
#var v0:ConvexVolume.VertexInfo = vol.vertices[e.start_index]
#var v1:ConvexVolume.VertexInfo = vol.vertices[e.end_index]
#if e.selected:
#new_sel_centroids.append((v0.point + v1.point) / 2 + move_offset_local)
#
#if !moved_indices.has(e.start_index):
#new_points.append(v0.point + move_offset_local)
#moved_indices.append(e.start_index)
#if !moved_indices.has(e.end_index):
#new_points.append(v1.point + move_offset_local)
#moved_indices.append(e.end_index)
#else:
#if !moved_indices.has(e.start_index):
#new_points.append(v0.point + move_offset_local)
#moved_indices.append(e.start_index)
#if !moved_indices.has(e.end_index):
#new_points.append(v1.point + move_offset_local)
#moved_indices.append(e.end_index)
#
#for v_idx in vol.vertices.size():
#if !moved_indices.has(v_idx):
#new_points.append(vol.vertices[v_idx].point)
##print("new points_ %s" % new_points)
#
#var new_vol:ConvexVolume = ConvexVolume.new()
#new_vol.init_from_points(new_points)
#
#new_vol.copy_face_attributes(vol)
#
##print("new init done")
#
##Copy selection data
#for e_idx in new_vol.edges.size():
#var e_new:ConvexVolume.EdgeInfo = new_vol.edges[e_idx]
#var centroid:Vector3 = (new_vol.vertices[e_new.start_index].point + new_vol.vertices[e_new.end_index].point) / 2
## print ("vol point %s " % v1.point)
#if new_sel_centroids.has(centroid):
## print("set sel")
#e_new.selected = true
#
#block.mesh_vector_data = new_vol.to_mesh_vector_data()
func undo_it():
for block_path in block_map.keys():
var rec:BlockEdgeChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = rec.tracked_block_data

View file

@ -0,0 +1,102 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandMoveFacePlanar
extends CyclopsCommand
#Public data to set before activating command
var blocks_root_path:NodePath
var block_path:NodePath
var move_dir_normal:Vector3
var move_amount:float
var face_index:int
var lock_uvs:bool = false
#Private
var block_name:String
var block_selected:bool
var tracked_block_data:MeshVectorData
var deleted:bool = false
func _init():
command_name = "Move face planar"
func move_to(offset:Vector3, intermediate:bool):
# print("move_to off %s faceindex %s amount %s movedir %s" % [offset, face_index, move_amount, move_dir_normal])
if !tracked_block_data:
var block:CyclopsBlock = builder.get_node(block_path)
block_name = block.name
block_selected = block.selected
tracked_block_data = block.mesh_vector_data
var ctl_mesh:ConvexVolume = ConvexVolume.new()
ctl_mesh.init_from_mesh_vector_data(tracked_block_data)
var new_mesh:ConvexVolume = ctl_mesh.translate_face_plane(face_index, offset, lock_uvs)
#print("offset %s" % offset)
#print("ctl_mesh %s" % ctl_mesh.get_points())
var block:CyclopsBlock = builder.get_node(block_path)
if new_mesh == null || new_mesh.is_empty():
#print("new_mesh EMPTY")
block.block_data = null
if !intermediate:
block.queue_free()
deleted = true
return
#print("new_mesh %s" % new_mesh.get_points())
var result_data:MeshVectorData = new_mesh.to_mesh_vector_data()
block.mesh_vector_data = result_data
func do_it_intermediate():
move_to(move_dir_normal * move_amount, true)
func do_it():
move_to(move_dir_normal * move_amount, false)
func undo_it():
if deleted:
var block:CyclopsBlock = preload("../nodes/cyclops_block.gd").new()
var blocks_root:Node = builder.get_node(blocks_root_path)
blocks_root.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()
block.mesh_vector_data = tracked_block_data
block.name = block_name
block.selected = block_selected
deleted = false
return
move_to(Vector3.ZERO, false)

View file

@ -0,0 +1,144 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandMoveFaces
extends CyclopsCommand
class BlockFaceChanges extends RefCounted:
var block_path:NodePath
var face_indices:Array[int] = []
var tracked_block_data:MeshVectorData
#Public
var move_offset:Vector3 = Vector3.ZERO
#Private
var block_map:Dictionary = {}
func add_face(block_path:NodePath, index:int):
# print("Adding face %s %s" % [block_path, index])
add_faces(block_path, [index])
func add_faces(block_path:NodePath, indices:Array[int]):
var changes:BlockFaceChanges
if block_map.has(block_path):
changes = block_map[block_path]
else:
changes = BlockFaceChanges.new()
changes.block_path = block_path
var block:CyclopsBlock = builder.get_node(block_path)
changes.tracked_block_data = block.mesh_vector_data
block_map[block_path] = changes
for index in indices:
if !changes.face_indices.has(index):
changes.face_indices.append(index)
func _init():
command_name = "Move faces"
func do_it():
# print("cmd move edges- DO IT")
for block_path in block_map.keys():
# print("%s" % block_path)
var block:CyclopsBlock = builder.get_node(block_path)
var rec:BlockFaceChanges = block_map[block_path]
var w2l:Transform3D = block.global_transform.affine_inverse()
var move_offset_local:Vector3 = w2l.basis * move_offset
# print("rec %s" % rec)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
var vert_indices:PackedInt32Array
for f_index in rec.face_indices:
var f:ConvexVolume.FaceInfo = vol.faces[f_index]
for v_idx in f.vertex_indices:
if !vert_indices.has(v_idx):
vert_indices.append(v_idx)
for v_idx in vert_indices:
var v:ConvexVolume.VertexInfo = vol.vertices[v_idx]
v.point += move_offset_local
block.mesh_vector_data = vol.to_mesh_vector_data()
####
# print("init done")
#var new_points:PackedVector3Array
#var new_sel_centroids:PackedVector3Array
#var moved_vert_indices:Array[int] = []
#for face_index in rec.face_indices:
#var f:ConvexVolume.FaceInfo = vol.faces[face_index]
#var centroid:Vector3 = f.get_centroid()
## var v0:ConvexVolume.VertexInfo = vol.vertices[e.start_index]
## var v1:ConvexVolume.VertexInfo = vol.vertices[e.end_index]
#if f.selected:
#new_sel_centroids.append(centroid + move_offset_local)
#
#for v_idx in f.vertex_indices:
#if !moved_vert_indices.has(v_idx):
#new_points.append(vol.vertices[v_idx].point + move_offset_local)
#moved_vert_indices.append(v_idx)
#else:
#for v_idx in f.vertex_indices:
#if !moved_vert_indices.has(v_idx):
#new_points.append(vol.vertices[v_idx].point + move_offset_local)
#moved_vert_indices.append(v_idx)
#
#for v_idx in vol.vertices.size():
#if !moved_vert_indices.has(v_idx):
#new_points.append(vol.vertices[v_idx].point)
##print("new points_ %s" % new_points)
#
#var new_vol:ConvexVolume = ConvexVolume.new()
#new_vol.init_from_points(new_points)
#
#new_vol.copy_face_attributes(vol)
##print("new init done")
#
##Copy selection data
#for f_idx in new_vol.faces.size():
#var f_new:ConvexVolume.FaceInfo = new_vol.faces[f_idx]
#var centroid:Vector3 = f_new.get_centroid()
## print ("vol point %s " % v1.point)
#if new_sel_centroids.has(centroid):
## print("set sel")
#f_new.selected = true
#
#block.mesh_vector_data = new_vol.to_mesh_vector_data()
func undo_it():
for block_path in block_map.keys():
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = rec.tracked_block_data

View file

@ -0,0 +1,117 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandMoveVertices
extends CyclopsCommand
class BlockVertexChanges extends RefCounted:
var block_path:NodePath
var vertex_indices:Array[int] = []
var tracked_block_data:MeshVectorData
#Public
@export var move_offset:Vector3 = Vector3.ZERO
@export var triplanar_lock_uvs:bool = false
#Private
var block_map:Dictionary = {}
func add_vertex(block_path:NodePath, index:int):
add_vertices(block_path, [index])
func add_vertices(block_path:NodePath, indices:Array[int]):
# print("adding vertex %s %s" % [block_path, indices])
var changes:BlockVertexChanges
if block_map.has(block_path):
changes = block_map[block_path]
else:
changes = BlockVertexChanges.new()
changes.block_path = block_path
var block:CyclopsBlock = builder.get_node(block_path)
changes.tracked_block_data = block.mesh_vector_data.duplicate()
block_map[block_path] = changes
for index in indices:
if !changes.vertex_indices.has(index):
changes.vertex_indices.append(index)
func _init():
command_name = "Move vertices"
func do_it():
# print("move verts do_it")
for block_path in block_map.keys():
var block:CyclopsBlock = builder.get_node(block_path)
var w2l:Transform3D = block.global_transform
w2l = w2l.affine_inverse()
var move_offset_local:Vector3 = w2l.basis * move_offset
#print("move offset %s" % move_offset)
#print("move offset local %s" % move_offset_local)
var rec:BlockVertexChanges = block_map[block_path]
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
for v_idx in vol.vertices.size():
if rec.vertex_indices.has(v_idx):
vol.vertices[v_idx].point += move_offset_local
block.mesh_vector_data = vol.to_mesh_vector_data()
#####
#var selected_points:PackedVector3Array
#var new_points:PackedVector3Array
#for v_idx in vol.vertices.size():
#if rec.vertex_indices.has(v_idx):
#var p:Vector3 = vol.vertices[v_idx].point + move_offset_local
#new_points.append(p)
#selected_points.append(p)
#else:
#new_points.append(vol.vertices[v_idx].point)
#
#
#var new_vol:ConvexVolume = ConvexVolume.new()
#new_vol.init_from_points(new_points)
#
#new_vol.copy_face_attributes(vol)
#
#for v_idx in new_vol.vertices.size():
#var v:ConvexVolume.VertexInfo = new_vol.vertices[v_idx]
## print ("vol point %s " % v.point)
#if selected_points.has(v.point):
## print("set sel")
#v.selected = true
#
#block.mesh_vector_data = new_vol.to_mesh_vector_data()
func undo_it():
# print("move verts undo_it")
for block_path in block_map.keys():
var rec:BlockVertexChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = rec.tracked_block_data

View file

@ -0,0 +1,239 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandSelectBlocks
extends CyclopsCommand
#Public
var selection_type:Selection.Type = Selection.Type.REPLACE
var block_paths:Array[NodePath]
#Private
#var tracked_selected_blocks:Array[NodePath]
#var tracked_active_blocks:Array[NodePath]
var cached_selection:Array[NodePath]
var init:bool = false
func _init():
command_name = "Select blocks"
#func will_change_anything()->bool:
#
# var active_path:NodePath
# if !block_paths.is_empty():
# active_path = block_paths[0]
## print("will change active %s" % active_path)
#
# for child in builder.get_blocks():
# if child is CyclopsBlock:
# var block:CyclopsBlock = child
# var path:NodePath = block.get_path()
#
# match selection_type:
# Selection.Type.REPLACE:
# if block.selected != block_paths.has(path):
# return true
#
# if block.active != (path == active_path):
# return true
#
# Selection.Type.ADD:
# if block_paths.has(path):
# if !block.selected:
# return true
# if block.active != (path == active_path):
# return true
#
# Selection.Type.SUBTRACT:
# if block_paths.has(path):
# if block.selected:
# return true
#
# Selection.Type.TOGGLE:
# if !block_paths.is_empty():
# return true
#
## print("will chage anything false")
# return false
func will_change_anything()->bool:
var selection:EditorSelection = builder.get_editor_interface().get_selection()
var cur_node_list:Array[Node] = selection.get_selected_nodes()
if !init:
for node in cur_node_list:
cached_selection.append(node.get_path())
init = true
var cur_paths:Array[NodePath]
for node in selection.get_selected_nodes():
cur_paths.append(node.get_path())
if selection_type == Selection.Type.REPLACE:
if cur_paths.size() != block_paths.size():
return true
for i in cur_paths.size():
if cur_paths[i] != block_paths[i]:
return true
return false
elif selection_type == Selection.Type.ADD:
for path in block_paths:
if !cur_paths.has(path):
return true
return false
elif selection_type == Selection.Type.SUBTRACT:
for path in block_paths:
if cur_paths.has(path):
return true
return false
elif selection_type == Selection.Type.TOGGLE:
if !block_paths.is_empty():
return true
return false
return false
func do_it():
var selection:EditorSelection = builder.get_editor_interface().get_selection()
var cur_node_list:Array[Node] = selection.get_selected_nodes()
if !init:
cached_selection = cur_node_list.duplicate()
init = true
var cur_paths:Array[NodePath]
for node in selection.get_selected_nodes():
cur_paths.append(node.get_path())
if selection_type == Selection.Type.REPLACE:
selection.clear()
for path in block_paths:
var node:Node = builder.get_node(path)
selection.add_node(node)
elif selection_type == Selection.Type.ADD:
for path in block_paths:
if !cur_paths.has(path):
var node:Node = builder.get_node(path)
selection.add_node(node)
elif selection_type == Selection.Type.SUBTRACT:
for path in block_paths:
if cur_paths.has(path):
var node:Node = builder.get_node(path)
selection.remove_node(node)
elif selection_type == Selection.Type.TOGGLE:
for path in block_paths:
var node:Node = builder.get_node(path)
if cur_paths.has(path):
selection.remove_node(node)
else:
selection.add_node(node)
#func do_it_old():
## print("sel verts do_it")
#
# #Cache state
# tracked_selected_blocks.clear()
# tracked_active_blocks.clear()
#
#
# var active_block:CyclopsBlock = builder.get_active_block()
# tracked_active_blocks.append(active_block.get_path())
#
# for child in builder.get_selected_blocks():
# var block:CyclopsBlock = child
# tracked_selected_blocks.append(block.get_path())
#
# #Do selection
# var active_path:NodePath
# if !block_paths.is_empty():
# active_path = block_paths[0]
#
# #print("do_it active %s" % active_path)
## print("Setting active %s" % active_path)
# for child in builder.get_blocks():
# var block:CyclopsBlock = child
# var path:NodePath = block.get_path()
#
# match selection_type:
# Selection.Type.REPLACE:
# block.selected = block_paths.has(path)
# block.active = path == active_path
# Selection.Type.ADD:
# if block_paths.has(path):
# block.selected = true
# block.active = path == active_path
# Selection.Type.SUBTRACT:
# if block_paths.has(path):
# block.selected = false
# block.active = false
# Selection.Type.TOGGLE:
# #print("Check block %s" % path)
# #print("act %s sel %s" % [block.active, block.selected])
# if path == active_path:
# #print("Match active")
# if !block.active:
# #print("Setting active %s" % block.name)
# block.active = true
# block.selected = true
# else:
# #print("Clearing active %s" % block.name)
# block.active = false
# block.selected = false
# else:
# if block_paths.has(path):
# #print("Setting sel")
# block.selected = !block.selected
# block.active = false
#
# builder.selection_changed.emit()
func undo_it():
var selection:EditorSelection = builder.get_editor_interface().get_selection()
selection.clear()
for path in cached_selection:
var node:Node = builder.get_node(path)
selection.add_node(node)
#func undo_it():
#
# for child in builder.get_blocks():
# if child is CyclopsBlock:
# var block:CyclopsBlock = child
# var path:NodePath = block.get_path()
#
# block.selected = tracked_selected_blocks.has(path)
# block.active = tracked_active_blocks.has(path)
#
# builder.selection_changed.emit()

View file

@ -0,0 +1,176 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandSelectEdges
extends CyclopsCommand
class BlockEdgeChanges extends RefCounted:
var block_path:NodePath
var edge_indices:Array[int] = []
var tracked_block_data:MeshVectorData
#Public
var selection_type:Selection.Type = Selection.Type.REPLACE
#Private
var block_map:Dictionary = {}
func add_edge(block_path:NodePath, index:int):
add_edges(block_path, [index])
func add_edges(block_path:NodePath, indices:Array[int]):
var changes:BlockEdgeChanges
if block_map.has(block_path):
changes = block_map[block_path]
else:
changes = BlockEdgeChanges.new()
changes.block_path = block_path
var block:CyclopsBlock = builder.get_node(block_path)
changes.tracked_block_data = block.mesh_vector_data
block_map[block_path] = changes
for index in indices:
if !changes.edge_indices.has(index):
changes.edge_indices.append(index)
func _init():
command_name = "Select edges"
func will_change_anything()->bool:
for block_path in block_map.keys():
#print("path %s" % node_path)
var rec:BlockEdgeChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
if !rec.edge_indices.is_empty():
if vol.active_edge != rec.edge_indices[0]:
return true
match selection_type:
Selection.Type.REPLACE:
for e_idx in vol.edges.size():
var e:ConvexVolume.EdgeInfo = vol.edges[e_idx]
if e.selected != rec.edge_indices.has(e_idx):
#print("will change SREP")
return true
Selection.Type.ADD:
for e_idx in rec.edge_indices:
var e:ConvexVolume.EdgeInfo = vol.edges[e_idx]
if rec.edge_indices.has(e_idx):
if !e.selected:
#print("will change ADD")
return true
Selection.Type.SUBTRACT:
for e_idx in rec.edge_indices:
var e:ConvexVolume.EdgeInfo = vol.edges[e_idx]
if rec.edge_indices.has(e_idx):
if e.selected:
#print("will change SUB")
return true
Selection.Type.TOGGLE:
#print("will change TOG")
return true
return false
func do_it():
# print("sel edges do_it")
for block_path in block_map.keys():
# print("path %s" % block_path)
var rec:BlockEdgeChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
if !rec.edge_indices.is_empty():
var active_index:int = rec.edge_indices[0]
match selection_type:
Selection.Type.REPLACE:
vol.active_edge = active_index
Selection.Type.ADD:
vol.active_edge = active_index
Selection.Type.SUBTRACT:
if rec.edge_indices.has(vol.active_edge):
vol.active_edge = -1
Selection.Type.TOGGLE:
if rec.edge_indices.has(vol.active_edge):
vol.active_edge = -1
elif !vol.edges[active_index].selected:
vol.active_edge = active_index
match selection_type:
Selection.Type.REPLACE:
for e_idx in vol.edges.size():
var e:ConvexVolume.EdgeInfo = vol.edges[e_idx]
e.selected = rec.edge_indices.has(e_idx)
Selection.Type.ADD:
for e_idx in vol.edges.size():
var e:ConvexVolume.EdgeInfo = vol.edges[e_idx]
if rec.edge_indices.has(e_idx):
e.selected = true
Selection.Type.SUBTRACT:
for e_idx in vol.edges.size():
var e:ConvexVolume.EdgeInfo = vol.edges[e_idx]
if rec.edge_indices.has(e_idx):
e.selected = false
Selection.Type.TOGGLE:
for e_idx in vol.edges.size():
var e:ConvexVolume.EdgeInfo = vol.edges[e_idx]
if rec.edge_indices.has(e_idx):
#print("flipping %s" % e.selected)
e.selected = !e.selected
if vol.active_edge != -1:
if vol.active_edge >= vol.edges.size() || !vol.edges[vol.active_edge].selected:
vol.active_edge = -1
block.mesh_vector_data = vol.to_mesh_vector_data()
builder.selection_changed.emit()
func undo_it():
# print("sel verts undo_it")
#print("sel vert undo_it()")
for block_path in block_map.keys():
var rec:BlockEdgeChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = rec.tracked_block_data
builder.selection_changed.emit()

View file

@ -0,0 +1,168 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandSelectFaces
extends CyclopsCommand
class BlockFaceChanges extends RefCounted:
var block_path:NodePath
var face_indices:Array[int] = []
var tracked_block_data:MeshVectorData
#Public
var selection_type:Selection.Type = Selection.Type.REPLACE
#Private
var block_map:Dictionary = {}
func add_face(block_path:NodePath, index:int):
add_faces(block_path, [index])
func add_faces(block_path:NodePath, indices:Array[int]):
var changes:BlockFaceChanges
if block_map.has(block_path):
changes = block_map[block_path]
else:
changes = BlockFaceChanges.new()
changes.block_path = block_path
var block:CyclopsBlock = builder.get_node(block_path)
changes.tracked_block_data = block.mesh_vector_data
block_map[block_path] = changes
for index in indices:
if !changes.face_indices.has(index):
changes.face_indices.append(index)
func _init():
command_name = "Select faces"
func will_change_anything()->bool:
for block_path in block_map.keys():
#print("path %s" % node_path)
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
# var active_idx:int = -1
if !rec.face_indices.is_empty():
if vol.active_face != rec.face_indices[0]:
return true
match selection_type:
Selection.Type.REPLACE:
for f_idx in vol.faces.size():
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
if f.selected != rec.face_indices.has(f_idx):
return true
Selection.Type.ADD:
for f_idx in vol.faces.size():
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
if rec.face_indices.has(f_idx):
if !f.selected:
return true
Selection.Type.SUBTRACT:
for f_idx in vol.faces.size():
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
if rec.face_indices.has(f_idx):
if f.selected:
return true
Selection.Type.TOGGLE:
return true
return false
func do_it():
#print("sel verts do_it")
#print("sel vert do_it()")
for block_path in block_map.keys():
# print("path %s" % block_path)
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
if !rec.face_indices.is_empty():
var active_index:int = rec.face_indices[0]
match selection_type:
Selection.Type.REPLACE:
vol.active_face = active_index
Selection.Type.ADD:
vol.active_face = active_index
Selection.Type.SUBTRACT:
if rec.face_indices.has(vol.active_face):
vol.active_face = -1
Selection.Type.TOGGLE:
if rec.face_indices.has(vol.active_face):
vol.active_face = -1
elif !vol.faces[active_index].selected:
vol.active_face = active_index
# print("face active index %s" % active_idx)
match selection_type:
Selection.Type.REPLACE:
for f_idx in vol.faces.size():
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
f.selected = rec.face_indices.has(f_idx)
Selection.Type.ADD:
for f_idx in vol.faces.size():
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
if rec.face_indices.has(f_idx):
f.selected = true
Selection.Type.SUBTRACT:
for f_idx in vol.faces.size():
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
if rec.face_indices.has(f_idx):
f.selected = false
Selection.Type.TOGGLE:
for f_idx in vol.faces.size():
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
if rec.face_indices.has(f_idx):
f.selected = !f.selected
if vol.active_face != -1:
if vol.active_face >= vol.faces.size() || !vol.faces[vol.active_face].selected:
vol.active_face = -1
block.mesh_vector_data = vol.to_mesh_vector_data()
builder.selection_changed.emit()
func undo_it():
# print("undo_it() select faces")
for block_path in block_map.keys():
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = rec.tracked_block_data
builder.selection_changed.emit()

View file

@ -0,0 +1,174 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandSelectVertices
extends CyclopsCommand
class BlockVertexChanges extends RefCounted:
var block_path:NodePath
var vertex_indices:Array[int] = []
var tracked_block_data:MeshVectorData
#Public
var selection_type:Selection.Type = Selection.Type.REPLACE
#Private
var block_map:Dictionary = {}
func add_vertex(block_path:NodePath, index:int):
add_vertices(block_path, [index])
func add_vertices(block_path:NodePath, indices:Array[int]):
var changes:BlockVertexChanges
if block_map.has(block_path):
changes = block_map[block_path]
else:
changes = BlockVertexChanges.new()
changes.block_path = block_path
var block:CyclopsBlock = builder.get_node(block_path)
changes.tracked_block_data = block.mesh_vector_data
block_map[block_path] = changes
for index in indices:
if !changes.vertex_indices.has(index):
changes.vertex_indices.append(index)
func _init():
command_name = "Select vertices"
func will_change_anything()->bool:
for block_path in block_map.keys():
#print("path %s" % node_path)
var rec:BlockVertexChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
if !rec.vertex_indices.is_empty():
if vol.active_vertex != rec.vertex_indices[0]:
return true
match selection_type:
Selection.Type.REPLACE:
for v_idx in vol.vertices.size():
var v:ConvexVolume.VertexInfo = vol.vertices[v_idx]
if v.selected != rec.vertex_indices.has(v_idx):
return true
Selection.Type.ADD:
for v_idx in rec.vertex_indices:
var v:ConvexVolume.VertexInfo = vol.vertices[v_idx]
if rec.vertex_indices.has(v_idx):
if !v.selected:
return true
Selection.Type.SUBTRACT:
for v_idx in rec.vertex_indices:
var v:ConvexVolume.VertexInfo = vol.vertices[v_idx]
if rec.vertex_indices.has(v_idx):
if v.selected:
return true
Selection.Type.TOGGLE:
return true
return false
func do_it():
# print("sel verts do_it")
for block_path in block_map.keys():
#print("path %s" % node_path)
var rec:BlockVertexChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
if !rec.vertex_indices.is_empty():
var active_index:int = rec.vertex_indices[0]
#print("active_index ", active_index)
match selection_type:
Selection.Type.REPLACE:
vol.active_vertex = active_index
Selection.Type.ADD:
vol.active_vertex = active_index
Selection.Type.SUBTRACT:
if rec.vertex_indices.has(vol.active_vertex):
vol.active_vertex = -1
Selection.Type.TOGGLE:
if rec.vertex_indices.has(vol.active_vertex):
vol.active_vertex = -1
elif !vol.vertices[active_index].selected:
vol.active_vertex = active_index
else:
if selection_type == Selection.Type.REPLACE:
vol.active_vertex = -1
match selection_type:
Selection.Type.REPLACE:
for v_idx in vol.vertices.size():
var v:ConvexVolume.VertexInfo = vol.vertices[v_idx]
v.selected = rec.vertex_indices.has(v_idx)
Selection.Type.ADD:
for v_idx in vol.vertices.size():
var v:ConvexVolume.VertexInfo = vol.vertices[v_idx]
if rec.vertex_indices.has(v_idx):
v.selected = true
Selection.Type.SUBTRACT:
for v_idx in vol.vertices.size():
var v:ConvexVolume.VertexInfo = vol.vertices[v_idx]
if rec.vertex_indices.has(v_idx):
v.selected = false
Selection.Type.TOGGLE:
for v_idx in vol.vertices.size():
var v:ConvexVolume.VertexInfo = vol.vertices[v_idx]
if rec.vertex_indices.has(v_idx):
v.selected = !v.selected
vol.update_edge_and_face_selection_from_vertices()
#print("vol.active_vertex ", vol.active_vertex)
block.mesh_vector_data = vol.to_mesh_vector_data()
#print("block.mesh_vector_data.active_vertex ", block.mesh_vector_data.active_vertex)
builder.selection_changed.emit()
func undo_it():
# print("sel verts undo_it")
#print("sel vert undo_it()")
for block_path in block_map.keys():
var rec:BlockVertexChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = rec.tracked_block_data
builder.selection_changed.emit()

View file

@ -0,0 +1,112 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandSetFaceColor
extends CyclopsCommand
class BlockFaceChanges extends RefCounted:
var block_path:NodePath
var face_indices:Array[int]
var tracked_block_data:MeshVectorData
var color:Color = Color.WHITE
#Private
var block_map:Dictionary = {}
func add_face(block_path:NodePath, index:int):
add_faces(block_path, [index])
func add_faces(block_path:NodePath, indices:Array[int]):
# print("adding_face %s %s" % [block_path, indices])
var changes:BlockFaceChanges
if block_map.has(block_path):
changes = block_map[block_path]
else:
changes = BlockFaceChanges.new()
changes.block_path = block_path
var block:CyclopsBlock = builder.get_node(block_path)
changes.tracked_block_data = block.mesh_vector_data
block_map[block_path] = changes
for index in indices:
if !changes.face_indices.has(index):
changes.face_indices.append(index)
func _init():
command_name = "Set Face Color"
func will_change_anything()->bool:
# print("CommandSetUvTransform will_change_anything")
for block_path in block_map.keys():
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
for f_idx in vol.faces.size():
if rec.face_indices.has(f_idx):
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
if f.color != color:
return true
return false
func do_it():
#print("sel verts do_it")
# print("sel uv_transform do_it()")
for block_path in block_map.keys():
# print("path %s" % block_path)
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
# print("block_path %s" % block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
for f_idx in vol.faces.size():
if rec.face_indices.has(f_idx):
# print("face_idx %s" % f_idx)
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
f.color = color
block.mesh_vector_data = vol.to_mesh_vector_data()
builder.selection_changed.emit()
func undo_it():
# print("undo_it() select faces")
for block_path in block_map.keys():
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = rec.tracked_block_data
builder.selection_changed.emit()

View file

@ -0,0 +1,114 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandSetFaceUvTransform
extends CyclopsCommand
class BlockFaceChanges extends RefCounted:
var block_path:NodePath
var face_indices:Array[int] = []
var tracked_block_data:MeshVectorData
var uv_transform:Transform2D = Transform2D.IDENTITY
var visible:bool = true
var color:Color = Color.WHITE
#Private
var block_map:Dictionary = {}
func add_face(block_path:NodePath, index:int):
add_faces(block_path, [index])
func add_faces(block_path:NodePath, indices:Array[int]):
# print("adding_face %s %s" % [block_path, indices])
var changes:BlockFaceChanges
if block_map.has(block_path):
changes = block_map[block_path]
else:
changes = BlockFaceChanges.new()
changes.block_path = block_path
var block:CyclopsBlock = builder.get_node(block_path)
changes.tracked_block_data = block.mesh_vector_data
block_map[block_path] = changes
for index in indices:
if !changes.face_indices.has(index):
changes.face_indices.append(index)
func _init():
command_name = "Set Face Uv Transform"
func will_change_anything()->bool:
# print("CommandSetUvTransform will_change_anything")
for block_path in block_map.keys():
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
for f_idx in vol.faces.size():
if rec.face_indices.has(f_idx):
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
if f.uv_transform != uv_transform:
return true
return false
func do_it():
#print("sel verts do_it")
# print("sel uv_transform do_it()")
for block_path in block_map.keys():
# print("path %s" % block_path)
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
# print("block_path %s" % block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
for f_idx in vol.faces.size():
if rec.face_indices.has(f_idx):
# print("face_idx %s" % f_idx)
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
f.uv_transform = uv_transform
block.mesh_vector_data = vol.to_mesh_vector_data()
builder.selection_changed.emit()
func undo_it():
# print("undo_it() select faces")
for block_path in block_map.keys():
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = rec.tracked_block_data
builder.selection_changed.emit()

View file

@ -0,0 +1,121 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandSetFaceVertexColor
extends CyclopsCommand
class BlockFaceVertexChanges extends RefCounted:
var block_path:NodePath
var face_vert_indices:Array[int]
var tracked_block_data:MeshVectorData
var color:Color = Color.WHITE
var strength:float = 1
#Private
var block_map:Dictionary = {}
#class StrokePoint:
#var position:Vector3
#var pressure:float
#
#var stroke_points:Array[StrokePoint]
func add_face_vertex(block_path:NodePath, index:int):
add_face_vertices(block_path, [index])
func add_face_vertices(block_path:NodePath, indices:Array[int]):
# print("adding_face %s %s" % [block_path, indices])
var changes:BlockFaceVertexChanges
if block_map.has(block_path):
changes = block_map[block_path]
else:
changes = BlockFaceVertexChanges.new()
changes.block_path = block_path
var block:CyclopsBlock = builder.get_node(block_path)
changes.tracked_block_data = block.mesh_vertex_data
block_map[block_path] = changes
for index in indices:
if !changes.face_vert_indices.has(index):
changes.face_vert_indices.append(index)
func _init():
command_name = "Set Face Vertex Color"
func will_change_anything()->bool:
return block_map.size() > 0
# print("CommandSetUvTransform will_change_anything")
#for block_path in block_map.keys():
#
#var rec:BlockFaceVertexChanges = block_map[block_path]
#var block:CyclopsBlock = builder.get_node(block_path)
#
#var vol:ConvexVolume = ConvexVolume.new()
#vol.init_from_convex_block_data(rec.tracked_block_data)
#
#for f_idx in vol.faces.size():
#if rec.face_indices.has(f_idx):
#var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
#if f.color != color:
#return true
#
#return false
func do_it():
#print("sel verts do_it")
#print("sel face vert color do_it()")
for block_path in block_map.keys():
# print("path %s" % block_path)
var rec:BlockFaceVertexChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
#print("block_path %s" % block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
for fv_idx in vol.face_vertices.size():
if rec.face_vert_indices.has(fv_idx):
#print("face_v_idx %s" % fv_idx)
var fv:ConvexVolume.FaceVertexInfo = vol.face_vertices[fv_idx]
fv.color = MathUtil.blend_colors_ignore_alpha(color, fv.color, strength)
block.mesh_vector_data = vol.to_mesh_vector_data()
builder.selection_changed.emit()
func undo_it():
# print("undo_it() select faces")
for block_path in block_map.keys():
var rec:BlockFaceVertexChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = rec.tracked_block_data
builder.selection_changed.emit()

View file

@ -0,0 +1,112 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandSetFaceVisible
extends CyclopsCommand
class BlockFaceChanges extends RefCounted:
var block_path:NodePath
var face_indices:Array[int] = []
var tracked_block_data:MeshVectorData
var visible:bool = true
#Private
var block_map:Dictionary = {}
func add_face(block_path:NodePath, index:int):
add_faces(block_path, [index])
func add_faces(block_path:NodePath, indices:Array[int]):
# print("adding_face %s %s" % [block_path, indices])
var changes:BlockFaceChanges
if block_map.has(block_path):
changes = block_map[block_path]
else:
changes = BlockFaceChanges.new()
changes.block_path = block_path
var block:CyclopsBlock = builder.get_node(block_path)
changes.tracked_block_data = block.mesh_vector_data
block_map[block_path] = changes
for index in indices:
if !changes.face_indices.has(index):
changes.face_indices.append(index)
func _init():
command_name = "Set Face Properties"
func will_change_anything()->bool:
# print("CommandSetUvTransform will_change_anything")
for block_path in block_map.keys():
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
for f_idx in vol.faces.size():
if rec.face_indices.has(f_idx):
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
if f.visible != visible:
return true
return false
func do_it():
#print("sel verts do_it")
# print("sel uv_transform do_it()")
for block_path in block_map.keys():
# print("path %s" % block_path)
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
# print("block_path %s" % block_path)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(rec.tracked_block_data)
for f_idx in vol.faces.size():
if rec.face_indices.has(f_idx):
# print("face_idx %s" % f_idx)
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
f.visible = visible
block.mesh_vector_data = vol.to_mesh_vector_data()
builder.selection_changed.emit()
func undo_it():
# print("undo_it() select faces")
for block_path in block_map.keys():
var rec:BlockFaceChanges = block_map[block_path]
var block:CyclopsBlock = builder.get_node(block_path)
block.mesh_vector_data = rec.tracked_block_data
builder.selection_changed.emit()

View file

@ -0,0 +1,158 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandSetMaterial
extends CyclopsCommand
class Target extends RefCounted:
var block_path:NodePath
var face_indices:PackedInt32Array
class BlockCache extends RefCounted:
var path:NodePath
var data:MeshVectorData
var materials:Array[Material]
#Public
var setting_material:bool = true
var material_path:String
var setting_color:bool = false
var color:Color = Color.WHITE
var setting_visibility:bool = false
var visibility:bool = true
var painting_uv:bool = false
var uv_matrix:Transform2D = Transform2D.IDENTITY
#Private
var target_list:Array[Target] = []
var cache_list:Array[BlockCache] = []
func add_target(block_path:NodePath, face_indices:PackedInt32Array):
# print("add target %s %s" % [block_path.get_name(block_path.get_name_count() - 1), face_indices])
var target:Target = null
for t in target_list:
if t.block_path == block_path:
target = t
break
if !target:
target = Target.new()
target.block_path = block_path
target_list.append(target)
for f_idx in face_indices:
if !target.face_indices.has(f_idx):
target.face_indices.append(f_idx)
func make_cache():
cache_list = []
for t in target_list:
var cache:BlockCache = BlockCache.new()
var block:CyclopsBlock = builder.get_node(t.block_path)
cache.path = block.get_path()
cache.data = block.mesh_vector_data
cache.materials = block.materials.duplicate()
cache_list.append(cache)
func will_change_anything()->bool:
return !target_list.is_empty()
func _init():
command_name = "Set material"
func do_it():
make_cache()
for tgt in target_list:
var block:CyclopsBlock = builder.get_node(tgt.block_path)
var data:MeshVectorData = block.mesh_vector_data
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(data)
if setting_material:
var target_material:Material = null
if ResourceLoader.exists(material_path, "Material"):
#print("loading material ", material_path)
var mat = load(material_path)
target_material = mat if mat is Material else null
var mat_reindex:Dictionary
var mat_list_reduced:Array[Material]
for f_idx in vol.faces.size():
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
var mat_to_apply:Material
if tgt.face_indices.has(f_idx):
mat_to_apply = target_material
else:
mat_to_apply = null if f.material_id == -1 else block.materials[f.material_id]
if !mat_to_apply:
f.material_id = -1
elif !mat_reindex.has(mat_to_apply):
var new_idx = mat_reindex.size()
mat_reindex[mat_to_apply] = new_idx
mat_list_reduced.append(mat_to_apply)
f.material_id = new_idx
else:
f.material_id = mat_reindex[mat_to_apply]
block.materials = mat_list_reduced
#Set other properties
for f_idx in vol.faces.size():
if tgt.face_indices.has(f_idx):
var f:ConvexVolume.FaceInfo = vol.faces[f_idx]
if setting_color:
f.color = color
for v_idx in f.vertex_indices:
var fv:ConvexVolume.FaceVertexInfo = \
vol.get_face_vertex(f_idx, v_idx)
fv.color = color
if setting_visibility:
f.visible = visibility
if painting_uv:
f.uv_transform = uv_matrix
block.mesh_vector_data = vol.to_mesh_vector_data()
func undo_it():
for cache in cache_list:
var block:CyclopsBlock = builder.get_node(cache.path)
block.materials = cache.materials.duplicate()
block.mesh_vector_data = cache.data

View file

@ -0,0 +1,84 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandSnapToGrid
extends CyclopsCommand
class TrackedInfo extends RefCounted:
var data:MeshVectorData
#Private
var blocks_to_move:Array[NodePath]
var tracked_block_data:Array[TrackedInfo]
func _init():
command_name = "Snap to grid"
#Add blocks to be moved here
func add_block(block_path:NodePath):
blocks_to_move.append(block_path)
var block:CyclopsBlock = builder.get_node(block_path)
#tracked_blocks.append(block)
var info:TrackedInfo = TrackedInfo.new()
info.data = block.mesh_vector_data.duplicate()
# info.materials = block.materials
tracked_block_data.append(info)
func do_it():
for i in blocks_to_move.size():
var block:CyclopsBlock = builder.get_node(blocks_to_move[i])
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(tracked_block_data[i].data)
var points_new:PackedVector3Array
for v_idx in vol.vertices.size():
var v:ConvexVolume.VertexInfo = vol.vertices[v_idx]
var p_snap:Vector3 = builder.get_snapping_manager().snap_point(
block.global_transform * v.point, SnappingQuery.new(null, []))
points_new.append(p_snap)
var new_vol:ConvexVolume = ConvexVolume.new()
new_vol.init_from_points(points_new)
new_vol.transform(block.global_transform.affine_inverse())
new_vol.copy_face_attributes(vol)
block.mesh_vector_data = new_vol.to_mesh_vector_data()
func undo_it():
for i in blocks_to_move.size():
var block:CyclopsBlock = builder.get_node(blocks_to_move[i])
block.mesh_vector_data = tracked_block_data[i].data

View file

@ -0,0 +1,158 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandSubtractBlock
extends CyclopsCommand
class NewBlockInfo extends RefCounted:
var data:MeshVectorData
var materials:Array[Material]
var path:NodePath
#var centroid:Vector3
var xform:Transform3D
#Public
var block_paths:Array[NodePath]
var block_to_subtract_path:NodePath
var block_name_prefix:String = "Block_"
#Private
var start_blocks:Array[TrackedBlock]
var subtracted_block_cache:TrackedBlock
var added_blocks:Array[NewBlockInfo]
func _init():
command_name = "Subtract blocks"
func restore_tracked_block(tracked:TrackedBlock)->CyclopsBlock:
var parent = builder.get_node(tracked.path_parent)
var block:CyclopsBlock = preload("../nodes/cyclops_block.gd").new()
block.mesh_vector_data = tracked.data
block.materials = tracked.materials
block.name = tracked.name
# block.selected = tracked.selected
block.global_transform = tracked.world_xform
block.collision_type = tracked.collision_type
block.collision_layer = tracked.collision_layers
block.collision_mask = tracked.collision_mask
parent.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()
if tracked.selected:
var selection:EditorSelection = builder.get_editor_interface().get_selection()
selection.add_node(block)
return block
func will_change_anything()->bool:
var subtrahend_block:CyclopsBlock = builder.get_node(block_to_subtract_path)
var subtrahend_vol:ConvexVolume = subtrahend_block.control_mesh
subtrahend_vol = subtrahend_vol.transformed(subtrahend_block.global_transform)
if block_paths.is_empty():
return false
for minuend_path in block_paths:
var minuend_block:CyclopsBlock = builder.get_node(minuend_path)
var minuend_vol:ConvexVolume = minuend_block.control_mesh
minuend_vol = minuend_vol.transformed(minuend_block.global_transform)
if minuend_vol.intersects_convex_volume(subtrahend_vol):
return true
return false
func do_it():
var subtrahend_block:CyclopsBlock = builder.get_node(block_to_subtract_path)
var snap_to_grid_util:SnapToGridUtil = CyclopsAutoload.calc_snap_to_grid_util()
if start_blocks.is_empty():
var subtrahend_vol:ConvexVolume = subtrahend_block.control_mesh
subtracted_block_cache = TrackedBlock.new(subtrahend_block)
subtrahend_vol = subtrahend_vol.transformed(subtrahend_block.global_transform)
var subtra_xform_inv:Transform3D = subtrahend_block.global_transform.affine_inverse()
for path in block_paths:
var block:CyclopsBlock = builder.get_node(path)
var minuend_vol:ConvexVolume = block.control_mesh
minuend_vol = minuend_vol.transformed(block.global_transform)
if !minuend_vol.intersects_convex_volume(subtrahend_vol):
continue
var tracker:TrackedBlock = TrackedBlock.new(block)
start_blocks.append(tracker)
var fragments:Array[ConvexVolume] = minuend_vol.subtract(subtrahend_vol)
for f in fragments:
f.copy_face_attributes(minuend_vol)
#var centroid:Vector3 = f.get_centroid()
#centroid = snap_to_grid_util.snap_point(centroid)
#f.translate(-centroid)
f = f.transformed(block.global_transform.affine_inverse())
var block_info:NewBlockInfo = NewBlockInfo.new()
block_info.data = f.to_mesh_vector_data()
block_info.materials = block.materials
block_info.xform = block.global_transform
#block_info.centroid = centroid
added_blocks.append(block_info)
#Delete source blocks
for block_info in start_blocks:
var del_block:CyclopsBlock = builder.get_node(block_info.path)
del_block.queue_free()
subtrahend_block.queue_free()
#Create blocks
for info in added_blocks:
var block:CyclopsBlock = preload("../nodes/cyclops_block.gd").new()
var parent:Node = builder.get_node(start_blocks[0].path_parent)
parent.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()
block.name = GeneralUtil.find_unique_name(parent, block_name_prefix)
block.mesh_vector_data = info.data
block.materials = info.materials
# block.global_transform = Transform3D.IDENTITY.translated(info.centroid)
block.global_transform = info.xform
info.path = block.get_path()
func undo_it():
for info in added_blocks:
var added_block:CyclopsBlock = builder.get_node(info.path)
added_block.queue_free()
restore_tracked_block(subtracted_block_cache)
for tracked in start_blocks:
restore_tracked_block(tracked)

View file

@ -0,0 +1,76 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandTransformBlocks
extends CyclopsCommand
#Public data to set before activating command
var transform:Transform3D
var lock_uvs:bool = false
#Private
var tracked_blocks:Array[TrackedBlock]
func _init():
command_name = "Transform blocks"
#Add blocks to be moved here
func add_block(block_path:NodePath):
var block:CyclopsBlock = builder.get_node(block_path)
var tracked:TrackedBlock = TrackedBlock.new(block)
tracked_blocks.append(tracked)
#Moves all blocks from the start position by this amount
func move_to(offset:Vector3):
for tracked in tracked_blocks:
var block:CyclopsBlock = builder.get_node(tracked.path)
var w_init_xform:Transform3D = tracked.world_xform
var new_w_xform:Transform3D = w_init_xform.translated(offset)
block.global_transform = new_w_xform
func do_it():
for tracked in tracked_blocks:
var block:CyclopsBlock = builder.get_node(tracked.path)
var w_init_xform:Transform3D = tracked.world_xform
var new_w_xform:Transform3D = transform * w_init_xform
block.global_transform = new_w_xform
if !lock_uvs:
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(tracked.data)
var uv_xform:Transform3D = transform.affine_inverse()
vol.transform_uvs(uv_xform)
block.mesh_vector_data = vol.to_mesh_vector_data()
func undo_it():
for tracked in tracked_blocks:
var block:CyclopsBlock = builder.get_node(tracked.path)
block.global_transform = tracked.world_xform

View file

@ -0,0 +1,73 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#Applied trnasorm of points in local space
@tool
class_name CommandTransformVertices
extends CyclopsCommand
class TrackedInfo extends RefCounted:
var data:MeshVectorData
# var materials:Array[Material]
#Local space transform of points
var transform:Transform3D
var lock_uvs:bool = false
#Private
var blocks_to_move:Array[NodePath]
var tracked_block_data:Array[TrackedInfo]
func _init():
command_name = "Transform vertices"
#Add blocks to be moved here
func add_block(block_path:NodePath):
blocks_to_move.append(block_path)
var block:CyclopsBlock = builder.get_node(block_path)
#tracked_blocks.append(block)
var info:TrackedInfo = TrackedInfo.new()
info.data = block.mesh_vector_data.duplicate()
# info.materials = block.materials
tracked_block_data.append(info)
#Moves all blocks from the start position by this amount
func apply_transform(xform:Transform3D):
for i in blocks_to_move.size():
var block:CyclopsBlock = builder.get_node(blocks_to_move[i])
var ctl_mesh:ConvexVolume = ConvexVolume.new()
ctl_mesh.init_from_mesh_vector_data(tracked_block_data[i].data)
ctl_mesh.transform(xform, lock_uvs)
var result_data:MeshVectorData = ctl_mesh.to_mesh_vector_data()
block.mesh_vector_data = result_data
func do_it():
apply_transform(transform)
func undo_it():
apply_transform(Transform3D.IDENTITY)

View file

@ -0,0 +1,130 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandVertexPaintStroke
extends CyclopsCommand
@export var color:Color = Color.WHITE
@export var strength:float = 1
@export var radius:float = 1
@export var falloff_curve:Curve
enum MaskType { NONE, VERTICES, FACES }
@export var mask:MaskType = MaskType.NONE
#Private
var block_map:Dictionary = {}
#var block_tgt_map:Dictionary = {}
var pen_stroke:PenStroke = PenStroke.new()
func append_block(block_path:NodePath):
if block_map.has(block_path):
return
var block:CyclopsBlock = builder.get_node(block_path)
#print("stroing block faces ", block.block_data.face_vertex_face_index)
block_map[block_path] = block.mesh_vector_data.duplicate(true)
#print("stroing block faces ", block.block_data.face_vertex_face_index)
# block_tgt_map[block_path] = block.block_data.duplicate(true)
func append_stroke_point(position:Vector3, pressure:float = 1):
pen_stroke.append_stroke_point(position, pressure)
#print("--pen_stroke ", pen_stroke.stroke_points)
func _init():
command_name = "Paint Vertex Color Stroke"
func will_change_anything()->bool:
return !(block_map.is_empty() || pen_stroke.is_empty())
func do_it():
#print("sel verts do_it")
# print("sel uv_transform do_it()")
#print("stroke pts ", str(pen_stroke.stroke_points))
var stroke_resamp:PenStroke = pen_stroke.resample_points(radius * .1)
#print("stroke resamp pts ", str(stroke_resamp.stroke_points))
for block_path in block_map.keys():
var block:CyclopsBlock = builder.get_node(block_path)
var w2l:Transform3D = block.global_transform.affine_inverse()
#print("painting block ", block.name)
var block_data:MeshVectorData = block_map[block_path]
#print("block_data raw faces ", block_data.face_vertex_face_index)
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_mesh_vector_data(block_data)
#Apply stroke
for stroke_pt in stroke_resamp.stroke_points:
var pos_local:Vector3 = w2l * stroke_pt.position
for fv in vol.face_vertices:
var v:ConvexVolume.VertexInfo = vol.vertices[fv.vertex_index]
var f:ConvexVolume.FaceInfo = vol.faces[fv.face_index]
if mask == MaskType.FACES:
if !f.selected:
continue
elif mask == MaskType.VERTICES:
if !v.selected:
continue
var dist:float = v.point.distance_to(pos_local)
if dist > radius:
continue
var falloff_frac:float = 1 - (dist / radius)
var falloff:float = falloff_curve.sample(falloff_frac) \
if falloff_curve else 1
fv.color = MathUtil.blend_colors_ignore_alpha(\
color, fv.color, strength * stroke_pt.pressure * falloff)
#print("fv_idx ", fv.index)
#print("fv color ", fv.color)
var new_block_data:MeshVectorData = vol.to_mesh_vector_data()
#print("new_block_data faces ", block.block_data.face_vertex_face_index)
block.mesh_vector_data = new_block_data
builder.selection_changed.emit()
func undo_it():
# print("undo_it() select faces")
for block_path in block_map.keys():
var block:CyclopsBlock = builder.get_node(block_path)
var block_data:MeshVectorData = block_map[block_path]
block.mesh_vector_data = block_data
builder.selection_changed.emit()

View file

@ -0,0 +1,77 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CyclopsCommand
extends RefCounted
var command_name:String = ""
var builder:CyclopsLevelBuilder
class TrackedBlock extends RefCounted:
var path:NodePath
var path_parent:NodePath
var data:MeshVectorData
var world_xform:Transform3D
var materials:Array[Material]
var selected:bool
var name:String
var collision_type:Collision.Type
var collision_layers:int
var collision_mask:int
func _init(block:CyclopsBlock):
path = block.get_path()
path_parent = block.get_parent().get_path()
name = block.name
data = block.mesh_vector_data.duplicate()
world_xform = block.global_transform
#selected = block.selected
materials = block.materials
collision_type = block.collision_type
collision_layers = block.collision_layer
collision_mask = block.collision_mask
func add_to_undo_manager(undo_manager:EditorUndoRedoManager):
undo_manager.create_action(command_name, UndoRedo.MERGE_DISABLE)
undo_manager.add_do_method(self, "do_it")
undo_manager.add_undo_method(self, "undo_it")
undo_manager.commit_action()
func node_global_transform(node:Node)->Transform3D:
var node_parent:Node3D
while node:
if node is Node3D:
node_parent = node
break
node = node.get_parent()
return node_parent.global_transform if node_parent else Transform3D.IDENTITY
func do_it()->void:
pass
func undo_it()->void:
pass

View file

@ -0,0 +1,87 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandImportCyclopsFile
extends CyclopsCommand
@export var file_path:String
@export var target_parent:NodePath
var added_blocks:Array[NodePath]
func _init():
command_name = "Import Cyclops File"
func will_change_anything()->bool:
return FileAccess.file_exists(file_path)
func do_it():
if !FileAccess.file_exists(file_path):
push_error("No such file: ", file_path)
return
var source:String = FileAccess.get_file_as_string(file_path)
var raw = JSON.parse_string(source)
if !(raw is Dictionary):
push_error("Invalid file format: ", file_path)
return
load_file(raw)
pass
func load_file(root:Dictionary):
var loader:CyclopsFileLoader = CyclopsFileLoader.new()
loader.load(root)
var editor_scene_root:Node = builder.get_editor_interface().get_edited_scene_root()
for scene_id in loader.scene_map.keys():
var root_node_id:int = loader.scene_map[scene_id]
var loaded_scene:Node3D = loader.node_map[root_node_id]
editor_scene_root.add_child(loaded_scene)
set_owner_recursive(loaded_scene, editor_scene_root)
added_blocks.append(loaded_scene.get_path())
func undo_it():
for block_path in added_blocks:
var block:Node3D = builder.get_node(block_path)
block.queue_free()
added_blocks.clear()
func set_owner_recursive(loaded_node:Node3D, owner_node:Node3D):
loaded_node.owner = owner_node
if loaded_node is CyclopsBlock:
#Do not set owner of hidden children
return
for child in loaded_node.get_children():
set_owner_recursive(child, owner_node)

View file

@ -0,0 +1,99 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
class_name CommandImportGodotMeshes
extends CyclopsCommand
@export var source_nodes:Array[NodePath]
@export var target_parent:NodePath
@export var collision_type:Collision.Type = Collision.Type.STATIC
@export var collision_layers:int = 1
@export var collision_mask:int = 1
var added_blocks:Array[NodePath]
func _init():
command_name = "Import Godot Meshes"
func will_change_anything()->bool:
return !target_parent.is_empty() && !source_nodes.is_empty()
func do_it():
var tgt_parent_node:Node = builder.get_node(target_parent)
if !tgt_parent_node || !(tgt_parent_node is Node3D):
return
for src_path in source_nodes:
var src_node:Node = builder.get_node(src_path)
if !src_node is MeshInstance3D:
continue
var src_mesh_inst:MeshInstance3D = src_node
if !src_mesh_inst.mesh:
continue
var block:CyclopsBlock = preload("res://addons/cyclops_level_builder/nodes/cyclops_block.gd").new()
var blocks_root:Node3D = tgt_parent_node
blocks_root.add_child(block)
block.owner = builder.get_editor_interface().get_edited_scene_root()
block.name = src_node.name
block.global_transform = src_node.global_transform
block.collision_type = collision_type
block.collision_layer = collision_layers
block.collision_mask = collision_mask
added_blocks.append(block.get_path())
var best_mat:Material
var points:PackedVector3Array
for i in src_mesh_inst.mesh.get_surface_count():
var mat:Material = src_mesh_inst.mesh.surface_get_material(i)
if best_mat != null:
best_mat = mat
var surface_arrs:Array = src_mesh_inst.mesh.surface_get_arrays(i)
if surface_arrs[Mesh.ARRAY_INDEX].is_empty():
for pt in surface_arrs[Mesh.ARRAY_VERTEX]:
points.append(pt)
else:
for idx in surface_arrs[Mesh.ARRAY_INDEX]:
points.append(surface_arrs[Mesh.ARRAY_VERTEX][idx])
if best_mat:
block.materials = [best_mat]
var vol:ConvexVolume = ConvexVolume.new()
vol.init_from_points(points, Transform2D.IDENTITY, 0 if best_mat else -1)
block.mesh_vector_data = vol.to_mesh_vector_data()
func undo_it():
for block_path in added_blocks:
var block:CyclopsBlock = builder.get_node(block_path)
block.queue_free()
added_blocks.clear()