Loop through all points in a spherical area located at a given origin

I'm working on voxel terrain in Godot which uses a chunk system. One component is scanning the area around the player to determine which new chunks to add as they move. I took the easy way and wrote the code in this structure:

# view_pos varies and can be anything
var chunk_size = 16
var view_dist = 64
var view_pos = Vector3(0, 0, 0)
var mins = Vector3(view_pos.x - view_dist, view_pos.y - view_dist, view_pos.z - view_dist)
var maxs = Vector3(view_pos.x + view_dist, view_pos.y + view_dist, view_pos.z + view_dist)

for x in range(mins.x, maxs.x, chunk_size):
    for y in range(mins.y, maxs.y, chunk_size):
        for z in range(mins.z, maxs.z, chunk_size):
            var pos = Vector3(x, y, z)
            if pos.distance_to(view_pos) <= view_dist:
                # Add a new chunk here

This works but I want to optimize my loop for performance, given it can induce notable slowdowns with a lot of chunks. As you can see I preform my loop in a cubic area encompassing the view, yet only care to add new chunks if they're within a spherical radius encompassing that cube: Every point that falls within the virtual cube but outside the virtual sphere encompassing it is a wasted iteration.

How do you suggest doing my x / y / z loop, or better off a single loop in a 1D array converted to 3D positions, so that each iteration is automatically a valid point within a sphere? Does Godot have a builtin for this, or if not how would you write such a function? I have an idea I might share after I try it, but would rather hear suggestions from others on how it can be best done.


Solution 1:

I went with my easy solution for now: Use the cubic loop to generate the sphere, but only do so once to bake the possible positions into an array of vectors, this array is then iterated each execution with the offset of the active position we want to check.

Game start function:

for x in range(-distance, distance + 1, chunk_size):
    for y in range(-distance, distance + 1, chunk_size):
        for z in range(-distance, distance + 1, chunk_size):
            var pos = Vector3(x, y, z)
            if pos.distance_to(Vector3i(0, 0, 0)) <= distance:
                view_sphere.append(pos)

Execution function:

for pos_point in view_sphere:
    var pos = pos_point + pos_view
    # Spawn a new chunk using pos