From c2a955f86c405d4ca28baec2887c886873f04845 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sun, 22 Oct 2023 19:34:03 +0200 Subject: [PATCH] Scale textures and models in LOD terrain to match their true scale. This also adds some support for rendering larger volumes of voxel models at once. That might allow greeding meshing for voxel models. Fixes #146 --- assets/cubyz/shaders/chunks/chunk_fragment.fs | 2 + assets/cubyz/shaders/chunks/chunk_vertex.vs | 12 ++- .../shaders/chunks/transparent_fragment.fs | 2 + .../shaders/chunks/voxel_model_fragment.fs | 10 +-- src/models.zig | 86 +++++++++---------- 5 files changed, 58 insertions(+), 54 deletions(-) diff --git a/assets/cubyz/shaders/chunks/chunk_fragment.fs b/assets/cubyz/shaders/chunks/chunk_fragment.fs index 97278ff9..2ab36a71 100644 --- a/assets/cubyz/shaders/chunks/chunk_fragment.fs +++ b/assets/cubyz/shaders/chunks/chunk_fragment.fs @@ -7,6 +7,8 @@ flat in int modelIndex; flat in int isBackFace; flat in int ditherSeed; // For raymarching: +flat in ivec3 minPos; +flat in ivec3 maxPos; in vec3 startPosition; in vec3 direction; diff --git a/assets/cubyz/shaders/chunks/chunk_vertex.vs b/assets/cubyz/shaders/chunks/chunk_vertex.vs index f3b8d9d1..b84f6a2a 100644 --- a/assets/cubyz/shaders/chunks/chunk_vertex.vs +++ b/assets/cubyz/shaders/chunks/chunk_vertex.vs @@ -7,6 +7,8 @@ flat out int faceNormal; flat out int isBackFace; flat out int ditherSeed; // For raymarching: +flat out ivec3 minPos; +flat out ivec3 maxPos; out vec3 startPosition; out vec3 direction; @@ -149,20 +151,24 @@ void main() { position *= 16; position -= ivec3(mirrorVector)*8 - 8; position = ivec3(permutationMatrix*(position*mirrorVector)); + position *= voxelSize; ivec3 totalOffset = (ivec3(normals[oldNormal])+1)/2; totalOffset += ivec3(equal(textureX[oldNormal], ivec3(-1))) + (vertexID>>1 & 1)*textureX[oldNormal]; totalOffset += ivec3(equal(textureY[oldNormal], ivec3(-1))) + (vertexID & 1)*textureY[oldNormal]; totalOffset = ivec3(permutationMatrix*(vec3(equal(mirrorVector, vec3(1)))*totalOffset + vec3(equal(mirrorVector, vec3(-1)))*(1 - totalOffset))); ivec3 lowerBound = voxelModels[modelIndex].minimum.xyz; ivec3 size = voxelModels[modelIndex].maximum.xyz - voxelModels[modelIndex].minimum.xyz; + size += (voxelSize - 1)*16; + minPos = lowerBound; + maxPos = lowerBound + size; totalOffset = lowerBound + size*totalOffset; - position += totalOffset - 16*ivec3(normals[normal]); + position += totalOffset - 16*voxelSize*ivec3(normals[normal]); startPosition = (totalOffset)*0.999; - direction = position.xyz/16.0 + permutationMatrix*(mirrorVector*modelPosition)/voxelSize; + direction = position.xyz/16.0 + permutationMatrix*(mirrorVector*modelPosition); - vec3 globalPosition = mirrorVector*(transpose(permutationMatrix)*position)*voxelSize/16.0 + modelPosition; + vec3 globalPosition = mirrorVector*(transpose(permutationMatrix)*position)/16.0 + modelPosition; vec4 mvPos = viewMatrix*vec4(globalPosition, 1); gl_Position = projectionMatrix*mvPos; diff --git a/assets/cubyz/shaders/chunks/transparent_fragment.fs b/assets/cubyz/shaders/chunks/transparent_fragment.fs index ba6382b1..66391dfc 100644 --- a/assets/cubyz/shaders/chunks/transparent_fragment.fs +++ b/assets/cubyz/shaders/chunks/transparent_fragment.fs @@ -6,6 +6,8 @@ flat in int faceNormal; flat in int modelIndex; flat in int isBackFace; flat in int ditherSeed; +flat in ivec3 minPos; +flat in ivec3 maxPos; in vec3 startPosition; in vec3 direction; diff --git a/assets/cubyz/shaders/chunks/voxel_model_fragment.fs b/assets/cubyz/shaders/chunks/voxel_model_fragment.fs index 886a537a..e959c735 100644 --- a/assets/cubyz/shaders/chunks/voxel_model_fragment.fs +++ b/assets/cubyz/shaders/chunks/voxel_model_fragment.fs @@ -7,6 +7,8 @@ flat in int modelIndex; flat in int isBackFace; flat in int ditherSeed; // For raymarching: +flat in ivec3 minPos; +flat in ivec3 maxPos; in vec3 startPosition; in vec3 direction; @@ -59,6 +61,7 @@ const vec3[6] normals = vec3[6]( ); int getVoxel(ivec3 voxelPos) { + voxelPos &= 15; int voxelIndex = (voxelPos.x << 8) | (voxelPos.y << 4) | (voxelPos.z); int shift = 4*(voxelIndex & 7); int arrayIndex = voxelIndex >> 3; @@ -87,9 +90,6 @@ RayMarchResult rayMarching(vec3 startPosition, vec3 direction) { // TODO: Mipmap ivec3 voxelPos = ivec3(floor(startPosition)); - ivec3 minPos = voxelModels[modelIndex].minimum.xyz; - ivec3 maxPos = voxelModels[modelIndex].maximum.xyz; - ivec3 compare = mix(-maxPos, minPos, lessThan(direction, vec3(0))); ivec3 inversionMasks = mix(ivec3(~0), ivec3(0), lessThan(direction, vec3(0))); @@ -137,7 +137,7 @@ RayMarchResult rayMarching(vec3 startPosition, vec3 direction) { // TODO: Mipmap } int textureDir = -block; if(textureDir == 6) textureDir = lastNormal; - return RayMarchResult(true, lastNormal, textureDir, voxelPos); + return RayMarchResult(true, lastNormal, textureDir, voxelPos & 15); } ivec2 getTextureCoords(ivec3 voxelPosition, int textureDir) { @@ -214,7 +214,7 @@ void main() { if(!passDitherTest(interp)) { result = rayMarching(startPosition, direction); } else { - result = RayMarchResult(true, faceNormal, faceNormal, ivec3(startPosition)); // At some point it doesn't make sense to even draw the model. + result = RayMarchResult(true, faceNormal, faceNormal, ivec3(startPosition) & 15); // At some point it doesn't make sense to even draw the model. } if(!result.hitAThing) discard; int textureIndex = textureData[blockType].textureIndices[result.textureDir]; diff --git a/src/models.zig b/src/models.zig index 705e04dc..63939c0c 100644 --- a/src/models.zig +++ b/src/models.zig @@ -18,25 +18,25 @@ const VoxelModel = extern struct { max: Vec3i, bitPackedData: [modelSize*modelSize*modelSize/8]u32, - pub fn init(self: *VoxelModel, distributionFunction: *const fn(u16, u16, u16) ?u4) void { + pub fn init(self: *VoxelModel, distributionFunction: *const fn(u4, u4, u4) ?u4) void { if(@sizeOf(VoxelModel) != 16 + 16 + modelSize*modelSize*modelSize*4/8) @compileError("Expected Vec3i to have 16 byte alignment."); @memset(&self.bitPackedData, 0); self.min = @splat(16); self.max = @splat(0); - var x: u16 = 0; - while(x < modelSize): (x += 1) { - var y: u16 = 0; - while(y < modelSize): (y += 1) { - var z: u16 = 0; - while(z < modelSize): (z += 1) { - var isSolid = distributionFunction(x, y, z); - var voxelIndex = (x << 2*modelShift) + (y << modelShift) + z; - var shift = 4*@as(u5, @intCast(voxelIndex & 7)); - var arrayIndex = voxelIndex >> 3; + for(0..modelSize) |_x| { + const x: u4 = @intCast(_x); + for(0..modelSize) |_y| { + const y: u4 = @intCast(_y); + for(0..modelSize) |_z| { + const z: u4 = @intCast(_z); + const isSolid = distributionFunction(x, y, z); + const voxelIndex = (_x << 2*modelShift) + (_y << modelShift) + _z; + const shift = 4*@as(u5, @intCast(voxelIndex & 7)); + const arrayIndex = voxelIndex >> 3; if(isSolid) |texture| { std.debug.assert(texture <= 6); self.min = @min(self.min, Vec3i{x, y, z}); - self.max = @max(self.max, Vec3i{x+1, y+1, z+1}); + self.max = @max(self.max, Vec3i{x, y, z}); self.bitPackedData[arrayIndex] |= @as(u32, 6 - texture) << shift; } else { self.bitPackedData[arrayIndex] |= @as(u32, 7) << shift; @@ -44,47 +44,41 @@ const VoxelModel = extern struct { } } } + self.max += @splat(1); // TODO: Use floodfill for this. var i: u32 = 7; while(i < 14) : (i += 1) { - x = 0; - while(x < modelSize): (x += 1) { - var y: u16 = 0; - while(y < modelSize): (y += 1) { - var z: u16 = 0; + for(0..modelSize) |_x| { + const x: u4 = @intCast(_x); + for(0..modelSize) |_y| { + const y: u4 = @intCast(_y); outer: - while(z < modelSize): (z += 1) { - var voxelIndex = (x << 2*modelShift) + (y << modelShift) + z; - var shift = 4*@as(u5, @intCast(voxelIndex & 7)); - var arrayIndex = voxelIndex >> 3; - var data = self.bitPackedData[arrayIndex]>>shift & 15; + for(0..modelSize) |_z| { + const z: u4 = @intCast(_z); + const voxelIndex = (_x << 2*modelShift) + (_y << modelShift) + _z; + const shift = 4*@as(u5, @intCast(voxelIndex & 7)); + const arrayIndex = voxelIndex >> 3; + const data = self.bitPackedData[arrayIndex]>>shift & 15; if(data <= i) continue; var dx: u2 = 0; while(dx < 3) : (dx += 1) { - if(dx == 0 and x == 0) continue; - if(dx + x - 1 >= 16) continue; var dy: u2 = 0; while(dy < 3) : (dy += 1) { - if(dy == 0 and y == 0) continue; - if(dy + y - 1 >= 16) continue; var dz: u2 = 0; while(dz < 3) : (dz += 1) { - if(dz == 0 and z == 0) continue; - if(dz + z - 1 >= 16) continue; - var nx = x + dx - 1; - var ny = y + dy - 1; - var nz = z + dz - 1; - var neighborVoxelIndex = (nx << 2*modelShift) + (ny << modelShift) + nz; - var neighborShift = 4*@as(u5, @intCast(neighborVoxelIndex & 7)); - var neighborArrayIndex = neighborVoxelIndex >> 3; - var neighborData = self.bitPackedData[neighborArrayIndex]>>neighborShift & 15; + const nx = x +% dx -% 1; + const ny = y +% dy -% 1; + const nz = z +% dz -% 1; + const neighborVoxelIndex = (@as(usize, nx) << 2*modelShift) + (@as(usize, ny) << modelShift) + @as(usize, nz); + const neighborShift = 4*@as(u5, @intCast(neighborVoxelIndex & 7)); + const neighborArrayIndex = neighborVoxelIndex >> 3; + const neighborData = self.bitPackedData[neighborArrayIndex]>>neighborShift & 15; if(neighborData < data) continue :outer; } } } - data += 1; self.bitPackedData[arrayIndex] &= ~(@as(u32, 15) << shift); - self.bitPackedData[arrayIndex] |= @as(u32, data) << shift; + self.bitPackedData[arrayIndex] |= @as(u32, data + 1) << shift; } } } @@ -92,12 +86,12 @@ const VoxelModel = extern struct { } }; -fn cube(_: u16, _: u16, _: u16) ?u4 { +fn cube(_: u4, _: u4, _: u4) ?u4 { return 6; } const Fence = struct { - fn fence0(_x: u16, _y: u16, _z: u16) ?u4 { + fn fence0(_x: u4, _y: u4, _z: u4) ?u4 { var x = @max(@as(i32, _x)-8, -@as(i32, _x)+7); var y = @max(@as(i32, _y)-8, -@as(i32, _y)+7); var z = @max(@as(i32, _z)-8, -@as(i32, _z)+7); @@ -106,7 +100,7 @@ const Fence = struct { return null; } - fn fence1(_x: u16, _y: u16, _z: u16) ?u4 { + fn fence1(_x: u4, _y: u4, _z: u4) ?u4 { var x = @max(@as(i32, _x)-8, -@as(i32, _x)+7); var y = @max(@as(i32, _y)-8, -@as(i32, _y)+7); var z = @max(@as(i32, _z)-8, -@as(i32, _z)+7); @@ -117,7 +111,7 @@ const Fence = struct { return null; } - fn fence2_neighbor(_x: u16, _y: u16, _z: u16) ?u4 { + fn fence2_neighbor(_x: u4, _y: u4, _z: u4) ?u4 { var x = @max(@as(i32, _x)-8, -@as(i32, _x)+7); var y = @max(@as(i32, _y)-8, -@as(i32, _y)+7); var z = @max(@as(i32, _z)-8, -@as(i32, _z)+7); @@ -129,7 +123,7 @@ const Fence = struct { return null; } - fn fence2_oppose(_x: u16, _y: u16, _z: u16) ?u4 { + fn fence2_oppose(_x: u4, _y: u4, _z: u4) ?u4 { var x = @max(@as(i32, _x)-8, -@as(i32, _x)+7); var y = @max(@as(i32, _y)-8, -@as(i32, _y)+7); var z = @max(@as(i32, _z)-8, -@as(i32, _z)+7); @@ -140,7 +134,7 @@ const Fence = struct { return null; } - fn fence3(_x: u16, _y: u16, _z: u16) ?u4 { + fn fence3(_x: u4, _y: u4, _z: u4) ?u4 { var x = @max(@as(i32, _x)-8, -@as(i32, _x)+7); var y = @max(@as(i32, _y)-8, -@as(i32, _y)+7); var z = @max(@as(i32, _z)-8, -@as(i32, _z)+7); @@ -152,7 +146,7 @@ const Fence = struct { return null; } - fn fence4(_x: u16, _y: u16, _z: u16) ?u4 { + fn fence4(_x: u4, _y: u4, _z: u4) ?u4 { var x = @max(@as(i32, _x)-8, -@as(i32, _x)+7); var y = @max(@as(i32, _y)-8, -@as(i32, _y)+7); var z = @max(@as(i32, _z)-8, -@as(i32, _z)+7); @@ -164,7 +158,7 @@ const Fence = struct { } }; -fn log(_x: u16, _y: u16, _z: u16) ?u4 { +fn log(_x: u4, _y: u4, _z: u4) ?u4 { var x = @as(f32, @floatFromInt(_x)) - 7.5; var y = @as(f32, @floatFromInt(_y)) - 7.5; var z = @as(f32, @floatFromInt(_z)) - 7.5; @@ -184,7 +178,7 @@ fn log(_x: u16, _y: u16, _z: u16) ?u4 { return null; } -fn octahedron(_x: u16, _y: u16, _z: u16) ?u4 { +fn octahedron(_x: u4, _y: u4, _z: u4) ?u4 { var x = _x; var y = _y; var z = _z;