xyz One issue with the code, it works, however, after I drag a selection box, the FPS will tank and not recover. Also the fans on my laptop will fire up.
I'm using your code verbatim. I tried to mash it all into a spoiler so it isn't taking up space in this post, but that wasn't working, so sorry.

extends Area3D

@export var camera: Camera3D
const near_far_margin = .1 # frustum near/far planes distance from camera near/far planes

# mouse dragging position
var mouse_down_pos: Vector2
var mouse_current_pos: Vector2

func _ready():
	# initial reference rect setup
	$ReferenceRect.editor_only = false
	$ReferenceRect.visible = false

func _input(event):
	if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
		if event.is_pressed():
			# initialize the rect when mouse is pressed
			mouse_down_pos = event.position
			mouse_current_pos = event.position
			$ReferenceRect.position = event.position
			$ReferenceRect.size = Vector2.ZERO
			$ReferenceRect.visible = true
		else:
			$ReferenceRect.visible = false
			# make a scelection when mouse is released
			select()	
	if event is InputEventMouseMotion and event.button_mask & MOUSE_BUTTON_MASK_LEFT:
		# set rect size when mouse is dragged
		mouse_current_pos = event.position
		$ReferenceRect.position.x = min(mouse_current_pos.x, mouse_down_pos.x)
		$ReferenceRect.position.y = min(mouse_current_pos.y, mouse_down_pos.y)
		$ReferenceRect.size = (event.position - mouse_down_pos).abs()
		
func select():
	# get frustum mesh and assign it as a collider and assignit to the area 3d
	$ReferenceRect.size.x = max(1, $ReferenceRect.size.x)
	$ReferenceRect.size.y = max(1, $ReferenceRect.size.y)
	$CollisionShape3D.shape = make_frustum_collision_mesh(Rect2($ReferenceRect.position, $ReferenceRect.size))
	# wait for collider asignment to take effect
	await get_tree().physics_frame
	await get_tree().physics_frame
	# actually get areas that intersest the frustum
	var selection = get_overlapping_areas()
	print("SELECTION: ", selection)
	# YOUR CODE THAT DECIDES WHAT TO DO WITH THE SELECTION GOES HERE

# function that construct frustum mesh collider
func make_frustum_collision_mesh(rect: Rect2) -> ConvexPolygonShape3D:
	# create a convex polygon collision shape
	var shape = ConvexPolygonShape3D.new()
	# project 4 corners of the rect to the camera near plane
	var pnear = project_rect(rect, camera, camera.near + near_far_margin)
	# project 4 corners of the rext to the camera far plane
	var pfar = project_rect(rect, camera, camera.far - near_far_margin)
	# create a frustum mesh from 8 projected points, 6 planes, 2 triangles per plane, 3 vertices per triangle
	shape.points = PackedVector3Array([
		# near plane
		pnear[0], pnear[1], pnear[2], 
		pnear[1], pnear[2], pnear[3],
		# far plane
		pfar[2], pfar[1], pfar[0],
		pfar[2], pfar[0], pfar[3],
		#top plane
		pnear[0], pfar[0], pfar[1],
		pnear[0], pfar[1], pnear[1],
		#bottom plane
		pfar[2], pfar[3], pnear[3],
		pfar[2], pnear[3], pnear[2],
		#left plane
		pnear[0], pnear[3], pfar[3],
		pnear[0], pfar[3], pfar[0],
		#right plane
		pnear[1], pfar[1], pfar[2],
		pnear[1], pfar[2], pnear[2]
	])
	return shape

