I've found myself in a bit of a pickle again and am going to need ideas. I reached the point in my voxel terrain engine where I'm working on adding textures. If I could texture each face independently this would be an easy one, but I read that for every material the mesh needs to be split which creates extra draw calls and reduces the performance I tried hard to maximize. So I decided to go for mixing most categories into a single tilemap and using UV coordinates to map an image to each face. Only problem is, I can't figure out how to get the coordinates right. To explain with a code snippet of the function in cause below, the full file and project can also be found on Github:
https://github.com/MirceaKitsune/godot_cubedot/blob/main/scripts/lib/voxel_mesh.gd#L39
func get_tris():
var c: PackedVector3Array
var u: PackedVector2Array
var invert = dir == 1 or dir == 2 or dir == 5
if dir == 0 or dir == 1:
c = [pos + Vector3(0, -size.x, -size.y), pos + Vector3(0, -size.x, +size.y), pos + Vector3(0, +size.x, -size.y), pos + Vector3(0, +size.x, +size.y)]
u = [Vector2(c[0].y, c[0].z), Vector2(c[1].y, c[1].z), Vector2(c[2].y, c[2].z), Vector2(c[3].y, c[3].z)]
elif dir == 2 or dir == 3:
c = [pos + Vector3(-size.x, 0, -size.y), pos + Vector3(-size.x, 0, +size.y), pos + Vector3(+size.x, 0, -size.y), pos + Vector3(+size.x, 0, +size.y)]
u = [Vector2(c[0].x, c[0].z), Vector2(c[1].x, c[1].z), Vector2(c[2].x, c[2].z), Vector2(c[3].x, c[3].z)]
elif dir == 4 or dir == 5:
c = [pos + Vector3(-size.x, -size.y, 0), pos + Vector3(-size.x, +size.y, 0), pos + Vector3(+size.x, -size.y, 0), pos + Vector3(+size.x, +size.y, 0)]
u = [Vector2(c[0].x, c[0].y), Vector2(c[1].x, c[1].y), Vector2(c[2].x, c[2].y), Vector2(c[3].x, c[3].y)]
The gist of that code is simple: Both c and u are an array containing 4 entries each representing a corner, one for spatial corners (4 x Vector3) the other for UV coordinates (4 x Vector2)... dir is the axis in which the face is pointing, this is important for 3D positioning and less relevant here, just to explain why I have those cases separating the X Y Z axes. Currently that code takes the corners in the direction the face spans across and treats them as UV coordinates. I already tested it and get good results with a single texture, some drift but if that was the only issue I'd correct it with a fixed offset.

With this test texture I have 2 x 2 tiles, so I'd need to zoom the texture in twice then show a 0.5 x 0.5 section per face. If I multiply u[0] / u[1] / u[2] / u[3] with 0.5 I should be getting the right scale, however I also need to offset the position accordingly. With trial and error I might be able to find an offset that works, yet I have an even bigger problem: After the scale syncs I need to choose which tile to show, I can't have one tile on one voxel then the other on the voxel next to it like a checkerboard.
The obvious solution would seem to manually set the UV corners per face and drop the face's position offset, but there's another problem there: There's also the a face merging algorithm I posted about earlier, meaning any surface may be a stretched rectangle representing multiple other surfaces with the same image. To make matters worse I also have a variable voxel size and already decided to go default at a resolution of 0.5m rather than the 1m most Minecraft inspired engines use; This shouldn't be a big issue as I want half-voxels to cut into the texture which may remain scaled to 1m by default... my real problem is the varying position and scale offsets caused by face mergers causing larger faces that need to repeat the same texture giving the appearance the smaller faces are there.
In that function I know the center position and scale of the face as well as its corners in 3D space. How do you suggest I use them to correctly put the UV coordinates at a position of choice to display a specific section of my texture? Will think on it till tomorrow and see what answers I can come up with myself.