mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65e2248b30 | ||
|
|
a89657591f | ||
|
|
cd929080f4 | ||
|
|
79e160348b | ||
|
|
5a6abc9b8b | ||
|
|
0f62637662 | ||
|
|
1d7029eb0f | ||
|
|
ceb4cef034 | ||
|
|
5873f12643 | ||
|
|
1b00fa67a5 | ||
|
|
dfeed4a5c9 | ||
|
|
ec83dfa21d | ||
|
|
d8f6be1358 | ||
|
|
72400e873d | ||
|
|
a0373366f6 | ||
|
|
8320689d3e | ||
|
|
4e87f5f144 | ||
|
|
3449463282 | ||
|
|
623ada4b2f | ||
|
|
4e377bb117 | ||
|
|
08613b9e82 | ||
|
|
4b032d2c7e | ||
|
|
9d52f77b43 | ||
|
|
7a777bb55e | ||
|
|
f87c0e2ecd | ||
|
|
488d2a55c4 | ||
|
|
cbfbd449ff | ||
|
|
4dcf1b5bec | ||
|
|
afc7594df9 | ||
|
|
0461669e13 |
@@ -5,3 +5,4 @@ build/
|
|||||||
recovery/
|
recovery/
|
||||||
vendor/directx
|
vendor/directx
|
||||||
Sandbox/imgui.ini
|
Sandbox/imgui.ini
|
||||||
|
Sandbox/worlds
|
||||||
|
|||||||
@@ -7,3 +7,6 @@
|
|||||||
[submodule ".\\GlitchyEngine\\vendor\\DirectXTK"]
|
[submodule ".\\GlitchyEngine\\vendor\\DirectXTK"]
|
||||||
path = .\\GlitchyEngine\\vendor\\DirectXTK
|
path = .\\GlitchyEngine\\vendor\\DirectXTK
|
||||||
url = https://github.com/aharabada/DirectXTK-beef.git
|
url = https://github.com/aharabada/DirectXTK-beef.git
|
||||||
|
[submodule "vendor/lodepng-beef"]
|
||||||
|
path = vendor/lodepng-beef
|
||||||
|
url = https://github.com/aharabada/lodepng-beef.git
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
FileVersion = 1
|
FileVersion = 1
|
||||||
Projects = {Sandbox = {Path = "Sandbox"}, GlitchyEngine = {Path = "GlitchyEngine"}, GlitchLog = {Path = "GlitchLog"}, DirectX = {Path = "GlitchyEngine/vendor/directx/DirectX"}, ImGui = {Path = "GlitchyEngine/vendor/imgui-beef/ImGui"}, ImGuiImplWin32 = {Path = "GlitchyEngine/vendor/imgui-beef/ImGuiImplWin32"}, ImGuiImplDX11 = {Path = "GlitchyEngine/vendor/imgui-beef/ImGuiImplDX11"}, DirectXTK = {Path = "GlitchyEngine/vendor/DirectXTK/DirectXTK-beef"}}
|
Projects = {Sandbox = {Path = "Sandbox"}, GlitchyEngine = {Path = "GlitchyEngine"}, GlitchLog = {Path = "GlitchLog"}, DirectX = {Path = "GlitchyEngine/vendor/directx/DirectX"}, ImGui = {Path = "GlitchyEngine/vendor/imgui-beef/ImGui"}, ImGuiImplWin32 = {Path = "GlitchyEngine/vendor/imgui-beef/ImGuiImplWin32"}, ImGuiImplDX11 = {Path = "GlitchyEngine/vendor/imgui-beef/ImGuiImplDX11"}, DirectXTK = {Path = "GlitchyEngine/vendor/DirectXTK/DirectXTK-beef"}, LodePng = {Path = "vendor/lodepng-beef/lodepng-beef"}}
|
||||||
|
|
||||||
[Workspace]
|
[Workspace]
|
||||||
StartupProject = "Sandbox"
|
StartupProject = "Sandbox"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ PreprocessorMacros = ["DEBUG", "GE_WINDOWS"]
|
|||||||
|
|
||||||
[Configs.Debug.Win64]
|
[Configs.Debug.Win64]
|
||||||
CLibType = "DynamicDebug"
|
CLibType = "DynamicDebug"
|
||||||
PreprocessorMacros = ["DEBUG", "GE_WINDOWS"]
|
PreprocessorMacros = ["DEBUG", "GE_WINDOWS", "GE_D3D11"]
|
||||||
|
|
||||||
[Configs.Release.Win32]
|
[Configs.Release.Win32]
|
||||||
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
|
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ namespace GlitchyEngine
|
|||||||
{
|
{
|
||||||
public static class Input
|
public static class Input
|
||||||
{
|
{
|
||||||
|
public static extern bool RawInput {get; set;}
|
||||||
|
|
||||||
public static extern bool IsKeyPressed(Key keycode);
|
public static extern bool IsKeyPressed(Key keycode);
|
||||||
public static extern bool IsKeyReleased(Key keycode);
|
public static extern bool IsKeyReleased(Key keycode);
|
||||||
public static extern bool IsKeyToggled(Key keycode);
|
public static extern bool IsKeyToggled(Key keycode);
|
||||||
@@ -41,6 +43,12 @@ namespace GlitchyEngine
|
|||||||
public static extern bool IsMouseButtonReleasiong(MouseButton button);
|
public static extern bool IsMouseButtonReleasiong(MouseButton button);
|
||||||
public static extern Point GetMouseMovement();
|
public static extern Point GetMouseMovement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the raw mouse (that is the mouse movement taken direcly from the device instead of the movement processed by the OS).
|
||||||
|
* Requires RawInput to be true. If RawInput is false (or could not be initialized) this method will return the same value as GetMouseMovement();
|
||||||
|
*/
|
||||||
|
public static extern Int32_2 GetRawMouseMovement();
|
||||||
|
|
||||||
public static extern void NewFrame();
|
public static extern void NewFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Math
|
||||||
|
{
|
||||||
|
public class BitArray
|
||||||
|
{
|
||||||
|
const int BitsPerInt = sizeof(uint) * 8;
|
||||||
|
|
||||||
|
private uint* _bits ~ Free(_);
|
||||||
|
private int _intCount;
|
||||||
|
private int _capacity;
|
||||||
|
|
||||||
|
public int Capacity
|
||||||
|
{
|
||||||
|
get => _capacity;
|
||||||
|
set => EnsureCapacity(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int initialCapacity = sizeof(uint))
|
||||||
|
{
|
||||||
|
EnsureCapacity(initialCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
static int IntCount(int bits)
|
||||||
|
{
|
||||||
|
int count = bits / BitsPerInt;
|
||||||
|
|
||||||
|
if(bits % BitsPerInt > 0)
|
||||||
|
count++;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
[LinkName("free")]
|
||||||
|
static extern void Free(void* memoryBlock);
|
||||||
|
|
||||||
|
[LinkName("realloc")]
|
||||||
|
static extern void* Realloc(void* memoryBlock, int size);
|
||||||
|
|
||||||
|
private void EnsureCapacity(int requestedCapacity)
|
||||||
|
{
|
||||||
|
if(requestedCapacity <= _capacity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int newIntCount = IntCount(requestedCapacity);
|
||||||
|
|
||||||
|
if(_intCount >= newIntCount)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int oldIntCount = _intCount;
|
||||||
|
_intCount = newIntCount;
|
||||||
|
_capacity = _intCount * BitsPerInt;
|
||||||
|
|
||||||
|
_bits = (.)Realloc(_bits, _intCount * sizeof(uint));
|
||||||
|
|
||||||
|
// Set new bits to 0
|
||||||
|
for(int i = oldIntCount; i < _intCount; i++)
|
||||||
|
{
|
||||||
|
_bits[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool this[int index]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Log.EngineLogger.AssertDebug(index >= 0);
|
||||||
|
|
||||||
|
if(index > _capacity)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int arrayIndex = index / _capacity;
|
||||||
|
int bitIndex = index % _capacity;
|
||||||
|
|
||||||
|
return ((_bits[arrayIndex] >> bitIndex) & 1) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Log.EngineLogger.AssertDebug(index >= 0);
|
||||||
|
|
||||||
|
if(index > _capacity)
|
||||||
|
EnsureCapacity(index);
|
||||||
|
|
||||||
|
int arrayIndex = index / _capacity;
|
||||||
|
int bitIndex = index % _capacity;
|
||||||
|
|
||||||
|
if(value)
|
||||||
|
{
|
||||||
|
_bits[arrayIndex] |= (1 << bitIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// create a mask that is all 1 except for the bit at the specified index
|
||||||
|
uint mask = uint.MaxValue;
|
||||||
|
mask ^= (1 << bitIndex);
|
||||||
|
|
||||||
|
_bits[arrayIndex] &= mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all bits to given value.
|
||||||
|
*/
|
||||||
|
public void Clear(bool value = false)
|
||||||
|
{
|
||||||
|
if(value)
|
||||||
|
{
|
||||||
|
for(int i < _intCount)
|
||||||
|
{
|
||||||
|
_bits[i] = (uint)-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(int i < _intCount)
|
||||||
|
{
|
||||||
|
_bits[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not this bitarray is 1 for every 1 in mask.
|
||||||
|
* i.e. mask == (this & mask)
|
||||||
|
*/
|
||||||
|
public bool MaskMatch(BitArray mask)
|
||||||
|
{
|
||||||
|
// The number of ints we can compare binary
|
||||||
|
int intCompares = Math.Min(mask._intCount, _intCount);
|
||||||
|
for(int i < intCompares)
|
||||||
|
{
|
||||||
|
if(mask._bits[i] != (_bits[i] & mask._bits[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if mask has more integers than "this", these integers must be 0
|
||||||
|
for(int i = _intCount; i < mask._intCount; i++)
|
||||||
|
{
|
||||||
|
if(mask._bits[i] > 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using GlitchyEngine.Math;
|
||||||
|
|
||||||
|
namespace System
|
||||||
|
{
|
||||||
|
extension Float
|
||||||
|
{
|
||||||
|
public float X
|
||||||
|
{
|
||||||
|
[Inline]
|
||||||
|
get => (float)this;
|
||||||
|
[Inline]
|
||||||
|
set mut => this = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public Vector2 XX => Vector2((float)this);
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public Vector3 XXX => Vector3((float)this);
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public Vector4 XXXX => Vector4((float)this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using GlitchyEngine.Math;
|
||||||
|
|
||||||
|
namespace System
|
||||||
|
{
|
||||||
|
extension Int32
|
||||||
|
{
|
||||||
|
public int32 X
|
||||||
|
{
|
||||||
|
[Inline]
|
||||||
|
get => (int32)this;
|
||||||
|
[Inline]
|
||||||
|
set mut => this = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public Int32_2 XX => Int32_2((int32)this);
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public Int32_3 XXX => Int32_3((int32)this);
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public Int32_4 XXXX => Int32_4((int32)this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace GlitchyEngine.Math
|
||||||
|
{
|
||||||
|
struct Ray
|
||||||
|
{
|
||||||
|
public Vector3 Start;
|
||||||
|
public Vector3 Direction;
|
||||||
|
|
||||||
|
public this() => this = default;
|
||||||
|
|
||||||
|
public this(Vector3 start, Vector3 direction)
|
||||||
|
{
|
||||||
|
Start = start;
|
||||||
|
Direction = direction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,9 +12,12 @@ namespace GlitchyEngine.Math
|
|||||||
|
|
||||||
public int VectorSize => _vectorSize;
|
public int VectorSize => _vectorSize;
|
||||||
|
|
||||||
this(int vectorSize)
|
private String _vectorTypeName;
|
||||||
|
|
||||||
|
this(int vectorSize, String vectorTypeName)
|
||||||
{
|
{
|
||||||
_vectorSize = vectorSize;
|
_vectorSize = vectorSize;
|
||||||
|
_vectorTypeName = vectorTypeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,8 +33,7 @@ namespace GlitchyEngine.Math
|
|||||||
[Comptime]
|
[Comptime]
|
||||||
public void ApplyToType(Type type)
|
public void ApplyToType(Type type)
|
||||||
{
|
{
|
||||||
// TODO: report bug... sized array not working
|
String[4] componentNames = .("X", "Y", "Z", "W");
|
||||||
String[] componentNames = scope String[]("X", "Y", "Z", "W");
|
|
||||||
|
|
||||||
for(int swizzleCount = 2; swizzleCount <= 4; swizzleCount++)
|
for(int swizzleCount = 2; swizzleCount <= 4; swizzleCount++)
|
||||||
{
|
{
|
||||||
@@ -47,7 +49,7 @@ namespace GlitchyEngine.Math
|
|||||||
{
|
{
|
||||||
String swizzleName = scope String(swizzleCount);
|
String swizzleName = scope String(swizzleCount);
|
||||||
String swizzleConstructor = scope String(swizzleCount * 3);
|
String swizzleConstructor = scope String(swizzleCount * 3);
|
||||||
String setter = scope String(128);
|
String setter = scope String();
|
||||||
|
|
||||||
bool setterInvalid = invalidSetter(cmp, swizzleCount);
|
bool setterInvalid = invalidSetter(cmp, swizzleCount);
|
||||||
|
|
||||||
@@ -61,7 +63,7 @@ namespace GlitchyEngine.Math
|
|||||||
}
|
}
|
||||||
swizzleConstructor.Append(componentNames[cmp[c]]);
|
swizzleConstructor.Append(componentNames[cmp[c]]);
|
||||||
|
|
||||||
if(!setterInvalid && c < _vectorSize) //
|
if(!setterInvalid && c < _vectorSize)
|
||||||
{
|
{
|
||||||
setter.AppendF($"\n\t\t{componentNames[cmp[c]]} = value.{componentNames[c]};");
|
setter.AppendF($"\n\t\t{componentNames[cmp[c]]} = value.{componentNames[c]};");
|
||||||
}
|
}
|
||||||
@@ -70,7 +72,7 @@ namespace GlitchyEngine.Math
|
|||||||
//{(setterInvalid ? "[Error(\"Cannot assign multiple values to same component.\")]" : String.Empty)}
|
//{(setterInvalid ? "[Error(\"Cannot assign multiple values to same component.\")]" : String.Empty)}
|
||||||
|
|
||||||
String swizzleString = scope $"""
|
String swizzleString = scope $"""
|
||||||
public Vector{swizzleCount} {swizzleName}
|
public {_vectorTypeName}{swizzleCount} {swizzleName}
|
||||||
{{
|
{{
|
||||||
get => .({swizzleConstructor});
|
get => .({swizzleConstructor});
|
||||||
set mut
|
set mut
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using System;
|
|||||||
|
|
||||||
namespace GlitchyEngine.Math
|
namespace GlitchyEngine.Math
|
||||||
{
|
{
|
||||||
[SwizzleVector(2)]
|
//[SwizzleVector(2, "Vector")]
|
||||||
public struct Vector2
|
public struct Vector2
|
||||||
{
|
{
|
||||||
public const Vector2 Zero = .(0f, 0f);
|
public const Vector2 Zero = .(0f, 0f);
|
||||||
@@ -223,5 +223,8 @@ namespace GlitchyEngine.Math
|
|||||||
|
|
||||||
[Inline]
|
[Inline]
|
||||||
public static implicit operator Self(in DirectX.Math.Vector2 value) => *(Self*)&value;
|
public static implicit operator Self(in DirectX.Math.Vector2 value) => *(Self*)&value;
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public static explicit operator Self(float value) => Self(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,21 @@ using System;
|
|||||||
|
|
||||||
namespace GlitchyEngine.Math
|
namespace GlitchyEngine.Math
|
||||||
{
|
{
|
||||||
[SwizzleVector(3)]
|
//[SwizzleVector(3, "Vector")]
|
||||||
public struct Vector3
|
public struct Vector3
|
||||||
{
|
{
|
||||||
public const Vector3 Zero = .(0f, 0f, 0f);
|
public const Vector3 Zero = .(0f, 0f, 0f);
|
||||||
public const Vector3 UnitX = .(1f, 0f, 0f);
|
public const Vector3 UnitX = .(1f, 0f, 0f);
|
||||||
public const Vector3 UnitY = .(0f, 1f, 0f);
|
public const Vector3 UnitY = .(0f, 1f, 0f);
|
||||||
public const Vector3 UnitZ = .(0f, 0f, 1f);
|
public const Vector3 UnitZ = .(0f, 0f, 1f);
|
||||||
public const Vector3 One = .(1f, 1f, 1f);
|
public const Vector3 One = .(1f, 1f, 1f);
|
||||||
|
|
||||||
public const Vector3 Forward = .(0f, 0f, 1f);
|
public const Vector3 Forward = .(0f, 0f, 1f);
|
||||||
public const Vector3 Backward = .(0f, 0f, -1f);
|
public const Vector3 Backward = .(0f, 0f, -1f);
|
||||||
public const Vector3 Left = .(-1f, 0f, 0f);
|
public const Vector3 Left = .(-1f, 0f, 0f);
|
||||||
public const Vector3 Right = .(1f, 0f, 0f);
|
public const Vector3 Right = .(1f, 0f, 0f);
|
||||||
public const Vector3 Up = .(0f, 1f, 0f);
|
public const Vector3 Up = .(0f, 1f, 0f);
|
||||||
public const Vector3 Down = .(0f, -1f, 0f);
|
public const Vector3 Down = .(0f, -1f, 0f);
|
||||||
|
|
||||||
public float X, Y, Z;
|
public float X, Y, Z;
|
||||||
|
|
||||||
@@ -77,6 +77,9 @@ namespace GlitchyEngine.Math
|
|||||||
return X * X + Y * Y + Z * Z;
|
return X * X + Y * Y + Z * Z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes this vector.
|
||||||
|
*/
|
||||||
[Checked]
|
[Checked]
|
||||||
public void Normalize() mut
|
public void Normalize() mut
|
||||||
{
|
{
|
||||||
@@ -86,11 +89,34 @@ namespace GlitchyEngine.Math
|
|||||||
this /= Magnitude();
|
this /= Magnitude();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes this vector.
|
||||||
|
*/
|
||||||
public void Normalize() mut
|
public void Normalize() mut
|
||||||
{
|
{
|
||||||
this /= Magnitude();
|
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)
|
public static Vector3 Normalize(Vector3 v)
|
||||||
{
|
{
|
||||||
return v / v.Magnitude();
|
return v / v.Magnitude();
|
||||||
@@ -125,6 +151,11 @@ namespace GlitchyEngine.Math
|
|||||||
return (a - b * (Dot(a, b) / Dot(b, b)));
|
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
|
// 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);
|
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
|
// Equality
|
||||||
|
|
||||||
public static bool operator ==(Vector3 left, Vector3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
|
public static bool operator ==(Vector3 left, Vector3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
|
||||||
@@ -254,5 +293,8 @@ namespace GlitchyEngine.Math
|
|||||||
|
|
||||||
[Inline]
|
[Inline]
|
||||||
public static implicit operator Self(in DirectX.Math.Vector3 value) => *(Self*)&value;
|
public static implicit operator Self(in DirectX.Math.Vector3 value) => *(Self*)&value;
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public static explicit operator Self(float value) => Self(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using System;
|
|||||||
|
|
||||||
namespace GlitchyEngine.Math
|
namespace GlitchyEngine.Math
|
||||||
{
|
{
|
||||||
[SwizzleVector(4)]
|
//[SwizzleVector(4, "Vector")]
|
||||||
public struct Vector4
|
public struct Vector4
|
||||||
{
|
{
|
||||||
public const Vector4 Zero = .(0f, 0f, 0f, 0f);
|
public const Vector4 Zero = .(0f, 0f, 0f, 0f);
|
||||||
@@ -274,5 +274,8 @@ namespace GlitchyEngine.Math
|
|||||||
|
|
||||||
[Inline]
|
[Inline]
|
||||||
public static implicit operator Self(in DirectX.Math.Vector4 value) => *(Self*)&value;
|
public static implicit operator Self(in DirectX.Math.Vector4 value) => *(Self*)&value;
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public static explicit operator Self(float value) => Self(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
#pragma warning disable 4204
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Math
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A Vector with two components of type int32.
|
||||||
|
*/
|
||||||
|
[SwizzleVector(2, "Int32_")]
|
||||||
|
public struct Int32_2 : IHashable
|
||||||
|
{
|
||||||
|
public const Int32_2 Zero = .(0, 0);
|
||||||
|
public const Int32_2 UnitX = .(1, 0);
|
||||||
|
public const Int32_2 UnitY = .(0, 1);
|
||||||
|
public const Int32_2 One = .(1, 1);
|
||||||
|
|
||||||
|
public int32 X, Y;
|
||||||
|
|
||||||
|
public this() => this = default;
|
||||||
|
|
||||||
|
public this(int32 value)
|
||||||
|
{
|
||||||
|
X = value;
|
||||||
|
Y = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int value)
|
||||||
|
{
|
||||||
|
X = (.)value;
|
||||||
|
Y = (.)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int32 x, int32 y)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int x, int y)
|
||||||
|
{
|
||||||
|
X = (.)x;
|
||||||
|
Y = (.)y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref int32 this[int index]
|
||||||
|
{
|
||||||
|
[Inline]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if(index < 0 || index >= 2)
|
||||||
|
Internal.ThrowIndexOutOfRange(1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ref (&X)[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int32 MagnitudeSquared() => X * X + Y * Y;
|
||||||
|
|
||||||
|
public float Magnitude() => Math.Sqrt(X * X + Y * Y);
|
||||||
|
|
||||||
|
public Int32_2 Abs() => .(Math.Abs(X), Math.Abs(Y));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Assignment operators
|
||||||
|
//
|
||||||
|
|
||||||
|
public void operator +=(Int32_2 value) mut
|
||||||
|
{
|
||||||
|
X += value.X;
|
||||||
|
Y += value.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator +=(int32 value) mut
|
||||||
|
{
|
||||||
|
X += value;
|
||||||
|
Y += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator -=(Int32_2 value) mut
|
||||||
|
{
|
||||||
|
X -= value.X;
|
||||||
|
Y -= value.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator -=(int32 value) mut
|
||||||
|
{
|
||||||
|
X -= value;
|
||||||
|
Y -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator *=(Int32_2 value) mut
|
||||||
|
{
|
||||||
|
X *= value.X;
|
||||||
|
Y *= value.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator *=(int32 value) mut
|
||||||
|
{
|
||||||
|
X *= value;
|
||||||
|
Y *= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator /=(Int32_2 value) mut
|
||||||
|
{
|
||||||
|
X /= value.X;
|
||||||
|
Y /= value.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator /=(int32 value) mut
|
||||||
|
{
|
||||||
|
X /= value;
|
||||||
|
Y /= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
|
||||||
|
public static Int32_2 operator +(Int32_2 value) => value;
|
||||||
|
public static Int32_2 operator +(Int32_2 left, Int32_2 right) => .(left.X + right.X, left.Y + right.Y);
|
||||||
|
public static Int32_2 operator +(Int32_2 left, int32 right) => .(left.X + right, left.Y + right);
|
||||||
|
public static Int32_2 operator +(int32 left, Int32_2 right) => .(left + right.X, left + right.Y);
|
||||||
|
|
||||||
|
public static Int32_2 operator -(Int32_2 value) => .(-value.X, -value.Y);
|
||||||
|
public static Int32_2 operator -(Int32_2 left, Int32_2 right) => .(left.X - right.X, left.Y - right.Y);
|
||||||
|
public static Int32_2 operator -(Int32_2 left, int32 right) => .(left.X - right, left.Y - right);
|
||||||
|
public static Int32_2 operator -(int32 left, Int32_2 right) => .(left - right.X, left - right.Y);
|
||||||
|
|
||||||
|
public static Int32_2 operator *(Int32_2 left, Int32_2 right) => .(left.X * right.X, left.Y * right.Y);
|
||||||
|
public static Int32_2 operator *(Int32_2 left, int32 right) => .(left.X * right, left.Y * right);
|
||||||
|
public static Int32_2 operator *(int32 left, Int32_2 right) => .(left * right.X, left * right.Y);
|
||||||
|
|
||||||
|
public static Int32_2 operator /(Int32_2 left, Int32_2 right) => .(left.X / right.X, left.Y / right.Y);
|
||||||
|
public static Int32_2 operator /(Int32_2 left, int32 right) => .(left.X / right, left.Y / right);
|
||||||
|
public static Int32_2 operator /(int32 left, Int32_2 right) => .(left / right.X, left / right.Y);
|
||||||
|
|
||||||
|
public static Int32_2 operator %(Int32_2 left, Int32_2 right) => .(left.X % right.X, left.Y % right.Y);
|
||||||
|
public static Int32_2 operator %(Int32_2 left, int32 right) => .(left.X % right, left.Y % right);
|
||||||
|
public static Int32_2 operator %(int32 left, Int32_2 right) => .(left % right.X, left % right.Y);
|
||||||
|
|
||||||
|
public static bool operator ==(Int32_2 left, Int32_2 right) => left.X == right.X && left.Y == right.Y;
|
||||||
|
public static bool operator ==(Int32_2 left, int32 right) => left.X == right && left.Y == right;
|
||||||
|
public static bool operator ==(int32 left, Int32_2 right) => left == right.X && left == right.Y;
|
||||||
|
|
||||||
|
public static bool operator !=(Int32_2 left, Int32_2 right) => left.X != right.X || left.Y != right.Y;
|
||||||
|
public static bool operator !=(Int32_2 left, int32 right) => left.X != right || left.Y != right;
|
||||||
|
public static bool operator !=(int32 left, Int32_2 right) => left != right.X || left != right.Y;
|
||||||
|
|
||||||
|
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y}");
|
||||||
|
|
||||||
|
public static explicit operator Vector2(Int32_2 point) => .(point.X, point.Y);
|
||||||
|
|
||||||
|
public static explicit operator Int32_2(Vector2 point) => .((int32)point.X, (int32)point.Y);
|
||||||
|
|
||||||
|
public int GetHashCode()
|
||||||
|
{
|
||||||
|
return (X * 39) ^ Y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
#pragma warning disable 4204
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Math
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A Vector with three components of type int32.
|
||||||
|
*/
|
||||||
|
[SwizzleVector(3, "Int32_")]
|
||||||
|
public struct Int32_3 : IHashable
|
||||||
|
{
|
||||||
|
public const Int32_3 Zero = .(0, 0, 0);
|
||||||
|
public const Int32_3 UnitX = .(1, 0, 0);
|
||||||
|
public const Int32_3 UnitY = .(0, 1, 0);
|
||||||
|
public const Int32_3 UnitZ = .(0, 0, 1);
|
||||||
|
public const Int32_3 One = .(1, 1, 1);
|
||||||
|
|
||||||
|
public int32 X, Y, Z;
|
||||||
|
|
||||||
|
public this() => this = default;
|
||||||
|
|
||||||
|
public this(int32 value)
|
||||||
|
{
|
||||||
|
X = value;
|
||||||
|
Y = value;
|
||||||
|
Z = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int value)
|
||||||
|
{
|
||||||
|
X = (.)value;
|
||||||
|
Y = (.)value;
|
||||||
|
Z = (.)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int32 x, int32 y, int32 z)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(Int32_2 xy, int32 z)
|
||||||
|
{
|
||||||
|
X = xy.X;
|
||||||
|
Y = xy.Y;
|
||||||
|
Z = z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int32 x, Int32_2 yz)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = yz.X;
|
||||||
|
Z = yz.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int x, int y, int z)
|
||||||
|
{
|
||||||
|
X = (.)x;
|
||||||
|
Y = (.)y;
|
||||||
|
Z = (.)z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(Int32_2 xy, int z)
|
||||||
|
{
|
||||||
|
X = xy.X;
|
||||||
|
Y = xy.Y;
|
||||||
|
Z = (.)z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int x, Int32_2 yz)
|
||||||
|
{
|
||||||
|
X = (.)x;
|
||||||
|
Y = yz.X;
|
||||||
|
Z = yz.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref int32 this[int index]
|
||||||
|
{
|
||||||
|
[Inline]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if(index < 0 || index >= 3)
|
||||||
|
Internal.ThrowIndexOutOfRange(1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ref (&X)[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int32 MagnitudeSquared() => X * X + Y * Y + Z * Z;
|
||||||
|
|
||||||
|
public float Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z);
|
||||||
|
|
||||||
|
public Int32_3 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Assignment operators
|
||||||
|
//
|
||||||
|
|
||||||
|
public void operator +=(Int32_3 value) mut
|
||||||
|
{
|
||||||
|
X += value.X;
|
||||||
|
Y += value.Y;
|
||||||
|
Z += value.Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator +=(int32 value) mut
|
||||||
|
{
|
||||||
|
X += value;
|
||||||
|
Y += value;
|
||||||
|
Z += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator -=(Int32_3 value) mut
|
||||||
|
{
|
||||||
|
X -= value.X;
|
||||||
|
Y -= value.Y;
|
||||||
|
Z -= value.Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator -=(int32 value) mut
|
||||||
|
{
|
||||||
|
X -= value;
|
||||||
|
Y -= value;
|
||||||
|
Z -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator *=(Int32_3 value) mut
|
||||||
|
{
|
||||||
|
X *= value.X;
|
||||||
|
Y *= value.Y;
|
||||||
|
Z *= value.Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator *=(int32 value) mut
|
||||||
|
{
|
||||||
|
X *= value;
|
||||||
|
Y *= value;
|
||||||
|
Z *= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator /=(Int32_3 value) mut
|
||||||
|
{
|
||||||
|
X /= value.X;
|
||||||
|
Y /= value.Y;
|
||||||
|
Z /= value.Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator /=(int32 value) mut
|
||||||
|
{
|
||||||
|
X /= value;
|
||||||
|
Y /= value;
|
||||||
|
Z /= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
|
||||||
|
public static Int32_3 operator +(Int32_3 value) => value;
|
||||||
|
public static Int32_3 operator +(Int32_3 left, Int32_3 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
|
||||||
|
public static Int32_3 operator +(Int32_3 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right);
|
||||||
|
public static Int32_3 operator +(int32 left, Int32_3 right) => .(left + right.X, left + right.Y, left + right.Z);
|
||||||
|
|
||||||
|
public static Int32_3 operator -(Int32_3 value) => .(-value.X, -value.Y, -value.Z);
|
||||||
|
public static Int32_3 operator -(Int32_3 left, Int32_3 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
|
||||||
|
public static Int32_3 operator -(Int32_3 left, int32 right) => .(left.X - right, left.Y - right, left.Z- right);
|
||||||
|
public static Int32_3 operator -(int32 left, Int32_3 right) => .(left - right.X, left - right.Y, left - right.Z);
|
||||||
|
|
||||||
|
public static Int32_3 operator *(Int32_3 left, Int32_3 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
|
||||||
|
public static Int32_3 operator *(Int32_3 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right);
|
||||||
|
public static Int32_3 operator *(int32 left, Int32_3 right) => .(left * right.X, left * right.Y, left * right.Z);
|
||||||
|
|
||||||
|
public static Int32_3 operator /(Int32_3 left, Int32_3 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
|
||||||
|
public static Int32_3 operator /(Int32_3 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right);
|
||||||
|
public static Int32_3 operator /(int32 left, Int32_3 right) => .(left / right.X, left / right.Y, left / right.Z);
|
||||||
|
|
||||||
|
public static Int32_3 operator %(Int32_3 left, Int32_3 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z);
|
||||||
|
public static Int32_3 operator %(Int32_3 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right);
|
||||||
|
public static Int32_3 operator %(int32 left, Int32_3 right) => .(left % right.X, left % right.Y, left % right.Z);
|
||||||
|
|
||||||
|
public static bool operator ==(Int32_3 left, Int32_3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
|
||||||
|
public static bool operator ==(Int32_3 left, int32 right) => left.X == right && left.Y == right && left.Z == right;
|
||||||
|
public static bool operator ==(int32 left, Int32_3 right) => left == right.X && left == right.Y && left == right.Z;
|
||||||
|
|
||||||
|
public static bool operator !=(Int32_3 left, Int32_3 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
|
||||||
|
public static bool operator !=(Int32_3 left, int32 right) => left.X != right || left.Y != right || left.Z != right;
|
||||||
|
public static bool operator !=(int32 left, Int32_3 right) => left != right.X || left != right.Y || left != right.Z;
|
||||||
|
|
||||||
|
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y} Z:{Z}");
|
||||||
|
|
||||||
|
public static explicit operator Vector3(Int32_3 point) => .(point.X, point.Y, point.Z);
|
||||||
|
|
||||||
|
public static explicit operator Int32_3(Vector3 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z);
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public static explicit operator Int32_2(in Int32_3 point) => *(Int32_2*)&point;
|
||||||
|
|
||||||
|
public int GetHashCode()
|
||||||
|
{
|
||||||
|
return (((X * 39) ^ Y) * 39) ^ Z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
#pragma warning disable 4204
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Math
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A Vector with four components of type int32.
|
||||||
|
*/
|
||||||
|
[SwizzleVector(4, "Int32_")]
|
||||||
|
public struct Int32_4 : IHashable
|
||||||
|
{
|
||||||
|
public const Int32_4 Zero = .(0, 0, 0, 0);
|
||||||
|
public const Int32_4 UnitX = .(1, 0, 0, 0);
|
||||||
|
public const Int32_4 UnitY = .(0, 1, 0, 0);
|
||||||
|
public const Int32_4 UnitZ = .(0, 0, 1, 0);
|
||||||
|
public const Int32_4 UnitW = .(0, 0, 0, 1);
|
||||||
|
public const Int32_4 One = .(1, 1, 1, 1);
|
||||||
|
|
||||||
|
public int32 X, Y, Z, W;
|
||||||
|
|
||||||
|
public this() => this = default;
|
||||||
|
|
||||||
|
public this(int32 value)
|
||||||
|
{
|
||||||
|
X = value;
|
||||||
|
Y = value;
|
||||||
|
Z = value;
|
||||||
|
W = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int value)
|
||||||
|
{
|
||||||
|
X = (.)value;
|
||||||
|
Y = (.)value;
|
||||||
|
Z = (.)value;
|
||||||
|
W = (.)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int32 x, int32 y, int32 z, int32 w)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Z = z;
|
||||||
|
W = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(Int32_2 xy, int32 z, int32 w)
|
||||||
|
{
|
||||||
|
X = xy.X;
|
||||||
|
Y = xy.Y;
|
||||||
|
Z = z;
|
||||||
|
W = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int32 x, Int32_2 yz, int32 w)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = yz.X;
|
||||||
|
Z = yz.Y;
|
||||||
|
W = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int32 x, int32 y, Int32_2 zw)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Z = zw.X;
|
||||||
|
W = zw.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(Int32_2 xy, Int32_2 zw)
|
||||||
|
{
|
||||||
|
X = xy.X;
|
||||||
|
Y = xy.Y;
|
||||||
|
Z = zw.X;
|
||||||
|
W = zw.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(Int32_3 xyz, int32 w)
|
||||||
|
{
|
||||||
|
X = xyz.X;
|
||||||
|
Y = xyz.Y;
|
||||||
|
Z = xyz.Z;
|
||||||
|
W = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(int32 x, Int32_3 yzw)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = yzw.X;
|
||||||
|
Z = yzw.Y;
|
||||||
|
W = yzw.Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref int32 this[int index]
|
||||||
|
{
|
||||||
|
[Inline]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if(index < 0 || index >= 3)
|
||||||
|
Internal.ThrowIndexOutOfRange(1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ref (&X)[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int32 MagnitudeSquared() => X * X + Y * Y + Z * Z + W * W;
|
||||||
|
|
||||||
|
public float Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
|
||||||
|
|
||||||
|
public Int32_4 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z), Math.Abs(W));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Assignment operators
|
||||||
|
//
|
||||||
|
|
||||||
|
public void operator +=(Int32_4 value) mut
|
||||||
|
{
|
||||||
|
X += value.X;
|
||||||
|
Y += value.Y;
|
||||||
|
Z += value.Z;
|
||||||
|
W += value.W;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator +=(int32 value) mut
|
||||||
|
{
|
||||||
|
X += value;
|
||||||
|
Y += value;
|
||||||
|
Z += value;
|
||||||
|
W += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator -=(Int32_4 value) mut
|
||||||
|
{
|
||||||
|
X -= value.X;
|
||||||
|
Y -= value.Y;
|
||||||
|
Z -= value.Z;
|
||||||
|
W -= value.W;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator -=(int32 value) mut
|
||||||
|
{
|
||||||
|
X -= value;
|
||||||
|
Y -= value;
|
||||||
|
Z -= value;
|
||||||
|
W -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator *=(Int32_4 value) mut
|
||||||
|
{
|
||||||
|
X *= value.X;
|
||||||
|
Y *= value.Y;
|
||||||
|
Z *= value.Z;
|
||||||
|
W *= value.W;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator *=(int32 value) mut
|
||||||
|
{
|
||||||
|
X *= value;
|
||||||
|
Y *= value;
|
||||||
|
Z *= value;
|
||||||
|
W *= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator /=(Int32_4 value) mut
|
||||||
|
{
|
||||||
|
X /= value.X;
|
||||||
|
Y /= value.Y;
|
||||||
|
Z /= value.Z;
|
||||||
|
W /= value.W;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void operator /=(int32 value) mut
|
||||||
|
{
|
||||||
|
X /= value;
|
||||||
|
Y /= value;
|
||||||
|
Z /= value;
|
||||||
|
W /= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
|
||||||
|
public static Int32_4 operator +(Int32_4 value) => value;
|
||||||
|
public static Int32_4 operator +(Int32_4 left, Int32_4 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
|
||||||
|
public static Int32_4 operator +(Int32_4 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right, left.W + right);
|
||||||
|
public static Int32_4 operator +(int32 left, Int32_4 right) => .(left + right.X, left + right.Y, left + right.Z, left + right.W);
|
||||||
|
|
||||||
|
public static Int32_4 operator -(Int32_4 value) => .(-value.X, -value.Y, -value.Z, -value.W);
|
||||||
|
public static Int32_4 operator -(Int32_4 left, Int32_4 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W);
|
||||||
|
public static Int32_4 operator -(Int32_4 left, int32 right) => .(left.X - right, left.Y - right, left.Z - right, left.W - right);
|
||||||
|
public static Int32_4 operator -(int32 left, Int32_4 right) => .(left - right.X, left - right.Y, left - right.Z, left - right.W);
|
||||||
|
|
||||||
|
public static Int32_4 operator *(Int32_4 left, Int32_4 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W);
|
||||||
|
public static Int32_4 operator *(Int32_4 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right, left.W * right);
|
||||||
|
public static Int32_4 operator *(int32 left, Int32_4 right) => .(left * right.X, left * right.Y, left * right.Z, left * right.W);
|
||||||
|
|
||||||
|
public static Int32_4 operator /(Int32_4 left, Int32_4 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W);
|
||||||
|
public static Int32_4 operator /(Int32_4 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right, left.W / right);
|
||||||
|
public static Int32_4 operator /(int32 left, Int32_4 right) => .(left / right.X, left / right.Y, left / right.Z, left / right.W);
|
||||||
|
|
||||||
|
public static Int32_4 operator %(Int32_4 left, Int32_4 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z, left.W % right.W);
|
||||||
|
public static Int32_4 operator %(Int32_4 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right, left.W % right);
|
||||||
|
public static Int32_4 operator %(int32 left, Int32_4 right) => .(left % right.X, left % right.Y, left % right.Z, left % right.W);
|
||||||
|
|
||||||
|
public static bool operator ==(Int32_4 left, Int32_4 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z && left.W == right.W;
|
||||||
|
public static bool operator ==(Int32_4 left, int32 right) => left.X == right && left.Y == right && left.Z == right && left.W == right;
|
||||||
|
public static bool operator ==(int32 left, Int32_4 right) => left == right.X && left == right.Y && left == right.Z && left == right.W;
|
||||||
|
|
||||||
|
public static bool operator !=(Int32_4 left, Int32_4 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z || left.W != right.W;
|
||||||
|
public static bool operator !=(Int32_4 left, int32 right) => left.X != right || left.Y != right || left.Z != right || left.W != right;
|
||||||
|
public static bool operator !=(int32 left, Int32_4 right) => left != right.X || left != right.Y || left != right.Z || left != right.W;
|
||||||
|
|
||||||
|
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y} Z:{Z} W:{W}");
|
||||||
|
|
||||||
|
public static explicit operator Vector4(Int32_4 point) => .(point.X, point.Y, point.Z, point.W);
|
||||||
|
|
||||||
|
public static explicit operator Int32_4(Vector4 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z, (int32)point.W);
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public static explicit operator Int32_2(in Int32_4 point) => *(Int32_2*)&point;
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public static explicit operator Int32_3(in Int32_4 point) => *(Int32_3*)&point;
|
||||||
|
|
||||||
|
public int GetHashCode()
|
||||||
|
{
|
||||||
|
return (((((X * 39) ^ Y) * 39) ^ Z) * 39) ^ W;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using DirectX.D3D11;
|
||||||
|
using internal GlitchyEngine.Renderer;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Renderer
|
||||||
|
{
|
||||||
|
extension DepthStencilTarget
|
||||||
|
{
|
||||||
|
internal ID3D11DepthStencilView* nativeView ~ _?.Release();
|
||||||
|
|
||||||
|
protected override void PlatformCreate()
|
||||||
|
{
|
||||||
|
Texture2DDescription desc = .();
|
||||||
|
desc.Format = .D32_Float;
|
||||||
|
desc.ArraySize = 1;
|
||||||
|
desc.BindFlags = .DepthStencil;
|
||||||
|
desc.Width = _width;
|
||||||
|
desc.Height = _height;
|
||||||
|
desc.SampleDesc = .(1, 0);
|
||||||
|
|
||||||
|
ID3D11Texture2D* tex = ?;
|
||||||
|
_context.nativeDevice.CreateTexture2D(ref desc, null, &tex);
|
||||||
|
|
||||||
|
_context.nativeDevice.CreateDepthStencilView(tex, null, &nativeView);
|
||||||
|
|
||||||
|
tex.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Bind()
|
||||||
|
{
|
||||||
|
_context.SetDepthStencilTarget(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Clear(float depthValue, uint8 stencilValue, DepthStencilClearFlag clearFlags)
|
||||||
|
{
|
||||||
|
_context.nativeContext.ClearDepthStencilView(nativeView, (.)clearFlags, depthValue, stencilValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -93,8 +93,14 @@ namespace GlitchyEngine.Renderer
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private ID3D11DepthStencilView* _depthStencilTarget;
|
||||||
private ID3D11RenderTargetView*[MaxRTVCount] _renderTargets;
|
private ID3D11RenderTargetView*[MaxRTVCount] _renderTargets;
|
||||||
|
|
||||||
|
internal void SetDepthStencilTarget(DepthStencilTarget target)
|
||||||
|
{
|
||||||
|
_depthStencilTarget = target?.nativeView;
|
||||||
|
}
|
||||||
|
|
||||||
public override void SetRenderTarget(RenderTarget renderTarget, int slot = 0)
|
public override void SetRenderTarget(RenderTarget renderTarget, int slot = 0)
|
||||||
{
|
{
|
||||||
if(renderTarget == null)
|
if(renderTarget == null)
|
||||||
@@ -109,7 +115,7 @@ namespace GlitchyEngine.Renderer
|
|||||||
|
|
||||||
public override void BindRenderTargets()
|
public override void BindRenderTargets()
|
||||||
{
|
{
|
||||||
nativeContext.OutputMerger.SetRenderTargets(MaxRTVCount, &_renderTargets, null);
|
nativeContext.OutputMerger.SetRenderTargets(MaxRTVCount, &_renderTargets, _depthStencilTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ClearRenderTarget(RenderTarget renderTarget, ColorRGBA color)
|
public override void ClearRenderTarget(RenderTarget renderTarget, ColorRGBA color)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using GlitchyEngine.Math;
|
using GlitchyEngine.Math;
|
||||||
|
|
||||||
|
using internal GlitchyEngine.Renderer;
|
||||||
|
|
||||||
namespace GlitchyEngine.Renderer
|
namespace GlitchyEngine.Renderer
|
||||||
{
|
{
|
||||||
extension RendererAPI
|
extension RendererAPI
|
||||||
@@ -24,6 +26,11 @@ namespace GlitchyEngine.Renderer
|
|||||||
_context.ClearRenderTarget(renderTarget, clearColor);
|
_context.ClearRenderTarget(renderTarget, clearColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Clear(DepthStencilTarget target, float depthValue, uint8 stencilValue, DepthStencilClearFlag clearFlags)
|
||||||
|
{
|
||||||
|
_context.nativeContext.ClearDepthStencilView(target.nativeView, (.)clearFlags, depthValue, stencilValue);
|
||||||
|
}
|
||||||
|
|
||||||
public override void DrawIndexed(GeometryBinding geometry)
|
public override void DrawIndexed(GeometryBinding geometry)
|
||||||
{
|
{
|
||||||
_context.DrawIndexed(geometry.IndexCount, geometry.IndexByteOffset, 0);
|
_context.DrawIndexed(geometry.IndexCount, geometry.IndexByteOffset, 0);
|
||||||
|
|||||||
@@ -5,13 +5,50 @@ using DirectXTK;
|
|||||||
|
|
||||||
using internal GlitchyEngine.Renderer;
|
using internal GlitchyEngine.Renderer;
|
||||||
|
|
||||||
|
typealias NativeTex2DDesc = DirectX.D3D11.Texture2DDescription;
|
||||||
|
|
||||||
namespace GlitchyEngine.Renderer
|
namespace GlitchyEngine.Renderer
|
||||||
{
|
{
|
||||||
|
extension Texture
|
||||||
|
{
|
||||||
|
protected internal ID3D11ShaderResourceView* nativeView ~ _?.Release();
|
||||||
|
|
||||||
|
protected override void ImplBind(uint32 slot)
|
||||||
|
{
|
||||||
|
_context.nativeContext.VertexShader.SetShaderResources(slot, 1, &nativeView);
|
||||||
|
_context.nativeContext.PixelShader.SetShaderResources(slot, 1, &nativeView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Texture2DDesc
|
||||||
|
{
|
||||||
|
public NativeTex2DDesc ToNative()
|
||||||
|
{
|
||||||
|
NativeTex2DDesc desc;
|
||||||
|
|
||||||
|
desc.Width = Width;
|
||||||
|
desc.Height = Height;
|
||||||
|
desc.MipLevels = MipLevels;
|
||||||
|
desc.ArraySize = ArraySize;
|
||||||
|
desc.Format = Format;
|
||||||
|
|
||||||
|
// TODO: missing options
|
||||||
|
desc.SampleDesc = .(1, 0);
|
||||||
|
desc.Usage = .Immutable;
|
||||||
|
desc.BindFlags = .ShaderResource;
|
||||||
|
desc.CpuAccessFlags = .None;
|
||||||
|
desc.MiscFlags = .None;
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator NativeTex2DDesc(Self desc) => desc.ToNative();
|
||||||
|
}
|
||||||
|
|
||||||
extension Texture2D
|
extension Texture2D
|
||||||
{
|
{
|
||||||
internal ID3D11Texture2D* nativeTexture ~ _?.Release();
|
protected internal ID3D11Texture2D* nativeTexture ~ _?.Release();
|
||||||
internal ID3D11ShaderResourceView* nativeView ~ _?.Release();
|
protected internal NativeTex2DDesc nativeDesc;
|
||||||
internal Texture2DDescription nativeDesc;
|
|
||||||
|
|
||||||
public override uint32 Width => nativeDesc.Width;
|
public override uint32 Width => nativeDesc.Width;
|
||||||
public override uint32 Height => nativeDesc.Height;
|
public override uint32 Height => nativeDesc.Height;
|
||||||
@@ -36,13 +73,65 @@ namespace GlitchyEngine.Renderer
|
|||||||
// TODO: load fallback texture
|
// TODO: load fallback texture
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let resType = nativeTexture.GetResourceType();
|
||||||
|
Log.EngineLogger.Assert(resType == .Texture2D, scope $"The texture \"{_path}\" is not a 2D texture (it is {resType}).");
|
||||||
|
|
||||||
nativeTexture.GetDescription(out nativeDesc);
|
nativeTexture.GetDescription(out nativeDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ImplBind(uint32 slot)
|
protected override void CreateTexturePlatform(Texture2DDesc desc, void* data, uint32 linePitch)
|
||||||
{
|
{
|
||||||
_context.nativeContext.VertexShader.SetShaderResources(slot, 1, &nativeView);
|
nativeTexture?.Release();
|
||||||
_context.nativeContext.PixelShader.SetShaderResources(slot, 1, &nativeView);
|
nativeView?.Release();
|
||||||
|
|
||||||
|
nativeDesc = desc;
|
||||||
|
SubresourceData resData = .(data, linePitch, 0);
|
||||||
|
|
||||||
|
var result = _context.[Friend]nativeDevice.CreateTexture2D(ref nativeDesc, &resData, &nativeTexture);
|
||||||
|
|
||||||
|
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture 2D. Error ({result.Underlying}): {result}");
|
||||||
|
|
||||||
|
result = _context.[Friend]nativeDevice.CreateShaderResourceView(nativeTexture, null, &nativeView);
|
||||||
|
|
||||||
|
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture view. Error ({result.Underlying}): {result}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TextureCube
|
||||||
|
{
|
||||||
|
protected internal ID3D11Texture2D* nativeTexture ~ _?.Release();
|
||||||
|
protected internal NativeTex2DDesc nativeDesc;
|
||||||
|
|
||||||
|
public override uint32 Width => nativeDesc.Width;
|
||||||
|
public override uint32 Height => nativeDesc.Height;
|
||||||
|
public override uint32 ArraySize => nativeDesc.ArraySize / 6;
|
||||||
|
public override uint32 MipLevels => nativeDesc.MipLevels;
|
||||||
|
|
||||||
|
protected override void LoadTexturePlatform()
|
||||||
|
{
|
||||||
|
nativeTexture?.Release();
|
||||||
|
nativeView?.Release();
|
||||||
|
|
||||||
|
HResult loadResult = DDSTextureLoader.CreateDDSTextureFromFile(_context.nativeDevice, _path.ToScopedNativeWChar!(),
|
||||||
|
(.)&nativeTexture, &nativeView);
|
||||||
|
|
||||||
|
if(loadResult.Failed)
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Error($"Failed to load texture \"{_path}\". Error({(int)loadResult}): {loadResult}");
|
||||||
|
|
||||||
|
nativeTexture?.Release();
|
||||||
|
nativeView?.Release();
|
||||||
|
|
||||||
|
// TODO: load fallback texture
|
||||||
|
}
|
||||||
|
|
||||||
|
let resType = nativeTexture.GetResourceType();
|
||||||
|
Log.EngineLogger.Assert(resType == .Texture2D, scope $"The texture \"{_path}\" is not a texture cube (it is {resType}).");
|
||||||
|
|
||||||
|
nativeTexture.GetDescription(out nativeDesc);
|
||||||
|
|
||||||
|
Log.EngineLogger.Assert(nativeDesc.MiscFlags.HasFlag(.TextureCube), scope $"The texture \"{_path}\" is not a texture cube.");
|
||||||
|
// TODO: load fallback texture
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#if BF_PLATFORM_WINDOWS
|
||||||
|
|
||||||
|
using static System.Windows;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Threading
|
||||||
|
{
|
||||||
|
extension Mutex
|
||||||
|
{
|
||||||
|
internal Handle nativeHandle;
|
||||||
|
|
||||||
|
public this(bool initialyOwned = false)
|
||||||
|
{
|
||||||
|
nativeHandle = CreateMutexW(null, initialyOwned, null);
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(nativeHandle != 0, scope $"Failed to create mutex. Error code: {GetLastError()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ~this()
|
||||||
|
{
|
||||||
|
CloseHandle(nativeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override LockResult Lock(uint32 timeout = InfiniteTimeout)
|
||||||
|
{
|
||||||
|
int i = WaitForSingleObject(nativeHandle, timeout);
|
||||||
|
|
||||||
|
switch(i)
|
||||||
|
{
|
||||||
|
case 0x00000000L:
|
||||||
|
return .Released;
|
||||||
|
case 0x00000080L:
|
||||||
|
return .Abandoned;
|
||||||
|
case 0x00000102L:
|
||||||
|
return .Timeout;
|
||||||
|
case 0xFFFFFFFF:
|
||||||
|
return .Failed;
|
||||||
|
default:
|
||||||
|
return .Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Unlock()
|
||||||
|
{
|
||||||
|
return ReleaseMutex(nativeHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#if BF_PLATFORM_WINDOWS
|
||||||
|
|
||||||
|
using static System.Windows;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.Threading
|
||||||
|
{
|
||||||
|
extension Semaphore
|
||||||
|
{
|
||||||
|
internal Handle nativeHandle;
|
||||||
|
|
||||||
|
public override this(int32 initialCount = 1, int32 maximumCount = 1)
|
||||||
|
{
|
||||||
|
nativeHandle = (.)CreateSemaphoreW(null, initialCount, maximumCount, null);
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(nativeHandle != 0, scope $"Failed to create semaphore. Error code: {GetLastError()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ~this()
|
||||||
|
{
|
||||||
|
CloseHandle(nativeHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override LockResult Lock(uint32 timeout = InfiniteTimeout)
|
||||||
|
{
|
||||||
|
int i = WaitForSingleObject(nativeHandle, timeout);
|
||||||
|
|
||||||
|
switch(i)
|
||||||
|
{
|
||||||
|
case 0x00000000L:
|
||||||
|
return .Released;
|
||||||
|
case 0x00000080L:
|
||||||
|
return .Abandoned;
|
||||||
|
case 0x00000102L:
|
||||||
|
return .Timeout;
|
||||||
|
case 0xFFFFFFFF:
|
||||||
|
return .Failed;
|
||||||
|
default:
|
||||||
|
return .Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Unlock(int32 releaseCount = 1)
|
||||||
|
{
|
||||||
|
return ReleaseSemaphore(nativeHandle, releaseCount, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Unlock(out int32 previousCount, int32 releaseCount = 1)
|
||||||
|
{
|
||||||
|
previousCount = ?;
|
||||||
|
return ReleaseSemaphore(nativeHandle, releaseCount, &previousCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
namespace System
|
||||||
|
{
|
||||||
|
extension Windows
|
||||||
|
{
|
||||||
|
public struct SecurityAttributes
|
||||||
|
{
|
||||||
|
public uint32 Length;
|
||||||
|
public SECURITY_DESCRIPTOR* SecurityDescriptor;
|
||||||
|
public IntBool InheritHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern Windows.Handle CreateSemaphoreW(SecurityAttributes* semaphoreAttributes, int32 initialCount, int32 maximumCount, char16* name);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern uint32 WaitForSingleObject(Handle handle, uint32 timeout);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern uint32 WaitForMultipleObjects(uint32 count, Handle *handles, IntBool bWaitAll, uint32 timeout);
|
||||||
|
|
||||||
|
public const uint32 InfiniteTimeout = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public static uint32 WaitForMultipleObjects(Handle[] handles, bool waitForAll, uint32 timeout = InfiniteTimeout)
|
||||||
|
{
|
||||||
|
return WaitForMultipleObjects((.)handles.Count, handles.CArray(), waitForAll, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern IntBool ReleaseSemaphore(Handle semaphore, int32 releaseCount, int32* previousCount);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern Handle CreateMutexW(SecurityAttributes* mutexAttributes, IntBool initialOwner, char16* name);
|
||||||
|
|
||||||
|
[LinkName(.C)]
|
||||||
|
public static extern IntBool ReleaseMutex(Handle mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,12 +12,30 @@ namespace GlitchyEngine
|
|||||||
/// Windows (WinApi) specific implementation of the Input-class
|
/// Windows (WinApi) specific implementation of the Input-class
|
||||||
extension Input
|
extension Input
|
||||||
{
|
{
|
||||||
|
internal static bool sUseRawInput = true;
|
||||||
|
|
||||||
|
public override static bool RawInput
|
||||||
|
{
|
||||||
|
get => sUseRawInput;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if(sUseRawInput == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sUseRawInput = value;
|
||||||
|
|
||||||
|
//if(sUseRawInput)
|
||||||
|
// TODO: init raw input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents the state of the input devices on the windows platform (WinApi that is).
|
/// Represents the state of the input devices on the windows platform (WinApi that is).
|
||||||
struct WindowsInputState
|
struct WindowsInputState
|
||||||
{
|
{
|
||||||
public int8[256] KeyStates;
|
public int8[256] KeyStates;
|
||||||
public Point CursorPosition;
|
public Point CursorPosition;
|
||||||
public Point CursorPositionDifference;
|
public Point CursorPositionDifference;
|
||||||
|
public Int32_2 RawMovement;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WindowsInputState* CurrentState = new WindowsInputState() ~ delete _;
|
static WindowsInputState* CurrentState = new WindowsInputState() ~ delete _;
|
||||||
@@ -152,6 +170,19 @@ namespace GlitchyEngine
|
|||||||
|
|
||||||
public override static Point GetMouseMovement() => CurrentState.CursorPositionDifference;
|
public override static Point GetMouseMovement() => CurrentState.CursorPositionDifference;
|
||||||
|
|
||||||
|
public override static Int32_2 GetRawMouseMovement()
|
||||||
|
{
|
||||||
|
if(sUseRawInput)
|
||||||
|
{
|
||||||
|
return CurrentState.RawMovement;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var v = GetMouseMovement();
|
||||||
|
|
||||||
|
return *(Int32_2*)&v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//[CLink, CallingConvention(.Stdcall)]
|
//[CLink, CallingConvention(.Stdcall)]
|
||||||
//static extern int16 GetKeyState(int32 keycode);
|
//static extern int16 GetKeyState(int32 keycode);
|
||||||
@@ -160,6 +191,8 @@ namespace GlitchyEngine
|
|||||||
[CLink, CallingConvention(.Stdcall)]
|
[CLink, CallingConvention(.Stdcall)]
|
||||||
static extern IntBool ScreenToClient(HWnd hWnd, ref Point p);
|
static extern IntBool ScreenToClient(HWnd hWnd, ref Point p);
|
||||||
|
|
||||||
|
internal static Int32_2 rawMovement;
|
||||||
|
|
||||||
public override static void NewFrame()
|
public override static void NewFrame()
|
||||||
{
|
{
|
||||||
// Switch last and current states
|
// Switch last and current states
|
||||||
@@ -192,6 +225,8 @@ namespace GlitchyEngine
|
|||||||
|
|
||||||
// Calculate cursor movement
|
// Calculate cursor movement
|
||||||
CurrentState.CursorPositionDifference = CurrentState.CursorPosition - LastState.CursorPosition;
|
CurrentState.CursorPositionDifference = CurrentState.CursorPosition - LastState.CursorPosition;
|
||||||
|
CurrentState.RawMovement = rawMovement;
|
||||||
|
rawMovement = .Zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using GlitchyEngine.Events;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using GlitchyEngine.Math;
|
using GlitchyEngine.Math;
|
||||||
using GlitchyEngine.Renderer;
|
using GlitchyEngine.Renderer;
|
||||||
|
using DirectX.Windows.Winuser.RawInput;
|
||||||
using static System.Windows;
|
using static System.Windows;
|
||||||
|
|
||||||
namespace GlitchyEngine
|
namespace GlitchyEngine
|
||||||
@@ -215,6 +216,41 @@ namespace GlitchyEngine
|
|||||||
LoadWindowRectangle();
|
LoadWindowRectangle();
|
||||||
|
|
||||||
Log.EngineLogger.Trace($"Created window \"{Title}\" ({Width}, {Height})");
|
Log.EngineLogger.Trace($"Created window \"{Title}\" ({Width}, {Height})");
|
||||||
|
|
||||||
|
if(Input.RawInput)
|
||||||
|
{
|
||||||
|
InitRawInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[CLink, CallingConvention(.Stdcall)]
|
||||||
|
static extern IntBool RegisterRawInputDevices(RAWINPUTDEVICE* rawInputDevices, uint32 numDevices, uint32 size);
|
||||||
|
|
||||||
|
private void InitRawInput()
|
||||||
|
{
|
||||||
|
RAWINPUTDEVICE[1] Rid;
|
||||||
|
|
||||||
|
Rid[0].UsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
|
||||||
|
Rid[0].Usage = 0x02; // HID_USAGE_GENERIC_MOUSE
|
||||||
|
Rid[0].Flags = 0;//RIDEV_NOLEGACY; // adds mouse and also ignores legacy mouse messages
|
||||||
|
Rid[0].Target = 0;
|
||||||
|
/*
|
||||||
|
Rid[1].UsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
|
||||||
|
Rid[1].Usage = 0x06; // HID_USAGE_GENERIC_KEYBOARD
|
||||||
|
Rid[1].Flags = RIDEV_NOLEGACY; // adds keyboard and also ignores legacy keyboard messages
|
||||||
|
Rid[1].Target = 0;
|
||||||
|
*/
|
||||||
|
if (!RegisterRawInputDevices(&Rid, Rid.Count, sizeof(RAWINPUTDEVICE)))
|
||||||
|
{
|
||||||
|
DirectX.Common.HResult errorCode = (.)GetLastError();
|
||||||
|
Log.EngineLogger.Error($"Failed to register raw input devices. Message({(int32)errorCode}):{errorCode}");
|
||||||
|
// Disable raw input
|
||||||
|
Input.RawInput = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Trace($"Registered raw input devices.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _isResizingOrMoving;
|
private bool _isResizingOrMoving;
|
||||||
@@ -438,11 +474,128 @@ namespace GlitchyEngine
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
// Raw Input
|
||||||
|
case 0x00FF: //WM_INPUT
|
||||||
|
{
|
||||||
|
uint32 dataSize = ?;
|
||||||
|
GetRawInputData((.)lParam, RID_INPUT, null, &dataSize, sizeof(RAWINPUTHEADER));
|
||||||
|
|
||||||
|
if (dataSize > 0)
|
||||||
|
{
|
||||||
|
uint8[] rawData = scope .[dataSize];
|
||||||
|
if (GetRawInputData((.)lParam, RID_INPUT, rawData.CArray(), &dataSize, sizeof(RAWINPUTHEADER)) == dataSize)
|
||||||
|
{
|
||||||
|
RAWINPUT* raw = (.)rawData.CArray();
|
||||||
|
if (raw.Header.Type == RIM_TYPEMOUSE)
|
||||||
|
{
|
||||||
|
int32 movementX = raw.Data.Mouse.lLastX;
|
||||||
|
int32 movementY = raw.Data.Mouse.lLastY;
|
||||||
|
|
||||||
|
// TODO: raw.Data.Mouse.usFlags defines whether movement is abosulte, relative, etc...
|
||||||
|
Input.[Friend]rawMovement += .(movementX, movementY);
|
||||||
|
/*
|
||||||
|
var event = scope MouseMovedEvent(movementX, movementY);
|
||||||
|
window._eventCallback(event);
|
||||||
|
|
||||||
|
// Convert button transition flags to a more manageable type
|
||||||
|
var buttonTransitions = (RawMouseButtonTransition)raw.Data.Mouse.DUMMYUNIONNAME.DUMMYSTRUCTNAME.usButtonFlags;
|
||||||
|
|
||||||
|
if(buttonTransitions.HasFlag(.LeftDown))
|
||||||
|
{
|
||||||
|
var buttonEvent = scope MouseButtonPressedEvent(.LeftButton);
|
||||||
|
window._eventCallback(buttonEvent);
|
||||||
|
}
|
||||||
|
else if(buttonTransitions.HasFlag(.LeftUp))
|
||||||
|
{
|
||||||
|
var buttonEvent = scope MouseButtonReleasedEvent(.LeftButton);
|
||||||
|
window._eventCallback(buttonEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buttonTransitions.HasFlag(.RightDown))
|
||||||
|
{
|
||||||
|
var buttonEvent = scope MouseButtonPressedEvent(.RightButton);
|
||||||
|
window._eventCallback(buttonEvent);
|
||||||
|
}
|
||||||
|
else if(buttonTransitions.HasFlag(.RightUp))
|
||||||
|
{
|
||||||
|
var buttonEvent = scope MouseButtonReleasedEvent(.RightButton);
|
||||||
|
window._eventCallback(buttonEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buttonTransitions.HasFlag(.MiddleDown))
|
||||||
|
{
|
||||||
|
var buttonEvent = scope MouseButtonPressedEvent(.MiddleButton);
|
||||||
|
window._eventCallback(buttonEvent);
|
||||||
|
}
|
||||||
|
else if(buttonTransitions.HasFlag(.MiddleUp))
|
||||||
|
{
|
||||||
|
var buttonEvent = scope MouseButtonReleasedEvent(.MiddleButton);
|
||||||
|
window._eventCallback(buttonEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buttonTransitions.HasFlag(.XButton1Down))
|
||||||
|
{
|
||||||
|
var buttonEvent = scope MouseButtonPressedEvent(.XButton1);
|
||||||
|
window._eventCallback(buttonEvent);
|
||||||
|
}
|
||||||
|
else if(buttonTransitions.HasFlag(.XButton1Up))
|
||||||
|
{
|
||||||
|
var buttonEvent = scope MouseButtonReleasedEvent(.XButton1);
|
||||||
|
window._eventCallback(buttonEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buttonTransitions.HasFlag(.XButton2Down))
|
||||||
|
{
|
||||||
|
var buttonEvent = scope MouseButtonPressedEvent(.XButton2);
|
||||||
|
window._eventCallback(buttonEvent);
|
||||||
|
}
|
||||||
|
else if(buttonTransitions.HasFlag(.XButton2Up))
|
||||||
|
{
|
||||||
|
var buttonEvent = scope MouseButtonReleasedEvent(.XButton2);
|
||||||
|
window._eventCallback(buttonEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buttonTransitions.HasFlag(.MouseWheel))
|
||||||
|
{
|
||||||
|
int32 rotation = raw.Data.Mouse.DUMMYUNIONNAME.DUMMYSTRUCTNAME.usButtonData / WHEEL_DELTA;
|
||||||
|
|
||||||
|
var scrollEvent = scope MouseScrolledEvent(0, rotation);
|
||||||
|
window._eventCallback(scrollEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buttonTransitions.HasFlag(.MouseHWheel))
|
||||||
|
{
|
||||||
|
int32 rotation = raw.Data.Mouse.DUMMYUNIONNAME.DUMMYSTRUCTNAME.usButtonData / WHEEL_DELTA;
|
||||||
|
|
||||||
|
var scrollEvent = scope MouseScrolledEvent(rotation, 0);
|
||||||
|
window._eventCallback(scrollEvent);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum RawMouseButtonTransition
|
||||||
|
{
|
||||||
|
LeftDown = 0x0001,
|
||||||
|
LeftUp = 0x0002,
|
||||||
|
MiddleDown = 0x0010,
|
||||||
|
MiddleUp = 0x0020,
|
||||||
|
RightDown = 0x0004,
|
||||||
|
RightUp = 0x0008,
|
||||||
|
XButton1Down = 0x0040,
|
||||||
|
XButton1Up = 0x0080,
|
||||||
|
XButton2Down = 0x0100,
|
||||||
|
XButton2Up = 0x0200,
|
||||||
|
MouseWheel = 0x0400,
|
||||||
|
MouseHWheel = 0x0800
|
||||||
|
}
|
||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
Message message = .();
|
Message message = .();
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
namespace GlitchyEngine.Renderer
|
||||||
|
{
|
||||||
|
// TODO: add all features.
|
||||||
|
public class DepthStencilTarget : RefCounted
|
||||||
|
{
|
||||||
|
protected internal GraphicsContext _context ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
protected uint32 _width, _height;
|
||||||
|
|
||||||
|
public uint32 Width => _width;
|
||||||
|
public uint32 Height => _height;
|
||||||
|
|
||||||
|
public this(GraphicsContext context, uint32 width, uint32 height)
|
||||||
|
{
|
||||||
|
_context = context..AddRef();
|
||||||
|
_width = width;
|
||||||
|
_height = height;
|
||||||
|
|
||||||
|
PlatformCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected extern void PlatformCreate();
|
||||||
|
|
||||||
|
public extern void Bind();
|
||||||
|
|
||||||
|
public extern void Clear(float depthValue, uint8 stencilValue, DepthStencilClearFlag clearFlags);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,11 +4,97 @@ using System.Collections;
|
|||||||
|
|
||||||
namespace GlitchyEngine.Renderer
|
namespace GlitchyEngine.Renderer
|
||||||
{
|
{
|
||||||
|
public class EffectLibrary
|
||||||
|
{
|
||||||
|
private GraphicsContext _context ~ _?.ReleaseRef();
|
||||||
|
private Dictionary<String, Effect> _effects = new .() ~ delete _;
|
||||||
|
|
||||||
|
private List<String> _ownedStrings = new .() ~ DeleteContainerAndItems!(_);
|
||||||
|
|
||||||
|
public this(GraphicsContext context)
|
||||||
|
{
|
||||||
|
_context = context..AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ~this()
|
||||||
|
{
|
||||||
|
for(let pair in _effects)
|
||||||
|
{
|
||||||
|
pair.value.ReleaseRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(Effect effect, String effectName = null)
|
||||||
|
{
|
||||||
|
String name;
|
||||||
|
|
||||||
|
if(effectName == null)
|
||||||
|
{
|
||||||
|
name = effect.Name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
name = new String(effectName);
|
||||||
|
_ownedStrings.Add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(!Exists(name), "Can't add two effects with the same name to library.");
|
||||||
|
|
||||||
|
_effects.Add(name, effect..AddRef());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the effect with the given file name.
|
||||||
|
* @param filepath The path to the effect file.
|
||||||
|
* @param effectName The optional custom effect name which will be used to identify the effect.
|
||||||
|
* @returns The loaded Effect. Note: This function will increment the reference counter of the effect, so the programmer must decrement it once it's not used anymore.
|
||||||
|
* If the return-value is not needed, use LoadNoRefInc instead.
|
||||||
|
*/
|
||||||
|
public Effect Load(String filepath, String effectName = null)
|
||||||
|
{
|
||||||
|
String name = effectName;
|
||||||
|
|
||||||
|
if(name == null)
|
||||||
|
{
|
||||||
|
name = scope:: String();
|
||||||
|
Path.GetFileNameWithoutExtension(filepath, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.EngineLogger.AssertDebug(!Exists(name), "Can't add two effects with the same name to library.");
|
||||||
|
|
||||||
|
Effect effect = new Effect(_context, filepath, name);
|
||||||
|
Add(effect);
|
||||||
|
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the effect with the given file name.
|
||||||
|
* @param filepath The path to the effect file.
|
||||||
|
* @param effectName The optional custom effect name which will be used to identify the effect.
|
||||||
|
*/
|
||||||
|
public void LoadNoRefInc(String filepath, String effectName = null)
|
||||||
|
{
|
||||||
|
var v = Load(filepath, effectName);
|
||||||
|
v.ReleaseRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Effect Get(String effectName)
|
||||||
|
{
|
||||||
|
Log.EngineLogger.AssertDebug(Exists(effectName), "Effect not found!");
|
||||||
|
|
||||||
|
return _effects.GetValue(effectName).Get()..AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Exists(String effectName) => _effects.ContainsKey(effectName);
|
||||||
|
}
|
||||||
|
|
||||||
public class Effect : RefCounted
|
public class Effect : RefCounted
|
||||||
{
|
{
|
||||||
protected GraphicsContext _context ~ _?.ReleaseRef();
|
protected GraphicsContext _context ~ _?.ReleaseRef();
|
||||||
internal VertexShader _vs ~ _?.ReleaseRef();
|
internal VertexShader _vs ~ _?.ReleaseRef();
|
||||||
internal PixelShader _ps ~ _?.ReleaseRef();
|
internal PixelShader _ps ~ _?.ReleaseRef();
|
||||||
|
protected String _name ~ delete _;
|
||||||
|
|
||||||
BufferCollection _bufferCollection ~ delete _;
|
BufferCollection _bufferCollection ~ delete _;
|
||||||
|
|
||||||
@@ -41,6 +127,8 @@ namespace GlitchyEngine.Renderer
|
|||||||
public BufferCollection Buffers => _bufferCollection;
|
public BufferCollection Buffers => _bufferCollection;
|
||||||
public BufferVariableCollection Variables => _variables;
|
public BufferVariableCollection Variables => _variables;
|
||||||
|
|
||||||
|
public String Name => _name;
|
||||||
|
|
||||||
public void ApplyChanges()
|
public void ApplyChanges()
|
||||||
{
|
{
|
||||||
for(let buffer in _bufferCollection)
|
for(let buffer in _bufferCollection)
|
||||||
@@ -65,13 +153,23 @@ namespace GlitchyEngine.Renderer
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public this(GraphicsContext context, String filename, String vsEntry, String psEntry)
|
public this(GraphicsContext context, String filename, String vsEntry, String psEntry, String shaderName = null)
|
||||||
{
|
{
|
||||||
_context = context..AddRef();
|
_context = context..AddRef();
|
||||||
CompileFromFile(filename, vsEntry, psEntry);
|
CompileFromFile(filename, vsEntry, psEntry);
|
||||||
|
|
||||||
|
if(shaderName == null)
|
||||||
|
{
|
||||||
|
_name = new String(shaderName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_name = new String();
|
||||||
|
Path.GetFileNameWithoutExtension(filename, _name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public this(GraphicsContext context, String filename)
|
public this(GraphicsContext context, String filename, String shaderName = null)
|
||||||
{
|
{
|
||||||
_context = context..AddRef();
|
_context = context..AddRef();
|
||||||
|
|
||||||
@@ -83,11 +181,23 @@ namespace GlitchyEngine.Renderer
|
|||||||
Compile(fileContent, vsName, psName);
|
Compile(fileContent, vsName, psName);
|
||||||
|
|
||||||
MergeResources();
|
MergeResources();
|
||||||
|
|
||||||
|
if(shaderName == null)
|
||||||
|
{
|
||||||
|
_name = new String();
|
||||||
|
Path.GetFileNameWithoutExtension(filename, _name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_name = new String(shaderName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public this(String vsPath, String vsEntry, String psPath, String psEntry)
|
public this(String shaderName, String vsPath, String vsEntry, String psPath, String psEntry)
|
||||||
{
|
{
|
||||||
Compile(vsPath, vsEntry, psPath, psEntry);
|
Compile(vsPath, vsEntry, psPath, psEntry);
|
||||||
|
|
||||||
|
_name = new String(shaderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CompileFromFile(String filename, String vsEntry, String psEntry)
|
private void CompileFromFile(String filename, String vsEntry, String psEntry)
|
||||||
|
|||||||
@@ -87,9 +87,9 @@ namespace GlitchyEngine.Renderer
|
|||||||
case .LimitedReversed:
|
case .LimitedReversed:
|
||||||
_projection = Matrix.ReversedPerspectiveProjection(_fovY, _aspect, _nearPlane, _farPlane);
|
_projection = Matrix.ReversedPerspectiveProjection(_fovY, _aspect, _nearPlane, _farPlane);
|
||||||
case .Infinite:
|
case .Infinite:
|
||||||
_projection = Matrix.InfinitePerspectiveProjection(_fovY, _aspect, _nearPlane); // todo: epsilon
|
_projection = Matrix.InfinitePerspectiveProjection(_fovY, _aspect, _nearPlane);
|
||||||
case .InfiniteReversed:
|
case .InfiniteReversed:
|
||||||
_projection = Matrix.ReversedInfinitePerspectiveProjection(_fovY, _aspect, _nearPlane); // todo: epsilon
|
_projection = Matrix.ReversedInfinitePerspectiveProjection(_fovY, _aspect, _nearPlane);
|
||||||
default:
|
default:
|
||||||
Log.EngineLogger.Assert(false, "Unknown projection type.");
|
Log.EngineLogger.Assert(false, "Unknown projection type.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
namespace GlitchyEngine.Renderer
|
namespace GlitchyEngine.Renderer
|
||||||
{
|
{
|
||||||
public enum FillMode
|
public enum FillMode
|
||||||
@@ -49,7 +50,7 @@ namespace GlitchyEngine.Renderer
|
|||||||
public static readonly RasterizerStateDescription Default = .(.Solid, .Back, false, 0, 0f, 0f, true, false, false, false);
|
public static readonly RasterizerStateDescription Default = .(.Solid, .Back, false, 0, 0f, 0f, true, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RasterizerState
|
public class RasterizerState : RefCounted
|
||||||
{
|
{
|
||||||
internal GraphicsContext _context ~ _?.ReleaseRef();
|
internal GraphicsContext _context ~ _?.ReleaseRef();
|
||||||
private RasterizerStateDescription _description;
|
private RasterizerStateDescription _description;
|
||||||
|
|||||||
@@ -3,6 +3,13 @@ using GlitchyEngine.Math;
|
|||||||
|
|
||||||
namespace GlitchyEngine.Renderer
|
namespace GlitchyEngine.Renderer
|
||||||
{
|
{
|
||||||
|
public enum DepthStencilClearFlag
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Depth = 1,
|
||||||
|
Stencil = 2
|
||||||
|
}
|
||||||
|
|
||||||
public static class RenderCommand
|
public static class RenderCommand
|
||||||
{
|
{
|
||||||
private static RendererAPI _rendererAPI;
|
private static RendererAPI _rendererAPI;
|
||||||
@@ -25,6 +32,13 @@ namespace GlitchyEngine.Renderer
|
|||||||
_rendererAPI.Clear(renderTarget, color);
|
_rendererAPI.Clear(renderTarget, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public static void Clear(DepthStencilTarget target, float depthValue, uint8 stencilValue, DepthStencilClearFlag clearFlags)
|
||||||
|
{
|
||||||
|
_rendererAPI.Clear(target, depthValue, stencilValue, clearFlags);
|
||||||
|
}
|
||||||
|
|
||||||
[Inline]
|
[Inline]
|
||||||
public static void DrawIndexed(GeometryBinding geometry)
|
public static void DrawIndexed(GeometryBinding geometry)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ namespace GlitchyEngine.Renderer
|
|||||||
|
|
||||||
public extern void Clear(RenderTarget renderTarget, ColorRGBA clearColor);
|
public extern void Clear(RenderTarget renderTarget, ColorRGBA clearColor);
|
||||||
|
|
||||||
|
public extern void Clear(DepthStencilTarget target, float depthValue, uint8 stencilValue, DepthStencilClearFlag clearFlags);
|
||||||
|
|
||||||
public extern void DrawIndexed(GeometryBinding geometry);
|
public extern void DrawIndexed(GeometryBinding geometry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace GlitchyEngine.Renderer
|
|||||||
_samplerState.Bind(slot);
|
_samplerState.Bind(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void ImplBind(uint32 slot);
|
protected extern void ImplBind(uint32 slot);
|
||||||
|
|
||||||
protected this(GraphicsContext context)
|
protected this(GraphicsContext context)
|
||||||
{
|
{
|
||||||
@@ -46,6 +46,15 @@ namespace GlitchyEngine.Renderer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct Texture2DDesc
|
||||||
|
{
|
||||||
|
public uint32 Width;
|
||||||
|
public uint32 Height;
|
||||||
|
public uint32 ArraySize;
|
||||||
|
public uint32 MipLevels;
|
||||||
|
public Format Format;
|
||||||
|
}
|
||||||
|
|
||||||
public class Texture2D : Texture
|
public class Texture2D : Texture
|
||||||
{
|
{
|
||||||
protected String _path ~ delete _;
|
protected String _path ~ delete _;
|
||||||
@@ -56,7 +65,30 @@ namespace GlitchyEngine.Renderer
|
|||||||
public override extern uint32 ArraySize {get;}
|
public override extern uint32 ArraySize {get;}
|
||||||
public override extern uint32 MipLevels {get;}
|
public override extern uint32 MipLevels {get;}
|
||||||
|
|
||||||
protected override extern void ImplBind(uint32 slot);
|
protected this(GraphicsContext context) : base(context) {}
|
||||||
|
|
||||||
|
public this(GraphicsContext context, String path) : base(context)
|
||||||
|
{
|
||||||
|
this._path = new String(path);
|
||||||
|
LoadTexturePlatform();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected extern void LoadTexturePlatform();
|
||||||
|
|
||||||
|
protected extern void CreateTexturePlatform(Texture2DDesc desc, void* data, uint32 linePitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TextureCube : Texture
|
||||||
|
{
|
||||||
|
protected String _path ~ delete _;
|
||||||
|
|
||||||
|
public override extern uint32 Width {get;}
|
||||||
|
public override extern uint32 Height {get;}
|
||||||
|
public override uint32 Depth => 1;
|
||||||
|
public override extern uint32 ArraySize {get;}
|
||||||
|
public override extern uint32 MipLevels {get;}
|
||||||
|
|
||||||
|
protected this(GraphicsContext context) : base(context) {}
|
||||||
|
|
||||||
public this(GraphicsContext context, String path) : base(context)
|
public this(GraphicsContext context, String path) : base(context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
namespace GlitchyEngine.Threading
|
||||||
|
{
|
||||||
|
public enum LockResult
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The lock was released by the owning thread.
|
||||||
|
*/
|
||||||
|
Released,
|
||||||
|
/**
|
||||||
|
* The owning thread terminated without releasing the lock.
|
||||||
|
*/
|
||||||
|
Abandoned,
|
||||||
|
/**
|
||||||
|
* The lock function timed out.
|
||||||
|
*/
|
||||||
|
Timeout,
|
||||||
|
/**
|
||||||
|
* The function has failed.
|
||||||
|
*/
|
||||||
|
Failed,
|
||||||
|
|
||||||
|
Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
namespace GlitchyEngine.Threading
|
||||||
|
{
|
||||||
|
public class Mutex
|
||||||
|
{
|
||||||
|
public extern this(bool initialyOwned = false);
|
||||||
|
|
||||||
|
public const uint32 InfiniteTimeout = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
public extern LockResult Lock(uint32 timeout = InfiniteTimeout);
|
||||||
|
|
||||||
|
public extern bool Unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
namespace GlitchyEngine.Threading
|
||||||
|
{
|
||||||
|
public class Semaphore
|
||||||
|
{
|
||||||
|
public extern this(int32 initialCount = 1, int32 maximumCount = 1);
|
||||||
|
|
||||||
|
public const uint32 InfiniteTimeout = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
public extern LockResult Lock(uint32 timeout = InfiniteTimeout);
|
||||||
|
|
||||||
|
public extern bool Unlock(int32 releaseCount = 1);
|
||||||
|
|
||||||
|
public extern bool Unlock(out int32 previousCount, int32 releaseCount = 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.World
|
||||||
|
{
|
||||||
|
public class ComponentPool
|
||||||
|
{
|
||||||
|
int _objectSize;
|
||||||
|
int _capacity;
|
||||||
|
|
||||||
|
int PoolSize => _objectSize * _capacity;
|
||||||
|
|
||||||
|
uint8* _rawData ~ delete _;
|
||||||
|
|
||||||
|
public this(int objectSize, int capacity)
|
||||||
|
{
|
||||||
|
_objectSize = objectSize;
|
||||||
|
_capacity = capacity;
|
||||||
|
|
||||||
|
_rawData = new uint8[_capacity * _objectSize]*;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
public void* Get(int index)
|
||||||
|
{
|
||||||
|
Log.EngineLogger.AssertDebug(index >= 0 && index < _capacity);
|
||||||
|
|
||||||
|
return _rawData + index * _objectSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using GlitchyEngine.Math;
|
||||||
|
|
||||||
|
using internal GlitchyEngine.World;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.World
|
||||||
|
{
|
||||||
|
public class EcsWorld
|
||||||
|
{
|
||||||
|
const int MaxEntities = 1024;
|
||||||
|
|
||||||
|
typealias BitmaskEntry = (Entity ID, BitArray ComponentMask);
|
||||||
|
List<BitmaskEntry> _entities = new .();
|
||||||
|
|
||||||
|
List<uint32> _freeIndices = new List<uint32>() ~ delete _;
|
||||||
|
|
||||||
|
typealias ComponentPoolEntry = (uint32 Id, ComponentPool Pool);
|
||||||
|
Dictionary<Type, ComponentPoolEntry> _componentPools = new .();
|
||||||
|
|
||||||
|
public ~this()
|
||||||
|
{
|
||||||
|
for(var entry in _componentPools)
|
||||||
|
{
|
||||||
|
delete entry.value.Pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete _componentPools;
|
||||||
|
|
||||||
|
for(var entry in _entities)
|
||||||
|
{
|
||||||
|
delete entry.ComponentMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete _entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new Component.
|
||||||
|
*/
|
||||||
|
public void Register<T>() where T: struct
|
||||||
|
{
|
||||||
|
_componentPools.Add(typeof(T), ((uint32)_componentPools.Count, new ComponentPool(sizeof(T), MaxEntities)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Entity and returns its ID.
|
||||||
|
*/
|
||||||
|
public Entity NewEntity()
|
||||||
|
{
|
||||||
|
Entity entity;
|
||||||
|
|
||||||
|
// Reuse freed entity slot
|
||||||
|
if(_freeIndices.Count > 0)
|
||||||
|
{
|
||||||
|
uint32 index = _freeIndices.PopBack();
|
||||||
|
|
||||||
|
entity = Entity.CreateEntityID(index, _entities[index].ID.Version);
|
||||||
|
|
||||||
|
_entities[index].ID = entity;
|
||||||
|
}
|
||||||
|
// Create new entity slot
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entity = Entity.CreateEntityID((.)_entities.Count, 0);
|
||||||
|
_entities.Add((entity, new BitArray(_componentPools.Count)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the specified Entity from the World.
|
||||||
|
*/
|
||||||
|
public void RemoveEntity(Entity entity)
|
||||||
|
{
|
||||||
|
var listEntity = ref _entities[entity.Index];
|
||||||
|
if(entity != listEntity.ID)
|
||||||
|
return;
|
||||||
|
|
||||||
|
listEntity.ID = Entity.CreateEntityID(Entity.InvalidEntity.Index, entity.Version + 1);
|
||||||
|
|
||||||
|
_entities[entity.Index].ComponentMask.Clear();
|
||||||
|
_freeIndices.Add(entity.Index);
|
||||||
|
|
||||||
|
// TODO: add "destructor" for components
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns a component of type T to the specified entity and returns it.
|
||||||
|
*/
|
||||||
|
public T* AssignComponent<T>(Entity entity) where T : struct
|
||||||
|
{
|
||||||
|
if(entity.Index > _entities.Count)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var listEntity = ref _entities[entity.Index];
|
||||||
|
|
||||||
|
if(entity != listEntity.ID)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
ComponentPoolEntry entry;
|
||||||
|
if(!_componentPools.TryGetValue(typeof(T), out entry))
|
||||||
|
{
|
||||||
|
entry = ((uint32)_componentPools.Count, new ComponentPool(sizeof(T), MaxEntities));
|
||||||
|
|
||||||
|
_componentPools.Add(typeof(T), entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: maybe assert?
|
||||||
|
if(listEntity.ComponentMask[entry.Id])
|
||||||
|
return null;
|
||||||
|
|
||||||
|
listEntity.ComponentMask[entry.Id] = true;
|
||||||
|
|
||||||
|
return (.)entry.Pool.Get(entity.Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a component of type T from the specified entity.
|
||||||
|
*/
|
||||||
|
public void RemoveComponent<T>(Entity entity) where T : struct
|
||||||
|
{
|
||||||
|
if(entity.Index > _entities.Count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var listEntity = ref _entities[entity.Index];
|
||||||
|
if(entity != listEntity.ID)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ComponentPoolEntry entry;
|
||||||
|
if(!_componentPools.TryGetValue(typeof(T), out entry))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: maybe assert?
|
||||||
|
if(!listEntity.ComponentMask[entry.Id])
|
||||||
|
return;
|
||||||
|
|
||||||
|
listEntity.ComponentMask[entry.Id] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T* GetComponent<T>(Entity entity) where T : struct
|
||||||
|
{
|
||||||
|
var listEntity = ref _entities[entity.Index];
|
||||||
|
if(entity != listEntity.ID)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
ComponentPoolEntry entry;
|
||||||
|
if(!_componentPools.TryGetValue(typeof(T), out entry))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// TODO: maybe assert?
|
||||||
|
if(!listEntity.ComponentMask[entry.Id])
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return (.)entry.Pool.Get(entity.Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorldEnumerator Enumerate(params Type[] componentTypes)
|
||||||
|
{
|
||||||
|
return WorldEnumerator(this, componentTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct WorldEnumerator : IEnumerator<Entity>, IDisposable
|
||||||
|
{
|
||||||
|
private EcsWorld _world;
|
||||||
|
private BitArray _bitMask;
|
||||||
|
private BitmaskEntry* _currentEntry;
|
||||||
|
private BitmaskEntry* _endEntry;
|
||||||
|
|
||||||
|
public this(EcsWorld world, Type[] componentTypes)
|
||||||
|
{
|
||||||
|
_world = world;
|
||||||
|
_currentEntry = _world._entities.Ptr;
|
||||||
|
_endEntry = _world._entities.Ptr + _world._entities.Count;
|
||||||
|
|
||||||
|
_bitMask = new BitArray(_world._componentPools.Count);
|
||||||
|
for(var type in componentTypes)
|
||||||
|
{
|
||||||
|
Log.EngineLogger.AssertDebug(type.IsStruct, "Components can only be structs.");
|
||||||
|
|
||||||
|
var result = _world._componentPools.GetValue(type);
|
||||||
|
|
||||||
|
if(result case .Ok(let entry))
|
||||||
|
{
|
||||||
|
_bitMask[entry.Id] = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.EngineLogger.AssertDebug(false, "Queried component is not registered for this world. This is invalid because the query would never return any results.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<Entity> GetNext() mut
|
||||||
|
{
|
||||||
|
while(_currentEntry < _endEntry)
|
||||||
|
{
|
||||||
|
BitmaskEntry* entry = _currentEntry++;
|
||||||
|
// Check whether or not mask matches
|
||||||
|
if(entry.ComponentMask.MaskMatch(_bitMask))
|
||||||
|
return entry.ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Err;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
delete _bitMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Test()
|
||||||
|
{
|
||||||
|
EcsWorld world = new .();
|
||||||
|
|
||||||
|
world.Register<TransformComponent>();
|
||||||
|
|
||||||
|
Entity entity = world.NewEntity();
|
||||||
|
|
||||||
|
TransformComponent* myComp = world.AssignComponent<TransformComponent>(entity);
|
||||||
|
myComp.Transform = Matrix.Identity;
|
||||||
|
|
||||||
|
TransformComponent gotComp = *world.GetComponent<TransformComponent>(entity);
|
||||||
|
|
||||||
|
world.RemoveComponent<TransformComponent>(entity);
|
||||||
|
|
||||||
|
Entity entity2 = world.NewEntity();
|
||||||
|
world.AssignComponent<TransformComponent>(entity2);
|
||||||
|
|
||||||
|
world.RemoveEntity(entity);
|
||||||
|
|
||||||
|
entity = world.NewEntity();
|
||||||
|
|
||||||
|
world.RemoveEntity(entity);
|
||||||
|
world.RemoveComponent<TransformComponent>(entity);
|
||||||
|
|
||||||
|
entity = world.NewEntity();
|
||||||
|
|
||||||
|
for(let forenty in world.Enumerate(typeof(TransformComponent)))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
delete world;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using internal GlitchyEngine.World;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.World
|
||||||
|
{
|
||||||
|
public struct Entity : uint64
|
||||||
|
{
|
||||||
|
// Binary Format:
|
||||||
|
// Bits: [0 - 31] [32 - 64]
|
||||||
|
// Data: Version Index
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
internal uint32 Version => (uint32)this;
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
internal uint32 Index => (uint32)(this >> 32);
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
static internal Entity CreateEntityID(uint32 index, uint32 version)
|
||||||
|
{
|
||||||
|
return ((uint64)index << 32) | version;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
internal bool IsValid => Index != InvalidEntity.Index;
|
||||||
|
|
||||||
|
public const Entity InvalidEntity = ((uint64)uint32.MaxValue << 32) | 0;//TODO: Report bug: CreateEntityID(uint32.MaxValue, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using GlitchyEngine.Math;
|
||||||
|
|
||||||
|
namespace GlitchyEngine.World
|
||||||
|
{
|
||||||
|
public struct TransformComponent
|
||||||
|
{
|
||||||
|
static int _id;
|
||||||
|
|
||||||
|
public static int ID {get => _id; set => _id = value; }
|
||||||
|
|
||||||
|
internal Matrix _transform;
|
||||||
|
|
||||||
|
public Matrix Transform
|
||||||
|
{
|
||||||
|
get => _transform;
|
||||||
|
set mut => _transform = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
FileVersion = 1
|
FileVersion = 1
|
||||||
Dependencies = {GlitchyEngine = "*", corlib = "*"}
|
Dependencies = {GlitchyEngine = "*", corlib = "*", LodePng = "*"}
|
||||||
|
|
||||||
[Project]
|
[Project]
|
||||||
Name = "Sandbox"
|
Name = "Sandbox"
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 533 B |
Binary file not shown.
|
After Width: | Height: | Size: 565 B |
Binary file not shown.
|
After Width: | Height: | Size: 482 B |
Binary file not shown.
|
After Width: | Height: | Size: 332 B |
+25
-13
@@ -8,6 +8,8 @@ using ImGui;
|
|||||||
using GlitchyEngine.Renderer;
|
using GlitchyEngine.Renderer;
|
||||||
using GlitchyEngine.Math;
|
using GlitchyEngine.Math;
|
||||||
|
|
||||||
|
using Sandbox.VoxelFun;
|
||||||
|
|
||||||
namespace Sandbox
|
namespace Sandbox
|
||||||
{
|
{
|
||||||
class ExampleLayer : Layer
|
class ExampleLayer : Layer
|
||||||
@@ -59,9 +61,6 @@ namespace Sandbox
|
|||||||
|
|
||||||
RasterizerState _rasterizerState ~ delete _;
|
RasterizerState _rasterizerState ~ delete _;
|
||||||
|
|
||||||
Effect _effect ~ _?.ReleaseRef();
|
|
||||||
Effect _textureEffect ~ _?.ReleaseRef();
|
|
||||||
|
|
||||||
GraphicsContext _context ~ _?.ReleaseRef();
|
GraphicsContext _context ~ _?.ReleaseRef();
|
||||||
|
|
||||||
Texture2D _texture ~ _?.ReleaseRef();
|
Texture2D _texture ~ _?.ReleaseRef();
|
||||||
@@ -70,6 +69,8 @@ namespace Sandbox
|
|||||||
BlendState _alphaBlendState ~ _?.ReleaseRef();
|
BlendState _alphaBlendState ~ _?.ReleaseRef();
|
||||||
BlendState _opaqueBlendState ~ _?.ReleaseRef();
|
BlendState _opaqueBlendState ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
EffectLibrary _effectLibrary ~ delete _;
|
||||||
|
|
||||||
private Vector3 CircleCoord(float angle)
|
private Vector3 CircleCoord(float angle)
|
||||||
{
|
{
|
||||||
return .(Math.Cos(angle), Math.Sin(angle), 0);
|
return .(Math.Cos(angle), Math.Sin(angle), 0);
|
||||||
@@ -80,13 +81,17 @@ namespace Sandbox
|
|||||||
{
|
{
|
||||||
_context = Application.Get().Window.Context..AddRef();
|
_context = Application.Get().Window.Context..AddRef();
|
||||||
|
|
||||||
_effect = new Effect(_context, "content\\Shaders\\basicShader.hlsl");
|
_effectLibrary = new EffectLibrary(_context);
|
||||||
|
|
||||||
_textureEffect = new Effect(_context, "content\\Shaders\\textureShader.hlsl");
|
_effectLibrary.LoadNoRefInc("content\\Shaders\\basicShader.hlsl");
|
||||||
|
|
||||||
|
var textureEffect = _effectLibrary.Load("content\\Shaders\\textureShader.hlsl");
|
||||||
|
|
||||||
// Create Input Layout
|
// Create Input Layout
|
||||||
|
|
||||||
_vertexLayout = new VertexLayout(_context, VertexColorTexture.VertexElements, _textureEffect.VertexShader);
|
_vertexLayout = new VertexLayout(_context, VertexColorTexture.VertexElements, textureEffect.VertexShader);
|
||||||
|
|
||||||
|
textureEffect.ReleaseRef();
|
||||||
|
|
||||||
// Create hexagon
|
// Create hexagon
|
||||||
{
|
{
|
||||||
@@ -239,29 +244,35 @@ namespace Sandbox
|
|||||||
|
|
||||||
_opaqueBlendState.Bind();
|
_opaqueBlendState.Bind();
|
||||||
|
|
||||||
|
var basicEffect = _effectLibrary.Get("basicShader");
|
||||||
|
var textureEffect = _effectLibrary.Get("textureShader");
|
||||||
|
|
||||||
for(int x < 20)
|
for(int x < 20)
|
||||||
for(int y < 20)
|
for(int y < 20)
|
||||||
{
|
{
|
||||||
if((x + y) % 2 == 0)
|
if((x + y) % 2 == 0)
|
||||||
_effect.Variables["BaseColor"].SetData(_squareColor0);
|
basicEffect.Variables["BaseColor"].SetData(_squareColor0);
|
||||||
else
|
else
|
||||||
_effect.Variables["BaseColor"].SetData(_squareColor1);
|
basicEffect.Variables["BaseColor"].SetData(_squareColor1);
|
||||||
|
|
||||||
Matrix transform = Matrix.Translation(x * 0.2f, y * 0.2f, 0) * Matrix.Scaling(0.1f);
|
Matrix transform = Matrix.Translation(x * 0.2f, y * 0.2f, 0) * Matrix.Scaling(0.1f);
|
||||||
Renderer.Submit(_quadGeometryBinding, _effect, transform);
|
Renderer.Submit(_quadGeometryBinding, basicEffect, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
_effect.Variables["BaseColor"].SetData(_squareColor1);
|
basicEffect.Variables["BaseColor"].SetData(_squareColor1);
|
||||||
|
|
||||||
_texture.Bind();
|
_texture.Bind();
|
||||||
Renderer.Submit(_quadGeometryBinding, _textureEffect, .Scaling(1.5f));
|
Renderer.Submit(_quadGeometryBinding, textureEffect, .Scaling(1.5f));
|
||||||
|
|
||||||
_alphaBlendState.Bind();
|
_alphaBlendState.Bind();
|
||||||
|
|
||||||
_ge_logo.Bind();
|
_ge_logo.Bind();
|
||||||
Renderer.Submit(_quadGeometryBinding, _textureEffect, .Scaling(1.5f));
|
Renderer.Submit(_quadGeometryBinding, textureEffect, .Scaling(1.5f));
|
||||||
|
|
||||||
Renderer.EndScene();
|
Renderer.EndScene();
|
||||||
|
|
||||||
|
basicEffect.ReleaseRef();
|
||||||
|
textureEffect.ReleaseRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorRGBA _squareColor0 = ColorRGBA.CornflowerBlue;
|
ColorRGBA _squareColor0 = ColorRGBA.CornflowerBlue;
|
||||||
@@ -292,7 +303,8 @@ namespace Sandbox
|
|||||||
{
|
{
|
||||||
public this()
|
public this()
|
||||||
{
|
{
|
||||||
PushLayer(new ExampleLayer());
|
PushLayer(new VoxelTestLayer());
|
||||||
|
//PushLayer(new ExampleLayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Export, LinkName("CreateApplication")]
|
[Export, LinkName("CreateApplication")]
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using GlitchyEngine;
|
||||||
|
using GlitchyEngine.Math;
|
||||||
|
|
||||||
|
namespace Sandbox.VoxelFun
|
||||||
|
{
|
||||||
|
class Block
|
||||||
|
{
|
||||||
|
private Model _model;
|
||||||
|
|
||||||
|
private uint16 _blockID;
|
||||||
|
|
||||||
|
public uint16 ID => _blockID;
|
||||||
|
|
||||||
|
public Model Model => _model;
|
||||||
|
|
||||||
|
public BlockFace VisibleNeighbors {get;}
|
||||||
|
|
||||||
|
public this(BlockFace visibleNeighbors = .None)
|
||||||
|
{
|
||||||
|
VisibleNeighbors = visibleNeighbors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will be called when the block is being destroyed by the player.
|
||||||
|
* @param blockCoordinate The coordinate the block is at.
|
||||||
|
*/
|
||||||
|
public virtual void OnBreaking(Int32_3 blockCoordinate)
|
||||||
|
{
|
||||||
|
Log.ClientLogger.Info($"I broke. {{{blockCoordinate}}}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will be called when the block is being placed by the player.
|
||||||
|
* @param blockCoordinate The coordinate the block is at.
|
||||||
|
*/
|
||||||
|
public virtual void OnPlacing(Int32_3 blockCoordinate)
|
||||||
|
{
|
||||||
|
Log.ClientLogger.Info($"I live! {{{blockCoordinate}}}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using GlitchyEngine.Math;
|
||||||
|
using static Sandbox.VoxelFun.VoxelTestLayer;
|
||||||
|
namespace Sandbox.VoxelFun
|
||||||
|
{
|
||||||
|
abstract class Model
|
||||||
|
{
|
||||||
|
public abstract void GenerateGeometry(Block block, Vector3 blockPosition, BlockFace visibleFaces, List<VertexColorTexture> vertices, List<uint32> indices, ref uint32 lastIndex);
|
||||||
|
|
||||||
|
protected static void AddQuadIndices(ref uint32 lastIndex, List<uint32> indices)
|
||||||
|
{
|
||||||
|
uint32[6] inds;
|
||||||
|
inds[0] = lastIndex;
|
||||||
|
inds[1] = lastIndex + 1;
|
||||||
|
inds[2] = lastIndex + 2;
|
||||||
|
|
||||||
|
inds[3] = lastIndex + 2;
|
||||||
|
inds[4] = lastIndex + 3;
|
||||||
|
inds[5] = lastIndex;
|
||||||
|
|
||||||
|
indices.AddRange(inds);
|
||||||
|
lastIndex += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BlockModel : Model
|
||||||
|
{
|
||||||
|
Color _color;
|
||||||
|
|
||||||
|
BlockTexture _textureTop;
|
||||||
|
BlockTexture _textureSide;
|
||||||
|
BlockTexture _textureBottom;
|
||||||
|
|
||||||
|
|
||||||
|
public this(Color color)
|
||||||
|
{
|
||||||
|
_color = color;
|
||||||
|
|
||||||
|
bottomCoords = sideCoords = topCoords = (.Zero, .UnitX, .UnitY, .One);
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(Color color, BlockTexture texture) : this(color, texture, texture, texture)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(Color color, BlockTexture topTexture, BlockTexture sideTexture, BlockTexture bottomTexture)
|
||||||
|
{
|
||||||
|
_color = color;
|
||||||
|
_textureTop = topTexture;
|
||||||
|
_textureSide = sideTexture;
|
||||||
|
_textureBottom = bottomTexture;
|
||||||
|
|
||||||
|
CalculateTexCoords();
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias TexCoords = (Vector2 Zero, Vector2 UnitX, Vector2 UnitY, Vector2 One);
|
||||||
|
|
||||||
|
TexCoords topCoords;
|
||||||
|
TexCoords sideCoords;
|
||||||
|
TexCoords bottomCoords;
|
||||||
|
|
||||||
|
private void CalculateTexCoords()
|
||||||
|
{
|
||||||
|
topCoords = GetQuadCoords(_textureTop);
|
||||||
|
sideCoords = GetQuadCoords(_textureSide);
|
||||||
|
bottomCoords = GetQuadCoords(_textureBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TexCoords GetQuadCoords(BlockTexture texture)
|
||||||
|
{
|
||||||
|
TexCoords coords;
|
||||||
|
|
||||||
|
coords.UnitX = texture.TransformTexCoords(.UnitX);
|
||||||
|
coords.UnitY = texture.TransformTexCoords(.UnitY);
|
||||||
|
coords.Zero = texture.TransformTexCoords(.Zero);
|
||||||
|
coords.One = texture.TransformTexCoords(.One);
|
||||||
|
|
||||||
|
return coords;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void GenerateGeometry(Block block, Vector3 blockPosition, BlockFace visibleFaces, List<VertexColorTexture> vertices, List<uint32> indices, ref uint32 lastIndex)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(visibleFaces.HasFlag(.Back))
|
||||||
|
{
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 0), _color, sideCoords.UnitY));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 0), _color, sideCoords.One));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 0), _color, sideCoords.UnitX));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 0), _color, sideCoords.Zero));
|
||||||
|
|
||||||
|
AddQuadIndices(ref lastIndex, indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(visibleFaces.HasFlag(.Top))
|
||||||
|
{
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 0), _color, topCoords.UnitY));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 0), _color, topCoords.One));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 1), _color, topCoords.UnitX));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 1), _color, topCoords.Zero));
|
||||||
|
|
||||||
|
AddQuadIndices(ref lastIndex, indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(visibleFaces.HasFlag(.Bottom))
|
||||||
|
{
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 1), _color, bottomCoords.UnitY));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 1), _color, bottomCoords.One));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 0), _color, bottomCoords.UnitX));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 0), _color, bottomCoords.Zero));
|
||||||
|
|
||||||
|
AddQuadIndices(ref lastIndex, indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(visibleFaces.HasFlag(.Front))
|
||||||
|
{
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 1), _color, sideCoords.UnitY));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 1), _color, sideCoords.One));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 1), _color, sideCoords.UnitX));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 1), _color, sideCoords.Zero));
|
||||||
|
|
||||||
|
AddQuadIndices(ref lastIndex, indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(visibleFaces.HasFlag(.Right))
|
||||||
|
{
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 0), _color, sideCoords.UnitY));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(1, 0, 1), _color, sideCoords.One));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 1), _color, sideCoords.UnitX));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(1, 1, 0), _color, sideCoords.Zero));
|
||||||
|
|
||||||
|
AddQuadIndices(ref lastIndex, indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(visibleFaces.HasFlag(.Left))
|
||||||
|
{
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 1), _color, sideCoords.UnitY));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(0, 0, 0), _color, sideCoords.One));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 0), _color, sideCoords.UnitX));
|
||||||
|
vertices.Add(VertexColorTexture(blockPosition + .(0, 1, 1), _color, sideCoords.Zero));
|
||||||
|
|
||||||
|
AddQuadIndices(ref lastIndex, indices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using GlitchyEngine.Math;
|
||||||
|
|
||||||
|
namespace Sandbox.VoxelFun
|
||||||
|
{
|
||||||
|
class BlockTexture
|
||||||
|
{
|
||||||
|
private String _fileName;
|
||||||
|
|
||||||
|
public String FileName => _fileName;
|
||||||
|
|
||||||
|
private Vector2 _atlasStart;
|
||||||
|
private Vector2 _atlasSize;
|
||||||
|
|
||||||
|
[AllowAppend]
|
||||||
|
public this(String fileName)
|
||||||
|
{
|
||||||
|
String str = append String(fileName);
|
||||||
|
|
||||||
|
_fileName = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 TransformTexCoords(Vector2 texCoords)
|
||||||
|
{
|
||||||
|
return _atlasStart + texCoords * _atlasSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using GlitchyEngine.Renderer;
|
||||||
|
|
||||||
|
namespace Sandbox.VoxelFun
|
||||||
|
{
|
||||||
|
class BlockTextures
|
||||||
|
{
|
||||||
|
static List<BlockTexture> _textures = new List<BlockTexture>() ~ DeleteContainerAndItems!(_);
|
||||||
|
|
||||||
|
static TextureAtlas _atlas ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
public static BlockTexture Stone;
|
||||||
|
public static BlockTexture Dirt;
|
||||||
|
public static BlockTexture GrassTop;
|
||||||
|
public static BlockTexture GrassSide;
|
||||||
|
|
||||||
|
public static TextureAtlas Atlas => _atlas;
|
||||||
|
|
||||||
|
public static void Init(GraphicsContext context)
|
||||||
|
{
|
||||||
|
Stone = RegisterTexture(.. new BlockTexture("Content\\Textures\\Stone.png"));
|
||||||
|
Dirt = RegisterTexture(.. new BlockTexture("Content\\Textures\\Dirt.png"));
|
||||||
|
GrassTop = RegisterTexture(.. new BlockTexture("Content\\Textures\\GrassTop.png"));
|
||||||
|
GrassSide = RegisterTexture(.. new BlockTexture("Content\\Textures\\GrassSide.png"));
|
||||||
|
|
||||||
|
GenerateAtlas(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterTexture(BlockTexture texture)
|
||||||
|
{
|
||||||
|
_textures.Add(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GenerateAtlas(GraphicsContext context)
|
||||||
|
{
|
||||||
|
_atlas = new TextureAtlas(context, _textures);
|
||||||
|
|
||||||
|
GlitchyEngine.Renderer.SamplerStateDescription desc = .();
|
||||||
|
desc.MagFilter = .Point;
|
||||||
|
desc.MinFilter = .Point;
|
||||||
|
desc.MipFilter = .Point;
|
||||||
|
desc.MipMaxLOD = 0;
|
||||||
|
desc.MipMinLOD = 0;
|
||||||
|
desc.MaxAnisotropy = 0;
|
||||||
|
|
||||||
|
_atlas.SamplerState = new SamplerState(context, desc)..ReleaseRefNoDelete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
namespace Sandbox.VoxelFun
|
||||||
|
{
|
||||||
|
static class Blocks
|
||||||
|
{
|
||||||
|
static uint16 _sLastId;
|
||||||
|
static List<Block> _blocks = new List<Block>() ~ DeleteContainerAndItems!(_);
|
||||||
|
|
||||||
|
/// Air is a special Block. It defines the absence of a block.
|
||||||
|
public static Block Air;
|
||||||
|
|
||||||
|
public static Block Stone;
|
||||||
|
public static Block Grass;
|
||||||
|
public static Block Dirt;
|
||||||
|
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
Air = RegisterBlock(.. new Block(.All));
|
||||||
|
Stone = RegisterBlock(.. new Block());
|
||||||
|
Grass = RegisterBlock(.. new Block());
|
||||||
|
Dirt = RegisterBlock(.. new Block());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterBlock(Block block)
|
||||||
|
{
|
||||||
|
block.[Friend]_blockID = _sLastId;
|
||||||
|
_sLastId++;
|
||||||
|
_blocks.Add(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Block GetFromId(int id)
|
||||||
|
{
|
||||||
|
return _blocks[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,642 @@
|
|||||||
|
using GlitchyEngine.Math;
|
||||||
|
using GlitchyEngine.Renderer;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Collections;
|
||||||
|
using GlitchyEngine.ImGui;
|
||||||
|
using ImGui;
|
||||||
|
using System.IO;
|
||||||
|
using GlitchyEngine;
|
||||||
|
using GlitchyEngine.Threading;
|
||||||
|
|
||||||
|
namespace Sandbox.VoxelFun
|
||||||
|
{
|
||||||
|
public class Chunk
|
||||||
|
{
|
||||||
|
public ChunkManager ChunkManager;
|
||||||
|
public GeometryBinding Geometry ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
public VoxelChunk Data;
|
||||||
|
public Matrix Transform;
|
||||||
|
public Int32_3 Position;
|
||||||
|
public Int32_3 Coordinate;
|
||||||
|
|
||||||
|
private bool _isDirty;
|
||||||
|
private bool _isGeometryDirty;
|
||||||
|
|
||||||
|
/// Neighbors that will trigger a regeneration of this chunks geometry
|
||||||
|
public BlockFace _reqiredNeighbors;
|
||||||
|
|
||||||
|
public bool IsDirty => _isDirty;
|
||||||
|
|
||||||
|
public bool IsGeometryDirty
|
||||||
|
{
|
||||||
|
get => _isGeometryDirty;
|
||||||
|
set => _isGeometryDirty = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBlock(Int32_3 coordinate, Block blockId)
|
||||||
|
{
|
||||||
|
Log.EngineLogger.AssertDebug(coordinate.X >= 0 && coordinate.Y >= 0 && coordinate.Z >= 0 && coordinate.X < VoxelChunk.Size.X && coordinate.Y < VoxelChunk.Size.Y && coordinate.Z < VoxelChunk.Size.Z, "Specified coordinate is out of range.");
|
||||||
|
|
||||||
|
Data.Data[coordinate.X][coordinate.Y][coordinate.Z] = blockId;
|
||||||
|
|
||||||
|
_isDirty = true;
|
||||||
|
_isGeometryDirty = true;
|
||||||
|
ChunkManager.[Friend]chunkPosLock.Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Block GetBlock(Int32_3 coordinate)
|
||||||
|
{
|
||||||
|
Log.EngineLogger.AssertDebug(coordinate.X >= 0 && coordinate.Y >= 0 && coordinate.Z >= 0 && coordinate.X < VoxelChunk.Size.X && coordinate.Y < VoxelChunk.Size.Y && coordinate.Z < VoxelChunk.Size.Z, "Specified coordinate is out of range.");
|
||||||
|
|
||||||
|
return Data.Data[coordinate.X][coordinate.Y][coordinate.Z];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Serialize(Stream stream)
|
||||||
|
{
|
||||||
|
uint16[VoxelChunk.SizeX][VoxelChunk.SizeY][VoxelChunk.SizeZ] rawData = ?;
|
||||||
|
|
||||||
|
for(int x = 0; x < VoxelChunk.SizeX; x++)
|
||||||
|
for(int y = 0; y < VoxelChunk.SizeY; y++)
|
||||||
|
for(int z = 0; z < VoxelChunk.SizeZ; z++)
|
||||||
|
{
|
||||||
|
rawData[x][y][z] = Data.Data[x][y][z].ID;
|
||||||
|
}
|
||||||
|
stream.Write(rawData);
|
||||||
|
_isDirty = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChunkManager
|
||||||
|
{
|
||||||
|
GraphicsContext _context ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
int _viewDistance = 8;
|
||||||
|
private World _world;
|
||||||
|
|
||||||
|
private String _chunkBasePath ~ delete _;
|
||||||
|
|
||||||
|
Dictionary<Int32_3, Chunk> _chunks = new .() ~ delete _;
|
||||||
|
|
||||||
|
private HashSet<Int32_3> _generatingChunks = new .() ~ delete _;
|
||||||
|
private Monitor _generatingChunksLock = new .() ~ delete _;
|
||||||
|
|
||||||
|
public Effect TextureEffect ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
VoxelGeometryGenerator voxelGeoGen = new VoxelGeometryGenerator() ~ delete _;
|
||||||
|
|
||||||
|
Thread chunkLoader;
|
||||||
|
bool stopChunkLoader;
|
||||||
|
Monitor chunkListLock = new Monitor() ~ delete _;
|
||||||
|
|
||||||
|
Int32_3 chunkPosition;
|
||||||
|
Int32_3 oldChunkPosition = .(Int.MaxValue, Int.MaxValue, Int.MaxValue);
|
||||||
|
|
||||||
|
Monitor chunkPosLock = new Monitor() ~ delete _;
|
||||||
|
|
||||||
|
Monitor chunkPosChanged = new Monitor()..Enter() ~ delete _;
|
||||||
|
|
||||||
|
public World World => _world;
|
||||||
|
|
||||||
|
public delegate void ChunkLoadedHandler(Int32_3 chunkCoordinate);
|
||||||
|
|
||||||
|
Event<ChunkLoadedHandler> _chunkLoaded ~ _.Dispose();
|
||||||
|
Monitor _chunkLoadedLock = new Monitor() ~ delete _;
|
||||||
|
|
||||||
|
public this(GraphicsContext context, VertexLayout vertexLayout, World world)
|
||||||
|
{
|
||||||
|
_context = context..AddRef();
|
||||||
|
_world = world;
|
||||||
|
|
||||||
|
_chunkBasePath = Path.InternalCombine(.. new String(), _world.Directory, "chunks", "");
|
||||||
|
|
||||||
|
if(!Directory.Exists(_chunkBasePath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(_chunkBasePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
voxelGeoGen.Context = _context;
|
||||||
|
voxelGeoGen.Layout = vertexLayout;
|
||||||
|
|
||||||
|
chunkLoader = new Thread(new => ChunkLoaderThread_Entry);
|
||||||
|
chunkLoader.Start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ~this()
|
||||||
|
{
|
||||||
|
stopChunkLoader = true;
|
||||||
|
|
||||||
|
chunkLoader.Join();
|
||||||
|
|
||||||
|
// Unload (and save) all chunks
|
||||||
|
for(var pair in _chunks)
|
||||||
|
{
|
||||||
|
UnloadChunk(pair.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the chunk with the given coordinate is currently loaded.
|
||||||
|
*/
|
||||||
|
[Inline]
|
||||||
|
public bool IsChunkLoaded(Int32_3 chunkCoordinate)
|
||||||
|
{
|
||||||
|
using(chunkListLock.Enter())
|
||||||
|
{
|
||||||
|
return _chunks.ContainsKey(chunkCoordinate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the chunk with the given chunk coordinate or null if the chunk isn't loaded.
|
||||||
|
*/
|
||||||
|
[Inline]
|
||||||
|
public Chunk GetChunk(Int32_3 chunkCoordinate)
|
||||||
|
{
|
||||||
|
using(chunkListLock.Enter())
|
||||||
|
{
|
||||||
|
return _chunks.TryGetValue(chunkCoordinate, let chunk) ? chunk : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads and returns the chunk with the given coordinate. If the chunk doesn't exist it will be generated.
|
||||||
|
* @param chunkCoordinate The coordinate of the chunk to load.
|
||||||
|
* @param noBlocking If set to true this method will not wait if the chunk cannot be loaded right now (because it is currently being loaded by another thread);
|
||||||
|
* otherwise the method will block until the chunk has been loaded.
|
||||||
|
* @returns The loaded chunk. If noBlocking is set to true and the chunk cannot be obtained right now the return value will be null.
|
||||||
|
*/
|
||||||
|
public Chunk LoadChunk(Int32_3 chunkCoordinate, bool noBlocking = false)
|
||||||
|
{
|
||||||
|
Chunk chunk = GetChunk(chunkCoordinate);
|
||||||
|
|
||||||
|
if(chunk == null)
|
||||||
|
{
|
||||||
|
// Add chunk coordinate to generating list
|
||||||
|
// If it could be added, we have to generate it ourselves, otherwise we have to wait for the other thread to finish.
|
||||||
|
bool alreadyInList = false;
|
||||||
|
using(_generatingChunksLock.Enter())
|
||||||
|
{
|
||||||
|
alreadyInList = !_generatingChunks.Add(chunkCoordinate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!alreadyInList)
|
||||||
|
{
|
||||||
|
chunk = new Chunk();
|
||||||
|
chunk.ChunkManager = this;
|
||||||
|
chunk.Coordinate = chunkCoordinate;
|
||||||
|
chunk.Position = chunkCoordinate * .(VoxelChunk.SizeX, VoxelChunk.SizeY, VoxelChunk.SizeZ);
|
||||||
|
chunk.Transform = .Translation(chunk.Position.X, chunk.Position.Y, chunk.Position.Z);
|
||||||
|
|
||||||
|
if(LoadChunkFromFile(chunkCoordinate, chunk) case .Err)
|
||||||
|
{
|
||||||
|
GenTestChunk(chunk);
|
||||||
|
SaveChunkToFile(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.IsGeometryDirty = true;
|
||||||
|
|
||||||
|
using(chunkListLock.Enter())
|
||||||
|
{
|
||||||
|
if(!_chunks.TryAdd(chunkCoordinate, chunk))
|
||||||
|
{
|
||||||
|
chunk = LoadChunk(chunkCoordinate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using(_generatingChunksLock.Enter())
|
||||||
|
{
|
||||||
|
_generatingChunks.Remove(chunkCoordinate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raise chunk loaded event (this will wake up all calls of LoadChunk waiting for our chunk)
|
||||||
|
OnChunkLoaded(chunkCoordinate);
|
||||||
|
}
|
||||||
|
else if(!noBlocking)
|
||||||
|
{
|
||||||
|
Semaphore semaphore = new Semaphore(0);
|
||||||
|
defer delete semaphore;
|
||||||
|
|
||||||
|
// event handler will increase semaphore as soon as our requested chunk is loaded.
|
||||||
|
ChunkLoadedHandler eventHandler = scope (coordinate) =>
|
||||||
|
{
|
||||||
|
if(coordinate == chunkCoordinate)
|
||||||
|
{
|
||||||
|
semaphore.Unlock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// register event listener
|
||||||
|
using(_chunkLoadedLock.Enter())
|
||||||
|
{
|
||||||
|
_chunkLoaded.Add(eventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.ClientLogger.Trace($"Sleeping until chunk ({chunkCoordinate}) has been generated...");
|
||||||
|
|
||||||
|
// wait for semaphore to be released (in the event handler)
|
||||||
|
semaphore.Lock();
|
||||||
|
|
||||||
|
// unregister event handler
|
||||||
|
using(_chunkLoadedLock.Enter())
|
||||||
|
{
|
||||||
|
_chunkLoaded.Remove(eventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// retry loading the chunk
|
||||||
|
chunk = LoadChunk(chunkCoordinate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnChunkLoaded(Int32_3 chunkCoordinate)
|
||||||
|
{
|
||||||
|
// Raise chunk loaded event (this will wake up all calls of LoadChunk waiting for our chunk)
|
||||||
|
using(_chunkLoadedLock.Enter())
|
||||||
|
{
|
||||||
|
_chunkLoaded.Invoke(chunkCoordinate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left
|
||||||
|
{
|
||||||
|
var chunkCoordinate;
|
||||||
|
|
||||||
|
chunkCoordinate.X--;
|
||||||
|
Chunk neighbor = GetChunk(chunkCoordinate);
|
||||||
|
|
||||||
|
if(neighbor != null && neighbor._reqiredNeighbors.HasFlag(.Right))
|
||||||
|
neighbor.IsGeometryDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right
|
||||||
|
{
|
||||||
|
var chunkCoordinate;
|
||||||
|
|
||||||
|
chunkCoordinate.X++;
|
||||||
|
Chunk neighbor = GetChunk(chunkCoordinate);
|
||||||
|
|
||||||
|
if(neighbor != null && neighbor._reqiredNeighbors.HasFlag(.Left))
|
||||||
|
neighbor.IsGeometryDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back
|
||||||
|
{
|
||||||
|
var chunkCoordinate;
|
||||||
|
|
||||||
|
chunkCoordinate.Z--;
|
||||||
|
Chunk neighbor = GetChunk(chunkCoordinate);
|
||||||
|
|
||||||
|
if(neighbor != null && neighbor._reqiredNeighbors.HasFlag(.Front))
|
||||||
|
neighbor.IsGeometryDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Front
|
||||||
|
{
|
||||||
|
var chunkCoordinate;
|
||||||
|
|
||||||
|
chunkCoordinate.Z++;
|
||||||
|
Chunk neighbor = GetChunk(chunkCoordinate);
|
||||||
|
|
||||||
|
if(neighbor != null && neighbor._reqiredNeighbors.HasFlag(.Back))
|
||||||
|
neighbor.IsGeometryDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetChunkPos(Int32_3 chunkPos)
|
||||||
|
{
|
||||||
|
chunkPosLock.Enter();
|
||||||
|
|
||||||
|
chunkPosition = chunkPos;
|
||||||
|
|
||||||
|
if(oldChunkPosition != chunkPosition)
|
||||||
|
chunkPosChanged.Exit();
|
||||||
|
|
||||||
|
chunkPosLock.Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(Vector3 cameraPosition)
|
||||||
|
{
|
||||||
|
Vector3 chunkPosition = cameraPosition / .(VoxelChunk.SizeX, VoxelChunk.SizeY, VoxelChunk.SizeZ);
|
||||||
|
|
||||||
|
Int32_3 p = (Int32_3)chunkPosition;
|
||||||
|
p.Y = 0;
|
||||||
|
|
||||||
|
SetChunkPos(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entrypoint for the chunk loader thread.
|
||||||
|
*/
|
||||||
|
void ChunkLoaderThread_Entry()
|
||||||
|
{
|
||||||
|
Int32_3 curChunkPosition = .(0, 0, 0);
|
||||||
|
Int32_3 oldChunkPosition = .(Int.MaxValue, Int.MaxValue, Int.MaxValue);
|
||||||
|
|
||||||
|
while(!stopChunkLoader)
|
||||||
|
{
|
||||||
|
// Wait for chunkPos to change
|
||||||
|
chunkPosChanged.Enter();
|
||||||
|
|
||||||
|
using(chunkPosLock.Enter())
|
||||||
|
{
|
||||||
|
curChunkPosition = chunkPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position didn't change -> skip
|
||||||
|
//if(curChunkPosition == oldChunkPosition)
|
||||||
|
// continue;
|
||||||
|
|
||||||
|
ChunkLoaderThread_GenerateChunks(curChunkPosition);
|
||||||
|
|
||||||
|
oldChunkPosition = curChunkPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnloadChunk(Chunk chunk)
|
||||||
|
{
|
||||||
|
if(chunk.IsDirty)
|
||||||
|
{
|
||||||
|
SaveChunkToFile(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
using(chunkListLock.Enter())
|
||||||
|
{
|
||||||
|
_chunks.Remove(chunk.Coordinate);
|
||||||
|
delete chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ChunkLoaderThread_GenerateChunks(Int32_3 chunkPos)
|
||||||
|
{
|
||||||
|
for(var chunk in _chunks)
|
||||||
|
{
|
||||||
|
Int32_3 dist = (chunk.key - chunkPos).Abs();
|
||||||
|
|
||||||
|
if(dist.X > _viewDistance || dist.Z > _viewDistance)
|
||||||
|
{
|
||||||
|
UnloadChunk(chunk.value);
|
||||||
|
}
|
||||||
|
else if(chunk.value.IsGeometryDirty)
|
||||||
|
{
|
||||||
|
GenChunkGeo(chunk.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Int32_3 chunkCoordinate = (Int32_3)chunkPos;
|
||||||
|
int x = 0, z = 0;
|
||||||
|
for(int r = 1; r < _viewDistance; r++)
|
||||||
|
{
|
||||||
|
for(; x < r; x++, chunkCoordinate.X++)
|
||||||
|
{
|
||||||
|
LoadChunk(chunkCoordinate, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(; z < r; z++, chunkCoordinate.Z++)
|
||||||
|
{
|
||||||
|
LoadChunk(chunkCoordinate, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(; x > -r; x--, chunkCoordinate.X--)
|
||||||
|
{
|
||||||
|
LoadChunk(chunkCoordinate, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(; z > -r; z--, chunkCoordinate.Z--)
|
||||||
|
{
|
||||||
|
LoadChunk(chunkCoordinate, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> LoadChunkFromFile(Int32_3 chunkCoordinate, Chunk outChunk)
|
||||||
|
{
|
||||||
|
String chunkFileName = scope String(_chunkBasePath);
|
||||||
|
chunkFileName.AppendF($"{chunkCoordinate.X}_{chunkCoordinate.Y}_{chunkCoordinate.Z}.cdata");
|
||||||
|
|
||||||
|
if(!File.Exists(chunkFileName))
|
||||||
|
return .Err;
|
||||||
|
|
||||||
|
uint16[VoxelChunk.SizeX][VoxelChunk.SizeY][VoxelChunk.SizeZ] rawData;
|
||||||
|
|
||||||
|
FileStream fs = scope FileStream();
|
||||||
|
|
||||||
|
fs.Open(chunkFileName, .Read, .Read);
|
||||||
|
|
||||||
|
rawData = fs.Read<decltype(rawData)>();
|
||||||
|
|
||||||
|
fs.Close();
|
||||||
|
|
||||||
|
for(int x = 0; x < VoxelChunk.SizeX; x++)
|
||||||
|
for(int y = 0; y < VoxelChunk.SizeY; y++)
|
||||||
|
for(int z = 0; z < VoxelChunk.SizeZ; z++)
|
||||||
|
{
|
||||||
|
outChunk.Data.Data[x][y][z] = Blocks.GetFromId(rawData[x][y][z]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return .Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> SaveChunkToFile(Chunk chunk)
|
||||||
|
{
|
||||||
|
String chunkFileName = scope String(_chunkBasePath);
|
||||||
|
chunkFileName.AppendF($"{chunk.Coordinate.X}_{chunk.Coordinate.Y}_{chunk.Coordinate.Z}.cdata");
|
||||||
|
|
||||||
|
FileStream fs = scope FileStream();
|
||||||
|
|
||||||
|
fs.Open(chunkFileName, FileMode.Create, .Write);
|
||||||
|
|
||||||
|
chunk.Serialize(fs);
|
||||||
|
|
||||||
|
fs.Close();
|
||||||
|
|
||||||
|
return .Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GeneratorSettings
|
||||||
|
{
|
||||||
|
public int32 ElevationOctaves = 5;
|
||||||
|
public float ElevationFrequency = 0.0075f;
|
||||||
|
public float ElevationLacunarity = 2f;
|
||||||
|
public float ElevationGain = 0.5f;
|
||||||
|
|
||||||
|
public Vector2 DetailAmplitude = .(32, 32);
|
||||||
|
|
||||||
|
public int32 TerrainFloorHeight = 128;
|
||||||
|
// The amplitude of the hills (eg. how high hills are and how deep valleys are)
|
||||||
|
public int32 TerrainAmplitude = 48;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateTerrain(Chunk chunk)
|
||||||
|
{
|
||||||
|
Stopwatch sw = .StartNew();
|
||||||
|
|
||||||
|
GeneratorSettings settings = .();
|
||||||
|
//settings.ElevationFrequency
|
||||||
|
|
||||||
|
let groundNoise = scope FastNoiseLite.FastNoiseLite();
|
||||||
|
groundNoise.SetFractalType(.FBm);
|
||||||
|
groundNoise.SetFractalOctaves(settings.ElevationOctaves);
|
||||||
|
groundNoise.SetFractalLacunarity(settings.ElevationLacunarity);
|
||||||
|
groundNoise.SetFractalGain(settings.ElevationGain);
|
||||||
|
groundNoise.SetFrequency(settings.ElevationFrequency);
|
||||||
|
|
||||||
|
for(int x < VoxelChunk.SizeX)
|
||||||
|
for(int y < VoxelChunk.SizeY)
|
||||||
|
for(int z < VoxelChunk.SizeZ)
|
||||||
|
{
|
||||||
|
float cx = x + chunk.Position.X;
|
||||||
|
float cy = y + chunk.Position.Y;
|
||||||
|
float cz = z + chunk.Position.Z;
|
||||||
|
|
||||||
|
cx += groundNoise.GetNoise(cz * 0.5f, cy) * settings.DetailAmplitude.X;
|
||||||
|
cz += groundNoise.GetNoise(cy, cx * 0.5f) * settings.DetailAmplitude.Y;
|
||||||
|
|
||||||
|
cy += groundNoise.GetNoise(cx, cz) * settings.TerrainAmplitude;
|
||||||
|
|
||||||
|
float gradientValue = cy / (float)(settings.TerrainFloorHeight * 2 - 1);
|
||||||
|
|
||||||
|
// determine whether or not gradient value is air
|
||||||
|
Block stepValue = gradientValue < 0.5f ? Blocks.Stone : Blocks.Air;
|
||||||
|
|
||||||
|
// Note these objects will be invalid
|
||||||
|
chunk.Data.Data[x][y][z] = stepValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
|
||||||
|
Debug.WriteLine($"Terrain Generation: {sw.ElapsedMilliseconds}ms");
|
||||||
|
|
||||||
|
delete sw;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GroundLayer
|
||||||
|
{
|
||||||
|
public int Depth;
|
||||||
|
public Block BlockType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GroundLayers(Chunk chunk)
|
||||||
|
{
|
||||||
|
GroundLayer[3] layers;
|
||||||
|
layers[0] = .()
|
||||||
|
{
|
||||||
|
Depth = 1,
|
||||||
|
BlockType = Blocks.Grass
|
||||||
|
};
|
||||||
|
layers[1] = .()
|
||||||
|
{
|
||||||
|
Depth = 4,
|
||||||
|
BlockType = Blocks.Dirt
|
||||||
|
};
|
||||||
|
layers[2] = .()
|
||||||
|
{
|
||||||
|
Depth = 0,
|
||||||
|
BlockType = Blocks.Stone
|
||||||
|
};
|
||||||
|
|
||||||
|
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(chunk.Data.Data[x][y][z] == Blocks.Air)
|
||||||
|
{
|
||||||
|
currentDepth--;
|
||||||
|
|
||||||
|
if(currentDepth < 0)
|
||||||
|
{
|
||||||
|
currentDepth = 0;
|
||||||
|
|
||||||
|
currentLayer--;
|
||||||
|
|
||||||
|
if(currentLayer < 0)
|
||||||
|
currentLayer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chunk.Data.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(Chunk chunk)
|
||||||
|
{
|
||||||
|
GenerateTerrain(chunk);
|
||||||
|
GroundLayers(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenChunkGeo(Chunk chunk)
|
||||||
|
{
|
||||||
|
var oldGeometry = chunk.Geometry;
|
||||||
|
|
||||||
|
var newGeometry = voxelGeoGen.GenerateGeometry(chunk);
|
||||||
|
|
||||||
|
Interlocked.Store(ref chunk.Geometry, newGeometry);
|
||||||
|
chunk.IsGeometryDirty = false;
|
||||||
|
|
||||||
|
oldGeometry?.ReleaseRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
chunkListLock.Enter();
|
||||||
|
for(let pair in _chunks)
|
||||||
|
{
|
||||||
|
Chunk chunk = pair.value;
|
||||||
|
|
||||||
|
GeometryBinding chunkGeo = Interlocked.Load(ref chunk.Geometry);
|
||||||
|
|
||||||
|
if(chunkGeo == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
chunkGeo.AddRef();
|
||||||
|
|
||||||
|
BlockTextures.Atlas.Bind();
|
||||||
|
|
||||||
|
Renderer.Submit(chunk.Geometry, TextureEffect, chunk.Transform);
|
||||||
|
chunkGeo.ReleaseRef();
|
||||||
|
}
|
||||||
|
chunkListLock.Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnImGuiRender()
|
||||||
|
{
|
||||||
|
ImGui.Begin("Voxel Manager");
|
||||||
|
|
||||||
|
int32 oldVd = (.)_viewDistance;
|
||||||
|
|
||||||
|
ImGui.DragInt("View distance", (.)&_viewDistance, 1.0f, 1, 1000);
|
||||||
|
|
||||||
|
if(oldVd != _viewDistance)
|
||||||
|
chunkPosChanged.Exit();
|
||||||
|
|
||||||
|
ImGui.End();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace Sandbox.VoxelFun
|
||||||
|
{
|
||||||
|
static class Models
|
||||||
|
{
|
||||||
|
static List<Model> _models = new List<Model>() ~ DeleteContainerAndItems!(_);
|
||||||
|
|
||||||
|
public static Model Stone;
|
||||||
|
public static Model Grass;
|
||||||
|
public static Model Dirt;
|
||||||
|
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
Stone = RegisterModel(Blocks.Stone, .. new BlockModel(.White, BlockTextures.Stone));
|
||||||
|
Grass = RegisterModel(Blocks.Grass, .. new BlockModel(.White, BlockTextures.GrassTop, BlockTextures.GrassSide, BlockTextures.Dirt));
|
||||||
|
Dirt = RegisterModel(Blocks.Dirt, .. new BlockModel(.White, BlockTextures.Dirt));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterModel(Block block, Model model)
|
||||||
|
{
|
||||||
|
_models.Add(model);
|
||||||
|
block.[Friend]_model = model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using GlitchyEngine;
|
||||||
|
using GlitchyEngine.Math;
|
||||||
|
using GlitchyEngine.Renderer;
|
||||||
|
|
||||||
|
namespace Sandbox.VoxelFun
|
||||||
|
{
|
||||||
|
class TextureAtlas : Texture2D
|
||||||
|
{
|
||||||
|
typealias TexEntry = (Color* data, uint32 width, uint32 height, BlockTexture blockTex);
|
||||||
|
|
||||||
|
public this(GraphicsContext context, List<BlockTexture> textures) : base(context)
|
||||||
|
{
|
||||||
|
// Todo: rewrite this garbage algorithm
|
||||||
|
|
||||||
|
List<TexEntry> textureDatas = new .(textures.Count);
|
||||||
|
defer
|
||||||
|
{
|
||||||
|
for(let item in textureDatas)
|
||||||
|
{
|
||||||
|
LodePng.LodePng.Free(item.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete textureDatas;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 maxWidth = 0;
|
||||||
|
uint32 maxHeight = 0;
|
||||||
|
|
||||||
|
Color[4] nullColors = .(.HotPink, .Black, .Black, .HotPink);
|
||||||
|
BlockTexture nullBT = scope .("NULL");
|
||||||
|
TexEntry nullTexture = (&nullColors, 2, 2, nullBT);
|
||||||
|
bool nullTexWriten = false;
|
||||||
|
|
||||||
|
for(let texture in textures)
|
||||||
|
{
|
||||||
|
(Color* data, uint32 width, uint32 height, BlockTexture blockTex) tex;
|
||||||
|
tex.blockTex = texture;
|
||||||
|
|
||||||
|
let error = LodePng.LodePng.Decode32File(out tex.data, out tex.width, out tex.height, texture.FileName);
|
||||||
|
|
||||||
|
if(error != 0)
|
||||||
|
Log.ClientLogger.Error($"Failed to load texture \"{texture.FileName}\". ({error}) \"{StringView(LodePng.LodePng.ErrorText(error))}\"");
|
||||||
|
|
||||||
|
if(tex.width > maxWidth)
|
||||||
|
maxWidth = tex.width;
|
||||||
|
|
||||||
|
if(tex.height > maxHeight)
|
||||||
|
maxHeight = tex.height;
|
||||||
|
|
||||||
|
textureDatas.Add(tex);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 texturesX = (.)System.Math.Ceiling(System.Math.Sqrt(textureDatas.Count));
|
||||||
|
uint32 texturesY = (.)System.Math.Ceiling((float)textureDatas.Count / (float)texturesX);
|
||||||
|
|
||||||
|
uint32 textureWidth = texturesX * maxWidth;
|
||||||
|
uint32 textureHeight = texturesY * maxHeight;
|
||||||
|
|
||||||
|
Color[] atlasColors = new DirectX.Color[textureWidth * textureHeight];
|
||||||
|
defer delete atlasColors;
|
||||||
|
|
||||||
|
uint32 currentX = 0;
|
||||||
|
uint32 currentY = 0;
|
||||||
|
|
||||||
|
/// Copies the data of the given texture into the atlas
|
||||||
|
void CopyTextureIntoAtlas(TexEntry texture)
|
||||||
|
{
|
||||||
|
texture.blockTex.[Friend]_atlasStart = .((float)currentX / (float)textureWidth, (float)currentY / (float)textureHeight);
|
||||||
|
texture.blockTex.[Friend]_atlasSize = .((float)texture.width / (float)textureWidth, (float)texture.height / (float)textureHeight);
|
||||||
|
|
||||||
|
for(uint32 penY = currentY, uint32 y = 0; y < texture.height; penY++, y++)
|
||||||
|
for(uint32 penX = currentX, uint32 x = 0; x < texture.width; penX++, x++)
|
||||||
|
{
|
||||||
|
atlasColors[penX + penY * textureWidth] = texture.data[x + y * texture.width];
|
||||||
|
}
|
||||||
|
|
||||||
|
currentX += maxWidth;
|
||||||
|
if(currentX >= textureWidth)
|
||||||
|
{
|
||||||
|
currentX = 0;
|
||||||
|
currentY += maxHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let texture in textureDatas)
|
||||||
|
{
|
||||||
|
if(texture.data != null)
|
||||||
|
{
|
||||||
|
CopyTextureIntoAtlas(texture);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!nullTexWriten)
|
||||||
|
{
|
||||||
|
CopyTextureIntoAtlas(nullTexture);
|
||||||
|
nullTexWriten = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
texture.blockTex.[Friend]_atlasStart = nullTexture.blockTex.[Friend]_atlasStart;
|
||||||
|
texture.blockTex.[Friend]_atlasSize = nullTexture.blockTex.[Friend]_atlasSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture2DDesc desc;
|
||||||
|
desc.Width = textureWidth;
|
||||||
|
desc.Height = textureHeight;
|
||||||
|
desc.ArraySize = 1;
|
||||||
|
desc.MipLevels = 1;
|
||||||
|
desc.Format = .R8G8B8A8_UNorm;
|
||||||
|
|
||||||
|
CreateTexturePlatform(desc, atlasColors.CArray(), textureWidth * sizeof(Color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using GlitchyEngine.Math;
|
||||||
|
|
||||||
|
namespace Sandbox.VoxelFun
|
||||||
|
{
|
||||||
|
public struct VoxelChunk
|
||||||
|
{
|
||||||
|
public const int SizeX = 16;
|
||||||
|
public const int SizeY = 256;
|
||||||
|
public const int SizeZ = 16;
|
||||||
|
|
||||||
|
public const Int32_3 Size = .(SizeX, SizeY, SizeZ);
|
||||||
|
public const Vector3 VectorSize = .(SizeX, SizeY, SizeZ);
|
||||||
|
|
||||||
|
public Block[SizeX][SizeY][SizeZ] Data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using GlitchyEngine.Renderer;
|
||||||
|
using GlitchyEngine.Math;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using static Sandbox.VoxelFun.VoxelTestLayer;
|
||||||
|
namespace Sandbox.VoxelFun
|
||||||
|
{
|
||||||
|
enum BlockFace
|
||||||
|
{
|
||||||
|
case None = 0;
|
||||||
|
case Front = 1;
|
||||||
|
case Back = 2;
|
||||||
|
case Left = 4;
|
||||||
|
case Right = 8;
|
||||||
|
case Top = 16;
|
||||||
|
case Bottom = 32;
|
||||||
|
case All = Bottom | Top | Right | Left | Back | Front;
|
||||||
|
|
||||||
|
public BlockFace Opposite
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch(this)
|
||||||
|
{
|
||||||
|
case Front:
|
||||||
|
return Back;
|
||||||
|
case Back:
|
||||||
|
return Front;
|
||||||
|
case Left:
|
||||||
|
return Right;
|
||||||
|
case Right:
|
||||||
|
return Left;
|
||||||
|
case Top:
|
||||||
|
return Bottom;
|
||||||
|
case Bottom:
|
||||||
|
return Top;
|
||||||
|
default:
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VoxelGeometryGenerator
|
||||||
|
{
|
||||||
|
public GraphicsContext Context;
|
||||||
|
public VertexLayout Layout;
|
||||||
|
|
||||||
|
BlockFace GetVisibleFaces(VoxelChunk chunk, int x, int y, int z, Chunk[3][3] chunks)
|
||||||
|
{
|
||||||
|
BlockFace visibleFaces = .None;
|
||||||
|
|
||||||
|
// Back
|
||||||
|
int idx = z;
|
||||||
|
Chunk readChunk = chunks[1][1];
|
||||||
|
|
||||||
|
if(idx == 0)
|
||||||
|
{
|
||||||
|
readChunk = chunks[1][0];
|
||||||
|
idx = VoxelChunk.Size.Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(readChunk == null || readChunk.Data.Data[x][y][idx - 1].VisibleNeighbors.HasFlag(.Front))
|
||||||
|
visibleFaces |= .Back;
|
||||||
|
|
||||||
|
// Front
|
||||||
|
idx = z + 1;
|
||||||
|
readChunk = chunks[1][1];
|
||||||
|
|
||||||
|
if(idx == VoxelChunk.Size.Z)
|
||||||
|
{
|
||||||
|
readChunk = chunks[1][2];
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(readChunk == null || readChunk.Data.Data[x][y][idx].VisibleNeighbors.HasFlag(.Back))
|
||||||
|
visibleFaces |= .Front;
|
||||||
|
|
||||||
|
// Left
|
||||||
|
idx = x;
|
||||||
|
readChunk = chunks[1][1];
|
||||||
|
|
||||||
|
if(idx == 0)
|
||||||
|
{
|
||||||
|
readChunk = chunks[0][1];
|
||||||
|
idx = VoxelChunk.Size.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(readChunk == null || readChunk.Data.Data[idx - 1][y][z].VisibleNeighbors.HasFlag(.Right))
|
||||||
|
visibleFaces |= .Left;
|
||||||
|
|
||||||
|
// Right
|
||||||
|
idx = x + 1;
|
||||||
|
readChunk = chunks[1][1];
|
||||||
|
|
||||||
|
if(idx == VoxelChunk.Size.X)
|
||||||
|
{
|
||||||
|
readChunk = chunks[2][1];
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(readChunk == null || readChunk.Data.Data[idx][y][z].VisibleNeighbors.HasFlag(.Left))
|
||||||
|
visibleFaces |= .Right;
|
||||||
|
|
||||||
|
// TODO: implement for top and bottom as we have 3D chunks
|
||||||
|
if(y == 0 || chunk.Data[x][y - 1][z].VisibleNeighbors.HasFlag(.Top))
|
||||||
|
visibleFaces |= .Bottom;
|
||||||
|
if(y == VoxelChunk.SizeY - 1 || chunk.Data[x][y + 1][z].VisibleNeighbors.HasFlag(.Bottom))
|
||||||
|
visibleFaces |= .Top;
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(x == 0 || chunk.Data[x - 1][y][z].VisibleNeighbors.HasFlag(.Right))
|
||||||
|
visibleFaces |= .Left;
|
||||||
|
if(x == VoxelChunk.SizeX - 1 || chunk.Data[x + 1][y][z].VisibleNeighbors.HasFlag(.Left))
|
||||||
|
visibleFaces |= .Right;
|
||||||
|
|
||||||
|
if(y == 0 || chunk.Data[x][y - 1][z].VisibleNeighbors.HasFlag(.Top))
|
||||||
|
visibleFaces |= .Bottom;
|
||||||
|
if(y == VoxelChunk.SizeY - 1 || chunk.Data[x][y + 1][z].VisibleNeighbors.HasFlag(.Bottom))
|
||||||
|
visibleFaces |= .Top;
|
||||||
|
*/
|
||||||
|
return visibleFaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GeometryBinding GenerateGeometry(Chunk chunk)
|
||||||
|
{
|
||||||
|
List<VertexColorTexture> vertices = scope List<VertexColorTexture>();
|
||||||
|
List<uint32> indices = scope List<uint32>();
|
||||||
|
uint32 lastIndex = 0;
|
||||||
|
|
||||||
|
Stopwatch sw = .StartNew();
|
||||||
|
|
||||||
|
var chunkData = chunk.Data.Data;
|
||||||
|
|
||||||
|
Chunk[3][3] chunks;
|
||||||
|
|
||||||
|
var coord = chunk.Coordinate;
|
||||||
|
|
||||||
|
// left
|
||||||
|
coord.X -= 1;
|
||||||
|
chunks[0][1] = chunk.ChunkManager.GetChunk(coord);
|
||||||
|
if(chunks[0][1] == null)
|
||||||
|
{
|
||||||
|
// TODO: register reload when neighbor generated
|
||||||
|
chunk._reqiredNeighbors |= .Left;
|
||||||
|
}
|
||||||
|
|
||||||
|
// back
|
||||||
|
coord.X += 1;
|
||||||
|
coord.Z -= 1;
|
||||||
|
chunks[1][0] = chunk.ChunkManager.GetChunk(coord);
|
||||||
|
if(chunks[1][0] == null)
|
||||||
|
{
|
||||||
|
// TODO: register reload when neighbor generated
|
||||||
|
chunk._reqiredNeighbors |= .Back;
|
||||||
|
}
|
||||||
|
|
||||||
|
// center
|
||||||
|
chunks[1][1] = chunk;
|
||||||
|
|
||||||
|
// front
|
||||||
|
coord.Z += 2;
|
||||||
|
chunks[1][2] = chunk.ChunkManager.GetChunk(coord);
|
||||||
|
if(chunks[1][2] == null)
|
||||||
|
{
|
||||||
|
// TODO: register reload when neighbor generated
|
||||||
|
chunk._reqiredNeighbors |= .Front;
|
||||||
|
}
|
||||||
|
|
||||||
|
// right
|
||||||
|
coord.X += 1;
|
||||||
|
coord.Z -= 1;
|
||||||
|
chunks[2][1] = chunk.ChunkManager.GetChunk(coord);
|
||||||
|
if(chunks[2][1] == null)
|
||||||
|
{
|
||||||
|
// TODO: register reload when neighbor generated
|
||||||
|
chunk._reqiredNeighbors |= .Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int x < VoxelChunk.SizeX)
|
||||||
|
for(int y < VoxelChunk.SizeY)
|
||||||
|
for(int z < VoxelChunk.SizeZ)
|
||||||
|
{
|
||||||
|
Block block = chunkData[x][y][z];
|
||||||
|
|
||||||
|
Vector3 blockPos = .(x, y, z);
|
||||||
|
|
||||||
|
if(block != Blocks.Air)
|
||||||
|
{
|
||||||
|
BlockFace visibleFaces = GetVisibleFaces(chunk.Data, x, y, z, chunks);
|
||||||
|
|
||||||
|
if(visibleFaces != .None)
|
||||||
|
block.Model.GenerateGeometry(block, blockPos, visibleFaces, vertices, indices, ref lastIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GeometryBinding gb;
|
||||||
|
|
||||||
|
if(indices.Count > 0)
|
||||||
|
{
|
||||||
|
VertexBuffer vb = new VertexBuffer(Context, typeof(VertexColorTexture), (.)vertices.Count, .Default, .None);
|
||||||
|
vb.SetData<VertexColorTexture>(vertices);
|
||||||
|
|
||||||
|
IndexBuffer ib = new IndexBuffer(Context, (.)indices.Count, .Default, .None, .Index32Bit);
|
||||||
|
ib.SetData<uint32>(indices);
|
||||||
|
|
||||||
|
gb = new GeometryBinding(Context);
|
||||||
|
gb.SetVertexBufferSlot(vb, 0);
|
||||||
|
gb.SetIndexBuffer(ib);
|
||||||
|
gb.SetPrimitiveTopology(.TriangleList);
|
||||||
|
gb.SetVertexLayout(Layout);
|
||||||
|
|
||||||
|
vb.ReleaseRef();
|
||||||
|
ib.ReleaseRef();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gb = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
|
||||||
|
Debug.WriteLine($"Geometry Generation: {sw.ElapsedMilliseconds}");
|
||||||
|
|
||||||
|
delete sw;
|
||||||
|
|
||||||
|
return gb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,499 @@
|
|||||||
|
using System;
|
||||||
|
using GlitchyEngine;
|
||||||
|
using GlitchyEngine.Math;
|
||||||
|
using GlitchyEngine.Renderer;
|
||||||
|
using ImGui;
|
||||||
|
using GlitchyEngine.ImGui;
|
||||||
|
using GlitchyEngine.Events;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace Sandbox.VoxelFun
|
||||||
|
{
|
||||||
|
public class VoxelTestLayer : Layer
|
||||||
|
{
|
||||||
|
private PerspectiveCamera _camera ~ delete _;
|
||||||
|
|
||||||
|
[Ordered]
|
||||||
|
public struct VertexColorTexture : IVertexData
|
||||||
|
{
|
||||||
|
public Vector3 Position;
|
||||||
|
public Color Color;
|
||||||
|
public Vector2 TexCoord;
|
||||||
|
|
||||||
|
public this() => this = default;
|
||||||
|
|
||||||
|
public this(Vector3 pos, Color color)
|
||||||
|
{
|
||||||
|
Position = pos;
|
||||||
|
Color = color;
|
||||||
|
TexCoord = .();
|
||||||
|
}
|
||||||
|
|
||||||
|
public this(Vector3 pos, Color color, Vector2 texCoord)
|
||||||
|
{
|
||||||
|
Position = pos;
|
||||||
|
Color = color;
|
||||||
|
TexCoord = texCoord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly VertexElement[] VertexElements ~ delete _;
|
||||||
|
|
||||||
|
public static VertexElement[] IVertexData.VertexElements => VertexElements;
|
||||||
|
|
||||||
|
static this()
|
||||||
|
{
|
||||||
|
VertexElements = new VertexElement[](
|
||||||
|
VertexElement(.R32G32B32_Float, "POSITION"),
|
||||||
|
VertexElement(.R8G8B8A8_UNorm, "COLOR"),
|
||||||
|
VertexElement(.R32G32_Float, "TEXCOORD"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VertexLayout _vertexLayout ~ delete _;
|
||||||
|
|
||||||
|
GeometryBinding _cubeGeo ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
GeometryBinding _geometryBinding ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
GeometryBinding _quadGeometryBinding ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
GeometryBinding _lineGeometryBinding ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
RasterizerState _rasterizerState ~ _?.ReleaseRef();
|
||||||
|
EffectLibrary _effectLibrary ~ delete _;
|
||||||
|
|
||||||
|
Effect _effect ~ _?.ReleaseRef();
|
||||||
|
Effect _textureEffect ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
GraphicsContext _context ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
Texture2D _texture ~ _?.ReleaseRef();
|
||||||
|
Texture2D _ge_logo ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
BlendState _alphaBlendState ~ _?.ReleaseRef();
|
||||||
|
BlendState _opaqueBlendState ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
DepthStencilTarget _depthStencilTarget ~ _?.ReleaseRef();
|
||||||
|
|
||||||
|
World _world ~ delete _;
|
||||||
|
|
||||||
|
private Vector3 CircleCoord(float angle)
|
||||||
|
{
|
||||||
|
return .(Math.Cos(angle), Math.Sin(angle), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAppend]
|
||||||
|
public this() : base("VoxelTest")
|
||||||
|
{
|
||||||
|
Blocks.Init();
|
||||||
|
|
||||||
|
_context = Application.Get().Window.Context..AddRef();
|
||||||
|
|
||||||
|
BlockTextures.Init(_context);
|
||||||
|
Models.Init();
|
||||||
|
|
||||||
|
_effectLibrary = new EffectLibrary(_context);
|
||||||
|
|
||||||
|
_depthStencilTarget = new DepthStencilTarget(_context, _context.SwapChain.Width, _context.SwapChain.Height);
|
||||||
|
|
||||||
|
_effect = _effectLibrary.Load("content\\Shaders\\basicShader.hlsl");
|
||||||
|
|
||||||
|
_textureEffect = _effectLibrary.Load("content\\Shaders\\textureShader.hlsl");
|
||||||
|
|
||||||
|
// Create Input Layout
|
||||||
|
|
||||||
|
_vertexLayout = new VertexLayout(_context, VertexColorTexture.VertexElements, _textureEffect.VertexShader);
|
||||||
|
// Create hexagon
|
||||||
|
{
|
||||||
|
_geometryBinding = new GeometryBinding(_context);
|
||||||
|
_geometryBinding.SetPrimitiveTopology(.TriangleList);
|
||||||
|
_geometryBinding.SetVertexLayout(_vertexLayout);
|
||||||
|
|
||||||
|
float pO3 = Math.PI_f / 3.0f;
|
||||||
|
VertexColorTexture[?] vertices = .(
|
||||||
|
VertexColorTexture(.Zero, Color(255,255,255)),
|
||||||
|
VertexColorTexture(CircleCoord(0), Color(255, 0, 0)),
|
||||||
|
VertexColorTexture(CircleCoord(pO3), Color(255,255, 0)),
|
||||||
|
VertexColorTexture(CircleCoord(pO3*2), Color( 0,255, 0)),
|
||||||
|
VertexColorTexture(CircleCoord(Math.PI_f), Color( 0,255,255)),
|
||||||
|
VertexColorTexture(CircleCoord(-pO3*2), Color( 0, 0,255)),
|
||||||
|
VertexColorTexture(CircleCoord(-pO3), Color(255, 0,255)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let vb = new VertexBuffer(_context, typeof(VertexColorTexture), (.)vertices.Count, .Immutable);
|
||||||
|
vb.SetData(vertices);
|
||||||
|
_geometryBinding.SetVertexBufferSlot(vb, 0);
|
||||||
|
vb.ReleaseRef();
|
||||||
|
|
||||||
|
uint16[?] indices = .(
|
||||||
|
0, 1, 2,
|
||||||
|
0, 2, 3,
|
||||||
|
0, 3, 4,
|
||||||
|
0, 4, 5,
|
||||||
|
0, 5, 6,
|
||||||
|
0, 6, 1);
|
||||||
|
|
||||||
|
let ib = new IndexBuffer(_context, (.)indices.Count, .Immutable);
|
||||||
|
ib.SetData(indices);
|
||||||
|
_geometryBinding.SetIndexBuffer(ib);
|
||||||
|
ib.ReleaseRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Quad
|
||||||
|
{
|
||||||
|
_quadGeometryBinding = new GeometryBinding(_context);
|
||||||
|
_quadGeometryBinding.SetPrimitiveTopology(.TriangleList);
|
||||||
|
_quadGeometryBinding.SetVertexLayout(_vertexLayout);
|
||||||
|
|
||||||
|
VertexColorTexture[?] vertices = .(
|
||||||
|
VertexColorTexture(Vector3(-0.75f, 0.75f, 0), Color.White, .(0, 0)),
|
||||||
|
VertexColorTexture(Vector3(-0.75f, -0.75f, 0), Color.White, .(0, 1)),
|
||||||
|
VertexColorTexture(Vector3(0.75f, -0.75f, 0), Color.White, .(1, 1)),
|
||||||
|
VertexColorTexture(Vector3(0.75f, 0.75f, 0), Color.White, .(1, 0)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let qvb = new VertexBuffer(_context, typeof(VertexColorTexture), (.)vertices.Count, .Immutable);
|
||||||
|
qvb.SetData(vertices);
|
||||||
|
_quadGeometryBinding.SetVertexBufferSlot(qvb, 0);
|
||||||
|
qvb.ReleaseRef();
|
||||||
|
|
||||||
|
uint16[?] indices = .(
|
||||||
|
0, 1, 2,
|
||||||
|
2, 3, 0);
|
||||||
|
|
||||||
|
let qib = new IndexBuffer(_context, (.)indices.Count, .Immutable);
|
||||||
|
qib.SetData(indices);
|
||||||
|
_quadGeometryBinding.SetIndexBuffer(qib);
|
||||||
|
qib.ReleaseRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Cube
|
||||||
|
{
|
||||||
|
_cubeGeo = new GeometryBinding(_context);
|
||||||
|
_cubeGeo.SetPrimitiveTopology(.TriangleList);
|
||||||
|
_cubeGeo.SetVertexLayout(_vertexLayout);
|
||||||
|
|
||||||
|
List<VertexColorTexture> vertices = new List<VertexColorTexture>();
|
||||||
|
defer delete vertices;
|
||||||
|
List<uint32> indices = new List<uint32>();
|
||||||
|
defer delete indices;
|
||||||
|
|
||||||
|
uint32 i = 0;
|
||||||
|
|
||||||
|
(scope BlockModel(.HotPink)).GenerateGeometry(Blocks.Air, .Zero, .All, vertices, indices, ref i);
|
||||||
|
|
||||||
|
let qvb = new VertexBuffer(_context, typeof(VertexColorTexture), (.)vertices.Count, .Immutable);
|
||||||
|
qvb.SetData<VertexColorTexture>(vertices);
|
||||||
|
_cubeGeo.SetVertexBufferSlot(qvb, 0);
|
||||||
|
qvb.ReleaseRef();
|
||||||
|
|
||||||
|
let qib = new IndexBuffer(_context, (.)indices.Count, .Immutable, .None, .Index32Bit);
|
||||||
|
qib.SetData<uint32>(indices);
|
||||||
|
_cubeGeo.SetIndexBuffer(qib);
|
||||||
|
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;
|
||||||
|
_rasterizerState = new RasterizerState(_context, rsDesc);
|
||||||
|
|
||||||
|
// Camera
|
||||||
|
_camera = new PerspectiveCamera();
|
||||||
|
_camera.NearPlane = 0.1f;
|
||||||
|
_camera.FarPlane = 1000.0f;
|
||||||
|
_camera.FovY = Math.PI_f / 4;
|
||||||
|
_camera.Position = .(0, 128, 0);//-320
|
||||||
|
|
||||||
|
_texture = new Texture2D(_context, "content/Textures/Checkerboard.dds");
|
||||||
|
_ge_logo = new Texture2D(_context, "content/Textures/GE_Logo.dds");
|
||||||
|
|
||||||
|
let sampler = SamplerStateManager.GetSampler(
|
||||||
|
SamplerStateDescription()
|
||||||
|
{
|
||||||
|
MagFilter = .Point
|
||||||
|
});
|
||||||
|
|
||||||
|
_texture.SamplerState = sampler;
|
||||||
|
_ge_logo.SamplerState = sampler;
|
||||||
|
|
||||||
|
sampler.ReleaseRef();
|
||||||
|
|
||||||
|
BlendStateDescription blendDesc = .();
|
||||||
|
blendDesc.RenderTarget[0] = .(true, .SourceAlpha, .InvertedSourceAlpha, .Add, .SourceAlpha, .InvertedSourceAlpha, .Add, .All);
|
||||||
|
_alphaBlendState = new BlendState(_context, blendDesc);
|
||||||
|
_opaqueBlendState = new BlendState(_context, .Default);
|
||||||
|
|
||||||
|
_world = new World();
|
||||||
|
if(World.CreateWorld("test", 1337, _world) case .Err(.WorldAlreadyExists))
|
||||||
|
{
|
||||||
|
World.LoadWorld("test", _world);
|
||||||
|
}
|
||||||
|
|
||||||
|
_world.ChunkManager = new ChunkManager(_context, _vertexLayout, _world);
|
||||||
|
_world.ChunkManager.TextureEffect = _textureEffect..AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateCamera(GameTime gameTime)
|
||||||
|
{
|
||||||
|
UpdateCameraRotation(gameTime);
|
||||||
|
UpdateCameraMovement(gameTime);
|
||||||
|
|
||||||
|
//_camera.Width = _context.SwapChain.BackbufferViewport.Width / 256;
|
||||||
|
//_camera.Height = _context.SwapChain.BackbufferViewport.Height / 256;
|
||||||
|
|
||||||
|
_camera.AspectRatio = Application.Get().Window.Context.SwapChain.BackbufferViewport.Width /
|
||||||
|
Application.Get().Window.Context.SwapChain.BackbufferViewport.Height;
|
||||||
|
|
||||||
|
_camera.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
double cameraRotationSpeedX = 0.0001f;
|
||||||
|
double cameraRotationSpeedY = 0.0001f;
|
||||||
|
|
||||||
|
bool b = true;
|
||||||
|
|
||||||
|
void UpdateCameraRotation(GameTime gameTime)
|
||||||
|
{
|
||||||
|
if(b)
|
||||||
|
{
|
||||||
|
b = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mouseMovement = Input.GetRawMouseMovement();
|
||||||
|
|
||||||
|
if(mouseMovement.X == 0 && mouseMovement.Y == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Vector3 rotation = _camera.Rotation;
|
||||||
|
|
||||||
|
rotation.Y = (float)(rotation.Y + mouseMovement.X * cameraRotationSpeedX * gameTime.FrameTime.TotalMilliseconds);
|
||||||
|
rotation.X = (float)(rotation.X + mouseMovement.Y * cameraRotationSpeedY * gameTime.FrameTime.TotalMilliseconds);
|
||||||
|
|
||||||
|
rotation.X = Math.Clamp(rotation.X, -Math.PI_f / 2, Math.PI_f / 2);
|
||||||
|
|
||||||
|
_camera.Rotation = rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
float movementSpeed = 2;
|
||||||
|
float movementSpeedFast = 20;
|
||||||
|
|
||||||
|
void UpdateCameraMovement(GameTime gameTime)
|
||||||
|
{
|
||||||
|
Vector3 movement = .();
|
||||||
|
|
||||||
|
if(Input.IsKeyPressed(Key.W))
|
||||||
|
{
|
||||||
|
movement.Z += 1;
|
||||||
|
}
|
||||||
|
if(Input.IsKeyPressed(Key.S))
|
||||||
|
{
|
||||||
|
movement.Z -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Input.IsKeyPressed(Key.A))
|
||||||
|
{
|
||||||
|
movement.X -= 1;
|
||||||
|
}
|
||||||
|
if(Input.IsKeyPressed(Key.D))
|
||||||
|
{
|
||||||
|
movement.X += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Input.IsKeyPressed(Key.Space))
|
||||||
|
{
|
||||||
|
movement.Y += 1;
|
||||||
|
}
|
||||||
|
if(Input.IsKeyPressed(Key.Control))
|
||||||
|
{
|
||||||
|
movement.Y -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(movement != .Zero)
|
||||||
|
movement.Normalize();
|
||||||
|
|
||||||
|
movement *= (float)(gameTime.FrameTime.TotalSeconds);
|
||||||
|
|
||||||
|
Matrix rot = .RotationY(_camera.Rotation.Y) * .RotationX(_camera.Rotation.X);
|
||||||
|
|
||||||
|
let bla = ((Vector4)(rot * Vector4(movement, 1.0f))); //TODO .XYZ
|
||||||
|
movement = .(bla.X, bla.Y, bla.Z);
|
||||||
|
|
||||||
|
float speed = Input.IsKeyPressed(Key.Shift) ? movementSpeedFast : movementSpeed;
|
||||||
|
|
||||||
|
_camera.Position += movement * speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ray ray = .(.(0, 128, 0), .UnitZ);
|
||||||
|
|
||||||
|
public override void Update(GameTime gameTime)
|
||||||
|
{
|
||||||
|
UpdateCamera(gameTime);
|
||||||
|
|
||||||
|
_world.ChunkManager.Update(_camera.Position);
|
||||||
|
|
||||||
|
//RenderCommand.Clear(null, .(0.2f, 0.2f, 0.2f));
|
||||||
|
RenderCommand.Clear(null, .CornflowerBlue);
|
||||||
|
RenderCommand.Clear(_depthStencilTarget, 1.0f, 0, .Depth);
|
||||||
|
|
||||||
|
// Draw test geometry
|
||||||
|
_depthStencilTarget.Bind();
|
||||||
|
_context.SetRenderTarget(null);
|
||||||
|
_context.BindRenderTargets();
|
||||||
|
|
||||||
|
_context.SetRasterizerState(_rasterizerState);
|
||||||
|
|
||||||
|
_context.SetViewport(_context.SwapChain.BackbufferViewport);
|
||||||
|
|
||||||
|
Renderer.BeginScene(_camera);
|
||||||
|
|
||||||
|
_opaqueBlendState.Bind();
|
||||||
|
|
||||||
|
for(int x < 20)
|
||||||
|
for(int y < 20)
|
||||||
|
{
|
||||||
|
if((x + y) % 2 == 0)
|
||||||
|
_effect.Variables["BaseColor"].SetData(_squareColor0);
|
||||||
|
else
|
||||||
|
_effect.Variables["BaseColor"].SetData(_squareColor1);
|
||||||
|
|
||||||
|
Matrix transform = Matrix.Translation(x * 0.2f, y * 0.2f, 0) * Matrix.Scaling(0.1f);
|
||||||
|
Renderer.Submit(_quadGeometryBinding, _effect, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
//_effect.Variables["BaseColor"].SetData(_squareColor1);
|
||||||
|
|
||||||
|
_texture.Bind();
|
||||||
|
Renderer.Submit(_quadGeometryBinding, _textureEffect, .Scaling(1.5f));
|
||||||
|
|
||||||
|
_alphaBlendState.Bind();
|
||||||
|
|
||||||
|
_ge_logo.Bind();
|
||||||
|
Renderer.Submit(_quadGeometryBinding, _textureEffect, .Scaling(1.5f));
|
||||||
|
|
||||||
|
_texture.Bind();
|
||||||
|
|
||||||
|
intersectInfo.Coordinate = _world.RaycastBlock(.(_camera.Position, _camera.Transform.Forward), 10, _cubeGeo, _textureEffect, out intersectInfo.Location, out intersectInfo.Face);
|
||||||
|
|
||||||
|
Renderer.Submit(_cubeGeo, _textureEffect, .Translation((Vector3)intersectInfo.Coordinate));
|
||||||
|
|
||||||
|
Renderer.Submit(_cubeGeo, _textureEffect, .Translation(intersectInfo.Location) * .Scaling(0.1f) * .Translation(-0.5f.XXX));
|
||||||
|
|
||||||
|
if(intersectInfo.Face != .None)
|
||||||
|
{
|
||||||
|
if(rightHandBlock == null)
|
||||||
|
rightHandBlock = Blocks.Stone;
|
||||||
|
|
||||||
|
if(Input.IsMouseButtonPressing(.LeftButton))
|
||||||
|
{
|
||||||
|
_world.BreakBlock(intersectInfo.Coordinate);
|
||||||
|
}
|
||||||
|
else if(Input.IsMouseButtonPressing(.RightButton))
|
||||||
|
{
|
||||||
|
_world.PlaceBlock(intersectInfo.Coordinate, rightHandBlock, intersectInfo.Face);
|
||||||
|
}
|
||||||
|
else if(Input.IsMouseButtonPressing(.MiddleButton))
|
||||||
|
{
|
||||||
|
rightHandBlock = _world.GetBlock(intersectInfo.Coordinate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_world.ChunkManager.Draw();
|
||||||
|
|
||||||
|
Renderer.EndScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
Block rightHandBlock;
|
||||||
|
|
||||||
|
struct IntersectionInfo
|
||||||
|
{
|
||||||
|
public Int32_3 Coordinate;
|
||||||
|
public Vector3 Location;
|
||||||
|
public BlockFace Face;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntersectionInfo intersectInfo;
|
||||||
|
|
||||||
|
ColorRGBA _squareColor0 = ColorRGBA.CornflowerBlue;
|
||||||
|
ColorRGBA _squareColor1;
|
||||||
|
|
||||||
|
public override void OnEvent(Event event)
|
||||||
|
{
|
||||||
|
EventDispatcher dispatcher = scope EventDispatcher(event);
|
||||||
|
|
||||||
|
dispatcher.Dispatch<ImGuiRenderEvent>(scope (e) => OnImGuiRender(e));
|
||||||
|
dispatcher.Dispatch<WindowResizeEvent>(scope (e) => OnWindowResize(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool OnWindowResize(WindowResizeEvent e)
|
||||||
|
{
|
||||||
|
_depthStencilTarget?.ReleaseRef();
|
||||||
|
|
||||||
|
_depthStencilTarget = new DepthStencilTarget(_context, _context.SwapChain.Width, _context.SwapChain.Height);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool OnImGuiRender(ImGuiRenderEvent e)
|
||||||
|
{
|
||||||
|
_world.ChunkManager.OnImGuiRender();
|
||||||
|
|
||||||
|
ImGui.Begin("Test");
|
||||||
|
|
||||||
|
ImGui.LabelText("Looked at block", $"Coord: {intersectInfo.Coordinate}, Block Face: {intersectInfo.Face}, Location: {intersectInfo.Location}");
|
||||||
|
|
||||||
|
ImGui.DragFloat("Slow Speed", &movementSpeed, 1.0f, 0.01f, 100.0f);
|
||||||
|
ImGui.DragFloat("Fast Speed", &movementSpeedFast, 1.0f, 1f, 10000.0f);
|
||||||
|
|
||||||
|
Vector3 fwd = _camera.Transform.Forward;
|
||||||
|
|
||||||
|
ImGui.DragFloat3("Forward", *(float[3]*)(void*)&fwd, 1.0f, float.NegativeInfinity, float.PositiveInfinity);
|
||||||
|
|
||||||
|
Vector3 camPos = _camera.Position;
|
||||||
|
|
||||||
|
ImGui.DragFloat3("Position", *(float[3]*)(void*)&camPos, 1.0f, float.NegativeInfinity, float.PositiveInfinity);
|
||||||
|
|
||||||
|
_camera.Position = camPos;
|
||||||
|
|
||||||
|
ImGui.ColorEdit3("Square Color", ref _squareColor0);
|
||||||
|
|
||||||
|
_squareColor1 = ColorRGBA.White - _squareColor0;
|
||||||
|
|
||||||
|
_camera.Position = camPos;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ImGui.End();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,471 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections;
|
||||||
|
using GlitchyEngine.Math;
|
||||||
|
using GlitchyEngine;
|
||||||
|
using GlitchyEngine.Renderer;
|
||||||
|
|
||||||
|
namespace Sandbox.VoxelFun
|
||||||
|
{
|
||||||
|
public class World
|
||||||
|
{
|
||||||
|
public enum CreateError
|
||||||
|
{
|
||||||
|
case WorldAlreadyExists;
|
||||||
|
case CreateDirectoryError(Platform.BfpFileResult error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LoadError
|
||||||
|
{
|
||||||
|
case WorldDoesNotExist;
|
||||||
|
case DirectoryError(Platform.BfpFileResult error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int64 _seed;
|
||||||
|
private String _name ~ delete _;
|
||||||
|
private String _directory ~ delete _;
|
||||||
|
|
||||||
|
private ChunkManager _chunkManager ~ delete _;
|
||||||
|
|
||||||
|
public int64 Seed => _seed;
|
||||||
|
public String Name => _name;
|
||||||
|
public String Directory => _directory;
|
||||||
|
|
||||||
|
public ChunkManager ChunkManager
|
||||||
|
{
|
||||||
|
get => _chunkManager;
|
||||||
|
set => _chunkManager = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String WorldsDirectory = "worlds";
|
||||||
|
const String WorldFileName = "world.info";
|
||||||
|
|
||||||
|
public static Result<void, CreateError> CreateWorld(String name, int64 seed, World outWorld)
|
||||||
|
{
|
||||||
|
String worldPath = new String();
|
||||||
|
Path.InternalCombine(worldPath, WorldsDirectory, name);
|
||||||
|
|
||||||
|
if(Directory.Exists(worldPath))
|
||||||
|
{
|
||||||
|
delete worldPath;
|
||||||
|
return .Err(.WorldAlreadyExists);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Directory.CreateDirectory(worldPath) case .Err(let error))
|
||||||
|
{
|
||||||
|
delete worldPath;
|
||||||
|
return .Err(.CreateDirectoryError(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
String worldFilePath = Path.InternalCombine(.. scope .(), worldPath, WorldFileName);
|
||||||
|
|
||||||
|
MemoryStream str = scope MemoryStream();
|
||||||
|
str.Write(seed);
|
||||||
|
|
||||||
|
Span<uint8> data = .(str.[Friend]mMemory.Ptr, str.Length);
|
||||||
|
|
||||||
|
File.WriteAll(worldFilePath, data);
|
||||||
|
|
||||||
|
outWorld._name = new String(name);
|
||||||
|
outWorld._directory = worldPath;
|
||||||
|
outWorld._seed = seed;
|
||||||
|
|
||||||
|
return .Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<void, LoadError> LoadWorld(String name, World outWorld)
|
||||||
|
{
|
||||||
|
String worldPath = new String();
|
||||||
|
Path.InternalCombine(worldPath, WorldsDirectory, name);
|
||||||
|
|
||||||
|
if(!Directory.Exists(worldPath))
|
||||||
|
return .Err(.WorldDoesNotExist);
|
||||||
|
|
||||||
|
String worldFilePath = Path.InternalCombine(.. scope .(), worldPath, WorldFileName);
|
||||||
|
|
||||||
|
List<uint8> data = scope .();
|
||||||
|
|
||||||
|
File.ReadAll(worldFilePath, data);
|
||||||
|
|
||||||
|
int64 seed = *(int64*)data.Ptr;
|
||||||
|
|
||||||
|
outWorld._name = new String(name);
|
||||||
|
outWorld._directory = worldPath;
|
||||||
|
outWorld._seed = seed;
|
||||||
|
|
||||||
|
return .Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public Int32_3 GetChunkCoordinate(Vector3 blockPosition)
|
||||||
|
{
|
||||||
|
Vector3 v = blockPosition / (Vector3)VoxelChunk.Size;
|
||||||
|
|
||||||
|
Int32_3 p = .();
|
||||||
|
p.X = (int)Math.Round(v.X);
|
||||||
|
p.Y = (int)Math.Round(v.Y);
|
||||||
|
p.Z = (int)Math.Round(v.Z);
|
||||||
|
|
||||||
|
p.Y = 0;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returs the coordinate of the chunk that contains the given coordinate.
|
||||||
|
*/
|
||||||
|
public static Vector3 GetChunkCoordinate(Vector3 blockCoordinate)
|
||||||
|
{
|
||||||
|
Vector3 chunkPosition = blockCoordinate / VoxelChunk.VectorSize;
|
||||||
|
|
||||||
|
return Vector3.Floor(chunkPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the coordinate of the chunk that contains the given block coordinate.
|
||||||
|
*/
|
||||||
|
public static Int32_3 ChunkCoordFromBlockCoord(Int32_3 blockCoordinate)
|
||||||
|
{
|
||||||
|
Int32_3 coordinate = blockCoordinate;
|
||||||
|
|
||||||
|
if(coordinate.X < 0)
|
||||||
|
coordinate.X -= VoxelChunk.Size.X - 1;
|
||||||
|
|
||||||
|
if(coordinate.Y < 0)
|
||||||
|
coordinate.Y -= VoxelChunk.Size.Y - 1;
|
||||||
|
|
||||||
|
if(coordinate.Z < 0)
|
||||||
|
coordinate.Z -= VoxelChunk.Size.Z - 1;
|
||||||
|
|
||||||
|
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 static Int32_3 BlockCoordInChunk(Int32_3 blockCoordinate)
|
||||||
|
{
|
||||||
|
Int32_3 chunkPosition = blockCoordinate % VoxelChunk.Size;
|
||||||
|
|
||||||
|
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 static 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 struct BlockIntersection
|
||||||
|
{
|
||||||
|
Int32_3 BlockCoordinate;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
public Int32_3 RaycastBlock(Ray ray, float maxDistance, GeometryBinding geo, Effect effect, out Vector3 intersectionPosition, out BlockFace intersectionFace)
|
||||||
|
{
|
||||||
|
Vector3 start = ray.Start;
|
||||||
|
Vector3 dir = ray.Direction.Normalized();
|
||||||
|
|
||||||
|
Vector3 unitStepSize = .((dir / dir.X).Magnitude(), (dir / dir.Y).Magnitude(), (dir / dir.Z).Magnitude());
|
||||||
|
|
||||||
|
Int32_3 walker = (Int32_3)Vector3.Floor(start);
|
||||||
|
|
||||||
|
Vector3 rayLengths = .();
|
||||||
|
|
||||||
|
Int32_3 step;
|
||||||
|
|
||||||
|
BlockFace xFace;
|
||||||
|
BlockFace yFace;
|
||||||
|
BlockFace zFace;
|
||||||
|
|
||||||
|
if(dir.X < 0)
|
||||||
|
{
|
||||||
|
xFace = .Right;
|
||||||
|
step.X = -1;
|
||||||
|
rayLengths.X = (start.X - (float)(walker.X)) * unitStepSize.X;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xFace = .Left;
|
||||||
|
step.X = 1;
|
||||||
|
rayLengths.X = ((float)(walker.X + 1) - start.X) * unitStepSize.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dir.Y < 0)
|
||||||
|
{
|
||||||
|
yFace = .Top;
|
||||||
|
step.Y = -1;
|
||||||
|
rayLengths.Y = (start.Y - (float)(walker.Y)) * unitStepSize.Y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yFace = .Bottom;
|
||||||
|
step.Y = 1;
|
||||||
|
rayLengths.Y = ((float)(walker.Y + 1) - start.Y) * unitStepSize.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dir.Z < 0)
|
||||||
|
{
|
||||||
|
zFace = .Back;
|
||||||
|
step.Z = -1;
|
||||||
|
rayLengths.Z = (start.Z - (float)(walker.Z)) * unitStepSize.Z;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
zFace = .Front;
|
||||||
|
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;
|
||||||
|
|
||||||
|
intersectionFace = xFace;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
walker.Z += step.Z;
|
||||||
|
distance = rayLengths.Z;
|
||||||
|
rayLengths.Z += unitStepSize.Z;
|
||||||
|
|
||||||
|
intersectionFace = zFace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(rayLengths.Y < rayLengths.Z)
|
||||||
|
{
|
||||||
|
walker.Y += step.Y;
|
||||||
|
distance = rayLengths.Y;
|
||||||
|
rayLengths.Y += unitStepSize.Y;
|
||||||
|
|
||||||
|
intersectionFace = yFace;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
walker.Z += step.Z;
|
||||||
|
distance = rayLengths.Z;
|
||||||
|
rayLengths.Z += unitStepSize.Z;
|
||||||
|
|
||||||
|
intersectionFace = zFace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Block blockData = [Inline]GetBlock(walker);
|
||||||
|
|
||||||
|
if(blockData != Blocks.Air)
|
||||||
|
{
|
||||||
|
intersectionPosition = start + distance * dir;
|
||||||
|
|
||||||
|
return walker;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
intersectionFace = .None;
|
||||||
|
intersectionPosition = .(float.NaN);
|
||||||
|
return .(int32.MaxValue, int32.MaxValue, int32.MaxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Breaks the block at the given coordinates.
|
||||||
|
*/
|
||||||
|
public void BreakBlock(Int32_3 blockCoordinate)
|
||||||
|
{
|
||||||
|
Int32_3 chunkCoordinate = ChunkCoordFromBlockCoord(blockCoordinate);
|
||||||
|
|
||||||
|
Chunk chunk = _chunkManager.LoadChunk(chunkCoordinate);
|
||||||
|
|
||||||
|
Int32_3 coordInChunk = BlockCoordInChunk(blockCoordinate);
|
||||||
|
|
||||||
|
Block block = chunk.GetBlock(coordInChunk);
|
||||||
|
|
||||||
|
block.OnBreaking(blockCoordinate);
|
||||||
|
|
||||||
|
chunk.SetBlock(coordInChunk, Blocks.Air);
|
||||||
|
|
||||||
|
MarkNeighborGeometryDirty(chunkCoordinate, coordInChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places a block at the given coordinates.
|
||||||
|
*/
|
||||||
|
public void PlaceBlock(Int32_3 blockCoordinate, Block block)
|
||||||
|
{
|
||||||
|
Int32_3 chunkCoordinate = ChunkCoordFromBlockCoord(blockCoordinate);
|
||||||
|
|
||||||
|
Chunk chunk = _chunkManager.LoadChunk(chunkCoordinate);
|
||||||
|
|
||||||
|
Int32_3 coordInChunk = BlockCoordInChunk(blockCoordinate);
|
||||||
|
|
||||||
|
// Todo: we will also be able to place block in fluids and some other blocks.
|
||||||
|
Log.ClientLogger.AssertDebug(chunk.GetBlock(coordInChunk) == Blocks.Air, "A block can only be placed in air (atm)");
|
||||||
|
|
||||||
|
block.OnPlacing(blockCoordinate);
|
||||||
|
|
||||||
|
chunk.SetBlock(coordInChunk, block);
|
||||||
|
|
||||||
|
MarkNeighborGeometryDirty(chunkCoordinate, coordInChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places a block on the given face of the block at the specified coordinates.
|
||||||
|
*/
|
||||||
|
public void PlaceBlock(Int32_3 blockCoordinate, Block block, BlockFace face)
|
||||||
|
{
|
||||||
|
var blockCoordinate;
|
||||||
|
|
||||||
|
switch(face)
|
||||||
|
{
|
||||||
|
case .Front:
|
||||||
|
blockCoordinate.Z--;
|
||||||
|
case .Back:
|
||||||
|
blockCoordinate.Z++;
|
||||||
|
case .Left:
|
||||||
|
blockCoordinate.X--;
|
||||||
|
case .Right:
|
||||||
|
blockCoordinate.X++;
|
||||||
|
case .Bottom:
|
||||||
|
blockCoordinate.Y--;
|
||||||
|
case .Top:
|
||||||
|
blockCoordinate.Y++;
|
||||||
|
default:
|
||||||
|
Log.ClientLogger.AssertDebug(false, "Unexpected block face.");
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaceBlock(blockCoordinate, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the block at the specified coordinate.
|
||||||
|
* @remarks If the blocks chunk is not loaded it will be loaded from the disk.
|
||||||
|
*/
|
||||||
|
public void SetBlock(Int32_3 blockCoordinate, Block block)
|
||||||
|
{
|
||||||
|
Int32_3 chunkCoordinate = ChunkCoordFromBlockCoord(blockCoordinate);
|
||||||
|
|
||||||
|
Chunk chunk = _chunkManager.LoadChunk(chunkCoordinate);
|
||||||
|
|
||||||
|
Int32_3 coordInChunk = BlockCoordInChunk(blockCoordinate);
|
||||||
|
|
||||||
|
chunk.SetBlock(coordInChunk, block);
|
||||||
|
|
||||||
|
MarkNeighborGeometryDirty(chunkCoordinate, coordInChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkNeighborGeometryDirty(Int32_3 chunkCoordinate, Int32_3 coordInChunk)
|
||||||
|
{
|
||||||
|
var chunkCoordinate;
|
||||||
|
|
||||||
|
Chunk chunk;
|
||||||
|
|
||||||
|
// if we are on a chunk border mark the neighbor as dirty
|
||||||
|
|
||||||
|
if(coordInChunk.X == 0)
|
||||||
|
{
|
||||||
|
chunkCoordinate.X--;
|
||||||
|
chunk = _chunkManager.GetChunk(chunkCoordinate);
|
||||||
|
chunk?.IsGeometryDirty = true;
|
||||||
|
}
|
||||||
|
else if(coordInChunk.X == VoxelChunk.Size.X - 1)
|
||||||
|
{
|
||||||
|
chunkCoordinate.X++;
|
||||||
|
chunk = _chunkManager.GetChunk(chunkCoordinate);
|
||||||
|
chunk?.IsGeometryDirty = true;
|
||||||
|
}
|
||||||
|
// TODO: implement Y
|
||||||
|
else if(coordInChunk.Z == 0)
|
||||||
|
{
|
||||||
|
chunkCoordinate.Z--;
|
||||||
|
chunk = _chunkManager.GetChunk(chunkCoordinate);
|
||||||
|
chunk?.IsGeometryDirty = true;
|
||||||
|
}
|
||||||
|
else if(coordInChunk.Z == VoxelChunk.Size.Z - 1)
|
||||||
|
{
|
||||||
|
chunkCoordinate.Z++;
|
||||||
|
chunk = _chunkManager.GetChunk(chunkCoordinate);
|
||||||
|
chunk?.IsGeometryDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the block at the specified coordinate.
|
||||||
|
* @remarks If the blocks chunk is not loaded it will be loaded from the disk.
|
||||||
|
*/
|
||||||
|
public Block GetBlock(Int32_3 blockCoordinate)
|
||||||
|
{
|
||||||
|
Int32_3 chunkCoordinate = ChunkCoordFromBlockCoord(blockCoordinate);
|
||||||
|
|
||||||
|
Chunk chunk = _chunkManager.LoadChunk(chunkCoordinate);
|
||||||
|
|
||||||
|
Int32_3 coordInChunk = BlockCoordInChunk(blockCoordinate);
|
||||||
|
|
||||||
|
return chunk.GetBlock(coordInChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
static void TestWorld()
|
||||||
|
{
|
||||||
|
// ChunkCoordFromBlockCoord
|
||||||
|
{
|
||||||
|
Int32_3 block = .(0, 0, 0);
|
||||||
|
Int32_3 expectedChunk = .(0, 0, 0);
|
||||||
|
Test.Assert(expectedChunk == ChunkCoordFromBlockCoord(block));
|
||||||
|
|
||||||
|
block = .(5, 0, 0);
|
||||||
|
expectedChunk = .(0, 0, 0);
|
||||||
|
Test.Assert(expectedChunk == ChunkCoordFromBlockCoord(block));
|
||||||
|
|
||||||
|
block = .(45, 80, 12);
|
||||||
|
expectedChunk = .(2, 0, 0);
|
||||||
|
Test.Assert(expectedChunk == ChunkCoordFromBlockCoord(block));
|
||||||
|
|
||||||
|
block = .(0, 0, -1);
|
||||||
|
expectedChunk = .(0, 0, -1);
|
||||||
|
Test.Assert(expectedChunk == ChunkCoordFromBlockCoord(block));
|
||||||
|
|
||||||
|
block = .(0, 0, -16);
|
||||||
|
expectedChunk = .(0, 0, -1);
|
||||||
|
Test.Assert(expectedChunk == ChunkCoordFromBlockCoord(block));
|
||||||
|
|
||||||
|
block = .(0, 0, -17);
|
||||||
|
expectedChunk = .(0, 0, -2);
|
||||||
|
Test.Assert(expectedChunk == ChunkCoordFromBlockCoord(block));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
+1
Submodule vendor/lodepng-beef added at 959b755ee9
Reference in New Issue
Block a user