xrjamfeb2025/scenes/world_grab_demo/proxy_collision_detector.gd

90 lines
3.8 KiB
GDScript

extends Node3D
# Exported node paths for references in the editor.
@export var real_particles_path: NodePath # The real GPUParticles3D in the scene.
@export var real_attractor_path: NodePath # The real attractor node.
@export var target_collision_box_path: NodePath # a node for the particle target, assumed to be a 1x1x1 cube around its origin.
@export var debug_label_path: NodePath # A Label node to show "Target Hit: true/false".
# Variables to hold the real nodes.
var real_particles: GPUParticles3D
var real_attractor: Node3D
var target_collision_box: Node3D
var debug_label: Label3D
# Cloned proxy nodes that will live in the offscreen SubViewport.
var clone_particles: GPUParticles3D
var clone_attractor: Node3D
# The SubViewport (and its container) for offscreen rendering.
var sub_viewport_container: SubViewportContainer
var sub_viewport: SubViewport
# Path to your special testing shader resource.
var test_shader_path := "res://scenes/world_grab_demo/particle_test.gdshader"
var test_shader: Shader
var test_shader_material: ShaderMaterial
func _ready():
# Get the real nodes from the exported paths.
real_particles = get_node(real_particles_path) as GPUParticles3D
real_attractor = get_node(real_attractor_path) as Node3D
target_collision_box = get_node(target_collision_box_path) as Node3D
debug_label = get_node(debug_label_path) as Label3D
# Create a SubViewportContainer and a SubViewport.
sub_viewport_container = SubViewportContainer.new()
add_child(sub_viewport_container)
sub_viewport = SubViewport.new()
sub_viewport_container.add_child(sub_viewport)
# Set the SubViewport resolution to a very low value (2x2 pixels).
sub_viewport.size = Vector2i(2, 2)
sub_viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
sub_viewport.own_world_3d = true
#sub_viewport.render_target_v_flip = false
# Add a Camera3D to the SubViewport
var camera = Camera3D.new()
sub_viewport.add_child(camera)
# Clone the particle system and attractor. Using duplicate() with default flags
# (which duplicates recursively) so that the clones have similar structure.
clone_particles = real_particles.duplicate() as GPUParticles3D
clone_attractor = real_attractor.duplicate() as Node3D
# Add the clones to the SubViewport so they render in its world.
sub_viewport.add_child(clone_particles)
sub_viewport.add_child(clone_attractor)
# Load the testing shader and assign it to the clone particle system.
test_shader = load(test_shader_path)
test_shader_material = ShaderMaterial.new()
test_shader_material.shader = test_shader
# Create a copy of the draw_pass_1 mesh
var mesh_copy = clone_particles.draw_pass_1.duplicate()
clone_particles.draw_pass_1 = mesh_copy
# Apply the shader material to the copied mesh
clone_particles.draw_pass_1.surface_set_material(0, test_shader_material)
func _process(_delta):
# Synchronize the global transforms of the clones with the real objects.
clone_particles.global_transform = real_particles.global_transform
clone_attractor.global_transform = real_attractor.global_transform
# update the shader parameter for the target bounds.
test_shader_material.set_shader_parameter("bbox_min", target_collision_box.global_position)
test_shader_material.set_shader_parameter("bbox_max", target_collision_box.global_position + Vector3(1, 1, 1))
# Get the rendered image from the SubViewport.
var viewport_tex := sub_viewport.get_texture()
if viewport_tex:
var img: Image = viewport_tex.get_image()
if img:
# Check only the first pixel in the 2x2 render output.
var col: Color = img.get_pixel(0, 0)
# Our testing shader outputs red (1,0,0) for particles inside the box.
var hit := col.r > 0.9 and col.g < 0.1 and col.b < 0.1
# Update the debug label to show whether the target is hit.
debug_label.text = "Target Hit: " + ("Yes" if hit else "No")