My voxel sculpting tool has a problem

Godot Version

4.4

Question

i am making a voxel terrain tool for my game. and i have ran into a problem with editing the vertices.

when i click where the green dot is here you can see where the terrain is raised to the right. its only the first two rows from the bottom that cause this bug. everywhere else the tool works as intended

That implies your method of mapping the collision to a specific vertex has a problem, but it’s hard to tell without more context.

1 Like


all the other vertices outside the red circle raise when i click them. and all the ones in the red circle act like the one in the first picture.

i know my raytracing works. as it tells me its location as well as the global and local positions of where i click and they are right . even in the code it says its raising the vertices at the right global and local position but when i click on one of the vertices in the circle it raises a different one. i would add my sculpting function but its really long. maybe its the extra floating point numbers after global and local positions that are messing it up. ill test that right now.

Is your array of vertices direct or indexed? It could be an indexing problem?

It could also be that your math goes wrong on underflow or negative coords or something.

1 Like

this is the code i am using for raising height. its actually a lot longer i just got the relevant parts.

for chunk in affected_chunks:

		var mesh: Mesh = chunk.mesh
		var arrays: Array = mesh.surface_get_arrays(0)
		var vertices: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
		for z in range(vertex_count):
			for x in range(vertex_count):
				var local_x: float = x * resolution
				var local_z: float = z * resolution
				var index: int = x + z * vertex_count
				var global_vertex_pos: Vector3 = chunk.to_global(Vector3(local_x, vertices[index].y, local_z))
				var vertex_pos: Vector2 = Vector2(global_vertex_pos.x, global_vertex_pos.z)
				var dist: float = vertex_pos.distance_to(global_brush_center)
				var snapped_dist: float = Vector2(snapped_pos.x, snapped_pos.z).distance_to(Vector2(local_x, local_z))
				var is_boundary: bool = x == 0 or x == vertex_count - 1 or z == 0 or z == vertex_count - 1
				var row: int = z
				var col: int = x
				
				if dist < min_dist:
					min_dist = dist
					closest_index = index

				if dist <= brush_radius:
					var height_change: float = intensity * (1.0 if raise else -1.0) * get_process_delta_time()
					var new_height: float = clamp(vertices[index].y + height_change, -height_scale, height_scale)
					var global_key := Vector2(global_vertex_pos.x, global_vertex_pos.z)
					vertex_updates[global_key] = new_height
					
	# Apply vertex updates
	if vertex_updates.size() > 0 and current_time - last_mesh_update >= MESH_UPDATE_INTERVAL:
		for chunk in affected_chunks:
			var mesh: Mesh = chunk.mesh
			var arrays: Array = mesh.surface_get_arrays(0)
			var vertices: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]
			var indices: PackedInt32Array = arrays[Mesh.ARRAY_INDEX]
			var uvs: PackedVector2Array = arrays[Mesh.ARRAY_TEX_UV]
			var chunk_modified: bool = false
			
			for z in range(vertex_count):
				for x in range(vertex_count):
					var local_x: float = x * resolution
					var local_z: float = z * resolution
					var index: int = x + z * vertex_count
					var global_vertex_pos: Vector3 = chunk.to_global(Vector3(local_x, vertices[index].y, local_z))
					var global_key: Vector2 = Vector2(global_vertex_pos.x, global_vertex_pos.z)
					var closest_key: Vector2 = global_key
					var min_key_dist: float = INF
					for key in vertex_updates:
						var dist: float = global_key.distance_to(key)
						if dist < min_key_dist and dist < 0.1:
							min_key_dist = dist
							closest_key = key
					if vertex_updates.has(closest_key):
						var new_height: float = vertex_updates[closest_key]
						if abs(vertices[index].y - new_height) > 0.001:
							vertices[index].y = new_height
							chunk_modified = true
							var is_boundary: bool = x == 0 or x == vertex_count - 1 or z == 0 or z == vertex_count - 1
							var row: int = z
							var col: int = x

You could probably simplify that a lot if you wanted to, and probably optimize a bunch as well.

For example, when you’re doing “which is closest” with distance checks, you can use distance_squared; it’ll eliminate a square root per vertex but produce the same result.

You can also do:

var index: int = 0
var local_x: float = 0.0
var local_z: float = 0.0

for z: int in vertex_count:
    for x: int in vertex_count:
        #    Do your stuff here; x, z, local_x, local_z and index should all be
        # correct, but not nearly as expensively calculated.
        local_x += resolution
    index += 1
    local_x = 0.0
    local_z += resolution

I think, though, you might want to experiment with snappedf() to calculate the vector offset without looping over the mesh.

As for the actual problem I’ve been ignoring, I’m wondering if it’s some bad interaction with whatever code is operating on is_boundary? I’m wondering if what’s happening is, the collision point is slightly off the mesh and “out of bounds”, and is getting corrected to the wrong place.

1 Like

thank you for the helpful tips i will look into those changes I am still new to voxels. i probably should of started with something simpler. but i like learning the hard way . you were right it was an indexing problem. I was using “var index: int = x + z * vertex_count” as that is what i used to build the terrain to find the vertices but pulling the vertices directly from mesh using this “var vertices: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX]” does not put them in the same order. that was the problem but adding this bit of code fixed it as it indexes based on vertices location

var vertex_index_map: Dictionary = {} 
			for index in range(vertices.size()):
				var vertex_pos: Vector3 = vertices[index]
				var local_x: float = round(vertex_pos.x / resolution) * resolution
				var local_z: float = round(vertex_pos.z / resolution) * resolution
				vertex_index_map[Vector2(local_x, local_z)] = index
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.