Added voxel raycasting and voxel chunk access

This commit is contained in:
Simon Lübeß
2021-03-02 22:07:21 +01:00
parent cbfbd449ff
commit 488d2a55c4
5 changed files with 306 additions and 263 deletions
+39
View File
@@ -77,6 +77,9 @@ namespace GlitchyEngine.Math
return X * X + Y * Y + Z * Z;
}
/**
* Normalizes this vector.
*/
[Checked]
public void Normalize() mut
{
@@ -86,11 +89,34 @@ namespace GlitchyEngine.Math
this /= Magnitude();
}
/**
* Normalizes this vector.
*/
public void Normalize() mut
{
this /= Magnitude();
}
/**
* Returns a copy of this Vector with a magnitude of 1.
*/
[Checked]
public Vector3 Normalized()
{
if(this == .Zero)
return .Zero;
return this / Magnitude();
}
/**
* Returns a copy of this Vector with a magnitude of 1.
*/
public Vector3 Normalized()
{
return this / Magnitude();
}
public static Vector3 Normalize(Vector3 v)
{
return v / v.Magnitude();
@@ -125,6 +151,11 @@ namespace GlitchyEngine.Math
return (a - b * (Dot(a, b) / Dot(b, b)));
}
public static Vector3 Floor(Vector3 value)
{
return .(Math.Floor(value.X), Math.Floor(value.Y), Math.Floor(value.Z));
}
//
// Assignment operators
//
@@ -239,6 +270,14 @@ namespace GlitchyEngine.Math
public static Vector3 operator /(float scalar, Vector3 value) => Vector3(scalar / value.X, scalar / value.Y, scalar / value.Z);
// Modulo
public static Vector3 operator %(Vector3 left, Vector3 right) => Vector3(left.X % right.X, left.Y % right.Y, left.Z % right.Z);
public static Vector3 operator %(Vector3 value, float scalar) => Vector3(value.X % scalar, value.Y % scalar, value.Z % scalar);
public static Vector3 operator %(float scalar, Vector3 value) => Vector3(scalar % value.X, scalar % value.Y, scalar % value.Z);
// Equality
public static bool operator ==(Vector3 left, Vector3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
+40 -6
View File
@@ -111,6 +111,7 @@ namespace Sandbox.VoxelFun
chunkLoader = new Thread(new => ChunkLoaderThread_Entry);
chunkLoader.Start();
}
public ~this()
@@ -209,21 +210,22 @@ namespace Sandbox.VoxelFun
// Wait for chunkPos to change
chunkPosChanged.Enter();
chunkPosLock.Enter();
using(chunkPosLock.Enter())
{
curChunkPosition = chunkPosition;
chunkPosLock.Exit();
}
// Position didn't change -> skip
if(curChunkPosition == oldChunkPosition)
continue;
GenerateChunks(curChunkPosition);
ChunkLoaderThread_GenerateChunks(curChunkPosition);
oldChunkPosition = curChunkPosition;
}
}
public void GenerateChunks(Point3 chunkPos)
public void ChunkLoaderThread_GenerateChunks(Point3 chunkPos)
{
for(var chunk in _chunks)
{
@@ -231,13 +233,44 @@ namespace Sandbox.VoxelFun
if(dist.X > _viewDistance || dist.Z > _viewDistance)
{
chunkListLock.Enter();
using(chunkListLock.Enter())
{
delete chunk.value;
_chunks.Remove(chunk.key);
chunkListLock.Exit();
}
}
}
int x = 0;
int z = 0;
for(int r = 1; r < _viewDistance; r++)
{
for(; x < r; x++)
{
Point3 chunkCoordinate = (Point3)chunkPos + .(x, 0, z);
LoadChunk(chunkCoordinate);
}
for(; z < r; z++)
{
Point3 chunkCoordinate = (Point3)chunkPos + .(x, 0, z);
LoadChunk(chunkCoordinate);
}
for(; x > -r; x--)
{
Point3 chunkCoordinate = (Point3)chunkPos + .(x, 0, z);
LoadChunk(chunkCoordinate);
}
for(; z > -r; z--)
{
Point3 chunkCoordinate = (Point3)chunkPos + .(x, 0, z);
LoadChunk(chunkCoordinate);
}
}
/*
for(int x = -_viewDistance; x < _viewDistance; x++)
//for(int y = -_viewDistance; y < _viewDistance; y++)
for(int z = -_viewDistance; z < _viewDistance; z++)
@@ -247,6 +280,7 @@ namespace Sandbox.VoxelFun
LoadChunk(chunkCoordinate);
}
*/
}
Result<void> LoadChunkFromFile(Point3 chunkCoordinate, Chunk outChunk)
+3
View File
@@ -1,3 +1,5 @@
using GlitchyEngine.Math;
namespace Sandbox.VoxelFun
{
public struct VoxelChunk
@@ -7,6 +9,7 @@ namespace Sandbox.VoxelFun
public const int SizeZ = 16;
public const Point3 Size = .(SizeX, SizeY, SizeZ);
public const Vector3 VectorSize = .(SizeX, SizeY, SizeZ);
public uint8[SizeX][SizeY][SizeZ] Data;
}
+61 -215
View File
@@ -59,6 +59,8 @@ namespace Sandbox.VoxelFun
GeometryBinding _quadGeometryBinding ~ _?.ReleaseRef();
GeometryBinding _lineGeometryBinding ~ _?.ReleaseRef();
RasterizerState _rasterizerState ~ delete _;
Effect _effect ~ _?.ReleaseRef();
@@ -185,6 +187,30 @@ namespace Sandbox.VoxelFun
qib.ReleaseRef();
}
// Create Line
{
_lineGeometryBinding = new GeometryBinding(_context);
_lineGeometryBinding.SetPrimitiveTopology(.LineList);
_lineGeometryBinding.SetVertexLayout(_vertexLayout);
VertexColorTexture[?] vertices = .(
VertexColorTexture(Vector3(0, 0, 0), Color.White, .(0, 0)),
VertexColorTexture(Vector3(0, 0, 10), Color.White, .(0, 1)),
);
let qvb = new VertexBuffer(_context, typeof(VertexColorTexture), (.)vertices.Count, .Immutable);
qvb.SetData(vertices);
_lineGeometryBinding.SetVertexBufferSlot(qvb, 0);
qvb.ReleaseRef();
uint16[?] indices = .(0, 1);
let qib = new IndexBuffer(_context, (.)indices.Count, .Immutable);
qib.SetData(indices);
_lineGeometryBinding.SetIndexBuffer(qib);
qib.ReleaseRef();
}
// Create rasterizer state
GlitchyEngine.Renderer.RasterizerStateDescription rsDesc = .(.Solid, .Back, true);
rsDesc.DepthClipEnabled = true;
@@ -226,219 +252,7 @@ namespace Sandbox.VoxelFun
_world.ChunkManager.Texture = _texture..AddRef();
_world.ChunkManager.TextureEffect = _textureEffect..AddRef();
}
/*
void GenerateTerrain(ref VoxelChunk vc)
{
Stopwatch sw = .StartNew();
/*
float[] gradient = scope .[VoxelChunk.SizeY](?);
for(int y = 0; y < VoxelChunk.SizeY; y++)
{
gradient[y] = y / (float)(VoxelChunk.SizeY - 1);
}
*/
//Random r = scope Random();
let groundNoise = scope FastNoiseLite.FastNoiseLite();
groundNoise.SetFractalOctaves(6);
groundNoise.SetFractalType(.FBm);
groundNoise.SetFrequency(0.0075f);
let perturbNoise = scope FastNoiseLite.FastNoiseLite();
perturbNoise.SetFractalOctaves(6);
perturbNoise.SetFractalType(.FBm);
perturbNoise.SetFrequency(0.005f);
for(int x < VoxelChunk.SizeX)
for(int y < VoxelChunk.SizeY)
for(int z < VoxelChunk.SizeZ)
{
//(int X, int Y, int Z) coordinate = (x, y, z);
// randomize coordinate
//coordinate.Y += (int)(groundNoise.GetNoise(x, 0, z) * VoxelChunk.SizeY / 4);//r.Next(-VoxelChunk.SizeY / 4, VoxelChunk.SizeY / 4);
//float pertubation = perturbNoise.GetNoise(y * 0.5f, -y * 0.5f) * 30;
//coordinate.Y += (int)(groundNoise.GetNoise(x + pertubation, z + pertubation) * VoxelChunk.SizeY / 4);//r.Next(-VoxelChunk.SizeY / 4, VoxelChunk.SizeY / 4);
//coordinate.Y = Math.Clamp(coordinate.Y, 0, VoxelChunk.SizeY - 1);
// get Gradient for coordinate
//float gradientValue = gradient[coordinate.Y];
float cy = (float)y;
cy += groundNoise.GetNoise(x, cy * 0.5f, z) * (VoxelChunk.SizeY / 4.0f);
float gradientValue = cy / (float)(VoxelChunk.SizeY - 1);
// determine whether or not gradient value is air
uint8 stepValue = gradientValue < 0.5f ? 1 : 0;
vc.Data[x][y][z] = stepValue;
}
sw.Stop();
Debug.WriteLine($"Terrain Generation: {sw.ElapsedMilliseconds}ms");
delete sw;
/*
let elevationNoise = scope FastNoiseLite.FastNoiseLite();
//elevationNoise.
for(int x < VoxelChunk.SizeX)
for(int y < VoxelChunk.SizeY)
for(int z < VoxelChunk.SizeZ)
{
/*
float elevation = elevationNoise.GetNoise(x, z);
int height = (int)((elevation) * 64 + 64);
for(int y < height)
{
vc.Data[x][y][z] = 1;
}
*/
vc.Data[x][y][z] = ((elevationNoise.GetNoise(x, y, z) * 64) + y) < 64 ? 1 : 0;
//vc.Data[x][y][z] = elevationNoise.GetNoise(x, y, z) > 0.0f ? 1 : 0;
//vc.Data[x][y][z] = (noise.GetNoise(x, y, z) + 0.5f) < 0 ? 0 : 1;
}
*/
/*
for(int x < VoxelChunk.SizeX)
for(int z < VoxelChunk.SizeZ)
{
float f = noise.GetNoise(x, z);
int yMax = (int)(f * VoxelChunk.SizeY);
for(int y < yMax)
{
vc.Data[x][y][z] = 1;
}
}
*/
}
struct GroundLayer
{
public int Depth;
public uint8 BlockType;
}
void GroundLayers(ref VoxelChunk vc)
{
GroundLayer[3] layers;
layers[0] = .()
{
Depth = 1,
BlockType = 3
};
layers[1] = .()
{
Depth = 4,
BlockType = 2
};
layers[2] = .()
{
Depth = 0,
BlockType = 1
};
for(int x < VoxelChunk.SizeX)
for(int z < VoxelChunk.SizeZ)
{
int currentLayer = 0;
int currentDepth = 0;
for(int y = VoxelChunk.SizeY - 1; y > 0; y--)
{
if(vc.Data[x][y][z] == 0)
{
currentDepth--;
if(currentDepth < 0)
{
currentDepth = 0;
currentLayer--;
if(currentLayer < 0)
currentLayer = 0;
}
}
else
{
vc.Data[x][y][z] = layers[currentLayer].BlockType;
currentDepth++;
if(currentDepth >= layers[currentLayer].Depth)
{
if(currentLayer >= layers.Count - 1)
{
currentLayer = layers.Count - 1;
}
else
{
currentDepth = 0;
currentLayer++;
}
}
}
}
}
}
void GenTestChunk()
{
VoxelChunk* vc = new .();
GenerateTerrain(ref *vc);
GroundLayers(ref *vc);
/*
Random r = scope Random();
for(int x < VoxelChunk.SizeX)
for(int y < VoxelChunk.SizeY)
for(int z < VoxelChunk.SizeZ)
{
vc.Data[x][y][z] = (.)y;//(.)r.Next(0, 2);
}
for(int x < VoxelChunk.SizeX)
for(int y < VoxelChunk.SizeY)
for(int z < VoxelChunk.SizeZ)
{
if(vc.Data[x][y][z] < VoxelChunk.SizeY / 2)
vc.Data[x][y][z] = 1;
else
vc.Data[x][y][z] = 0;
}
*/
/*
vc.Data[8][VoxelChunk.SizeY / 2][8] = 1;
vc.Data[8][VoxelChunk.SizeY / 2 + 1][8] = 1;
vc.Data[8][VoxelChunk.SizeY / 2 + 2][8] = 1;
vc.Data[8][VoxelChunk.SizeY / 2 + 3][8] = 1;
for(int x = 6; x < 11; x++)
for(int y = VoxelChunk.SizeY / 2 + 2; y < VoxelChunk.SizeY / 2 + 5; y++)
for(int z = 6; z < 11; z++)
{
vc.Data[x][y][z] = 1;
}
*/
_chunkGeoBinding = voxelGeoGen.GenerateGeometry(*vc);
delete vc;
}
*/
void UpdateCamera(GameTime gameTime)
{
UpdateCameraRotation(gameTime);
@@ -529,6 +343,8 @@ namespace Sandbox.VoxelFun
_camera.Position += movement * speed;
}
Ray ray = .(.(0, 128, 0), .UnitZ);
public override void Update(GameTime gameTime)
{
UpdateCamera(gameTime);
@@ -574,13 +390,33 @@ namespace Sandbox.VoxelFun
_ge_logo.Bind();
Renderer.Submit(_quadGeometryBinding, _textureEffect, .Scaling(1.5f));
Ray ray = .(_camera.Position, _camera.Transform.Forward);
Point3 lookedAtBlock = _world.RaycastBlock(ray, 10);
//Ray ray = .(.(0.5f, 128.5f, 0.5f), .(0, -0.1f, -0.1f));
_effect.Variables["BaseColor"].SetData(.Red);
_texture.Bind();
Matrix mat = .Translation(ray.Start);
Matrix mat2 = .Identity;
mat2.Up = .(0, 1, 0);
mat2.Forward = ray.Direction;
mat2.Forward.Normalize();
mat2.Right = Vector3.Cross(mat2.Up, mat2.Forward);
mat2.Right.Normalize();
mat2.Up = Vector3.Cross(mat2.Forward, mat2.Right);
mat2.Up.Normalize();
Matrix mat3 = mat * mat2;
Renderer.Submit(_lineGeometryBinding, _textureEffect, mat3);
//Point3 lookedAtBlock = _world.RaycastBlock(ray, 10, _cubeGeo, _textureEffect);
Point3 lookedAtBlock = _world.RaycastBlock(.(_camera.Position, _camera.Transform.Forward), 10, _cubeGeo, _textureEffect);
Renderer.Submit(_cubeGeo, _textureEffect, .Translation((Vector3)lookedAtBlock));
_world.ChunkManager.Draw();
@@ -631,6 +467,16 @@ namespace Sandbox.VoxelFun
_squareColor1 = ColorRGBA.White - _squareColor0;
ImGui.DragFloat3("Ray Position", *(float[3]*)(void*)&ray.Start, 0.1f, float.NegativeInfinity, float.PositiveInfinity);
ImGui.DragFloat3("Ray Direction", *(float[3]*)(void*)&ray.Direction, 0.001f, -1f, 1.0f);
_camera.Position = camPos;
ImGui.End();
return false;
+149 -28
View File
@@ -3,6 +3,7 @@ using System.IO;
using System.Collections;
using GlitchyEngine.Math;
using GlitchyEngine;
using GlitchyEngine.Renderer;
namespace Sandbox.VoxelFun
{
@@ -111,49 +112,169 @@ namespace Sandbox.VoxelFun
}
*/
public Point3 GetChunkCoordinate(Point3 blockCoordinate)
/**
* Returs the coordinate of the chunk that contains the given coordinate.
*/
public Vector3 GetChunkCoordinate(Vector3 blockCoordinate)
{
Point3 p = blockCoordinate;
if(p.X < 0)
p.X -= VoxelChunk.Size.X;
if(p.Y < 0)
p.Y -= VoxelChunk.Size.Y;
if(p.Z < 0)
p.Z -= VoxelChunk.Size.Z;
Vector3 chunkPosition = blockCoordinate / VoxelChunk.VectorSize;
return .(p.X / VoxelChunk.Size.X, 0, p.Z / VoxelChunk.Size.Z);//p.Y / VoxelChunk.Size.Y
return Vector3.Floor(chunkPosition);
}
public Point3 RaycastBlock(Ray ray, float maxDistance)
/**
* Calculates the coordinate of the chunk that contains the given block coordinate.
*/
public Point3 ChunkCoordFromBlockCoord(Point3 blockCoordinate)
{
Vector3 rayWalker = ray.Start;
Vector3 direction = ray.Direction.Normalized();
Point3 coordinate = blockCoordinate;
for(float walkedLength = 0; walkedLength < maxDistance; rayWalker += direction, walkedLength++)
if(coordinate.X < 0)
coordinate.X -= VoxelChunk.Size.X;
if(coordinate.Y < 0)
coordinate.Y -= VoxelChunk.Size.Y;
if(coordinate.Z < 0)
coordinate.Z -= VoxelChunk.Size.Z;
return coordinate / VoxelChunk.Size;
}
/**
* Calculates the block coordinate relative to its chunk.
* For Example: Consider the block at the global location of (25, 17, -12)
* The coordinate of this block inside its chunk would be (9, 17, 4) because the chunk starts at (16, 0, -16)
*/
public Point3 BlockCoordInChunk(Point3 blockCoordinate)
{
Point3 blockCoordinate = (.)rayWalker;
Point3 chunkPosition = blockCoordinate % VoxelChunk.Size;
Point3 chunkCoordinate = GetChunkCoordinate(blockCoordinate);
if(chunkPosition.X < 0)
chunkPosition.X += VoxelChunk.Size.X;
if(chunkPosition.Y < 0)
chunkPosition.Y += VoxelChunk.Size.Y;
if(chunkPosition.Z < 0)
chunkPosition.Z += VoxelChunk.Size.Z;
return chunkPosition;
}
/**
* Returs the coordinate of the block relative to the chunk.
*/
public Vector3 GetPositionInChunk(Vector3 position)
{
Vector3 chunkPosition = position % VoxelChunk.VectorSize;
if(chunkPosition.X < 0)
chunkPosition.X += VoxelChunk.VectorSize.X;
if(chunkPosition.Y < 0)
chunkPosition.Y += VoxelChunk.VectorSize.Y;
if(chunkPosition.Z < 0)
chunkPosition.Z += VoxelChunk.VectorSize.Z;
return Vector3.Floor(chunkPosition);
}
public Point3 RaycastBlock(Ray ray, float maxDistance, GeometryBinding geo, Effect effect)
{
Vector3 start = ray.Start;
Vector3 dir = ray.Direction.Normalized();
Vector3 unitStepSize = .((dir / dir.X).Magnitude(), (dir / dir.Y).Magnitude(), (dir / dir.Z).Magnitude());
Point3 walker = (Point3)Vector3.Floor(start);
Vector3 rayLengths = .();
Point3 step;
if(dir.X < 0)
{
step.X = -1;
rayLengths.X = (start.X - (float)(walker.X)) * unitStepSize.X;
}
else
{
step.X = 1;
rayLengths.X = ((float)(walker.X + 1) - start.X) * unitStepSize.X;
}
if(dir.Y < 0)
{
step.Y = -1;
rayLengths.Y = (start.Y - (float)(walker.Y)) * unitStepSize.Y;
}
else
{
step.Y = 1;
rayLengths.Y = ((float)(walker.Y + 1) - start.Y) * unitStepSize.Y;
}
if(dir.Z < 0)
{
step.Z = -1;
rayLengths.Z = (start.Z - (float)(walker.Z)) * unitStepSize.Z;
}
else
{
step.Z = 1;
rayLengths.Z = ((float)(walker.Z + 1) - start.Z) * unitStepSize.Z;
}
float distance = 0.0f;
while(distance < maxDistance)
{
// Walk
if(rayLengths.X < rayLengths.Y)
{
if(rayLengths.X < rayLengths.Z)
{
walker.X += step.X;
distance = rayLengths.X;
rayLengths.X += unitStepSize.X;
}
else
{
walker.Z += step.Z;
distance = rayLengths.Z;
rayLengths.Z += unitStepSize.Z;
}
}
else
{
if(rayLengths.Y < rayLengths.Z)
{
walker.Y += step.Y;
distance = rayLengths.Y;
rayLengths.Y += unitStepSize.Y;
}
else
{
walker.Z += step.Z;
distance = rayLengths.Z;
rayLengths.Z += unitStepSize.Z;
}
}
Point3 chunkCoordinate = ChunkCoordFromBlockCoord(walker);
Chunk chunk = _chunkManager.LoadChunk(chunkCoordinate);
Point3 coordInChunk = blockCoordinate - chunkCoordinate * VoxelChunk.Size;
Point3 coordInChunk = BlockCoordInChunk(walker);
int blockData = chunk.Data.Data[coordInChunk.X][coordInChunk.Y][coordInChunk.Z];
uint8 blockData = chunk.Data.Data[coordInChunk.X][coordInChunk.Y][coordInChunk.Z];
if(blockData != 0)
return blockCoordinate;
{
return walker;
}
//Point3 chunkCoordinate = GetChunkCoordinate(rayWalker);
//Chunk chunk = _chunkManager.LoadChunk(chunkCoordinate);
//Point3 blockIndex = (Point3)rayWalker - chunkCoordinate * VoxelChunk.Size;
//int blockData = chunk.Data.Data[blockIndex.X][blockIndex.Y][blockIndex.Z];
//if(blockData != 0)
// return blockIndex;
}
return .(int.MaxValue, int.MaxValue, int.MaxValue);