Files
fps_project_1/addons/proton_scatter/src/modifiers/array.gd

183 lines
5.5 KiB
GDScript

@tool
extends "base_modifier.gd"
# Takes existing objects and duplicates them recursively with given transforms
@export var amount := 1
@export var min_amount := -1
@export var local_offset := false
@export var offset := Vector3.ZERO
@export var local_rotation := false
@export var rotation := Vector3.ZERO
@export var individual_rotation_pivots := true
@export var rotation_pivot := Vector3.ZERO
@export var local_scale := true
@export var scale := Vector3.ONE
@export var randomize_indices := true
var _rng: RandomNumberGenerator
func _init() -> void:
display_name = "Array"
category = "Create"
can_override_seed = true
can_restrict_height = false
global_reference_frame_available = false
local_reference_frame_available = false
individual_instances_reference_frame_available = false
documentation.add_paragraph(
"Recursively creates copies of the existing transforms, with each copy
being offset from the previous one in any of a number of possible ways.")
var p := documentation.add_parameter("Amount")
p.set_type("int")
p.set_cost(2)
p.set_description(
"The iteration count. If set to 1, each existing transforms are copied
once.")
p.add_warning("If set to 0, no copies are created.")
p = documentation.add_parameter("Minimum amount")
p.set_type("int")
p.set_description(
"Creates a random amount of copies for each transforms, between this
value and the amount value.")
p.add_warning("Ignored if set to a negative value.")
p = documentation.add_parameter("Offset")
p.set_type("Vector3")
p.set_description(
"Adds a constant offset between each copies and the previous one.")
p = documentation.add_parameter("Local offset")
p.set_type("bool")
p.set_description(
"If enabled, offset is relative to the previous copy orientation.
Otherwise, the offset is in global space.")
p = documentation.add_parameter("Rotation")
p.set_type("Vector3")
p.set_description(
"The rotation offset (on each axes) to add on each copy.")
p = documentation.add_parameter("Local rotation")
p.set_type("bool")
p.set_description(
"If enabled, the rotation is applied in local space relative to each
individual transforms. Otherwise, the rotation is applied in global
space.")
p = documentation.add_parameter("Rotation Pivot")
p.set_type("Vector3")
p.set_description(
"The point around which each copies are rotated. By default, each
transforms are rotated around their individual centers.")
p = documentation.add_parameter("Individual Rotation Pivots")
p.set_type("bool")
p.set_description(
"If enabled, each copies will use their own pivot relative to the
previous copy. Otherwise, a single pivot point (defined in global space)
will be used for the rotation of [b]all[/b] the copies.")
p = documentation.add_parameter("Scale")
p.set_type("Vector3")
p.set_description(
"Scales the copies relative to the transforms they are from.")
p = documentation.add_parameter("Local Scale")
p.set_type("bool")
p.set_description(
"If enabled, scaling is applied in local space relative to each
individual transforms. Otherwise, global axes are used, resulting
in skewed transforms in most cases.")
p = documentation.add_parameter("Randomize Indices")
p.set_type("bool")
p.set_description(
"Randomize the transform list order. This is only useful to break up the
repetitive patterns if you're using multiple ScatterItem nodes.")
func _process_transforms(transforms, domain, random_seed: int) -> void:
_rng = RandomNumberGenerator.new()
_rng.set_seed(random_seed)
var new_transforms: Array[Transform3D] = []
var rotation_rad := Vector3.ZERO
rotation_rad.x = deg_to_rad(rotation.x)
rotation_rad.y = deg_to_rad(rotation.y)
rotation_rad.z = deg_to_rad(rotation.z)
var axis_x := Vector3.RIGHT
var axis_y := Vector3.UP
var axis_z := Vector3.FORWARD
for t in transforms.size():
new_transforms.push_back(transforms.list[t])
var steps = amount
if min_amount >= 0:
steps = _rng.randi_range(min_amount, amount)
for a in steps:
a += 1
# use original object's transform as base transform
var transform : Transform3D = transforms.list[t]
var basis := transform.basis
# first move to rotation point defined in rotation offset
var rotation_pivot_offset = rotation_pivot
if individual_rotation_pivots:
rotation_pivot_offset = transform * rotation_pivot
transform.origin -= rotation_pivot_offset
# then rotate
if local_rotation:
axis_x = basis.x.normalized()
axis_y = basis.y.normalized()
axis_z = basis.z.normalized()
transform = transform.rotated(axis_x, rotation_rad.x * a)
transform = transform.rotated(axis_y, rotation_rad.y * a)
transform = transform.rotated(axis_z, rotation_rad.z * a)
# scale
# If the scale is different than 1, each transform gets bigger or
# smaller for each iteration.
var s = scale
s.x = pow(s.x, a)
s.y = pow(s.y, a)
s.z = pow(s.z, a)
if local_scale:
transform.basis.x *= s.x
transform.basis.y *= s.y
transform.basis.z *= s.z
else:
transform.basis = transform.basis.scaled(s)
# apply changes back to the transform and undo the rotation pivot offset
transform.origin += rotation_pivot_offset
# offset
if local_offset:
transform.origin += offset * a
else:
transform.origin += (basis * offset) * a
# store the final result if the position is valid
if not domain.is_point_excluded(transform.origin):
new_transforms.push_back(transform)
transforms.list = new_transforms
if randomize_indices:
transforms.shuffle(random_seed)