# helper function that projects 4 rect corners into space, onto a viewing plane at z distance from the given camera
# projection is done using given camera's perspective projection settings 
func project_rect(rect: Rect2, cam: Camera3D, z: float) -> PackedVector3Array:
	var p = PackedVector3Array() # our projected points
	p.resize(4)
	p[0] = cam.project_position(rect.position, z)
	p[1] = cam.project_position(rect.position + Vector2(rect.size.x, 0.0), z)
	p[2] = cam.project_position(rect.position + Vector2(rect.size.x, rect.size.y), z)
	p[3] = cam.project_position(rect.position + Vector2(0.0, rect.size.y), z)
	return p
  • xyz replied to this.

    Lousifr Looks like fps starts dropping after you release the mouse, so the problem can't be in the code you posted

    Maybe it has to do with the mesh generation code, because I noticed that the StaticBody3D nor the CollisionShape3D show up in the debug console:

    	# actually get areas that intersest the frustum
    	var selection = get_overlapping_areas()
    	print("SELECTION: ", selection)
    	# YOUR CODE THAT DECIDES WHAT TO DO WITH THE SELECTION GOES HERE
    func generate_mesh(i: int):  
    	mesh = ArrayMesh.new()
    	var plane = PlaneMesh.new()
    	plane.size = chunk_size
    	plane.subdivide_width = chunk_size.x
    	plane.subdivide_depth = chunk_size.y
    	mesh.clear_surfaces()
    	mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, plane.get_mesh_arrays())
    
    	md.create_from_surface(mesh, 0)
    	md.set_material(preload("res://Shaders/terrain_shader_material.tres"))
    	for v in md.get_vertex_count():
    		var cyl_coord = cylinder_uv_to_xyz(md.get_vertex_uv(v) * (Vector2(1.0 / num_chunks, 1.0) * uv_scale) + Vector2(i * (1.0 / num_chunks), 0.0))
    		var plains = plain_noise.get_noise_3dv(cyl_coord) * (plain_scale)
    		var oceans = ocean_noise.get_noise_3dv(cyl_coord) * (ocean_scale)
    		var mountains = mountain_noise.get_noise_3dv(cyl_coord) * (mountain_scale)
    		var elevation = plains - (oceans * 1.5)
    		elevation = elevation + (mountains - elevation) * (mountains * 0.5)
    		if elevation < 0.0:
    			elevation *= 1.5
    		md.set_vertex(v, md.get_vertex(v) + Vector3(0.0, elevation, 0.0))
    	mesh.clear_surfaces()
    	md.commit_to_surface(mesh)
    	st.create_from(mesh, 0)
    	st.generate_normals()
    	mesh = st.commit()
    	collision_shape.shape = mesh.create_trimesh_shape()
    • xyz replied to this.

      Lousifr Likely. Watch out not to re-generate meshes each frame. Put some print statements into all generating functions to see when they get called. You should get into habit of planting prints all over your code when something fishy starts to happen. It's the first line of defense against bugs.

        xyz I commented that code out because it was taxing my system, and I found variables that work with what I want in terms of the mesh generation, so no need for mesh regeneration every frame. Also, the code uses get_overlapping_areas(), not get_overlapping_bodies(), so the StaticBody3D wouldn't show up anyway. When I add an Area3D to the building, it does show up in the debug console. Honestly, I might dig up the HexMap I created using C++ last year, convert it back to GDScript and save the mesh generation code in a new project for later. The only issue being the fact that I iterate through all hexes every frame, which I could probably solve with an if hex_distance < map_size / 2: return at the top of the function. The only thing being I'd constantly be reparenting units to different hexes in order to have proper unit placement despite hex location.

        • xyz replied to this.

          Lousifr I think you just need to work a bit on your debugging skills 😃 I suggested to re-generate mesh every frame in a simple separate project just so you can observe in real time what happens with sampled texture as you change the uv and tiling parameters. In your actual game each mesh should be generated only once.

          So check that you don't accidentally do something performance intensive (like mesh generation) every frame. Print statements are best for detecting that. Put a print in each function that does some heavy job. It you get endless printout - it means that it gets called every frame. Easy peasy.

            xyz I went through most of the functions I created and they're not being called unless necessary, no mass output to the console except for in generate_mesh(), but that's because it's going through each vertex at creation, but when I placed the print() after the displacement of vertices, the map is only generating once.

            SOLVED: It seems to have been an issue where the CollisionShape3D for the selection wasn't being reset.

            func select():
            	...
            	# actually get areas that intersest the frustum
            	var selection = get_overlapping_areas()
            	print("SELECTION: ", selection)
            	$CollisionShape3D.shape = CollisionShape3D.new()
            	# YOUR CODE THAT DECIDES WHAT TO DO WITH THE SELECTION GOES HERE
            • xyz replied to this.

              Lousifr $CollisionShape3D.shape = CollisionShape3D.new()

              Would that even work? Even if it would, it'd cause a memory leak. CollisionShape3D is a node type and you're trying to assign it to a property that expect Shape3D resource type.

                xyz It worked to stop the drop in FPS, but I see what you're saying. I'm away from my computer for a few hours, but I'll work on it when I get back.

                xyz This should be safe, no?

                $CollisionShape3D.shape = CollisionShape3D.new().shape
                • xyz replied to this.

                  Lousifr This should be safe, no?
                  $CollisionShape3D.shape = CollisionShape3D.new().shape

                  No, it's also a memory leak. What's your intention with this?

                    xyz I had the thought that maybe it had something to do with the frustum collider, so I tried to play with it and the first move I made "corrected," the issue, but if it's a memory leak, then that's not a solution. My intention was to just see if I could manually reset it, and although now the fps doesn't drop, I don't want memory leaks.

                    What if I constructed a node with new() whenever the mouse button is pressed, then did queue_free() after the mouse button is released? It's less performant than your initial solution, but maybe then the FPS won't drop.

                    I'd say it was likely my laptop (i5 + GTX 1650 Ti + 8GB + Debian Stable), but constructing a new() node made the FPS not drop so IDK the cause.

                    • xyz replied to this.

                      Lousifr If the existence of this collider is causing slowdowns then you likely have too many other physics object in the scene. Run the profiler and identify the bottleneck. No sense in blind guessing while you have tools to measure the performance of every part of the engine.

                      If you want the collider to be ignored, simply disable it. Or assign null to the shape property.