Megalomaniak Yeah I think I remember it. It was a classis rectangle/frustum 3d selection example, with and without Godot's physics engine involvement. I actually looked for a good place in the docs to cram this into, but in the end couldn't decide where it should go and then forgot about it.
I cant find the thread now though. This forum's search capabilities could use some improvements.

    Megalomaniak luckily there's google

    I'll have to remember that 🙂

    I tried searching the forum with google on several occasions before but still couldn't find things I was looking for. Although truth be told, my patience with google is very limited these days. The web altogether seems to be turning into mush. But I digress...

      xyz I actually looked for a good place in the docs to cram this into, but in the end couldn't decide where it should go and then forgot about it.

      I believe your solutions in general should be put together in one place as an example. It will of course compete with GDQuest. But competition is a good thing?

      Megalomaniak the code provided in that thread handles everything I wanted and more, and seems cleaner than my methodology. Thanks, @xyz, for being so willing to help noobs make games.

        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.