Added footsteps, new tree, various other tweaks
This commit is contained in:
131
addons/proton_scatter/src/modifiers/clusterize.gd
Normal file
131
addons/proton_scatter/src/modifiers/clusterize.gd
Normal file
@@ -0,0 +1,131 @@
|
||||
@tool
|
||||
extends "base_modifier.gd"
|
||||
|
||||
|
||||
@export_file("Texture") var mask: String
|
||||
@export var mask_rotation := 0.0
|
||||
@export var mask_offset := Vector2.ZERO
|
||||
@export var mask_scale := Vector2.ONE
|
||||
@export var pixel_to_unit_ratio := 64.0
|
||||
@export_range(0.0, 1.0) var remove_below = 0.1
|
||||
@export_range(0.0, 1.0) var remove_above = 1.0
|
||||
@export var scale_transforms := true
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
display_name = "Clusterize"
|
||||
category = "Edit"
|
||||
global_reference_frame_available = true
|
||||
local_reference_frame_available = false # TODO, enable this and handle this case
|
||||
individual_instances_reference_frame_available = false
|
||||
|
||||
documentation.add_paragraph(
|
||||
"Clump transforms together based on a mask.
|
||||
Sampling the mask returns values between 0 and 1. The transforms are
|
||||
scaled against these values which means, bright areas don't affect their
|
||||
scale while dark area scales them down. Transforms are then removed
|
||||
below a threshold, leaving clumps behind.")
|
||||
|
||||
var p := documentation.add_parameter("Mask")
|
||||
p.set_type("Texture")
|
||||
p.set_description("The texture used as a mask.")
|
||||
p.add_warning(
|
||||
"The amount of texture fetch depends on the amount of transforms
|
||||
generated in the previous modifiers (4 reads for each transform).
|
||||
In theory, the texture size shouldn't affect performances in a
|
||||
noticeable way.")
|
||||
|
||||
p = documentation.add_parameter("Mask scale")
|
||||
p.set_type("Vector2")
|
||||
p.set_description(
|
||||
"Depending on the mask resolution, the perceived scale will change.
|
||||
Use this parameter to increase or decrease the area covered by the mask.")
|
||||
|
||||
p = documentation.add_parameter("Mask offset")
|
||||
p.set_type("Vector2")
|
||||
p.set_description("Moves the mask XZ position in 3D space")
|
||||
|
||||
p = documentation.add_parameter("Mask rotation")
|
||||
p.set_type("Float")
|
||||
p.set_description("Rotates the mask around the Y axis. (Angle in degrees)")
|
||||
|
||||
p = documentation.add_parameter("Remove below")
|
||||
p.set_type("Float")
|
||||
p.set_description("Threshold below which the transforms are removed.")
|
||||
|
||||
p = documentation.add_parameter("Remove above")
|
||||
p.set_type("Float")
|
||||
p.set_description("Threshold above which the transforms are removed.")
|
||||
|
||||
func _process_transforms(transforms, domain, _seed) -> void:
|
||||
if not ResourceLoader.exists(mask):
|
||||
warning += "The specified file " + mask + " could not be loaded."
|
||||
return
|
||||
|
||||
var texture: Texture = load(mask)
|
||||
|
||||
if not texture is Texture:
|
||||
warning += "The specified file is not a valid texture."
|
||||
return
|
||||
|
||||
var image: Image
|
||||
|
||||
# Wait for a frame or risk the whole editor to freeze because of get_image()
|
||||
# TODO: Check if more safe guards are required here.
|
||||
await domain.get_root().get_tree().process_frame
|
||||
|
||||
if texture is Texture2D:
|
||||
image = texture.get_image()
|
||||
|
||||
elif texture is Texture3D:
|
||||
image = texture.get_data()[0] # TMP, this should depends on the transforms Y coordinates
|
||||
|
||||
elif texture is TextureLayered:
|
||||
image = texture.get_layer_data(0) # TMP
|
||||
|
||||
image.decompress()
|
||||
|
||||
var width := image.get_width()
|
||||
var height := image.get_height()
|
||||
var i := 0
|
||||
var angle := deg_to_rad(mask_rotation)
|
||||
|
||||
while i < transforms.list.size():
|
||||
var t: Transform3D = transforms.list[i]
|
||||
var origin := t.origin.rotated(Vector3.UP, angle)
|
||||
|
||||
var x := origin.x * (pixel_to_unit_ratio / mask_scale.x) + mask_offset.x
|
||||
x = fposmod(x, width - 1)
|
||||
var y := origin.z * (pixel_to_unit_ratio / mask_scale.y) + mask_offset.y
|
||||
y = fposmod(y, height - 1)
|
||||
|
||||
var level := _get_pixel(image, x, y)
|
||||
if level < remove_below:
|
||||
transforms.list.remove_at(i)
|
||||
continue
|
||||
|
||||
if level > remove_above:
|
||||
transforms.list.remove_at(i)
|
||||
continue
|
||||
|
||||
if scale_transforms:
|
||||
t.basis = t.basis.scaled(Vector3(level, level, level))
|
||||
|
||||
transforms.list[i] = t
|
||||
i += 1
|
||||
|
||||
|
||||
# x and y don't always match an exact pixel, so we sample the neighboring
|
||||
# pixels as well and return a weighted value based on the input coords.
|
||||
func _get_pixel(image: Image, x: float, y: float) -> float:
|
||||
var ix = int(x)
|
||||
var iy = int(y)
|
||||
x -= ix
|
||||
y -= iy
|
||||
|
||||
var nw = image.get_pixel(ix, iy).v
|
||||
var ne = image.get_pixel(ix + 1, iy).v
|
||||
var sw = image.get_pixel(ix, iy + 1).v
|
||||
var se = image.get_pixel(ix + 1, iy + 1).v
|
||||
|
||||
return nw * (1 - x) * (1 - y) + ne * x * (1 - y) + sw * (1 - x) * y + se * x * y
|
||||
Reference in New Issue
Block a user