mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Compare commits
219
Commits
VoxelGame
...
dotnet_test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4874cfe511 | ||
|
|
63b9270710 | ||
|
|
2bac6a8d05 | ||
|
|
e51fc77c3f | ||
|
|
8c53f21690 | ||
|
|
ad1b37dc64 | ||
|
|
8e46945362 | ||
|
|
6a250d4927 | ||
|
|
9c93ccee47 | ||
|
|
f1ce63ee3b | ||
|
|
e685b4fbba | ||
|
|
9105c12e33 | ||
|
|
863c264001 | ||
|
|
bec202e23a | ||
|
|
bca72cf0d4 | ||
|
|
af37154071 | ||
|
|
c7fde476e8 | ||
|
|
604e34a12a | ||
|
|
b94c84d304 | ||
|
|
d47bc37886 | ||
|
|
c13b0a26d1 | ||
|
|
e82f70ba02 | ||
|
|
cbee9f59d8 | ||
|
|
afe36b19df | ||
|
|
9197a209d6 | ||
|
|
ba5ccd7107 | ||
|
|
c80ca4d9a1 | ||
|
|
92fd8bb572 | ||
|
|
b341c70a26 | ||
|
|
8c8f0d9fc7 | ||
|
|
8638b7ac3b | ||
|
|
be7c3d1d8b | ||
|
|
699522ff67 | ||
|
|
6f999da1b8 | ||
|
|
373980d5ad | ||
|
|
0db85de067 | ||
|
|
fae2bdf084 | ||
|
|
0f0afc2a90 | ||
|
|
66dad82dc6 | ||
|
|
0e684af17d | ||
|
|
82898f1d5c | ||
|
|
a2db0774f8 | ||
|
|
5b1f694bc9 | ||
|
|
69be98bb38 | ||
|
|
7eb4001a1b | ||
|
|
bca4552341 | ||
|
|
d96ec6f21b | ||
|
|
44fe2c3281 | ||
|
|
6a55108a37 | ||
|
|
a7b2515cf4 | ||
|
|
ee9dfd4593 | ||
|
|
3c1897b926 | ||
|
|
8a84843f54 | ||
|
|
06469f3350 | ||
|
|
78faf4cb81 | ||
|
|
e292029015 | ||
|
|
5373d92297 | ||
|
|
deedb62a03 | ||
|
|
6199496987 | ||
|
|
826ac68873 | ||
|
|
b2ce560bfc | ||
|
|
664ba552ea | ||
|
|
b555cc37d1 | ||
|
|
b3ab30e72a | ||
|
|
1cc37ee168 | ||
|
|
f4d64b15fa | ||
|
|
e55b73db0f | ||
|
|
f28984bec5 | ||
|
|
0fd6ac1dee | ||
|
|
419509ab7f | ||
|
|
2bc87b437a | ||
|
|
b3f9376dc8 | ||
|
|
ffafa380ea | ||
|
|
90e654b750 | ||
|
|
adaf62de62 | ||
|
|
dcb8424c2d | ||
|
|
84df8c111b | ||
|
|
cd82a99450 | ||
|
|
9216462a51 | ||
|
|
3eb7068bca | ||
|
|
54870330b6 | ||
|
|
ac74aeb116 | ||
|
|
b5bfcf5741 | ||
|
|
0cbbf94fe6 | ||
|
|
84daee95e4 | ||
|
|
2cd9ab3e69 | ||
|
|
5a82b89622 | ||
|
|
b020855ec5 | ||
|
|
4dc6c916ae | ||
|
|
9a9d75afbc | ||
|
|
fab0f99951 | ||
|
|
122f7e9904 | ||
|
|
eb2bbf4b82 | ||
|
|
611782653c | ||
|
|
229d2b578e | ||
|
|
8e39d7988b | ||
|
|
4debaee7d4 | ||
|
|
4af61a372f | ||
|
|
3f1f5736b5 | ||
|
|
2afff4ec20 | ||
|
|
764b9f27b6 | ||
|
|
2d5c408b54 | ||
|
|
22a4bc355e | ||
|
|
90284739c0 | ||
|
|
8471b7d7b9 | ||
|
|
38fd5fa2c8 | ||
|
|
0c4fd27226 | ||
|
|
a80a63fbc1 | ||
|
|
9cd95ed0e4 | ||
|
|
b5efaf2b5b | ||
|
|
da6d242353 | ||
|
|
0b1ec9a837 | ||
|
|
45d298b755 | ||
|
|
266d7a4651 | ||
|
|
d051b392d0 | ||
|
|
e2fc373a06 | ||
|
|
48de55f987 | ||
|
|
0079b80b1c | ||
|
|
f6b073557a | ||
|
|
b8626f3c27 | ||
|
|
0b8be33d68 | ||
|
|
e14ebec387 | ||
|
|
b4bb5c4a39 | ||
|
|
c04cd8a956 | ||
|
|
908167c83e | ||
|
|
ab17598972 | ||
|
|
db05a5a6a9 | ||
|
|
cb2c83678e | ||
|
|
b0b5f76423 | ||
|
|
44374a84a7 | ||
|
|
39f5893c7e | ||
|
|
89bb475994 | ||
|
|
c4ed04ba96 | ||
|
|
886ce2fe3d | ||
|
|
8442f7741d | ||
|
|
29749f64e9 | ||
|
|
5c95668a1d | ||
|
|
cb51880925 | ||
|
|
da24643c64 | ||
|
|
f761f87e1d | ||
|
|
fc2f1cd7e6 | ||
|
|
155495eeef | ||
|
|
1491ea8e75 | ||
|
|
ff4cd48f1e | ||
|
|
d555a80d8d | ||
|
|
2d8efa0154 | ||
|
|
78aeacc8a1 | ||
|
|
5545d84284 | ||
|
|
30280c5f93 | ||
|
|
e42b587d88 | ||
|
|
c270fdcb03 | ||
|
|
0534824c07 | ||
|
|
5ce7ebe674 | ||
|
|
c9e9a11e4e | ||
|
|
d1175519eb | ||
|
|
566b4ea59e | ||
|
|
0e7217778f | ||
|
|
b376525d30 | ||
|
|
ce03f7c600 | ||
|
|
eafdc2f17f | ||
|
|
22252f9161 | ||
|
|
7dda8bdd25 | ||
|
|
41754f5c67 | ||
|
|
099763e1e3 | ||
|
|
a8bef51834 | ||
|
|
c880c4ef73 | ||
|
|
8ab740fa2e | ||
|
|
93513f3ea8 | ||
|
|
9dbf9fd501 | ||
|
|
e239de52d6 | ||
|
|
9e06d4d51f | ||
|
|
d200c2c849 | ||
|
|
f5f3bad8db | ||
|
|
344accea60 | ||
|
|
2326e69c0c | ||
|
|
80e0bae347 | ||
|
|
5c0ee3bae0 | ||
|
|
e04d058381 | ||
|
|
0735aae74f | ||
|
|
3ac99491cf | ||
|
|
303578a83f | ||
|
|
ce163db30b | ||
|
|
dbdb8a0d32 | ||
|
|
9b418521af | ||
|
|
b1ad7b5ba3 | ||
|
|
9158da60c2 | ||
|
|
7f8379ea6c | ||
|
|
8c957f9d9f | ||
|
|
819476a3ec | ||
|
|
1f1b32e101 | ||
|
|
f131b21273 | ||
|
|
5ae0192bc6 | ||
|
|
dc088dcb0a | ||
|
|
bf94f10a64 | ||
|
|
562f4855bd | ||
|
|
64579f9603 | ||
|
|
ae290936b9 | ||
|
|
0f2c481616 | ||
|
|
b19dd60e52 | ||
|
|
2f281f13fd | ||
|
|
3eeca63d91 | ||
|
|
3f938d3488 | ||
|
|
7fb1f30303 | ||
|
|
3e0c4b2eb7 | ||
|
|
bfd81745e9 | ||
|
|
6376bde112 | ||
|
|
047c09017c | ||
|
|
1f687f9b96 | ||
|
|
ad4933a1f9 | ||
|
|
838bb7ec2e | ||
|
|
92a3eaa426 | ||
|
|
4227882afa | ||
|
|
5a8499ac43 | ||
|
|
72cb982b13 | ||
|
|
92b22acb91 | ||
|
|
2ad45edef5 | ||
|
|
cfff2cb738 | ||
|
|
163dadbe37 | ||
|
|
08daa2f59e |
+1
-1
@@ -4,4 +4,4 @@ build/
|
||||
.vs/
|
||||
recovery/
|
||||
vendor/directx
|
||||
Sandbox/imgui.ini
|
||||
imgui.ini
|
||||
|
||||
+24
-6
@@ -1,9 +1,27 @@
|
||||
[submodule "GlitchyEngine/vendor/directx"]
|
||||
path = GlitchyEngine/vendor/directx
|
||||
url = https://github.com/aharabada/directx-beef.git
|
||||
[submodule "GlitchyEngine/vendor/imgui-beef"]
|
||||
path = GlitchyEngine/vendor/imgui-beef
|
||||
url = https://github.com/aharabada/imgui-beef
|
||||
[submodule ".\\GlitchyEngine\\vendor\\DirectXTK"]
|
||||
path = .\\GlitchyEngine\\vendor\\DirectXTK
|
||||
url = https://github.com/aharabada/DirectXTK-beef.git
|
||||
[submodule "vendor/lodepng-beef"]
|
||||
path = vendor/lodepng-beef
|
||||
url = https://github.com/aharabada/lodepng-beef.git
|
||||
[submodule "GlitchyEngine/vendor/freetype"]
|
||||
path = GlitchyEngine/vendor/freetype
|
||||
url = https://github.com/aharabada/FreeType-beef.git
|
||||
[submodule "GlitchyEngine/vendor/gltf"]
|
||||
path = GlitchyEngine/vendor/gltf
|
||||
url = https://github.com/aharabada/cgltf-beef.git
|
||||
[submodule "GlitchyEngine/vendor/msdfgen"]
|
||||
path = GlitchyEngine/vendor/msdfgen
|
||||
url = https://github.com/aharabada/msdfgen-beef.git
|
||||
[submodule "GlitchyEngine/vendor/imgui"]
|
||||
path = GlitchyEngine/vendor/imgui
|
||||
url = https://github.com/aharabada/ImGui_GlitchyEngine.git
|
||||
[submodule "GlitchyEngineHelper/vendor/xxHash"]
|
||||
path = GlitchyEngineHelper/vendor/xxHash
|
||||
url = https://github.com/Cyan4973/xxHash.git
|
||||
[submodule "GlitchyEngine/vendor/bon"]
|
||||
path = GlitchyEngine/vendor/bon
|
||||
url = https://github.com/EinScott/bon.git
|
||||
[submodule "GlitchyEngineHelper/vendor/DirectXTK"]
|
||||
path = GlitchyEngineHelper/vendor/DirectXTK
|
||||
url = https://github.com/microsoft/DirectXTK.git
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
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"}, LodePng = {Path = "vendor/lodepng-beef/lodepng-beef"}, FreeType = {Path = "GlitchyEngine/vendor/freetype"}, cgltf-beef = {Path = "GlitchyEngine/vendor/gltf/cgltf-beef"}, GlitchyEditor = {Path = "GlitchyEditor"}, msdfgen-beef = {Path = "GlitchyEngine/vendor/msdfgen/msdfgen-beef"}, ImGui = {Path = "GlitchyEngine/vendor/imgui/ImGui"}, ImGuiImplDX11 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplDX11"}, ImGuiImplWin32 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplWin32"}, ImGuizmo = {Path = "GlitchyEngine/vendor/imgui/ImGuizmo"}, GlitchyEngineHelper = {Path = "GlitchyEngineHelper"}, bon = {Path = "GlitchyEngine/vendor/bon"}}
|
||||
WorkspaceFolders = {GlitchyEngine = ["GlitchyEngine", "GlitchLog", "GlitchyEngineHelper"], "GlitchyEngine/Dependencies" = ["cgltf-beef", "DirectX", "FreeType", "ImGui", "ImGuiImplDX11", "ImGuiImplWin32", "ImGuizmo", "LodePng", "msdfgen-beef", "bon"]}
|
||||
|
||||
[Workspace]
|
||||
StartupProject = "Sandbox"
|
||||
StartupProject = "GlitchyEditor"
|
||||
|
||||
@@ -50,10 +50,10 @@ namespace GlitchLog
|
||||
|
||||
public this()
|
||||
{
|
||||
Runtime.Assert(Debug.IsDebuggerPresent, "The DebugLogger requires a debugger to be present.");
|
||||
//Debug.Assert(Debug.IsDebuggerPresent, "The DebugLogger requires a debugger to be present.");
|
||||
}
|
||||
|
||||
#if GL_NOLOG || GL_NOTRACE
|
||||
#if GL_NOLOG || GL_LOG_NOTRACE
|
||||
[SkipCall]
|
||||
#endif
|
||||
[Inline]
|
||||
@@ -62,7 +62,7 @@ namespace GlitchLog
|
||||
InternalLog(.Trace, format, params args);
|
||||
}
|
||||
|
||||
#if GL_NOLOG || GL_NOINFO
|
||||
#if GL_NOLOG || GL_LOG_NOINFO
|
||||
[SkipCall]
|
||||
#endif
|
||||
[Inline]
|
||||
@@ -71,7 +71,7 @@ namespace GlitchLog
|
||||
InternalLog(.Info, format, params args);
|
||||
}
|
||||
|
||||
#if GL_NOLOG || GL_NOWARNING
|
||||
#if GL_NOLOG || GL_LOG_NOWARNING
|
||||
[SkipCall]
|
||||
#endif
|
||||
[Inline]
|
||||
@@ -80,7 +80,7 @@ namespace GlitchLog
|
||||
InternalLog(.Warning, format, params args);
|
||||
}
|
||||
|
||||
#if GL_NOLOG || GL_NOERROR
|
||||
#if GL_NOLOG || GL_LOG_NOERROR
|
||||
[SkipCall]
|
||||
#endif
|
||||
[Inline]
|
||||
@@ -89,7 +89,7 @@ namespace GlitchLog
|
||||
InternalLog(.Error, format, params args);
|
||||
}
|
||||
|
||||
#if GL_NOLOG || GL_NOCRITICAL
|
||||
#if GL_NOLOG || GL_LOG_NOCRITICAL
|
||||
[SkipCall]
|
||||
#endif
|
||||
[Inline]
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
FileVersion = 1
|
||||
Dependencies = {corlib = "*", GlitchLog = "*", GlitchyEngine = "*"}
|
||||
|
||||
[Project]
|
||||
Name = "GlitchyEditor"
|
||||
TargetType = "BeefGUIApplication"
|
||||
StartupObject = "GlitchyEngine.Program"
|
||||
Binary file not shown.
@@ -0,0 +1,62 @@
|
||||
Texture2D Texture : register(t0);
|
||||
SamplerState Sampler : register(s0);
|
||||
|
||||
cbuffer Constants : register(b0)
|
||||
{
|
||||
float4x4 ViewProjection;
|
||||
};
|
||||
|
||||
struct VS_Input
|
||||
{
|
||||
float2 Position : POSITION;
|
||||
float2 Texcoord : TEXCOORD0;
|
||||
float4x4 Transform : TRANSFORM;
|
||||
float4 Color : COLOR;
|
||||
float4 UVTransform : TEXCOORD1;
|
||||
float InnerRadius : TEXCOORD2;
|
||||
};
|
||||
|
||||
struct PS_Input
|
||||
{
|
||||
float4 Position : SV_Position;
|
||||
float2 RawPos : TEXCOORD0;
|
||||
float2 Texcoord : TEXCOORD1;
|
||||
float4 Color : COLOR;
|
||||
float InnerRadius : TEXCOORD2;
|
||||
};
|
||||
|
||||
PS_Input VS(VS_Input input)
|
||||
{
|
||||
PS_Input output;
|
||||
|
||||
output.Position = mul(ViewProjection, mul(input.Transform, float4(input.Position, 0.0f, 1.0f)));
|
||||
output.Texcoord = input.UVTransform.xy + input.UVTransform.zw * input.Texcoord;
|
||||
output.RawPos = input.Position;
|
||||
output.Color = input.Color;
|
||||
output.InnerRadius = input.InnerRadius;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
float4 PS(PS_Input input) : SV_Target0
|
||||
{
|
||||
float2 uv = input.RawPos * 2;
|
||||
|
||||
float distance = 1.0f - length(uv);
|
||||
|
||||
// Smoothing edges based on derivative (not sure if RawPos is the best value for this)
|
||||
float f = fwidth(input.RawPos.x) + fwidth(input.RawPos.y);
|
||||
|
||||
float amount = smoothstep(0.0f, f, distance);
|
||||
amount *= smoothstep(input.InnerRadius + f, input.InnerRadius, distance);
|
||||
|
||||
// Discard invisible pixels
|
||||
clip(amount - 0.5f);
|
||||
|
||||
float4 color = Texture.Sample(Sampler, input.Texcoord) * input.Color;
|
||||
color.a *= amount;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
#effect[VS=VS, PS=PS]
|
||||
@@ -0,0 +1,31 @@
|
||||
cbuffer Constants : register(b0)
|
||||
{
|
||||
float4x4 ViewProjection;
|
||||
float4 Color;
|
||||
};
|
||||
|
||||
struct VS_Input
|
||||
{
|
||||
float4 Position : POSITION;
|
||||
};
|
||||
|
||||
struct PS_Input
|
||||
{
|
||||
float4 Position : SV_Position;
|
||||
};
|
||||
|
||||
PS_Input VS(VS_Input input)
|
||||
{
|
||||
PS_Input output;
|
||||
|
||||
output.Position = mul(ViewProjection, input.Position);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
float4 PS(PS_Input input) : SV_Target0
|
||||
{
|
||||
return Color;
|
||||
}
|
||||
|
||||
#effect[VS=VS, PS=PS]
|
||||
@@ -0,0 +1,93 @@
|
||||
Texture2D<float3> Texture : register(t0);
|
||||
SamplerState Sampler : register(s0);
|
||||
|
||||
cbuffer Constants
|
||||
{
|
||||
float4x4 ViewProjection;
|
||||
float2 UnitRange;
|
||||
float screenPixelRange = 2;
|
||||
}
|
||||
|
||||
struct VS_Input
|
||||
{
|
||||
float2 Position : POSITION;
|
||||
float2 Texcoord : TEXCOORD0;
|
||||
float4x4 Tranform : TRANSFORM;
|
||||
float4 Color : COLOR;
|
||||
float4 UVTransform : TEXCOORD1;
|
||||
};
|
||||
|
||||
struct PS_Input
|
||||
{
|
||||
float4 Position : SV_Position;
|
||||
float2 TexCoord : TEXCOORD0;
|
||||
float4 Color : COLOR;
|
||||
};
|
||||
|
||||
PS_Input VS(VS_Input input)
|
||||
{
|
||||
PS_Input output;
|
||||
|
||||
output.Position = mul(ViewProjection, mul(input.Tranform, float4(input.Position, 0.0f, 1.0f)));
|
||||
output.TexCoord = input.UVTransform.xy + input.UVTransform.zw * input.Texcoord;
|
||||
output.Color = input.Color;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
in vec2 texCoord;
|
||||
out vec4 color;
|
||||
uniform sampler2D msdf;
|
||||
uniform vec4 bgColor;
|
||||
uniform vec4 fgColor;
|
||||
|
||||
float median(float r, float g, float b) {
|
||||
return max(min(r, g), min(max(r, g), b));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 msd = texture(msdf, texCoord).rgb;
|
||||
float sd = median(msd.r, msd.g, msd.b);
|
||||
float screenPxDistance = screenPxRange()*(sd - 0.5);
|
||||
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||
color = mix(bgColor, fgColor, opacity);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
float median(float r, float g, float b)
|
||||
{
|
||||
return max(min(r, g), min(max(r, g), b));
|
||||
}
|
||||
|
||||
float ScreenPxRange(float2 texcoord)
|
||||
{
|
||||
float2 screenTexSize = 1.0f / fwidth(texcoord);
|
||||
return max(0.5f * dot(UnitRange, screenTexSize), 1.0f);
|
||||
}
|
||||
|
||||
float4 PS(PS_Input input) : SV_Target0
|
||||
{
|
||||
float3 msd = Texture.Sample(Sampler, input.TexCoord);
|
||||
float sd = median(msd.r, msd.g, msd.b);
|
||||
float screenPxDistance = ScreenPxRange(input.TexCoord) * (sd - 0.5);
|
||||
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||
|
||||
return float4(input.Color.rgb, opacity * input.Color.a);
|
||||
}
|
||||
|
||||
/*
|
||||
// 2D
|
||||
float4 PS(PS_Input input) : SV_Target0
|
||||
{
|
||||
float3 msd = Texture.Sample(Sampler, input.TexCoord).rgb;
|
||||
float sd = median(msd.r, msd.g, msd.b);
|
||||
float screenPxDistance = screenPixelRange*(sd - 0.5);
|
||||
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||
|
||||
return float4(input.Color.rgb, opacity * input.Color.a);
|
||||
}
|
||||
*/
|
||||
|
||||
#effect[VS=VS, PS=PS]
|
||||
@@ -0,0 +1,41 @@
|
||||
Texture2D Texture : register(t0);
|
||||
SamplerState Sampler : register(s0);
|
||||
|
||||
cbuffer Constants : register(b0)
|
||||
{
|
||||
float4x4 ViewProjection;
|
||||
};
|
||||
|
||||
struct VS_Input
|
||||
{
|
||||
float2 Position : POSITION;
|
||||
float2 Texcoord : TEXCOORD0;
|
||||
float4x4 Transform : TRANSFORM;
|
||||
float4 Color : COLOR;
|
||||
float4 UVTransform : TEXCOORD1;
|
||||
};
|
||||
|
||||
struct PS_Input
|
||||
{
|
||||
float4 Position : SV_Position;
|
||||
float2 Texcoord : TEXCOORD;
|
||||
float4 Color : COLOR;
|
||||
};
|
||||
|
||||
PS_Input VS(VS_Input input)
|
||||
{
|
||||
PS_Input output;
|
||||
|
||||
output.Position = mul(ViewProjection, mul(input.Transform, float4(input.Position, 0.0f, 1.0f)));
|
||||
output.Texcoord = input.UVTransform.xy + input.UVTransform.zw * input.Texcoord;
|
||||
output.Color = input.Color;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
float4 PS(PS_Input input) : SV_Target0
|
||||
{
|
||||
return Texture.Sample(Sampler, input.Texcoord) * input.Color;
|
||||
}
|
||||
|
||||
#effect[VS=VS, PS=PS]
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 258 B |
@@ -0,0 +1,298 @@
|
||||
using ImGui;
|
||||
using GlitchyEngine.World;
|
||||
using System;
|
||||
using GlitchyEngine.Math;
|
||||
using System.Collections;
|
||||
|
||||
namespace GlitchyEditor.EditWindows
|
||||
{
|
||||
using internal GlitchyEngine.World.TransformComponent;
|
||||
|
||||
class ComponentEditWindow : EditorWindow
|
||||
{
|
||||
public const String s_WindowTitle = "Components";
|
||||
|
||||
private EntityHierarchyWindow _entityHierarchyWindow;
|
||||
|
||||
private List<(Type, ComponentAttribute attribute)> _componentTypes = new .() ~ delete _;
|
||||
|
||||
public this(EntityHierarchyWindow entityHierarchyWindow)
|
||||
{
|
||||
_entityHierarchyWindow = entityHierarchyWindow;
|
||||
|
||||
for (let type in Type.Types)
|
||||
{
|
||||
if (let componentAttribute = type.GetCustomAttribute<ComponentAttribute>())
|
||||
{
|
||||
_componentTypes.Add((type, componentAttribute));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void InternalShow()
|
||||
{
|
||||
ImGui.PushStyleVar(.WindowMinSize, ImGui.Vec2(1000, 100));
|
||||
|
||||
if(!ImGui.Begin(s_WindowTitle, &_open, .None))
|
||||
{
|
||||
ImGui.PopStyleVar();
|
||||
ImGui.End();
|
||||
return;
|
||||
}
|
||||
|
||||
if(_entityHierarchyWindow.SelectedEntities.Count == 1)
|
||||
{
|
||||
Entity entity = _entityHierarchyWindow.SelectedEntities.Front;
|
||||
|
||||
ShowComponents(entity);
|
||||
}
|
||||
|
||||
ImGui.PopStyleVar();
|
||||
ImGui.End();
|
||||
}
|
||||
|
||||
private void ShowComponents(Entity entity)
|
||||
{
|
||||
ShowNameComponentEditor(entity);
|
||||
|
||||
ShowComponentEditor<TransformComponent>("Transform", entity, => ShowTransformComponentEditor);
|
||||
ShowComponentEditor<CameraComponent>("Camera", entity, => ShowCameraComponentEditor, => ShowComponentContextMenu<CameraComponent>);
|
||||
ShowComponentEditor<SpriterRendererComponent>("Sprite Renderer", entity, => ShowSpriteRendererComponentEditor, => ShowComponentContextMenu<SpriterRendererComponent>);
|
||||
|
||||
ShowAddComponentButton(entity);
|
||||
}
|
||||
|
||||
private static void ShowComponentContextMenu<TComponent>(Entity entity, TComponent* component) where TComponent: struct, new
|
||||
{
|
||||
if (ImGui.Selectable("Remove Component"))
|
||||
entity.RemoveComponent<TComponent>();
|
||||
}
|
||||
|
||||
private static void ShowComponentEditor<TComponent>(String header, Entity entity, function void(Entity, TComponent*) showComponentEditor, function void(Entity, TComponent*) showComponentContextMenu = null) where TComponent: struct, new
|
||||
{
|
||||
if (!entity.HasComponent<TComponent>())
|
||||
return;
|
||||
|
||||
TComponent* component = entity.GetComponent<TComponent>();
|
||||
|
||||
ImGui.PushID(header);
|
||||
|
||||
bool nodeOpen = ImGui.TreeNodeEx(header.CStr(), .DefaultOpen | .AllowItemOverlap | .Framed | .SpanFullWidth);
|
||||
|
||||
if (showComponentContextMenu != null)
|
||||
{
|
||||
ImGui.SameLine(ImGui.GetWindowContentRegionMax().x - ImGui.CalcTextSize("...").x - 2 * ImGui.GetStyle().FramePadding.x);
|
||||
|
||||
if (ImGui.SmallButton("..."))
|
||||
{
|
||||
ImGui.OpenPopup("component_popup");
|
||||
}
|
||||
|
||||
if (ImGui.BeginPopup("component_popup"))
|
||||
{
|
||||
showComponentContextMenu(entity, component);
|
||||
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeOpen)
|
||||
{
|
||||
showComponentEditor(entity, component);
|
||||
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
ImGui.PopID();
|
||||
}
|
||||
|
||||
private static void ShowNameComponentEditor(Entity entity)
|
||||
{
|
||||
if (!entity.HasComponent<DebugNameComponent>())
|
||||
return;
|
||||
|
||||
char8[256] nameBuffer = default;
|
||||
|
||||
DebugNameComponent* component = entity.GetComponent<DebugNameComponent>();
|
||||
|
||||
String name = null;
|
||||
|
||||
if(component != null)
|
||||
{
|
||||
name = component.DebugName;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = scope:: $"Entity {entity.Handle.[Friend]Index}";
|
||||
}
|
||||
|
||||
// Copy name to buffer
|
||||
Internal.MemCpy(&nameBuffer, name.Ptr, Math.Min(nameBuffer.Count, name.Length));
|
||||
|
||||
if(ImGui.InputText("Name", &nameBuffer, nameBuffer.Count))
|
||||
{
|
||||
if(component == null)
|
||||
{
|
||||
component = entity.AddComponent<DebugNameComponent>();
|
||||
}
|
||||
|
||||
component.DebugName.Clear();
|
||||
component.DebugName.Append(&nameBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ShowTransformComponentEditor(Entity entity, TransformComponent* transform)
|
||||
{
|
||||
float textWidth = ImGui.CalcTextSize("Position".CStr()).x;
|
||||
textWidth = Math.Max(textWidth, ImGui.CalcTextSize("Rotation".CStr()).x);
|
||||
textWidth = Math.Max(textWidth, ImGui.CalcTextSize("Scale".CStr()).x);
|
||||
|
||||
textWidth += ImGui.GetStyle().FramePadding.x * 3.0f;
|
||||
|
||||
Vector3 position = transform.Position;
|
||||
if (ImGui.EditVector3("Position", ref position, .Zero, 0.1f, textWidth))
|
||||
transform.Position = position;
|
||||
|
||||
Vector3 rotationEuler = MathHelper.ToDegrees(transform.EditorRotationEuler);
|
||||
if (ImGui.EditVector3("Rotation", ref rotationEuler, .Zero, 0.1f, textWidth))
|
||||
transform.EditorRotationEuler = MathHelper.ToRadians(rotationEuler);
|
||||
|
||||
Vector3 scale = transform.Scale;
|
||||
if (ImGui.EditVector3("Scale", ref scale, .One, 0.1f, textWidth))
|
||||
transform.Scale = scale;
|
||||
}
|
||||
|
||||
static String[] strings = new String[]("Orthographic", "Perspective", "Perspective (Infinite)") ~ delete _;
|
||||
|
||||
private static void ShowCameraComponentEditor(Entity entity, CameraComponent* cameraComponent)
|
||||
{
|
||||
ImGui.Checkbox("Primary", &cameraComponent.Primary);
|
||||
|
||||
var camera = ref cameraComponent.Camera;
|
||||
|
||||
String typeName = strings[camera.ProjectionType.Underlying];
|
||||
|
||||
if (ImGui.BeginCombo("Projection", typeName.CStr()))
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
bool isSelected = (typeName == strings[i]);
|
||||
|
||||
if (ImGui.Selectable(strings[i], isSelected))
|
||||
{
|
||||
camera.ProjectionType = (.)i;
|
||||
}
|
||||
|
||||
if (isSelected)
|
||||
ImGui.SetItemDefaultFocus();
|
||||
}
|
||||
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
|
||||
if (camera.ProjectionType == .Perspective)
|
||||
{
|
||||
float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY);
|
||||
if (ImGui.DragFloat("Fov Y", &fovY, 0.1f))
|
||||
camera.PerspectiveFovY = MathHelper.ToRadians(fovY);
|
||||
|
||||
float near = camera.PerspectiveNearPlane;
|
||||
if (ImGui.DragFloat("Near", &near, 0.1f))
|
||||
camera.PerspectiveNearPlane = near;
|
||||
|
||||
float far = camera.PerspectiveFarPlane;
|
||||
if (ImGui.DragFloat("Far", &far, 0.1f))
|
||||
camera.PerspectiveFarPlane = far;
|
||||
}
|
||||
else if (camera.ProjectionType == .InfinitePerspective)
|
||||
{
|
||||
float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY);
|
||||
if (ImGui.DragFloat("Vertical FOV", &fovY, 0.1f))
|
||||
camera.PerspectiveFovY = MathHelper.ToRadians(fovY);
|
||||
|
||||
float near = camera.PerspectiveNearPlane;
|
||||
if (ImGui.DragFloat("Near", &near, 0.1f))
|
||||
camera.PerspectiveNearPlane = near;
|
||||
}
|
||||
else if (camera.ProjectionType == .Orthographic)
|
||||
{
|
||||
float size = camera.OrthographicHeight;
|
||||
if (ImGui.DragFloat("Size", &size, 0.1f))
|
||||
camera.OrthographicHeight = size;
|
||||
|
||||
float near = camera.OrthographicNearPlane;
|
||||
if (ImGui.DragFloat("Near", &near, 0.1f))
|
||||
camera.OrthographicNearPlane = near;
|
||||
|
||||
float far = camera.OrthographicFarPlane;
|
||||
if (ImGui.DragFloat("Far", &far, 0.1f))
|
||||
camera.OrthographicFarPlane = far;
|
||||
}
|
||||
|
||||
bool fixedAspectRatio = camera.FixedAspectRatio;
|
||||
if (ImGui.Checkbox("Fixed Aspect Ratio", &fixedAspectRatio))
|
||||
camera.FixedAspectRatio = fixedAspectRatio;
|
||||
|
||||
if (fixedAspectRatio)
|
||||
{
|
||||
float aspect = camera.AspectRatio;
|
||||
if (ImGui.DragFloat("Aspect Ratio", &aspect, 0.1f))
|
||||
camera.AspectRatio = aspect;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ShowSpriteRendererComponentEditor(Entity entity, SpriterRendererComponent* spriteRendererComponent)
|
||||
{
|
||||
ImGui.ColorEdit4("Color", ref spriteRendererComponent.Color);
|
||||
}
|
||||
|
||||
private static void ShowAddComponentButton(Entity entity)
|
||||
{
|
||||
static char8[128] searchBuffer = .();
|
||||
static StringView searchFilter = .();
|
||||
|
||||
static float buttonWidth = 100;
|
||||
|
||||
ImGui.NewLine();
|
||||
ImGui.Separator();
|
||||
ImGui.NewLine();
|
||||
|
||||
void ShowComponentButton<TComponent>(String name) where TComponent : struct, new
|
||||
{
|
||||
float textWidth = ImGui.CalcTextSize(name.CStr()).x;
|
||||
buttonWidth = Math.Max(buttonWidth, textWidth + ImGui.GetStyle().FramePadding.x * 2);
|
||||
|
||||
if (name.Contains(searchFilter, true) && ImGui.Selectable(name))
|
||||
{
|
||||
if (!entity.HasComponent<TComponent>())
|
||||
entity.AddComponent<TComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
float textWidth = ImGui.CalcTextSize("Add Component...").x;
|
||||
buttonWidth = Math.Max(buttonWidth, textWidth + ImGui.GetStyle().FramePadding.x * 2);
|
||||
|
||||
ImGui.PushItemWidth(buttonWidth);
|
||||
|
||||
ImGui.NewLine();
|
||||
ImGui.SameLine(ImGui.GetContentRegionMax().x / 2 - buttonWidth / 2);
|
||||
|
||||
if (ImGui.BeginCombo("##add_component_combo", "Add Component...", .NoArrowButton))
|
||||
{
|
||||
if (ImGui.InputText("##search_component_name", &searchBuffer, (.)searchBuffer.Count))
|
||||
{
|
||||
searchFilter = .(&searchBuffer);
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
ShowComponentButton<CameraComponent>("Camera");
|
||||
ShowComponentButton<SpriterRendererComponent>("Sprite Renderer");
|
||||
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
|
||||
ImGui.PopItemWidth();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace GlitchyEditor.EditWindows
|
||||
{
|
||||
abstract class EditorWindow
|
||||
{
|
||||
protected Editor _editor;
|
||||
|
||||
protected bool _open = true;
|
||||
protected bool _hasFocus;
|
||||
|
||||
public bool Open
|
||||
{
|
||||
get => _open;
|
||||
set => _open = value;
|
||||
}
|
||||
|
||||
public bool HasFocus
|
||||
{
|
||||
get => _hasFocus;
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
if(!_open)
|
||||
return;
|
||||
|
||||
InternalShow();
|
||||
}
|
||||
|
||||
protected abstract void InternalShow();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
using GlitchyEngine.Collections;
|
||||
using GlitchyEngine.World;
|
||||
using ImGui;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using GlitchyEngine;
|
||||
|
||||
namespace GlitchyEditor.EditWindows
|
||||
{
|
||||
using internal GlitchyEditor;
|
||||
|
||||
/// A window for viewing and editing the scene hierarchy
|
||||
class EntityHierarchyWindow : EditorWindow
|
||||
{
|
||||
public const String s_WindowTitle = "Entity Hierarchy";
|
||||
|
||||
/// Buffer for the entity search string.
|
||||
private char8[64] _entitySearchChars;
|
||||
|
||||
private Scene _scene;
|
||||
|
||||
private List<Entity> _selectedEntities = new .() ~ delete _;
|
||||
|
||||
public List<Entity> SelectedEntities => _selectedEntities;
|
||||
|
||||
public this(Scene scene)
|
||||
{
|
||||
SetContext(scene);
|
||||
}
|
||||
|
||||
public void SetContext(Scene scene)
|
||||
{
|
||||
_scene = scene;
|
||||
}
|
||||
|
||||
protected override void InternalShow()
|
||||
{
|
||||
if(!ImGui.Begin(s_WindowTitle, &_open, .MenuBar))
|
||||
{
|
||||
ImGui.End();
|
||||
return;
|
||||
}
|
||||
|
||||
ShowEntityHierarchyMenuBar();
|
||||
|
||||
if (ImGui.BeginPopupContextWindow(s_WindowTitle))
|
||||
{
|
||||
Show_ContextMenu_Create(true, false, false);
|
||||
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
|
||||
ShowEntityHierarchy();
|
||||
|
||||
if ((ImGui.IsMouseDown(.Left) || ImGui.IsMouseDown(.Right)) && !ImGui.IsAnyItemHovered() && !ImGui.GetIO().KeyCtrl && ImGui.IsWindowHovered(.AllowWhenBlockedByPopup))
|
||||
_selectedEntities.Clear();
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
|
||||
/// Returns whether or not all selected entities have the same parent.
|
||||
internal bool AllSelectionsOnSameLevel()
|
||||
{
|
||||
EcsEntity? parent = .InvalidEntity;
|
||||
|
||||
for(var selectedEntity in _selectedEntities)
|
||||
{
|
||||
var transformComponent = selectedEntity.GetComponent<TransformComponent>();
|
||||
|
||||
if(parent == .InvalidEntity)
|
||||
{
|
||||
parent = transformComponent.Parent;
|
||||
}
|
||||
else if(transformComponent.Parent != parent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Finds all children of the given entity and stores their IDs in the given list.
|
||||
internal void FindChildren(EcsEntity entity, List<EcsEntity> entities)
|
||||
{
|
||||
for(var (child, childTransform) in _scene.[Friend]_ecsWorld.Enumerate<TransformComponent>())
|
||||
{
|
||||
if(childTransform.Parent == entity)
|
||||
{
|
||||
if(!entities.Contains(child))
|
||||
entities.Add(child);
|
||||
|
||||
FindChildren(child, entities);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deletes all selected entities and their children.
|
||||
internal void DeleteSelectedEntities()
|
||||
{
|
||||
for (var entity in _selectedEntities)
|
||||
{
|
||||
_scene.DestroyEntity(entity, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowEntityHierarchyMenuBar()
|
||||
{
|
||||
if(ImGui.BeginMenuBar())
|
||||
{
|
||||
Show_ContextMenu_Create(true, true, true);
|
||||
|
||||
Show_ContextMenu_Delete();
|
||||
|
||||
if(ImGui.MenuItem("Delete", null, false, !_selectedEntities.IsEmpty) ||
|
||||
(Input.IsKeyPressed(.Delete) && ImGui.IsWindowHovered()))
|
||||
{
|
||||
DeleteSelectedEntities();
|
||||
}
|
||||
|
||||
if(ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip("Deletes the selected Entities and their children.");
|
||||
|
||||
ImGui.Text("Search:");
|
||||
|
||||
ImGui.InputText(String.Empty, &_entitySearchChars, (.)_entitySearchChars.Count);
|
||||
|
||||
ImGui.EndMenuBar();
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new entity that is a child of the given entity.
|
||||
private void CreateChild(Entity? entity)
|
||||
{
|
||||
var newEntity = _scene.CreateEntity();
|
||||
|
||||
var transformCmp = newEntity.GetComponent<TransformComponent>();
|
||||
// Last entity in list is the entity that has been selected last.
|
||||
transformCmp.Parent = entity?.Handle ?? .InvalidEntity;
|
||||
}
|
||||
|
||||
/// Creates a new entity that is a parent of the selected entities.
|
||||
private void CreateParent()
|
||||
{
|
||||
if (_selectedEntities.IsEmpty || !AllSelectionsOnSameLevel())
|
||||
{
|
||||
Log.EngineLogger.Error("Cannot create parent entity.");
|
||||
return;
|
||||
}
|
||||
|
||||
var commonParent = _selectedEntities.Front.GetComponent<TransformComponent>();
|
||||
|
||||
var newEntity = _scene.CreateEntity();
|
||||
|
||||
if(commonParent != null)
|
||||
{
|
||||
// parent of selected entities is parent of the new entity.
|
||||
// (which is why this doesn't work if the entities don't have the same parent)
|
||||
var newEntityTransform = newEntity.GetComponent<TransformComponent>();
|
||||
newEntityTransform.Parent = commonParent.Parent;
|
||||
}
|
||||
|
||||
// new entity is parent of all selected entities.
|
||||
for(var selectedEntity in _selectedEntities)
|
||||
{
|
||||
var selectedTransform = selectedEntity.GetComponent<TransformComponent>();
|
||||
selectedTransform.Parent = newEntity.Handle;
|
||||
}
|
||||
}
|
||||
|
||||
private void Show_ContextMenu_Create(bool allowEmpty = true, bool allowChild = true, bool allowParent = true)
|
||||
{
|
||||
if (ImGui.BeginMenu("Create"))
|
||||
{
|
||||
if (allowEmpty)
|
||||
{
|
||||
if(ImGui.MenuItem("Empty Entity"))
|
||||
{
|
||||
if (_selectedEntities.IsEmpty)
|
||||
_scene.CreateEntity();
|
||||
else
|
||||
{
|
||||
Entity? parent = _selectedEntities.Back.Parent;
|
||||
CreateChild(parent);
|
||||
}
|
||||
}
|
||||
|
||||
if(ImGui.IsItemHovered())
|
||||
{
|
||||
ImGui.SetTooltip("Create a new Entity.");
|
||||
}
|
||||
}
|
||||
|
||||
if (allowChild)
|
||||
{
|
||||
if(ImGui.MenuItem("Empty Child", null, false, !_selectedEntities.IsEmpty))
|
||||
{
|
||||
CreateChild(_selectedEntities.Back);
|
||||
}
|
||||
|
||||
if(ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip("Create a new Entity that is a child of the selected entity.");
|
||||
}
|
||||
|
||||
if (allowParent)
|
||||
{
|
||||
if(ImGui.MenuItem("Parent", null, false, !_selectedEntities.IsEmpty && AllSelectionsOnSameLevel()))
|
||||
{
|
||||
CreateParent();
|
||||
}
|
||||
|
||||
if(ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip("Create a new Entity that is the parent of the selected entities.");
|
||||
}
|
||||
|
||||
ImGui.EndMenu();
|
||||
}
|
||||
|
||||
if(ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip("Create a new Entity.");
|
||||
}
|
||||
|
||||
private bool Show_ContextMenu_Delete()
|
||||
{
|
||||
bool deleted = false;
|
||||
|
||||
if(ImGui.MenuItem("Delete", null, false, !_selectedEntities.IsEmpty))
|
||||
{
|
||||
DeleteSelectedEntities();
|
||||
|
||||
deleted = true;
|
||||
}
|
||||
|
||||
if(ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip("Deletes the selected Entities and their children.");
|
||||
|
||||
return deleted;
|
||||
}
|
||||
|
||||
private void ImGuiPrintEntityTree(TreeNode<Entity> tree)
|
||||
{
|
||||
String name = null;
|
||||
|
||||
var nameComponent = tree.Value.GetComponent<DebugNameComponent>();
|
||||
|
||||
if(nameComponent != null)
|
||||
{
|
||||
name = nameComponent.DebugName;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = scope:: $"Entity {(tree.Value.Handle.[Friend]Index)}";
|
||||
}
|
||||
|
||||
ImGui.TreeNodeFlags flags = .OpenOnArrow | .DefaultOpen | .SpanAvailWidth;
|
||||
|
||||
if(tree.Children.Count == 0)
|
||||
flags |= .Leaf;
|
||||
|
||||
bool inSelectedList = _selectedEntities.Contains(tree.Value);
|
||||
|
||||
if(inSelectedList)
|
||||
flags |= .Selected;
|
||||
|
||||
bool isOpen = ImGui.TreeNodeEx((void*)(uint)tree.Value.Handle.[Friend]Index, flags, $"{name}");
|
||||
|
||||
ImGui.PushID((void*)(uint)tree.Value.Handle.[Friend]Index);
|
||||
|
||||
bool deleted = false;
|
||||
|
||||
if (ImGui.BeginPopupContextItem("treeNodePopup"))
|
||||
{
|
||||
Show_ContextMenu_Create(true, true, true);
|
||||
deleted = Show_ContextMenu_Delete();
|
||||
|
||||
ImGui.EndPopup();
|
||||
}
|
||||
|
||||
ImGui.PopID();
|
||||
|
||||
if (deleted)
|
||||
return;
|
||||
|
||||
if(ImGui.BeginDragDropSource())
|
||||
{
|
||||
ImGui.SetDragDropPayload("DND_Entity", &tree.Value, sizeof(Entity));
|
||||
|
||||
ImGui.Text(name);
|
||||
|
||||
ImGui.EndDragDropSource();
|
||||
}
|
||||
|
||||
if(ImGui.BeginDragDropTarget())
|
||||
{
|
||||
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("DND_Entity");
|
||||
|
||||
if(payload != null)
|
||||
{
|
||||
Log.ClientLogger.AssertDebug(payload.DataSize == sizeof(Entity));
|
||||
|
||||
Entity movedEntity = *(Entity*)payload.Data;
|
||||
|
||||
bool dropLegal = true;
|
||||
|
||||
Entity walker = tree.Value;
|
||||
|
||||
// make sure the dropped entity is not a parent of the entity we dropped it on.
|
||||
while(true)
|
||||
{
|
||||
var parentTransform = walker.GetComponent<TransformComponent>();
|
||||
|
||||
if(parentTransform.Parent == .InvalidEntity)
|
||||
{
|
||||
dropLegal = true;
|
||||
break;
|
||||
}
|
||||
else if(parentTransform.Parent == movedEntity.Handle)
|
||||
{
|
||||
dropLegal = false;
|
||||
break;
|
||||
}
|
||||
|
||||
walker = .(parentTransform.Parent, _scene);
|
||||
}
|
||||
|
||||
if(dropLegal)
|
||||
{
|
||||
var movedEntityTransform = movedEntity.GetComponent<TransformComponent>();
|
||||
movedEntityTransform.Parent = tree.Value.Handle;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndDragDropTarget();
|
||||
}
|
||||
|
||||
bool clicked = ImGui.IsItemClicked(.Left);
|
||||
bool clickedRight = ImGui.IsItemClicked(.Right);
|
||||
|
||||
if(isOpen)
|
||||
{
|
||||
for(var child in tree.Children)
|
||||
{
|
||||
ImGuiPrintEntityTree(child);
|
||||
}
|
||||
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
if (clicked || clickedRight)
|
||||
{
|
||||
if (inSelectedList && !clickedRight)
|
||||
{
|
||||
_selectedEntities.Remove(tree.Value);
|
||||
inSelectedList = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ImGui.GetIO().KeyCtrl && !clickedRight)
|
||||
{
|
||||
_selectedEntities.Clear();
|
||||
}
|
||||
|
||||
_selectedEntities.Add(tree.Value);
|
||||
inSelectedList = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowEntityHierarchy()
|
||||
{
|
||||
StringView searchString = StringView(&_entitySearchChars);
|
||||
|
||||
if(searchString.IsWhiteSpace)
|
||||
{
|
||||
// Show entity hierarchy as tree
|
||||
|
||||
TreeNode<Entity> root = scope .(Entity(.InvalidEntity, _scene));
|
||||
|
||||
TreeNode<Entity> InsertIntoTree(Entity entity)
|
||||
{
|
||||
var transform = entity.GetComponent<TransformComponent>();
|
||||
|
||||
if(transform == null)
|
||||
{
|
||||
return root.AddChild(entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
var parentEntity = Entity(transform.Parent, _scene);
|
||||
|
||||
var parentNode = root.FindNode(parentEntity);
|
||||
|
||||
if(parentNode == null)
|
||||
parentNode = InsertIntoTree(parentEntity);
|
||||
|
||||
return parentNode.AddChild(entity);
|
||||
}
|
||||
}
|
||||
|
||||
for(var entityId in _scene.[Friend]_ecsWorld.Enumerate())
|
||||
{
|
||||
Entity entity = .(entityId, _scene);
|
||||
|
||||
//if (!entity.HasComponent<EditorComponent>())
|
||||
InsertIntoTree(entity);
|
||||
}
|
||||
|
||||
if(ImGui.TreeNodeEx("Scene", .DefaultOpen))
|
||||
{
|
||||
if(ImGui.BeginDragDropTarget())
|
||||
{
|
||||
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("DND_Entity");
|
||||
|
||||
if(payload != null)
|
||||
{
|
||||
Log.ClientLogger.AssertDebug(payload.DataSize == sizeof(Entity));
|
||||
|
||||
Entity movedEntity = *(Entity*)payload.Data;
|
||||
|
||||
// Also mark transform as dirty
|
||||
var transformComponent = movedEntity.GetComponent<TransformComponent>();
|
||||
transformComponent.Parent = .InvalidEntity;
|
||||
//transformComponent?.IsDirty = true;
|
||||
}
|
||||
|
||||
ImGui.EndDragDropTarget();
|
||||
}
|
||||
|
||||
for(var child in root.Children)
|
||||
{
|
||||
ImGuiPrintEntityTree(child);
|
||||
}
|
||||
|
||||
ImGui.TreePop();
|
||||
}
|
||||
}
|
||||
// Otherwise
|
||||
else
|
||||
{
|
||||
// Show search results as flat list
|
||||
|
||||
List<StringView> searchTokens = new:ScopedAlloc! .(searchString.Split(' ', .RemoveEmptyEntries));
|
||||
|
||||
worldEnumeration:
|
||||
for(var entityId in _scene.[Friend]_ecsWorld.Enumerate())
|
||||
{
|
||||
Entity entity = .(entityId, _scene);
|
||||
|
||||
String name = null;
|
||||
|
||||
var nameComponent = entity.GetComponent<DebugNameComponent>();
|
||||
|
||||
if(nameComponent != null)
|
||||
{
|
||||
name = nameComponent.DebugName;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = scope:worldEnumeration $"Entity {entityId.[Friend]Index}";
|
||||
}
|
||||
|
||||
StringView nameView = StringView(name);
|
||||
|
||||
for(var token in searchTokens)
|
||||
{
|
||||
if(nameView.IndexOf(token, true) == -1)
|
||||
{
|
||||
continue worldEnumeration;
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiPrintEntityTree(scope .(entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using ImGui;
|
||||
using GlitchyEngine.Renderer;
|
||||
using GlitchyEngine.Math;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEngine.World;
|
||||
using ImGuizmo;
|
||||
|
||||
namespace GlitchyEditor.EditWindows
|
||||
{
|
||||
class SceneViewportWindow : EditorWindow
|
||||
{
|
||||
//public OldCamera _camera;
|
||||
|
||||
public const String s_WindowTitle = "Scene";
|
||||
|
||||
private RenderTarget2D _renderTarget ~ _?.ReleaseRef();
|
||||
|
||||
public Event<EventHandler<Vector2>> ViewportSizeChangedEvent ~ _.Dispose();
|
||||
|
||||
public RenderTarget2D RenderTarget
|
||||
{
|
||||
get => _renderTarget;
|
||||
set
|
||||
{
|
||||
if(_renderTarget == value)
|
||||
return;
|
||||
|
||||
SetReference!(_renderTarget, value);
|
||||
}
|
||||
}
|
||||
|
||||
public this(Editor editor)
|
||||
{
|
||||
_editor = editor;
|
||||
}
|
||||
|
||||
private ImGui.Vec2 oldViewportSize;
|
||||
private bool viewPortChanged;
|
||||
|
||||
public Entity CameraEntity { get; set; }
|
||||
|
||||
protected override void InternalShow()
|
||||
{
|
||||
ImGui.PushStyleVar(.WindowPadding, ImGui.Vec2(1, 1));
|
||||
defer ImGui.PopStyleVar();
|
||||
|
||||
if(!ImGui.Begin(s_WindowTitle, &_open, .NoScrollbar))
|
||||
{
|
||||
ImGui.End();
|
||||
return;
|
||||
}
|
||||
|
||||
if(ImGui.IsWindowHovered() && Input.IsMouseButtonPressing(.RightButton))
|
||||
{
|
||||
var currentWindow = ImGui.GetCurrentWindow();
|
||||
ImGui.FocusWindow(currentWindow);
|
||||
}
|
||||
|
||||
_hasFocus = ImGui.IsWindowFocused();
|
||||
|
||||
var viewportSize = ImGui.GetContentRegionAvail();
|
||||
|
||||
if(_renderTarget != null)
|
||||
{
|
||||
ImGui.Image(_renderTarget, viewportSize);
|
||||
}
|
||||
|
||||
DrawImGuizmo(viewportSize);
|
||||
|
||||
ImGui.End();
|
||||
|
||||
if(oldViewportSize != viewportSize)
|
||||
{
|
||||
ViewportSizeChangedEvent.Invoke(this, (Vector2)viewportSize);
|
||||
viewPortChanged = true;
|
||||
oldViewportSize = viewportSize;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawImGuizmo(ImGui.Vec2 viewportSize)
|
||||
{
|
||||
ImGuizmo.SetDrawlist();
|
||||
|
||||
var topLeft = ImGui.GetWindowPos();
|
||||
var cntMin = ImGui.GetWindowContentRegionMin();
|
||||
|
||||
topLeft.x += cntMin.x;
|
||||
topLeft.y += cntMin.y;
|
||||
ImGuizmo.SetRect(topLeft.x, topLeft.y, viewportSize.x, viewportSize.y);
|
||||
|
||||
var cameraTransformCmp = CameraEntity.GetComponent<TransformComponent>();
|
||||
var view = cameraTransformCmp.WorldTransform.Invert();
|
||||
|
||||
var cameraCmp = CameraEntity.GetComponent<CameraComponent>();
|
||||
var projection = cameraCmp.Camera.Projection;
|
||||
|
||||
Matrix mat = .Identity;
|
||||
ImGuizmo.DrawGrid((.)&view, (.)&projection, (.)&mat, 10);
|
||||
|
||||
if(_editor.SelectedEntities.Count > 0)
|
||||
{
|
||||
var entity = _editor.SelectedEntities.Front;
|
||||
|
||||
var transformCmp = _editor.World.GetComponent<TransformComponent>(entity);
|
||||
|
||||
var transform = transformCmp.LocalTransform;
|
||||
|
||||
ImGuizmo.SetRect(topLeft.x, topLeft.y, viewportSize.x, viewportSize.y);
|
||||
|
||||
ImGuizmo.Manipulate((.)&view, (.)&projection, .TRANSLATE, .LOCAL, (.)&transform);
|
||||
|
||||
transformCmp.LocalTransform = transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
using GlitchyEngine.World;
|
||||
using ImGui;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using GlitchyEngine.Collections;
|
||||
using GlitchyEditor.EditWindows;
|
||||
using GlitchyEngineHelper;
|
||||
using System.IO;
|
||||
using GlitchyEngineHelper.DotNet;
|
||||
|
||||
namespace GlitchyEditor
|
||||
{
|
||||
class Editor
|
||||
{
|
||||
private EcsWorld _ecsWorld;
|
||||
private Scene _scene;
|
||||
|
||||
private EntityHierarchyWindow _entityHierarchyWindow ~ delete _;
|
||||
private ComponentEditWindow _componentEditWindow ~ delete _;
|
||||
private SceneViewportWindow _sceneViewportWindow = new .(this) ~ delete _;
|
||||
|
||||
private List<EcsEntity> _selectedEntities = new .() ~ delete _;
|
||||
|
||||
public EcsWorld World => _ecsWorld;
|
||||
|
||||
public List<EcsEntity> SelectedEntities => _selectedEntities;
|
||||
|
||||
public EntityHierarchyWindow EntityHierarchyWindow => _entityHierarchyWindow;
|
||||
public ComponentEditWindow ComponentEditWindow => _componentEditWindow;
|
||||
public SceneViewportWindow SceneViewportWindow => _sceneViewportWindow;
|
||||
|
||||
/// Creates a new editor for the given world
|
||||
public this(Scene scene)
|
||||
{
|
||||
String exePath = Environment.GetExecutableFilePath(.. scope String());
|
||||
|
||||
String exeDir = Path.GetDirectoryPath(exePath, .. scope String());
|
||||
|
||||
String assemblyPath = scope String(exeDir, "/DotNetScriptingHelper.dll");
|
||||
|
||||
DotNetContext dotty = new DotNetContext(assemblyPath);
|
||||
defer delete dotty;
|
||||
|
||||
dotty.Init();
|
||||
|
||||
dotty.GetFunctionPointerUnmanagedCallersOnly("DotNetScriptingHelper.ScriptableEntity, DotNetScriptingHelper", "CreateInstance", out DotNetScriptComponent.CreateInstanceFn);
|
||||
dotty.GetFunctionPointerUnmanagedCallersOnly("DotNetScriptingHelper.ScriptableEntity, DotNetScriptingHelper", "UpdateEntity", out DotNetScriptComponent.UpdateInstanceFn);
|
||||
dotty.GetFunctionPointerUnmanagedCallersOnly("DotNetScriptingHelper.ScriptableEntity, DotNetScriptingHelper", "DestroyEntity", out DotNetScriptComponent.DestroyInstanceFn);
|
||||
|
||||
_scene = scene;
|
||||
_ecsWorld = _scene.[Friend]_ecsWorld;
|
||||
|
||||
_entityHierarchyWindow = new EntityHierarchyWindow(_scene);
|
||||
_componentEditWindow = new ComponentEditWindow(_entityHierarchyWindow);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
_entityHierarchyWindow.Show();
|
||||
_componentEditWindow.Show();
|
||||
_sceneViewportWindow.Show();
|
||||
}
|
||||
|
||||
/// Creates a new entity with a transform component.
|
||||
internal EcsEntity CreateEntityWithTransform()
|
||||
{
|
||||
var entity = _ecsWorld.NewEntity();
|
||||
|
||||
var transformComponent = ref *_ecsWorld.AssignComponent<TransformComponent>(entity);
|
||||
transformComponent = TransformComponent();
|
||||
|
||||
var nameComponent = ref *_ecsWorld.AssignComponent<DebugNameComponent>(entity);
|
||||
nameComponent.SetName("Entity");
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
/// Returns whether or not all selected entities have the same parent.
|
||||
internal bool AllSelectionsOnSameLevel()
|
||||
{
|
||||
EcsEntity? parent = .InvalidEntity;
|
||||
|
||||
for(var selectedEntity in _selectedEntities)
|
||||
{
|
||||
var parentComponent = _ecsWorld.GetComponent<ParentComponent>(selectedEntity);
|
||||
|
||||
if(parent == .InvalidEntity)
|
||||
{
|
||||
parent = parentComponent?.Entity;
|
||||
}
|
||||
else if(parentComponent?.Entity != parent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Finds all children of the given entity and stores their IDs in the given list.
|
||||
internal void FindChildren(EcsEntity entity, List<EcsEntity> entities)
|
||||
{
|
||||
for(var (child, childParent) in _ecsWorld.Enumerate<ParentComponent>())
|
||||
{
|
||||
if(childParent.Entity == entity)
|
||||
{
|
||||
if(!entities.Contains(child))
|
||||
entities.Add(child);
|
||||
|
||||
FindChildren(child, entities);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deletes all selected entities and their children.
|
||||
internal void DeleteSelectedEntities()
|
||||
{
|
||||
List<EcsEntity> entities = scope .();
|
||||
|
||||
for(var entity in _selectedEntities)
|
||||
{
|
||||
entities.Add(entity);
|
||||
|
||||
FindChildren(entity, entities);
|
||||
}
|
||||
|
||||
for(var entity in entities)
|
||||
{
|
||||
_ecsWorld.RemoveEntity(entity);
|
||||
}
|
||||
|
||||
_selectedEntities.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using GlitchyEngine;
|
||||
|
||||
namespace GlitchyEditor
|
||||
{
|
||||
class EditorApp : Application
|
||||
{
|
||||
public this()
|
||||
{
|
||||
PushLayer(new EditorLayer());
|
||||
}
|
||||
|
||||
[Export, LinkName("CreateApplication")]
|
||||
public static Application CreateApplication()
|
||||
{
|
||||
return new EditorApp();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using GlitchyEngine;
|
||||
using GlitchyEngine.Math;
|
||||
using GlitchyEngine.World;
|
||||
|
||||
namespace GlitchyEditor
|
||||
{
|
||||
class EditorCameraController : ScriptableEntity
|
||||
{
|
||||
private float _cameraTranslationSpeed = 2.0f;
|
||||
private float _cameraRotationSpeedX = 0.001f;
|
||||
private float _cameraRotationSpeedY = 0.001f;
|
||||
|
||||
public bool IsEnabled = false;
|
||||
|
||||
protected override void OnUpdate(GameTime gt)
|
||||
{
|
||||
if (!IsEnabled)
|
||||
return;
|
||||
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
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;
|
||||
|
||||
var transformComponent = transform;
|
||||
|
||||
if(movement != .Zero)
|
||||
{
|
||||
movement.Normalize();
|
||||
|
||||
movement *= (float)(gt.FrameTime.TotalSeconds) * _cameraTranslationSpeed;
|
||||
|
||||
Matrix view = transformComponent.WorldTransform.Invert();
|
||||
|
||||
Vector4 delta = Vector4(movement, 1.0f) * view;
|
||||
|
||||
transformComponent.Position = transformComponent.Position + delta.XYZ;
|
||||
}
|
||||
|
||||
// Camera rotation
|
||||
var mouseDelta = Input.GetMouseMovement();
|
||||
|
||||
float rotY = mouseDelta.X * _cameraRotationSpeedX;
|
||||
float rotX = mouseDelta.Y * _cameraRotationSpeedY;
|
||||
|
||||
transformComponent.RotationEuler = transformComponent.RotationEuler + .(rotX, rotY, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
using System;
|
||||
using ImGui;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEditor.EditWindows;
|
||||
using GlitchyEngine.Events;
|
||||
using GlitchyEngine.ImGui;
|
||||
using GlitchyEngine.Math;
|
||||
using GlitchyEngine.Renderer;
|
||||
using GlitchyEngine.World;
|
||||
|
||||
namespace GlitchyEditor
|
||||
{
|
||||
class EditorLayer : Layer
|
||||
{
|
||||
RasterizerState _rasterizerState ~ _?.ReleaseRef();
|
||||
RasterizerState _rasterizerStateClockWise ~ _?.ReleaseRef();
|
||||
|
||||
GraphicsContext _context ~ _.ReleaseRef();
|
||||
|
||||
BlendState _alphaBlendState ~ _.ReleaseRef();
|
||||
BlendState _opaqueBlendState ~ _.ReleaseRef();
|
||||
DepthStencilState _depthStencilState ~ _.ReleaseRef();
|
||||
|
||||
Scene _scene = new Scene() ~ delete _;
|
||||
|
||||
Editor _editor ~ delete _;
|
||||
|
||||
RenderTarget2D _viewportTarget ~ _?.ReleaseRef();
|
||||
|
||||
SettingsWindow _settingsWindow = new .() ~ delete _;
|
||||
|
||||
Entity _cameraEntity;
|
||||
Entity _otherCameraEntity;
|
||||
|
||||
class CameraController : ScriptableEntity
|
||||
{
|
||||
protected override void OnCreate()
|
||||
{
|
||||
Log.EngineLogger.Trace("Cam controller created!");
|
||||
}
|
||||
|
||||
protected override void OnUpdate(GameTime gameTime)
|
||||
{
|
||||
var transformCmp = GetComponent<TransformComponent>();
|
||||
|
||||
Vector3 position = transformCmp.Position;
|
||||
|
||||
if (Input.IsKeyPressed(Key.A))
|
||||
{
|
||||
position.X -= gameTime.DeltaTime;
|
||||
}
|
||||
if (Input.IsKeyPressed(Key.D))
|
||||
{
|
||||
position.X += gameTime.DeltaTime;
|
||||
}
|
||||
if (Input.IsKeyPressed(Key.W))
|
||||
{
|
||||
position.Y += gameTime.DeltaTime;
|
||||
}
|
||||
if (Input.IsKeyPressed(Key.S))
|
||||
{
|
||||
position.Y -= gameTime.DeltaTime;
|
||||
}
|
||||
|
||||
transformCmp.Position = position;
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
Log.EngineLogger.Trace("Cam controller destroyed!");
|
||||
}
|
||||
}
|
||||
|
||||
public this() : base("Example")
|
||||
{
|
||||
Application.Get().Window.IsVSync = false;
|
||||
|
||||
InitGraphics();
|
||||
|
||||
{
|
||||
_cameraEntity = _scene.CreateEntity("Camera Entity");
|
||||
let camera = _cameraEntity.AddComponent<CameraComponent>();
|
||||
camera.Camera.SetPerspective(MathHelper.ToRadians(75), 0.1f, 10000.0f);
|
||||
camera.Primary = true;
|
||||
camera.FixedAspectRatio = false;
|
||||
let transform = _cameraEntity.GetComponent<TransformComponent>();
|
||||
transform.Position = .(0, 0, -5);
|
||||
|
||||
_cameraEntity.AddComponent<NativeScriptComponent>().Bind<EditorCameraController>();
|
||||
_cameraEntity.AddComponent<EditorComponent>();
|
||||
}
|
||||
|
||||
{
|
||||
_otherCameraEntity = _scene.CreateEntity("Other Camera Entity");
|
||||
let camera = _otherCameraEntity.AddComponent<CameraComponent>();
|
||||
camera.Camera.SetPerspective(MathHelper.ToRadians(45), 0.1f, 1000.0f);
|
||||
camera.Primary = false;
|
||||
camera.FixedAspectRatio = false;
|
||||
let transform = _otherCameraEntity.GetComponent<TransformComponent>();
|
||||
transform.Position = .(0, 0, -5);
|
||||
|
||||
_otherCameraEntity.AddComponent<NativeScriptComponent>().Bind<EditorCameraController>();
|
||||
_otherCameraEntity.AddComponent<EditorComponent>();
|
||||
}
|
||||
|
||||
InitEditor();
|
||||
}
|
||||
|
||||
private void InitGraphics()
|
||||
{
|
||||
_context = Application.Get().Window.Context..AddRef();
|
||||
|
||||
RasterizerStateDescription rsDesc = .(.Solid, .Back, true);
|
||||
_rasterizerState = new RasterizerState(rsDesc);
|
||||
|
||||
rsDesc.FrontCounterClockwise = false;
|
||||
_rasterizerStateClockWise = new RasterizerState(rsDesc);
|
||||
|
||||
BlendStateDescription blendDesc = .();
|
||||
blendDesc.RenderTarget[0] = .(true, .SourceAlpha, .InvertedSourceAlpha, .Add, .SourceAlpha, .InvertedSourceAlpha, .Add, .All);
|
||||
_alphaBlendState = new BlendState(blendDesc);
|
||||
_opaqueBlendState = new BlendState(.Default);
|
||||
|
||||
DepthStencilStateDescription dsDesc = .();
|
||||
_depthStencilState = new DepthStencilState(dsDesc);
|
||||
|
||||
_viewportTarget = new RenderTarget2D(RenderTarget2DDescription(.R8G8B8A8_UNorm, 100, 100) {DepthStencilFormat = .D32_Float});
|
||||
_viewportTarget.SamplerState = SamplerStateManager.LinearClamp;
|
||||
}
|
||||
|
||||
private void InitEditor()
|
||||
{
|
||||
_editor = new Editor(_scene);
|
||||
_editor.SceneViewportWindow.ViewportSizeChangedEvent.Add(new (s, e) => ViewportSizeChanged(s, e));
|
||||
|
||||
//_editor.[Friend]CreateEntityWithTransform();
|
||||
|
||||
_editor.SceneViewportWindow.CameraEntity = _cameraEntity;
|
||||
}
|
||||
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
var scriptComponent = _cameraEntity.GetComponent<NativeScriptComponent>();
|
||||
|
||||
if (var camController = scriptComponent.Instance as EditorCameraController)
|
||||
{
|
||||
camController.IsEnabled = (_editor.SceneViewportWindow.HasFocus && Input.IsMouseButtonPressed(.RightButton));
|
||||
}
|
||||
|
||||
//TransformSystem.Update(_world);
|
||||
|
||||
RenderCommand.Clear(_viewportTarget, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0);
|
||||
|
||||
RenderCommand.SetRenderTarget(_viewportTarget, 0, true);
|
||||
RenderCommand.BindRenderTargets();
|
||||
|
||||
RenderCommand.SetViewport(Viewport(0, 0, _viewportTarget.Width, _viewportTarget.Height));
|
||||
|
||||
RenderCommand.SetBlendState(_alphaBlendState);
|
||||
RenderCommand.SetDepthStencilState(_depthStencilState);
|
||||
|
||||
//Renderer.BeginScene(_cameraController.Camera);
|
||||
|
||||
//DebugRenderer.Render(_scene.[Friend]_ecsWorld);
|
||||
|
||||
//Renderer.EndScene();
|
||||
|
||||
_scene.Update(gameTime);
|
||||
|
||||
RenderCommand.Clear(null, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0);
|
||||
|
||||
RenderCommand.SetRenderTarget(null, 0, true);
|
||||
RenderCommand.BindRenderTargets();
|
||||
|
||||
RenderCommand.SetViewport(_context.SwapChain.BackbufferViewport);
|
||||
}
|
||||
|
||||
public override void OnEvent(Event event)
|
||||
{
|
||||
EventDispatcher dispatcher = EventDispatcher(event);
|
||||
|
||||
dispatcher.Dispatch<ImGuiRenderEvent>(scope (e) => OnImGuiRender(e));
|
||||
dispatcher.Dispatch<WindowResizeEvent>(scope (e) => OnWindowResize(e));
|
||||
}
|
||||
|
||||
ImGui.ID _mainDockspaceId;
|
||||
|
||||
private bool OnImGuiRender(ImGuiRenderEvent event)
|
||||
{
|
||||
ImGui.Begin("Test");
|
||||
|
||||
static bool cameraA = true;
|
||||
|
||||
if (ImGui.Checkbox("Camera A", &cameraA))
|
||||
{
|
||||
_cameraEntity.GetComponent<CameraComponent>().Primary = cameraA;
|
||||
_otherCameraEntity.GetComponent<CameraComponent>().Primary = !cameraA;
|
||||
}
|
||||
|
||||
ImGui.End();
|
||||
|
||||
ImGui.Viewport* viewport = ImGui.GetMainViewport();
|
||||
ImGui.DockSpaceOverViewport(viewport);
|
||||
|
||||
DrawMainMenuBar();
|
||||
|
||||
_editor.SceneViewportWindow.RenderTarget = _viewportTarget;
|
||||
|
||||
_editor.Update();
|
||||
|
||||
_settingsWindow.Show();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void DrawMainMenuBar()
|
||||
{
|
||||
ImGui.BeginMainMenuBar();
|
||||
|
||||
if(ImGui.BeginMenu("File", true))
|
||||
{
|
||||
if (ImGui.MenuItem("Settings"))
|
||||
_settingsWindow.Open = true;
|
||||
|
||||
ImGui.EndMenu();
|
||||
}
|
||||
|
||||
if(ImGui.BeginMenu("View", true))
|
||||
{
|
||||
if(ImGui.MenuItem(EntityHierarchyWindow.s_WindowTitle))
|
||||
{
|
||||
_editor.EntityHierarchyWindow.Open = true;
|
||||
}
|
||||
|
||||
if(ImGui.MenuItem(ComponentEditWindow.s_WindowTitle))
|
||||
{
|
||||
_editor.ComponentEditWindow.Open = true;
|
||||
}
|
||||
|
||||
if(ImGui.MenuItem(SceneViewportWindow.s_WindowTitle))
|
||||
{
|
||||
_editor.SceneViewportWindow.Open = true;
|
||||
}
|
||||
|
||||
ImGui.EndMenu();
|
||||
}
|
||||
|
||||
|
||||
ImGui.EndMainMenuBar();
|
||||
}
|
||||
|
||||
private bool OnWindowResize(WindowResizeEvent e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ViewportSizeChanged(Object sender, Vector2 viewportSize)
|
||||
{
|
||||
uint32 sizeX = (uint32)viewportSize.X;
|
||||
uint32 sizeY = (uint32)viewportSize.Y;
|
||||
|
||||
if(sizeX == 0 || sizeY == 0)
|
||||
return;
|
||||
|
||||
_viewportTarget.Resize(sizeX, sizeY);
|
||||
|
||||
_scene.OnViewportResize(sizeX, sizeY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
using GlitchyEditor.EditWindows;
|
||||
using ImGui;
|
||||
using GlitchyEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
|
||||
namespace GlitchyEditor
|
||||
{
|
||||
class SettingsWindow : EditorWindow
|
||||
{
|
||||
class Binding
|
||||
{
|
||||
public Object SettingsObject;
|
||||
public String Name ~ delete _;
|
||||
public String FieldName ~ delete _;
|
||||
|
||||
public this(StringView name, StringView fieldName, Object settingsObject)
|
||||
{
|
||||
Name = new String(name);
|
||||
FieldName = new String(fieldName);
|
||||
SettingsObject = settingsObject;
|
||||
}
|
||||
}
|
||||
|
||||
class Category
|
||||
{
|
||||
public List<Binding> _bindings = new .() ~ DeleteContainerAndItems!(_);
|
||||
|
||||
public String Header ~ delete _;
|
||||
|
||||
public Object SettingsObject;
|
||||
|
||||
public this(String header)
|
||||
{
|
||||
Header = new String(header);
|
||||
}
|
||||
|
||||
public void AddSetting(StringView name, StringView fieldName, Object settingsObject = null)
|
||||
{
|
||||
Binding binding = new .(name, fieldName, settingsObject ?? SettingsObject);
|
||||
_bindings.Add(binding);
|
||||
}
|
||||
}
|
||||
|
||||
Settings _settings;
|
||||
|
||||
bool _settingsChanged = false;
|
||||
|
||||
private Dictionary<String, Category> _categories = new .() ~ DeleteDictionaryAndValues!(_);
|
||||
|
||||
public this()
|
||||
{
|
||||
_open = false;
|
||||
|
||||
_settings = Application.Get().Settings;
|
||||
|
||||
Create();
|
||||
}
|
||||
|
||||
void Create()
|
||||
{
|
||||
ScanForSettings(_settings);
|
||||
|
||||
/*for (ISettings settings in _settings.[Friend]_userSettings)
|
||||
{
|
||||
ScanForSettings(settings);
|
||||
}*/
|
||||
}
|
||||
|
||||
Category AddCategory(String header)
|
||||
{
|
||||
if (_categories.TryGetValue(header, let category))
|
||||
return category;
|
||||
|
||||
Category newCat = new Category(header);
|
||||
_categories.Add(newCat.Header, newCat);
|
||||
|
||||
return newCat;
|
||||
}
|
||||
|
||||
void ScanForSettings(Object container)
|
||||
{
|
||||
Type type = container.GetType();
|
||||
|
||||
for (var field in type.GetFields())
|
||||
{
|
||||
Result<SettingAttribute> settingResult = field.GetCustomAttribute<SettingAttribute>();
|
||||
|
||||
if (settingResult case .Ok(let settingInfo))
|
||||
{
|
||||
AddSetting(settingInfo.Category, settingInfo.Name, field.Name, container);
|
||||
}
|
||||
|
||||
Result<SettingContainerAttribute> containerResult = field.GetCustomAttribute<SettingContainerAttribute>();
|
||||
|
||||
if (containerResult case .Ok(let containerInfo))
|
||||
{
|
||||
var res = field.GetValue<Object>(container, let childContainer);
|
||||
|
||||
if (res case .Err(let error))
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to get settings container. Error: {error}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ScanForSettings(childContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddSetting(String categoryName, String name, StringView fieldName, Object container)
|
||||
{
|
||||
Category category = AddCategory(categoryName);
|
||||
|
||||
category.AddSetting(name, fieldName, container);
|
||||
}
|
||||
|
||||
protected override void InternalShow()
|
||||
{
|
||||
ImGui.Begin("Settings", &_open, .NoDocking);
|
||||
defer ImGui.End();
|
||||
|
||||
// Leave room for 1 line below us
|
||||
ImGui.BeginChild("item view", ImGui.Vec2(0, -ImGui.GetFrameHeightWithSpacing()));
|
||||
|
||||
ImGui.BeginTabBar("##Tabs");
|
||||
|
||||
for (Category category in _categories.Values)
|
||||
{
|
||||
if (ImGui.BeginTabItem(category.Header))
|
||||
{
|
||||
ImGui.Columns(2);
|
||||
defer ImGui.Columns(1);
|
||||
ImGui.SetColumnWidth(0, 100);
|
||||
|
||||
for (Binding setting in category._bindings)
|
||||
{
|
||||
ImGui.TextUnformatted(setting.Name);
|
||||
|
||||
ImGui.NextColumn();
|
||||
|
||||
Type settingsObjectType = setting.SettingsObject.GetType();
|
||||
|
||||
Result<FieldInfo> result = settingsObjectType.GetField(setting.FieldName);
|
||||
|
||||
if (result case .Err)
|
||||
{
|
||||
ImGui.PushStyleColor(.Text, ImGui.Vec4(1f, 0f, 0f, 1f));
|
||||
ImGui.Text($"Field {setting.FieldName} not found.");
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
FieldInfo fieldInfo = result.Get();
|
||||
|
||||
Type fieldType = fieldInfo.FieldType;
|
||||
|
||||
mixin GetSettingValue<T>()
|
||||
{
|
||||
var error = fieldInfo.GetValue<T>(setting.SettingsObject, var value);
|
||||
|
||||
if (error case .Err(let err))
|
||||
{
|
||||
Log.EngineLogger.Error($"Could not get value of setting {setting.FieldName}. Error: {err}");
|
||||
|
||||
ImGui.PushStyleColor(.Text, ImGui.Vec4(1f, 0f, 0f, 1f));
|
||||
ImGui.Text($"Could not get value of setting {setting.FieldName}. Error: {err}");
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
mixin SetSettingValue<T>(T value)
|
||||
{
|
||||
var error = fieldInfo.SetValue(setting.SettingsObject, value);
|
||||
|
||||
if (error case .Err(let err))
|
||||
{
|
||||
Log.EngineLogger.Error($"Could not set value of setting {setting.FieldName}. Error: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
switch (fieldType)
|
||||
{
|
||||
case typeof(int32):
|
||||
int32 value = GetSettingValue!<int32>();
|
||||
|
||||
if (!ImGui.InputInt(scope $"##{setting.Name}", &value))
|
||||
break;
|
||||
|
||||
SetSettingValue!(value);
|
||||
|
||||
_settingsChanged = true;
|
||||
case typeof(String):
|
||||
String value = GetSettingValue!<String>();
|
||||
|
||||
char8[256] buffer = .();
|
||||
|
||||
value.CopyTo(buffer);
|
||||
|
||||
if (ImGui.InputText(scope $"##{setting.Name}", &buffer, buffer.Count))
|
||||
{
|
||||
value..Clear().Append(&buffer);
|
||||
|
||||
_settingsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.NextColumn();
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndTabBar();
|
||||
|
||||
ImGui.EndChild();
|
||||
|
||||
ImGui.BeginDisabled(!_settingsChanged);
|
||||
|
||||
if (ImGui.Button("Save"))
|
||||
{
|
||||
_settings.Save();
|
||||
_settings.Apply();
|
||||
_open = false;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Apply"))
|
||||
{
|
||||
_settings.Apply();
|
||||
}
|
||||
|
||||
ImGui.EndDisabled();
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Cancel"))
|
||||
{
|
||||
Settings.Load();
|
||||
_open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
FileVersion = 1
|
||||
Dependencies = {corlib = "*", GlitchyEngine = "*"}
|
||||
|
||||
[Project]
|
||||
Name = "GlitchyEngine.Test"
|
||||
TargetType = "BeefTest"
|
||||
StartupObject = "GlitchyEngine.Test.Program"
|
||||
@@ -0,0 +1,5 @@
|
||||
FileVersion = 1
|
||||
Projects = {"GlitchyEngine.Test" = {Path = "."}, 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"}, FreeType = {Path = "../GlitchyEngine/vendor/freetype"}, cgltf-beef = {Path = "../GlitchyEngine/vendor/gltf/cgltf-beef"}}
|
||||
|
||||
[Workspace]
|
||||
StartupProject = "GlitchyEngine.Test"
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using GlitchyEngine.Math;
|
||||
|
||||
namespace GlitchyEngine.Test.Math
|
||||
{
|
||||
static class MathHelperTest
|
||||
{
|
||||
[Test]
|
||||
public static void TestDegToRad()
|
||||
{
|
||||
{
|
||||
float deg = 0;
|
||||
float rad = MathHelper.ToRadians(deg);
|
||||
Test.Assert(rad == 0.0f);
|
||||
}
|
||||
|
||||
{
|
||||
float deg = 45;
|
||||
float rad = MathHelper.ToRadians(deg);
|
||||
Test.Assert(rad == MathHelper.PiOverFour);
|
||||
}
|
||||
|
||||
{
|
||||
float deg = 180;
|
||||
float rad = MathHelper.ToRadians(deg);
|
||||
Test.Assert(rad == MathHelper.Pi);
|
||||
}
|
||||
|
||||
{
|
||||
float deg = -360;
|
||||
float rad = MathHelper.ToRadians(deg);
|
||||
Test.Assert(rad == -MathHelper.TwoPi);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void TestRadToDeg()
|
||||
{
|
||||
{
|
||||
float rad = 0;
|
||||
float deg = MathHelper.ToDegrees(rad);
|
||||
Test.Assert(deg == 0.0f);
|
||||
}
|
||||
|
||||
{
|
||||
float rad = MathHelper.PiOverFour;
|
||||
float deg = MathHelper.ToDegrees(rad);
|
||||
Test.Assert(deg == 45);
|
||||
}
|
||||
|
||||
{
|
||||
float rad = MathHelper.Pi;
|
||||
float deg = MathHelper.ToDegrees(rad);
|
||||
Test.Assert(deg == 180);
|
||||
}
|
||||
|
||||
{
|
||||
float rad = -MathHelper.TwoPi;
|
||||
float deg = MathHelper.ToDegrees(rad);
|
||||
Test.Assert(deg == -360);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
using System;
|
||||
using GlitchyEngine.Math;
|
||||
|
||||
namespace GlitchyEngine.Test.Math
|
||||
{
|
||||
/**
|
||||
* @Note Testing 180° Angles is quite stupid as 180° = -180° and thus the result is quite dependant of floating point precision...
|
||||
*/
|
||||
static class QuaternionTest
|
||||
{
|
||||
static bool QuatEquals(Quaternion q1, Quaternion q2, float delta = 0.0001f)
|
||||
{
|
||||
return Math.Abs(q1.X - q2.X) < delta && Math.Abs(q1.Y - q2.Y) < delta &&
|
||||
Math.Abs(q1.Z - q2.Z) < delta && Math.Abs(q1.W - q2.W) < delta;
|
||||
}
|
||||
|
||||
static bool Vector3Equals(Vector3 v1, Vector3 v2, float delta = 0.0001f)
|
||||
{
|
||||
return Math.Abs(v1.X - v2.X) < delta && Math.Abs(v1.Y - v2.Y) < delta &&
|
||||
Math.Abs(v1.Z - v2.Z) < delta;
|
||||
}
|
||||
|
||||
static bool FloatEquals(float l, float r, float delta = 0.0001f)
|
||||
{
|
||||
return Math.Abs(l - r) < delta;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void TestAxisAngleToQuaternion()
|
||||
{
|
||||
// angle in degrees
|
||||
void Test(Vector3 axis, float angle, Quaternion expectedQuat)
|
||||
{
|
||||
float angleRad = MathHelper.ToRadians(angle);
|
||||
Quaternion result = .FromAxisAngle(axis, angleRad);
|
||||
|
||||
Quaternion resultNormalized = .Normalize(result);
|
||||
Quaternion expectedNormalized = .Normalize(expectedQuat);
|
||||
|
||||
Test.Assert(QuatEquals(resultNormalized, expectedNormalized));
|
||||
}
|
||||
|
||||
// Reference for conversions: https://www.andre-gaschler.com/rotationconverter/
|
||||
|
||||
Test(.(0, 0, 0), 55, Quaternion.Identity);
|
||||
|
||||
// x: 90°
|
||||
Test(.(1, 0, 0), 90, Quaternion(1, 0, 0, 1));
|
||||
// y: 90°
|
||||
Test(.(0, 1, 0), 90, Quaternion(0, 1, 0, 1));
|
||||
// z: 90°
|
||||
Test(.(0, 0, 1), 90, Quaternion(0, 0, 1, 1));
|
||||
|
||||
Test(.(1, 1, 0), 90, Quaternion(0.5f, 0.5f, 0, 0.707107f));
|
||||
|
||||
Test(.(1, 1, 1), 90, Quaternion(0.4082483f, 0.4082483f, 0.4082483f, 0.7071068f));
|
||||
|
||||
Test(.(0.3f, 0.5f, 0.4f), -25, Quaternion(-0.0918276f, -0.1530459f, -0.1224367f, 0.976296f));
|
||||
|
||||
Test(.(0.3f, -0.5f, 0.4f), -25, Quaternion(-0.0918276f, 0.1530459f, -0.1224367f, 0.976296f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void TestQuaternionToAxisAngle()
|
||||
{
|
||||
// angle in degrees
|
||||
void Test(Quaternion quat, Vector3 expectedAxis, float expectedAngle)
|
||||
{
|
||||
Quaternion quatNrm = .Normalize(quat);
|
||||
(Vector3 resultAxis, float resultAngle) = quatNrm.ToAxisAngle();
|
||||
|
||||
resultAxis.Normalize();
|
||||
resultAngle = MathHelper.ToDegrees(resultAngle);
|
||||
|
||||
Vector3 nrmExpectedAxis = .Normalize(expectedAxis);
|
||||
|
||||
Test.Assert(Vector3Equals(resultAxis, nrmExpectedAxis) && FloatEquals(expectedAngle, resultAngle));
|
||||
}
|
||||
|
||||
// Reference for conversions: https://www.andre-gaschler.com/rotationconverter/
|
||||
|
||||
Test(Quaternion.Identity, .(0, 0, 0), 0);
|
||||
|
||||
// x: 90°
|
||||
Test(Quaternion(1, 0, 0, 1), .(1, 0, 0), 90);
|
||||
// y: 90°
|
||||
Test(Quaternion(0, 1, 0, 1), .(0, 1, 0), 90);
|
||||
// z: 90°
|
||||
Test(Quaternion(0, 0, 1, 1), .(0, 0, 1), 90);
|
||||
|
||||
Test(Quaternion(0.5f, 0.5f, 0, 0.707107f), .(1, 1, 0), 90);
|
||||
|
||||
Test(Quaternion(0.4082483f, 0.4082483f, 0.4082483f, 0.7071068f), .(1, 1, 1), 90);
|
||||
|
||||
Test(Quaternion(-0.0918276f, -0.1530459f, -0.1224367f, 0.976296f), .(-0.4242643f, -0.7071067f, -0.5656853f), 24.9999988f);
|
||||
Test(Quaternion(-0.0918276f, 0.1530459f, -0.1224367f, 0.976296f), .(-0.4242643f, 0.7071067f, -0.5656853f), 24.9999988f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void TestEulerToQuaternion()
|
||||
{
|
||||
void Test(Vector3 inEuler, Quaternion expectedQuat)
|
||||
{
|
||||
Quaternion result = .FromEulerAngles(MathHelper.ToRadians(inEuler.Y), MathHelper.ToRadians(inEuler.X), MathHelper.ToRadians(inEuler.Z));
|
||||
|
||||
Test.Assert(QuatEquals(result, expectedQuat));
|
||||
}
|
||||
|
||||
Test(Vector3.Zero, Quaternion.Identity);
|
||||
|
||||
// X: 90°
|
||||
Test(.(90, 0, 0), Quaternion(1, 0, 0, 1)..Normalize());
|
||||
|
||||
// Y: 90°
|
||||
Test(.(0, 90, 0), Quaternion(0, 1, 0, 1)..Normalize());
|
||||
|
||||
// Z: 90°
|
||||
Test(.(0, 0, 90), Quaternion(0, 0, 1, 1)..Normalize());
|
||||
|
||||
// X: 90° Y: 90°
|
||||
Test(.(90, 90, 0), Quaternion(1, 1, -1, 1)..Normalize());
|
||||
|
||||
// 90° 45° 30°
|
||||
Test(.(90, 45, 30), Quaternion(0.7010574f, 0.4304593f, -0.092296f, 0.5609855f)..Normalize());
|
||||
|
||||
// 105° 90° 0°
|
||||
Test(.(105, 90, 0), Quaternion(0.560986f, 0.430459f, -0.560986f, 0.430459f)..Normalize());
|
||||
|
||||
// 180° 90° -75°
|
||||
Test(.(180, 90, -75), Quaternion(0.5609855f, -0.4304593f, -0.5609855f, 0.4304593f)..Normalize());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void TestQuaternionToEuler()
|
||||
{
|
||||
void Test(Quaternion inQuat, Vector3 expectedEuler)
|
||||
{
|
||||
Quaternion nrmInQuat = .Normalize(inQuat);
|
||||
|
||||
Vector3 result = Quaternion.ToEulerAngles(nrmInQuat);
|
||||
Vector3 resultDeg = MathHelper.ToDegrees(result);
|
||||
|
||||
Test.Assert(Vector3Equals(resultDeg, expectedEuler));
|
||||
}
|
||||
|
||||
Test(Quaternion.Identity, Vector3.Zero);
|
||||
|
||||
// X: 90°
|
||||
Test(Quaternion(1, 0, 0, 1), .(90, 0, 0));
|
||||
|
||||
// Y: 90°
|
||||
Test(Quaternion(0, 1, 0, 1), .(0, 90, 0));
|
||||
|
||||
// Z: 90°
|
||||
Test(Quaternion(0, 0, 1, 1), .(0, 0, 90));
|
||||
|
||||
// X: 90° Y: 90°
|
||||
Test(Quaternion(1, 1, -1, 1), .(90, 90, 0));
|
||||
|
||||
// 90° 45° 30°
|
||||
Test(Quaternion(0.7010574f, 0.4304593f, -0.092296f, 0.5609855f), .(90, 45, 30));
|
||||
|
||||
// 105° 90° 0°
|
||||
Test(Quaternion(0.560986f, 0.430459f, -0.560986f, 0.430459f), .(105, 90, 0));
|
||||
|
||||
// 180° 90° -75°
|
||||
Test(Quaternion(0.5609855f, -0.4304593f, -0.5609855f, 0.4304593f), .(180, 90, -75));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void TestEulerToQautToEuler()
|
||||
{
|
||||
void Test(float yaw, float pitch, float roll)
|
||||
{
|
||||
Quaternion quat = .FromEulerAngles(MathHelper.ToRadians(yaw), MathHelper.ToRadians(pitch), MathHelper.ToRadians(roll));
|
||||
|
||||
Vector3 result = Quaternion.ToEulerAngles(quat);
|
||||
|
||||
result = .(MathHelper.ToDegrees(result.X), MathHelper.ToDegrees(result.Y), MathHelper.ToDegrees(result.Z));
|
||||
|
||||
Test.Assert(Vector3Equals(.(pitch, yaw, roll), result));
|
||||
}
|
||||
|
||||
Test(0, 90, 0);
|
||||
Test(90, 0, 0);
|
||||
Test(0, 0, 90);
|
||||
|
||||
Test(90, 90, 0);
|
||||
|
||||
Test(90, 45, 0);
|
||||
|
||||
Test(90, 45, 30);
|
||||
|
||||
Test(105, 90, 0);
|
||||
|
||||
Test(179, 90, -75);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-15
@@ -1,22 +1,9 @@
|
||||
FileVersion = 1
|
||||
Dependencies = {GlitchLog = "*", corlib = "*", DirectX = "*", ImGui = "*", ImGuiImplWin32 = "*", ImGuiImplDX11 = "*", DirectXTK = "*"}
|
||||
Dependencies = {GlitchLog = "*", corlib = "*", DirectX = "*", FreeType = "*", cgltf-beef = "*", msdfgen-beef = "*", ImGui = "*", ImGuiImplDX11 = "*", ImGuiImplWin32 = "*", ImGuizmo = "*", Beefy2D = "*", LodePng = "*", GlitchyEngineHelper = "*", bon = "*"}
|
||||
|
||||
[Project]
|
||||
Name = "GlitchyEngine"
|
||||
ProcessorMacros = ["GE_ERROR_SHADER_MATRIX_MISMATCH", "GE_ERROR_SHADER_VAR_TYPE_MISMATCH"]
|
||||
|
||||
[Configs.Debug.Win32]
|
||||
PreprocessorMacros = ["DEBUG", "GE_WINDOWS"]
|
||||
|
||||
[Configs.Debug.Win64]
|
||||
CLibType = "DynamicDebug"
|
||||
PreprocessorMacros = ["DEBUG", "GE_WINDOWS"]
|
||||
|
||||
[Configs.Release.Win32]
|
||||
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
|
||||
|
||||
[Configs.Release.Win64]
|
||||
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
|
||||
ProcessorMacros = ["GE_GRAPHICS_DX11", "GE_SHADER_MATRIX_MISMATCH_IS_ERROR", "GE_SHADER_VAR_TYPE_MISMATCH_IS_ERROR", "GE_SHADER_UNUSED_VARIABLE_IS_WARNING", "GE_WINDOWS"]
|
||||
|
||||
[Configs.Paranoid.Win32]
|
||||
PreprocessorMacros = ["DEBUG", "PARANOID", "GE_WINDOWS"]
|
||||
@@ -24,8 +11,17 @@ PreprocessorMacros = ["DEBUG", "PARANOID", "GE_WINDOWS"]
|
||||
[Configs.Paranoid.Win64]
|
||||
PreprocessorMacros = ["DEBUG", "PARANOID", "GE_WINDOWS"]
|
||||
|
||||
[Configs.Release.Win32]
|
||||
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
|
||||
|
||||
[Configs.Release.Win64]
|
||||
PreprocessorMacros = ["IMGUI", "RELEASE", "GE_PROFILE"]
|
||||
|
||||
[Configs.Test.Win32]
|
||||
PreprocessorMacros = ["TEST", "GE_WINDOWS"]
|
||||
|
||||
[Configs.Test.Win64]
|
||||
PreprocessorMacros = ["TEST", "GE_WINDOWS"]
|
||||
|
||||
[Configs.Debug.Win64]
|
||||
PreprocessorMacros = ["DEBUG", "GE_PROFILE", "GE_PROFILE_RENDERER", "GE_PROFILE_RESOURCES", "IMGUI"]
|
||||
|
||||
@@ -1,96 +1,205 @@
|
||||
using System;
|
||||
using GlitchyEngine.Events;
|
||||
using GlitchyEngine.ImGui;
|
||||
using GlitchyEngine.Renderer;
|
||||
|
||||
namespace GlitchyEngine
|
||||
{
|
||||
public class Application
|
||||
{
|
||||
static Application s_Instance = null;
|
||||
|
||||
private Window _window ~ delete _;
|
||||
private RendererAPI _rendererApi ~ delete _;
|
||||
private bool _running = true;
|
||||
|
||||
private LayerStack _layerStack = new LayerStack() ~ delete _;
|
||||
|
||||
private ImGuiLayer _imGuiLayer;
|
||||
|
||||
private GameTime _gameTime = new GameTime(true) ~ delete _;
|
||||
|
||||
public bool IsRunning => _running;
|
||||
public Window Window => _window;
|
||||
|
||||
[Inline]
|
||||
public static Application Get() => s_Instance;
|
||||
|
||||
public this()
|
||||
{
|
||||
Log.EngineLogger.Assert(s_Instance == null, "Tried to create a second application.");
|
||||
s_Instance = this;
|
||||
|
||||
_window = new Window(.Default);
|
||||
_window.EventCallback = new => OnEvent;
|
||||
|
||||
_rendererApi = new RendererAPI();
|
||||
_rendererApi.Context = _window.Context;
|
||||
|
||||
SamplerStateManager.Init(_window.Context);
|
||||
|
||||
RenderCommand.RendererAPI = _rendererApi;
|
||||
|
||||
Renderer.Init(_window.Context);
|
||||
|
||||
_imGuiLayer = new ImGuiLayer();
|
||||
PushOverlay(_imGuiLayer);
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
SamplerStateManager.Uninit();
|
||||
}
|
||||
|
||||
public void OnEvent(Event e)
|
||||
{
|
||||
EventDispatcher dispatcher = scope .(e);
|
||||
dispatcher.Dispatch<WindowCloseEvent>(scope => OnWindowClose);
|
||||
|
||||
for(Layer layer in _layerStack)
|
||||
{
|
||||
layer.OnEvent(e);
|
||||
if(e.Handled)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
while(_running)
|
||||
{
|
||||
_gameTime.NewFrame();
|
||||
|
||||
Input.NewFrame();
|
||||
|
||||
for(Layer layer in _layerStack)
|
||||
layer.Update(_gameTime);
|
||||
|
||||
_window.Update();
|
||||
|
||||
_imGuiLayer.ImGuiRender();
|
||||
|
||||
_window.Context.SwapChain.Present();
|
||||
}
|
||||
}
|
||||
|
||||
public void PushLayer(Layer ownLayer) => _layerStack.PushLayer(ownLayer);
|
||||
|
||||
public void PushOverlay(Layer ownOverlay) => _layerStack.PushOverlay(ownOverlay);
|
||||
|
||||
public bool OnWindowClose(WindowCloseEvent e)
|
||||
{
|
||||
_running = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using GlitchyEngine.Events;
|
||||
using GlitchyEngine.ImGui;
|
||||
using GlitchyEngine.Renderer;
|
||||
using GlitchyEngine.Debug;
|
||||
using GlitchyEngine.Content;
|
||||
|
||||
namespace GlitchyEngine
|
||||
{
|
||||
public class Application
|
||||
{
|
||||
static Application s_Instance = null;
|
||||
|
||||
private Window _window;
|
||||
private RendererAPI _rendererApi;
|
||||
private EffectLibrary _effectLibrary;
|
||||
|
||||
private bool _running = true;
|
||||
private bool _isMinimized = false;
|
||||
|
||||
private LayerStack _layerStack;
|
||||
|
||||
#if IMGUI
|
||||
private ImGuiLayer _imGuiLayer;
|
||||
#endif
|
||||
|
||||
private GameTime _gameTime;
|
||||
|
||||
private IContentManager _contentManager;
|
||||
|
||||
public bool IsRunning => _running;
|
||||
public Window Window => _window;
|
||||
|
||||
public EffectLibrary EffectLibrary => _effectLibrary;
|
||||
|
||||
public IContentManager ContentManager => _contentManager;
|
||||
|
||||
public bool IsMinimized => _isMinimized;
|
||||
|
||||
[Inline]
|
||||
public static Application Get() => s_Instance;
|
||||
|
||||
public Settings Settings {get; private set;} = new .() ~ delete _;
|
||||
|
||||
public this()
|
||||
{
|
||||
Profiler.ProfileFunction!();
|
||||
|
||||
Log.EngineLogger.Assert(s_Instance == null, "Tried to create a second application.");
|
||||
s_Instance = this;
|
||||
|
||||
_layerStack = new LayerStack();
|
||||
_gameTime = new GameTime(true);
|
||||
|
||||
_window = new Window(.Default);
|
||||
_window.EventCallback = new => OnEvent;
|
||||
|
||||
_rendererApi = new RendererAPI();
|
||||
_rendererApi.Context = _window.Context;
|
||||
|
||||
_contentManager = new ContentManager("./content");
|
||||
|
||||
SamplerStateManager.Init();
|
||||
|
||||
RenderCommand.RendererAPI = _rendererApi;
|
||||
|
||||
_effectLibrary = new EffectLibrary();
|
||||
|
||||
Renderer.Init(_window.Context, _effectLibrary);
|
||||
|
||||
#if IMGUI
|
||||
_imGuiLayer = new ImGuiLayer();
|
||||
PushOverlay(_imGuiLayer);
|
||||
#endif
|
||||
|
||||
GlitchyEngine.Settings.Load();
|
||||
Settings.Apply();
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
Profiler.ProfileFunction!();
|
||||
|
||||
SamplerStateManager.Uninit();
|
||||
Renderer.Deinit();
|
||||
|
||||
delete _effectLibrary;
|
||||
|
||||
delete _contentManager;
|
||||
|
||||
delete _rendererApi;
|
||||
delete _window;
|
||||
|
||||
delete _gameTime;
|
||||
delete _layerStack;
|
||||
}
|
||||
|
||||
public void OnEvent(Event e)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
if(!_running)
|
||||
return;
|
||||
|
||||
EventDispatcher dispatcher = EventDispatcher(e);
|
||||
dispatcher.Dispatch<WindowCloseEvent>(scope => OnWindowClose);
|
||||
|
||||
dispatcher.Dispatch<WindowResizeEvent>(scope => OnWindowResize);
|
||||
|
||||
for(Layer layer in _layerStack)
|
||||
{
|
||||
layer.OnEvent(e);
|
||||
if(e.Handled)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if GE_APP_SINGLESTEP
|
||||
bool allowFrame = false;
|
||||
bool disableSingleFrame = false;
|
||||
#else
|
||||
const bool allowFrame = true;
|
||||
#endif
|
||||
|
||||
public void Run()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
while(_running)
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("Loop");
|
||||
|
||||
if(allowFrame)
|
||||
{
|
||||
_gameTime.NewFrame();
|
||||
}
|
||||
|
||||
Input.NewFrame();
|
||||
|
||||
#if GE_APP_SINGLESTEP
|
||||
allowFrame = disableSingleFrame || Input.IsKeyPressing(Key.F10);
|
||||
|
||||
if(Input.IsKeyPressing(Key.F11))
|
||||
{
|
||||
disableSingleFrame = !disableSingleFrame;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(allowFrame && !_isMinimized)
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("Update Layers");
|
||||
|
||||
for(Layer layer in _layerStack)
|
||||
layer.Update(_gameTime);
|
||||
}
|
||||
|
||||
_window.Update();
|
||||
|
||||
if(allowFrame)
|
||||
{
|
||||
#if IMGUI
|
||||
_imGuiLayer.ImGuiRender();
|
||||
#endif
|
||||
|
||||
_window.Context.SwapChain.Present();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PushLayer(Layer ownLayer)
|
||||
{
|
||||
Profiler.ProfileFunction!();
|
||||
|
||||
_layerStack.PushLayer(ownLayer);
|
||||
ownLayer.OnAttach();
|
||||
}
|
||||
|
||||
public void PushOverlay(Layer ownOverlay)
|
||||
{
|
||||
Profiler.ProfileFunction!();
|
||||
|
||||
_layerStack.PushOverlay(ownOverlay);
|
||||
ownOverlay.OnAttach();
|
||||
}
|
||||
|
||||
public bool OnWindowClose(WindowCloseEvent e)
|
||||
{
|
||||
_running = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnWindowResize(WindowResizeEvent e)
|
||||
{
|
||||
if(e.Width == 0 || e.Height == 0)
|
||||
{
|
||||
_isMinimized = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
_isMinimized = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace GlitchyEngine.Collections
|
||||
{
|
||||
public class TreeNode<T>
|
||||
{
|
||||
public T Value;
|
||||
|
||||
public List<Self> Children = new .() ~ DeleteContainerAndItems!(_);
|
||||
|
||||
public this() {}
|
||||
|
||||
public this(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public Self AddChild(T value)
|
||||
{
|
||||
for (var child in Children)
|
||||
{
|
||||
if(child.Value == value)
|
||||
return child;
|
||||
}
|
||||
|
||||
Self newChild = new .(value);
|
||||
|
||||
Children.Add(newChild);
|
||||
|
||||
return newChild;
|
||||
}
|
||||
|
||||
public Self FindNode(T value)
|
||||
{
|
||||
if (Value == value)
|
||||
return this;
|
||||
|
||||
for (var child in Children)
|
||||
{
|
||||
var childResult = child.FindNode(value);
|
||||
if (childResult != null)
|
||||
return childResult;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using xxHash;
|
||||
|
||||
namespace GlitchyEngine.Content
|
||||
{
|
||||
class ContentId
|
||||
{
|
||||
private readonly String _string;
|
||||
private readonly XXH64_hash _hash;
|
||||
|
||||
public String String => _string;
|
||||
public XXH64_hash Hash => _hash;
|
||||
|
||||
[AllowAppend]
|
||||
public this(StringView id)
|
||||
{
|
||||
String str = append String(id);
|
||||
|
||||
_string = str;
|
||||
|
||||
_hash = xxHash.ComputeHash(id);
|
||||
}
|
||||
}
|
||||
|
||||
interface IContentManager
|
||||
{
|
||||
void GetFilePath(String outFilename, String filename);
|
||||
|
||||
Stream GetFile(String filename);
|
||||
}
|
||||
|
||||
class ContentManager : IContentManager
|
||||
{
|
||||
private String _contentRoot;
|
||||
|
||||
[AllowAppend]
|
||||
public this(String contentRoot)
|
||||
{
|
||||
String cntRoot = append String(contentRoot);
|
||||
_contentRoot = cntRoot;
|
||||
|
||||
Runtime.Assert(Directory.Exists(contentRoot), "Content root directory doesn't exist.");
|
||||
}
|
||||
|
||||
public void GetFilePath(String outFilename, String filename)
|
||||
{
|
||||
Path.InternalCombine(outFilename, _contentRoot, filename);
|
||||
}
|
||||
|
||||
public Stream GetFile(String filename)
|
||||
{
|
||||
String fullpath = scope .(_contentRoot.Length + 1 + filename.Length);
|
||||
GetFilePath(fullpath, filename);
|
||||
|
||||
FileStream stream = new FileStream();
|
||||
var result = stream.Open(fullpath, .Read, .Read);
|
||||
|
||||
if (result case .Err(let error))
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to open file \"{fullpath}\". Error: {error}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,635 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using cgltf;
|
||||
using GlitchyEngine.Math;
|
||||
using GlitchyEngine.Renderer;
|
||||
using GlitchyEngine.Renderer.Animation;
|
||||
using GlitchyEngine.World;
|
||||
|
||||
namespace GlitchyEngine.Content
|
||||
{
|
||||
public static class ModelLoader
|
||||
{
|
||||
static readonly Matrix RightToLeftHand = .Scaling(1, 1, -1);
|
||||
|
||||
public static void LoadModel(String filename, Effect validationEffect, Material material, EcsWorld world,
|
||||
List<AnimationClip> outClips)
|
||||
{
|
||||
CGLTF.Options options = .();
|
||||
CGLTF.Data* data;
|
||||
CGLTF.Result result = CGLTF.ParseFile(options, filename, out data);
|
||||
|
||||
Log.EngineLogger.Assert(result == .Success, "Failed to load model.");
|
||||
|
||||
result = CGLTF.LoadBuffers(options, data, filename);
|
||||
|
||||
Log.EngineLogger.Assert(result == .Success, "Failed to load buffers");
|
||||
|
||||
for(var node in data.Scenes[0].Nodes)
|
||||
{
|
||||
NodesToEntities(data, node, null, world, validationEffect, material, outClips);
|
||||
}
|
||||
|
||||
CGLTF.Free(data);
|
||||
}
|
||||
|
||||
private static void NodesToEntities(CGLTF.Data* data, CGLTF.Node* node, EcsEntity? parentEntity, EcsWorld world, Effect validationEffect, Material material, List<AnimationClip> clips)
|
||||
{
|
||||
EcsEntity entity = world.NewEntity();
|
||||
|
||||
#if DEBUG
|
||||
var nameComponent = world.AssignComponent<DebugNameComponent>(entity);
|
||||
|
||||
if (node.Name != null)
|
||||
{
|
||||
nameComponent.SetName(StringView(node.Name));
|
||||
}
|
||||
else
|
||||
{
|
||||
nameComponent.SetName("Unnamed Node");
|
||||
}
|
||||
#endif
|
||||
|
||||
if(parentEntity.HasValue)
|
||||
{
|
||||
var childParent = world.AssignComponent<ParentComponent>(entity);
|
||||
childParent.Entity = parentEntity.Value;
|
||||
}
|
||||
|
||||
var childTransform = world.AssignComponent<TransformComponent>(entity);
|
||||
|
||||
if(node.HasMatrix)
|
||||
{
|
||||
childTransform.LocalTransform = *(Matrix*)&node.Matrix;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(node.HasTranslation)
|
||||
childTransform.Position = *(Vector3*)&node.Translation;
|
||||
else
|
||||
childTransform.Position = .Zero;
|
||||
|
||||
if(node.HasRotation)
|
||||
childTransform.Rotation = *(Quaternion*)&node.Rotation;
|
||||
else
|
||||
childTransform.Rotation = .Identity;
|
||||
|
||||
if(node.HasScale)
|
||||
childTransform.Scale = *(Vector3*)&node.Scale;
|
||||
else
|
||||
childTransform.Scale = .(1, 1, 1);
|
||||
}
|
||||
|
||||
// Invert the Z-Axis of the root Node to convert the coordinate system from right-handed to left-handed
|
||||
if(parentEntity == null)
|
||||
childTransform.Scale *= .(1, 1, -1);
|
||||
|
||||
Skeleton skeleton = null;
|
||||
|
||||
if(node.Skin != null)
|
||||
{
|
||||
skeleton = ExtractSkeleton(node.Skin);
|
||||
|
||||
LoadAnimationClips(data, node.Skin, skeleton, clips);
|
||||
}
|
||||
|
||||
if(node.Mesh != null)
|
||||
{
|
||||
// If we have only one primitive, add it directly to the entity
|
||||
if(node.Mesh.Primitives.Length == 1)
|
||||
{
|
||||
var mesh = world.AssignComponent<MeshComponent>(entity);
|
||||
|
||||
using (var geo = PrimitiveToGeoBinding(node.Mesh.Primitives[0], validationEffect))
|
||||
{
|
||||
mesh.Mesh = geo;
|
||||
}
|
||||
|
||||
if(skeleton == null)
|
||||
{
|
||||
var meshRenderer = world.AssignComponent<MeshRendererComponent>(entity);
|
||||
meshRenderer.Material = material;
|
||||
}
|
||||
else
|
||||
{
|
||||
var meshRenderer = world.AssignComponent<SkinnedMeshRendererComponent>(entity);
|
||||
meshRenderer.Material = material;
|
||||
meshRenderer.Skeleton = skeleton;
|
||||
}
|
||||
}
|
||||
// otherwise one child-entity per primitive
|
||||
else
|
||||
{
|
||||
for(var primitive in node.Mesh.Primitives)
|
||||
{
|
||||
EcsEntity meshEntity = world.NewEntity();
|
||||
|
||||
var meshParent = world.AssignComponent<ParentComponent>(meshEntity);
|
||||
meshParent.Entity = entity;
|
||||
|
||||
var mesh = world.AssignComponent<MeshComponent>(meshEntity);
|
||||
mesh.Mesh = PrimitiveToGeoBinding(primitive, validationEffect);
|
||||
|
||||
if(skeleton == null)
|
||||
{
|
||||
var meshRenderer = world.AssignComponent<MeshRendererComponent>(meshEntity);
|
||||
meshRenderer.Material = material;
|
||||
}
|
||||
else
|
||||
{
|
||||
var meshRenderer = world.AssignComponent<SkinnedMeshRendererComponent>(meshEntity);
|
||||
meshRenderer.Material = material;
|
||||
meshRenderer.Skeleton = skeleton;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skeleton?.ReleaseRef();
|
||||
|
||||
for(var child in node.Children)
|
||||
{
|
||||
NodesToEntities(data, child, entity, world, validationEffect, material, clips);
|
||||
}
|
||||
}
|
||||
|
||||
public static GeometryBinding PrimitiveToGeoBinding(CGLTF.Primitive primitive, Effect validationEffect)
|
||||
{
|
||||
GeometryBinding binding = new GeometryBinding();
|
||||
|
||||
// primitive topology
|
||||
switch(primitive.Type)
|
||||
{
|
||||
case .Points:
|
||||
binding.SetPrimitiveTopology(.PointList);
|
||||
case .Lines:
|
||||
binding.SetPrimitiveTopology(.LineList);
|
||||
case .LineStrip:
|
||||
binding.SetPrimitiveTopology(.LineStrip);
|
||||
case .Triangles:
|
||||
binding.SetPrimitiveTopology(.TriangleList);
|
||||
case .TriangleStrip:
|
||||
binding.SetPrimitiveTopology(.TriangleStrip);
|
||||
default:
|
||||
Log.EngineLogger.Assert(false, scope $"{primitive.Type} not supported.");
|
||||
}
|
||||
|
||||
// indices
|
||||
{
|
||||
CGLTF.Accessor* indices = primitive.Indices;
|
||||
|
||||
if(indices != null)
|
||||
{
|
||||
bool is16bit = indices.ComponentType == .R_16u || indices.ComponentType == .R_16;
|
||||
|
||||
IndexBuffer ib = new IndexBuffer((.)indices.Count, .Immutable, .None, is16bit ? .Index16Bit : .Index32Bit);
|
||||
|
||||
uint8* bufferData = (uint8*)indices.BufferView.Buffer.Data;
|
||||
bufferData += indices.BufferView.Offset;
|
||||
|
||||
// TODO: this is a mess
|
||||
ib.SetData<uint8>(bufferData, (.)indices.BufferView.Size);
|
||||
|
||||
binding.SetIndexBuffer(ib);
|
||||
|
||||
ib.ReleaseRef();
|
||||
}
|
||||
}
|
||||
|
||||
bool hasNormals = false;
|
||||
bool hasTangents = false;
|
||||
|
||||
// vertices
|
||||
{
|
||||
List<VertexElement> elements = scope .(primitive.Attributes.Length);
|
||||
Dictionary<void*, VertexBuffer> buffers = scope .();
|
||||
List<VertexBufferBinding> bindings = scope .();
|
||||
|
||||
for(var attribute in primitive.Attributes)
|
||||
{
|
||||
{
|
||||
StringView attributeName = StringView(attribute.Name);
|
||||
|
||||
if(attributeName.Equals("NORMAL"))
|
||||
{
|
||||
hasNormals = true;
|
||||
}
|
||||
else if(attributeName.Equals("TANGENT"))
|
||||
{
|
||||
hasTangents = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Get Input Element format
|
||||
Format format = FormatFromVectorComponent(attribute.Data.Type, attribute.Data.ComponentType);
|
||||
Log.EngineLogger.AssertDebug(format != .Unknown, "Vertex element format must not be \"Unknown.\"");
|
||||
|
||||
VertexBuffer vertexBuffer = null;
|
||||
|
||||
binding.VertexCount = (uint32)attribute.Data.Count;
|
||||
|
||||
// Get vertex buffer
|
||||
{
|
||||
CGLTF.BufferView* bufferView = attribute.Data.BufferView;
|
||||
|
||||
// if buffer doesn't exist -> create
|
||||
if(!buffers.TryGetValue(bufferView, out vertexBuffer))
|
||||
{
|
||||
vertexBuffer = new VertexBuffer(1, (uint32)bufferView.Size, .Immutable)..ReleaseRefNoDelete();
|
||||
|
||||
uint8* bufferData = (uint8*)bufferView.Buffer.Data;
|
||||
bufferData += bufferView.Offset;
|
||||
|
||||
vertexBuffer.SetData(bufferData, (.)bufferView.Size);
|
||||
|
||||
buffers.Add(attribute.Data.BufferView, vertexBuffer);
|
||||
}
|
||||
|
||||
Log.EngineLogger.AssertDebug(vertexBuffer != null);
|
||||
}
|
||||
|
||||
VertexBufferBinding bufferBinding = .(vertexBuffer, (.)attribute.Data.Stride, (.)attribute.Data.Offset);
|
||||
|
||||
// get slot of bufferBinding
|
||||
int bindingSlot = bindings.IndexOf(bufferBinding);
|
||||
|
||||
// binding has no slot -> add to list
|
||||
if(bindingSlot == -1)
|
||||
{
|
||||
bindingSlot = bindings.Count;
|
||||
bindings.Add(bufferBinding);
|
||||
|
||||
binding.SetVertexBufferSlot(bufferBinding, (.)bindingSlot);
|
||||
}
|
||||
|
||||
StringView strView = .(attribute.Name);
|
||||
|
||||
// Remove number from end of name
|
||||
while((*(strView.EndPtr - 1)).IsDigit)
|
||||
{
|
||||
strView.Length--;
|
||||
}
|
||||
|
||||
VertexElement element = .(format, new String(strView), true, (.)attribute.Index, (.)bindingSlot);
|
||||
elements.Add(element);
|
||||
}
|
||||
|
||||
// Generate normals if missing
|
||||
if(!hasNormals)
|
||||
{
|
||||
CGLTF.Accessor* positions = null;
|
||||
|
||||
// Find position accessor
|
||||
for(var attribute in primitive.Attributes)
|
||||
{
|
||||
if(StringView(attribute.Name).Equals("POSITION"))
|
||||
{
|
||||
positions = attribute.Data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Log.EngineLogger.AssertDebug(positions != null, "The model appears to have no position data?!");
|
||||
|
||||
Vector3[] normals = new Vector3[positions.Count];
|
||||
|
||||
if(primitive.Indices != null)
|
||||
GenerateNormals(primitive.Indices, positions, normals);
|
||||
else
|
||||
GenerateNormals(positions, normals);
|
||||
|
||||
VertexBuffer vertexBuffer = new VertexBuffer((uint32)sizeof(Vector3), (uint32)normals.Count, .Immutable);
|
||||
vertexBuffer.SetData<Vector3>(normals);
|
||||
|
||||
bindings.Add(vertexBuffer.Binding);
|
||||
|
||||
uint32 bindingSlot = (.)bindings.Count - 1;
|
||||
|
||||
binding.SetVertexBufferSlot(vertexBuffer.Binding, (.)bindingSlot);
|
||||
VertexElement element = .(.R32G32B32_Float, "NORMAL", false, 0, bindingSlot);
|
||||
elements.Add(element);
|
||||
}
|
||||
|
||||
// TODO: validate vertex layout somewhere else
|
||||
|
||||
VertexElement[] vertexElements = new VertexElement[elements.Count];
|
||||
for(int i < elements.Count)
|
||||
{
|
||||
vertexElements[i] = elements[i];
|
||||
}
|
||||
|
||||
VertexLayout layout = new VertexLayout(vertexElements, true, validationEffect.VertexShader);
|
||||
binding.SetVertexLayout(layout..ReleaseRefNoDelete());
|
||||
}
|
||||
|
||||
// TODO: calculate tangents if missing (MikkTSpace algorithm)
|
||||
// Note: Bitangent = cross(normal, tangent.xyz) * tangent.w
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
/// Generates the normals for one triangle
|
||||
static mixin GenerateTriangleNormals(int index0, int index1, int index2, CGLTF.Accessor* positions, Vector3[] normals)
|
||||
{
|
||||
Vector3 position0 = GetEntry<Vector3>(positions, (.)index0);
|
||||
Vector3 position1 = GetEntry<Vector3>(positions, (.)index1);
|
||||
Vector3 position2 = GetEntry<Vector3>(positions, (.)index2);
|
||||
|
||||
ref Vector3 normal0 = ref normals[(.)index0];
|
||||
ref Vector3 normal1 = ref normals[(.)index1];
|
||||
ref Vector3 normal2 = ref normals[(.)index2];
|
||||
|
||||
Vector3 e0 = position1 - position0;
|
||||
Vector3 e1 = position2 - position0;
|
||||
|
||||
Vector3 normal = Vector3.Cross(e0, e1);
|
||||
|
||||
normal0 += normal;
|
||||
normal1 += normal;
|
||||
normal2 += normal;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
static void NormalizeNormals(Vector3[] normals)
|
||||
{
|
||||
for(int i < normals.Count)
|
||||
{
|
||||
normals[i].Normalize();
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates normals for the given model using indexed geometry
|
||||
static void GenerateNormals(CGLTF.Accessor* indices, CGLTF.Accessor* positions, Vector3[] normals)
|
||||
{
|
||||
/// Enumerate triangle-wise
|
||||
for(uint t = 0; t < indices.Count; t += 3)
|
||||
{
|
||||
int index0 = (.)CGLTF.AccessorReadIndex(indices, t);
|
||||
int index1 = (.)CGLTF.AccessorReadIndex(indices, t + 1);
|
||||
int index2 = (.)CGLTF.AccessorReadIndex(indices, t + 2);
|
||||
|
||||
GenerateTriangleNormals!(index0, index1, index2, positions, normals);
|
||||
}
|
||||
|
||||
NormalizeNormals(normals);
|
||||
}
|
||||
|
||||
/// Generates normals for the given model using nonindexed geometry
|
||||
static void GenerateNormals(CGLTF.Accessor* positions, Vector3[] normals)
|
||||
{
|
||||
/// Enumerate triangle-wise
|
||||
for(int i = 0; i < (.)positions.Count; i += 3)
|
||||
{
|
||||
GenerateTriangleNormals!(i, i + 1, i + 2, positions, normals);
|
||||
}
|
||||
|
||||
NormalizeNormals(normals);
|
||||
}
|
||||
|
||||
static Skeleton ExtractSkeleton(CGLTF.Skin* skin)
|
||||
{
|
||||
Skeleton skeleton = new Skeleton();
|
||||
skeleton.Joints = new Joint[skin.Joints.Length];
|
||||
|
||||
for(int i < skin.Joints.Length)
|
||||
{
|
||||
ref Joint joint = ref skeleton.Joints[i];
|
||||
|
||||
joint.InverseBindPose = GetEntry<Matrix>(skin.InverseBindMatrices, i);
|
||||
|
||||
if (skin.Joints[i].Name != null)
|
||||
joint.Name = new String(skin.Joints[i].Name);
|
||||
|
||||
int parentId = skin.Joints.IndexOf(skin.Joints[i].Parent);
|
||||
|
||||
Log.EngineLogger.AssertDebug(parentId < uint8.MaxValue, scope $"A skeleton must not have more than {uint8.MaxValue - 1} bones.");
|
||||
|
||||
if(parentId == -1)
|
||||
joint.ParentID = uint8.MaxValue;
|
||||
else
|
||||
joint.ParentID = (uint8)parentId;
|
||||
}
|
||||
|
||||
return skeleton;
|
||||
}
|
||||
|
||||
static bool AnimationBelongsToSkeleton(CGLTF.Animation animation, CGLTF.Skin* skin)
|
||||
{
|
||||
for(var channel in animation.Channels)
|
||||
{
|
||||
if(skin.Joints.IndexOf(channel.TargetNode) == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void LoadAnimationClips(CGLTF.Data* data, CGLTF.Skin* skin, Skeleton skeleton, List<AnimationClip> clips)
|
||||
{
|
||||
for(var animation in data.Animations)
|
||||
{
|
||||
if(!AnimationBelongsToSkeleton(animation, skin))
|
||||
continue;
|
||||
|
||||
AnimationClip clip = new AnimationClip(skeleton);
|
||||
clips.Add(clip);
|
||||
clip.IsLooping = true;
|
||||
|
||||
for(var channel in animation.Channels)
|
||||
{
|
||||
int nodeIndex = skin.Joints.IndexOf(channel.TargetNode);
|
||||
|
||||
Log.EngineLogger.AssertDebug(nodeIndex != -1);
|
||||
|
||||
ref JointAnimation jointAnimation = ref clip.JointAnimations[nodeIndex];
|
||||
|
||||
if(jointAnimation == null)
|
||||
jointAnimation = new JointAnimation();
|
||||
|
||||
Log.EngineLogger.AssertDebug(channel.Sampler.Input.Count == channel.Sampler.Output.Count);
|
||||
|
||||
int samples = (int)channel.Sampler.Input.Count;
|
||||
|
||||
Log.EngineLogger.AssertDebug(channel.Sampler.Input.ComponentType == .R_32f);
|
||||
Log.EngineLogger.AssertDebug(channel.Sampler.Input.Type == .Scalar);
|
||||
|
||||
InterpolationMode interpolationMode;
|
||||
|
||||
switch(channel.Sampler.Interpolation)
|
||||
{
|
||||
case .Step:
|
||||
interpolationMode = .Step;
|
||||
case .Linear:
|
||||
interpolationMode = .Linear;
|
||||
case .CubicSpline:
|
||||
interpolationMode = .CubicSpline;
|
||||
}
|
||||
|
||||
switch(channel.TargetPath)
|
||||
{
|
||||
case .Translation:
|
||||
jointAnimation.TranslationChannel = new .(samples, interpolationMode);
|
||||
|
||||
Log.EngineLogger.AssertDebug(channel.Sampler.Output.ComponentType == .R_32f);
|
||||
Log.EngineLogger.AssertDebug(channel.Sampler.Output.Type == .Vec3);
|
||||
|
||||
for(int i < samples)
|
||||
{
|
||||
float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
|
||||
Vector3 sample = GetEntry<Vector3>(channel.Sampler.Output, i);
|
||||
|
||||
jointAnimation.TranslationChannel.TimeStamps[i] = timeStamp;
|
||||
jointAnimation.TranslationChannel.Values[i] = sample;
|
||||
}
|
||||
case .Rotation:
|
||||
jointAnimation.RotationChannel = new .(samples, interpolationMode);
|
||||
|
||||
Log.EngineLogger.AssertDebug(channel.Sampler.Output.ComponentType == .R_32f);
|
||||
Log.EngineLogger.AssertDebug(channel.Sampler.Output.Type == .Vec4);
|
||||
|
||||
for(int i < samples)
|
||||
{
|
||||
float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
|
||||
Quaternion sample = GetEntry<Quaternion>(channel.Sampler.Output, i);
|
||||
|
||||
jointAnimation.RotationChannel.TimeStamps[i] = timeStamp;
|
||||
jointAnimation.RotationChannel.Values[i] = sample;
|
||||
}
|
||||
case .Scale:
|
||||
jointAnimation.ScaleChannel = new .(samples, interpolationMode);
|
||||
|
||||
Log.EngineLogger.AssertDebug(channel.Sampler.Output.ComponentType == .R_32f);
|
||||
Log.EngineLogger.AssertDebug(channel.Sampler.Output.Type == .Vec3);
|
||||
|
||||
for(int i < samples)
|
||||
{
|
||||
float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
|
||||
Vector3 sample = GetEntry<Vector3>(channel.Sampler.Output, i);
|
||||
|
||||
jointAnimation.ScaleChannel.TimeStamps[i] = timeStamp;
|
||||
jointAnimation.ScaleChannel.Values[i] = sample;
|
||||
}
|
||||
default:
|
||||
Log.EngineLogger.Error($"Unknown channel target path \"{channel.TargetPath}\"");
|
||||
}
|
||||
|
||||
clip.Duration = Math.Max(clip.Duration, jointAnimation.Duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static T GetEntry<T>(CGLTF.Accessor* accessor, int index)
|
||||
{
|
||||
Log.EngineLogger.AssertDebug((uint)index < accessor.Count);
|
||||
|
||||
T result = ?;
|
||||
|
||||
switch(typeof(T))
|
||||
{
|
||||
case typeof(float):
|
||||
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 1);
|
||||
case typeof(Vector2):
|
||||
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 2);
|
||||
case typeof(Vector3):
|
||||
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 3);
|
||||
case typeof(Vector4), typeof(Quaternion):
|
||||
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 4);
|
||||
case typeof(Matrix):
|
||||
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 16);
|
||||
case typeof(uint16):
|
||||
CGLTF.AccessorReadUint(accessor, (uint)index, (uint32*)&result, 2);
|
||||
case default:
|
||||
uint8* data = (uint8*)accessor.BufferView.Buffer.Data;
|
||||
|
||||
data += accessor.BufferView.Offset;
|
||||
|
||||
data += accessor.Offset;
|
||||
|
||||
data += accessor.Stride * (uint)index;
|
||||
|
||||
result = *(T*)data;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
/*
|
||||
uint8* data = (uint8*)accessor.BufferView.Buffer.Data;
|
||||
|
||||
data += accessor.BufferView.Offset;
|
||||
|
||||
data += accessor.Offset;
|
||||
|
||||
data += accessor.Stride * (uint)index;
|
||||
|
||||
return *(T*)data;
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the vector and component type to the corresponding Format.
|
||||
*/
|
||||
static Format FormatFromVectorComponent(CGLTF.Type vectorType, CGLTF.ComponentType componentType)
|
||||
{
|
||||
switch((vectorType, componentType))
|
||||
{
|
||||
case (.Scalar, .R_8):
|
||||
return .R8_SInt;
|
||||
case (.Scalar, .R_8u):
|
||||
return .R8_UInt;
|
||||
case (.Scalar, .R_16):
|
||||
return .R16_SInt;
|
||||
case (.Scalar, .R_16u):
|
||||
return .R16_UInt;
|
||||
case (.Scalar, .R_32u):
|
||||
return .R32_UInt;
|
||||
case (.Scalar, .R_32f):
|
||||
return .R32_Float;
|
||||
|
||||
case (.Vec2, .R_8):
|
||||
return .R8G8_SInt;
|
||||
case (.Vec2, .R_8u):
|
||||
return .R8G8_UInt;
|
||||
case (.Vec2, .R_16):
|
||||
return .R16G16_SInt;
|
||||
case (.Vec2, .R_16u):
|
||||
return .R16G16_UInt;
|
||||
case (.Vec2, .R_32u):
|
||||
return .R32G32_UInt;
|
||||
case (.Vec2, .R_32f):
|
||||
return .R32G32_Float;
|
||||
|
||||
//case (.Vec3, .R_8):
|
||||
//case (.Vec3, .R_8u):
|
||||
//case (.Vec3, .R_16):
|
||||
//case (.Vec3, .R_16u):
|
||||
case (.Vec3, .R_32u):
|
||||
return .R32G32B32_UInt;
|
||||
case (.Vec3, .R_32f):
|
||||
return .R32G32B32_Float;
|
||||
|
||||
case (.Vec4, .R_8):
|
||||
return .R8G8B8A8_SInt;
|
||||
case (.Vec4, .R_8u):
|
||||
return .R8G8B8A8_UInt;
|
||||
case (.Vec4, .R_16):
|
||||
return .R16G16B16A16_SInt;
|
||||
case (.Vec4, .R_16u):
|
||||
return .R16G16B16A16_UInt;
|
||||
case (.Vec4, .R_32u):
|
||||
return .R32G32B32A32_UInt;
|
||||
case (.Vec4, .R_32f):
|
||||
return .R32G32B32A32_Float;
|
||||
|
||||
case (.Mat4, .R_32f):
|
||||
return .R32G32B32A32_Float;
|
||||
|
||||
default:
|
||||
Log.EngineLogger.Assert(false, scope $"Unhandled vector - componenttype combination. ({vectorType}, {componentType})");
|
||||
}
|
||||
|
||||
return .Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace GlitchyEngine.Core
|
||||
{
|
||||
/**
|
||||
* Provides a reference counter that, when reaching 0, automatically deletes itself.
|
||||
* Implements the IDisposable interface so that it can be used with a using-Block so that the counter
|
||||
* will be decremented automatically after leaving the block.
|
||||
*/
|
||||
public class RefCounter : System.RefCounted, IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
ReleaseRef();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using Beefy.utils;
|
||||
|
||||
namespace GlitchyEngine.Debug
|
||||
{
|
||||
public static class Profiler
|
||||
{
|
||||
#if !GE_PROFILE
|
||||
[SkipCall]
|
||||
#endif
|
||||
public static void BeginProfiling(StringView serverName = "127.0.0.1", StringView sessionName = "GlitchyEngine")
|
||||
{
|
||||
BeefPerf.Init(serverName, sessionName);
|
||||
}
|
||||
|
||||
#if !GE_PROFILE
|
||||
[SkipCall]
|
||||
#endif
|
||||
public static void EndProfiling()
|
||||
{
|
||||
BeefPerf.Shutdown();
|
||||
}
|
||||
|
||||
#if !GE_PROFILE
|
||||
[SkipCall]
|
||||
#endif
|
||||
public static mixin ProfileFunction()
|
||||
{
|
||||
scope:mixin PerformanceTimer()
|
||||
}
|
||||
|
||||
#if !GE_PROFILE
|
||||
[SkipCall]
|
||||
#endif
|
||||
public static mixin ProfileScope(char8* scopeName)
|
||||
{
|
||||
scope:mixin PerformanceTimer(scopeName)
|
||||
}
|
||||
|
||||
// Extension
|
||||
|
||||
#if !GE_PROFILE_RENDERER
|
||||
[SkipCall]
|
||||
#endif
|
||||
public static mixin ProfileRendererFunction()
|
||||
{
|
||||
scope:mixin PerformanceTimer()
|
||||
}
|
||||
|
||||
#if !GE_PROFILE_RENDERER
|
||||
[SkipCall]
|
||||
#endif
|
||||
public static mixin ProfileRendererScope(char8* scopeName)
|
||||
{
|
||||
scope:mixin PerformanceTimer(scopeName)
|
||||
}
|
||||
|
||||
#if !GE_PROFILE_RESOURCES
|
||||
[SkipCall]
|
||||
#endif
|
||||
public static mixin ProfileResourceFunction()
|
||||
{
|
||||
scope:mixin PerformanceTimer()
|
||||
}
|
||||
|
||||
#if !GE_PROFILE_RESOURCES
|
||||
[SkipCall]
|
||||
#endif
|
||||
public static mixin ProfileResourceScope(char8* scopeName)
|
||||
{
|
||||
scope:mixin PerformanceTimer(scopeName)
|
||||
}
|
||||
}
|
||||
|
||||
class PerformanceTimer
|
||||
{
|
||||
[Inline]
|
||||
public this(char8* name = Compiler.CallerMemberName)
|
||||
{
|
||||
#if GE_PROFILE
|
||||
[Inline]
|
||||
BeefPerf.Enter(name);
|
||||
#endif
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public ~this()
|
||||
{
|
||||
#if GE_PROFILE
|
||||
[Inline]
|
||||
BeefPerf.Leave();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,9 @@ namespace GlitchyEngine.Events
|
||||
private bool _isResizing;
|
||||
|
||||
/// The new width of the window.
|
||||
public int32 Width = _width;
|
||||
public int32 Width => _width;
|
||||
/// The new height of the window.
|
||||
public int32 Height = _height;
|
||||
public int32 Height => _height;
|
||||
/*
|
||||
* If true, indicates that the resizing is not finished.
|
||||
*/
|
||||
@@ -89,4 +89,44 @@ namespace GlitchyEngine.Events
|
||||
strBuffer.AppendF("WindowMovedEvent: {}, {} (is moving: {})", _x, _y, _isMoving);
|
||||
}
|
||||
}
|
||||
|
||||
public class WindowActivateEvent : Event, IEvent
|
||||
{
|
||||
public override EventType EventType => .WindowMoved;
|
||||
|
||||
public override StringView Name => "WindowActivated";
|
||||
|
||||
public override EventCategory Category => .Application;
|
||||
|
||||
public static EventType StaticType => .WindowActivated;
|
||||
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
strBuffer.AppendF("WindowActivatedEvent");
|
||||
}
|
||||
}
|
||||
|
||||
public class WindowDeactivateEvent : Event, IEvent
|
||||
{
|
||||
public override EventType EventType => .WindowMoved;
|
||||
|
||||
public override StringView Name => "WindowDeactivated";
|
||||
|
||||
public override EventCategory Category => .Application;
|
||||
|
||||
public static EventType StaticType => .WindowDeactivated;
|
||||
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
strBuffer.AppendF("WindowDeactivatedEvent");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace GlitchyEngine.Events
|
||||
public enum EventType
|
||||
{
|
||||
None = 0,
|
||||
WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved,
|
||||
WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved, WindowActivated, WindowDeactivated,
|
||||
AppTick, AppUpdate, AppRender,
|
||||
KeyPressed, KeyReleased, KeyTyped,
|
||||
MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled
|
||||
@@ -50,7 +50,7 @@ namespace GlitchyEngine.Events
|
||||
}
|
||||
}
|
||||
|
||||
public class EventDispatcher
|
||||
public struct EventDispatcher
|
||||
{
|
||||
private Event _event;
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace System
|
||||
{
|
||||
public extension Array1<T>
|
||||
{
|
||||
/// Returns the index of the given element or -1 if it is not in this array.
|
||||
public int IndexOf(T element)
|
||||
{
|
||||
for(int i < mLength)
|
||||
if(Ptr[i] == element)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses Binary Search to find the index of the given Element.
|
||||
* @param element The element whose element will be returned.
|
||||
* @returns The index of the element in the array or -1 if it is not in the array.
|
||||
* @Note this function only works if the entries of the array are sorted in ascending order.
|
||||
*/
|
||||
public int IndexOfBinary(T element) where bool : operator T < T
|
||||
{
|
||||
int lowerBound = 0;
|
||||
int upperBound = mLength;
|
||||
|
||||
while(lowerBound != upperBound)
|
||||
{
|
||||
int index = (lowerBound + upperBound) / 2;
|
||||
|
||||
T elementAtIndex = Ptr[index];
|
||||
|
||||
if(elementAtIndex == element)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
else if(elementAtIndex < element)
|
||||
{
|
||||
upperBound = index;
|
||||
}
|
||||
else
|
||||
{
|
||||
lowerBound = index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace System
|
||||
{
|
||||
extension Runtime
|
||||
{
|
||||
[NoReturn, Warn("The method is not implemented.")]
|
||||
#unwarn
|
||||
public static void NotImplemented(String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum)
|
||||
{
|
||||
String failStr = scope .()..AppendF("Not Implemented at line {} in {}", line, filePath);
|
||||
Internal.FatalError(failStr, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace System
|
||||
{
|
||||
extension String
|
||||
{
|
||||
[Inline]
|
||||
public void CopyTo(Span<char8> target)
|
||||
{
|
||||
CopyTo(target.Ptr, target.Length);
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public void CopyTo(char8* target, int targetLength)
|
||||
{
|
||||
int copiedChars = Math.Min(targetLength - 1, Length);
|
||||
|
||||
Internal.MemCpy(target, Ptr, copiedChars);
|
||||
|
||||
target[copiedChars] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace GlitchyEngine
|
||||
*/
|
||||
public class GameTime
|
||||
{
|
||||
private Stopwatch _stopwatch ~ delete:append _;
|
||||
private Stopwatch _stopwatch;
|
||||
|
||||
private uint64 _frameCount;
|
||||
|
||||
@@ -27,7 +27,10 @@ namespace GlitchyEngine
|
||||
* The number of frames that have been finished since the timer was started.
|
||||
*/
|
||||
public uint64 FrameCount => _frameCount;
|
||||
|
||||
|
||||
/// The interval in seconds from the last frame to the current one.
|
||||
public float DeltaTime => (float)_frameTime.TotalSeconds;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of a GameTime.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using static System.Compiler;
|
||||
|
||||
namespace GlitchyEngine.Generators
|
||||
{
|
||||
public class NewDisposableComponentGenerator : Generator
|
||||
{
|
||||
public override String Name => "New Disposable Component";
|
||||
|
||||
public override void InitUI()
|
||||
{
|
||||
AddEdit("name", "Component Name", "");
|
||||
}
|
||||
|
||||
public override void Generate(String outFileName, String outText, ref Flags generateFlags)
|
||||
{
|
||||
var name = mParams["name"];
|
||||
if (name.EndsWith(".bf", .OrdinalIgnoreCase))
|
||||
name.RemoveFromEnd(3);
|
||||
outFileName.Append(name);
|
||||
|
||||
outText.AppendF(
|
||||
$"""
|
||||
using GlitchyEngine.World;
|
||||
|
||||
namespace {Namespace}
|
||||
{{
|
||||
struct {name} : IDisposableComponent
|
||||
{{
|
||||
public void Dispose() mut
|
||||
{{
|
||||
// TODO: Dispose of the content
|
||||
}}
|
||||
}}
|
||||
}}
|
||||
""");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using static System.Compiler;
|
||||
|
||||
namespace GlitchyEngine.Generators
|
||||
{
|
||||
public class NewStructGenerator : Generator
|
||||
{
|
||||
public override String Name => "New Struct";
|
||||
|
||||
public override void InitUI()
|
||||
{
|
||||
AddEdit("name", "Struct Name", "");
|
||||
}
|
||||
|
||||
public override void Generate(String outFileName, String outText, ref Flags generateFlags)
|
||||
{
|
||||
var name = mParams["name"];
|
||||
if (name.EndsWith(".bf", .OrdinalIgnoreCase))
|
||||
name.RemoveFromEnd(3);
|
||||
outFileName.Append(name);
|
||||
outText.AppendF(
|
||||
$"""
|
||||
namespace {Namespace}
|
||||
{{
|
||||
struct {name}
|
||||
{{
|
||||
}}
|
||||
}}
|
||||
""");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using DirectX.Common;
|
||||
using GlitchyEngine.Core;
|
||||
|
||||
namespace GlitchyEngine
|
||||
{
|
||||
static
|
||||
{
|
||||
/// Releases the reference to destination, writes newValue into it and adds a reference to newValue.
|
||||
public static mixin SetReference<T>(T destination, T newValue) where T: RefCounter
|
||||
{
|
||||
var oldDest = destination;
|
||||
destination = newValue;
|
||||
destination?.AddRef();
|
||||
oldDest?.ReleaseRef();
|
||||
}
|
||||
|
||||
/// Releases the reference to value and nullifies it.
|
||||
public static mixin ReleaseRefAndNullify<T>(T value) where T: RefCounter
|
||||
{
|
||||
value?.ReleaseRef();
|
||||
value = null;
|
||||
}
|
||||
|
||||
public static mixin DeleteContainerAndReleaseItems(var container)
|
||||
{
|
||||
if (container != null)
|
||||
{
|
||||
for (var value in container)
|
||||
value?.ReleaseRef();
|
||||
delete container;
|
||||
}
|
||||
}
|
||||
|
||||
public static mixin ClearAndReleaseItems(var container)
|
||||
{
|
||||
for (var value in container)
|
||||
value?.ReleaseRef();
|
||||
container.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,24 @@
|
||||
using GlitchyEngine.Math;
|
||||
using GlitchyEngine.Renderer;
|
||||
using System;
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
extension ImGui
|
||||
{
|
||||
// TODO: Color-functions
|
||||
extension Vec2
|
||||
{
|
||||
public static explicit operator Vector2(Vec2 v) => .(v.x, v.y);
|
||||
public static explicit operator Vec2(Vector2 v) => .(v.X, v.Y);
|
||||
}
|
||||
|
||||
extension Vec4
|
||||
{
|
||||
public static explicit operator Vector4(Vec4 v) => .(v.x, v.y, v.z, v.w);
|
||||
public static explicit operator Vec4(Vector4 v) => .(v.X, v.Y, v.Z, v.W);
|
||||
}
|
||||
|
||||
// TODO: Color-functions
|
||||
public static bool ColorEdit3(char* label, ref ColorRGB col, ColorEditFlags flags = (ColorEditFlags) 0) => ColorEdit3Impl(label, *(float[3]*)&col, flags);
|
||||
public static bool ColorEdit3(char* label, ref ColorRGBA col, ColorEditFlags flags = (ColorEditFlags) 0) => ColorEdit3Impl(label, *(float[3]*)&col, flags);
|
||||
|
||||
@@ -14,5 +28,99 @@ namespace ImGui
|
||||
public static bool ColorPicker3(char* label, ref ColorRGBA col, ColorEditFlags flags = (ColorEditFlags) 0) => ColorPicker3Impl(label, *(float[3]*)&col, flags);
|
||||
|
||||
public static bool ColorPicker4(char* label, ref ColorRGBA col, ColorEditFlags flags = (ColorEditFlags) 0, float* ref_col = null) => ColorPicker4Impl(label, *(float[4]*)&col, flags, ref_col);
|
||||
|
||||
public static extern void Image(Texture2D texture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec4 tint_col = Vec4.Ones, Vec4 border_col = Vec4.Zero);
|
||||
// Wouldn't be necesseary if RenderTarget2D was Texture2D
|
||||
public static extern void Image(RenderTarget2D texture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec4 tint_col = Vec4.Ones, Vec4 border_col = Vec4.Zero);
|
||||
|
||||
public static void TextUnformatted(StringView text) => TextUnformattedImpl(text.Ptr, text.Ptr + text.Length);
|
||||
|
||||
public static void PushID(StringView id) => PushID(id.Ptr, id.Ptr + id.Length);
|
||||
|
||||
/// Releases references that accumulated calls like ImGui::Image
|
||||
protected internal static extern void CleanupFrame();
|
||||
|
||||
/// Control to edit a vector 3 with drag functionality and reset buttons
|
||||
public static bool EditVector3(StringView label, ref Vector3 value, Vector3 resetValues = .Zero, float dragSpeed = 0.1f, float columnWidth = 100f)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
PushID(label);
|
||||
defer PopID();
|
||||
|
||||
Columns(2);
|
||||
defer Columns(1);
|
||||
SetColumnWidth(0, columnWidth);
|
||||
|
||||
TextUnformatted(label);
|
||||
|
||||
NextColumn();
|
||||
|
||||
PushMultiItemsWidths(3, CalcItemWidth());
|
||||
PushStyleVar(.ItemSpacing, Vec2.Zero);
|
||||
defer PopStyleVar();
|
||||
|
||||
float lineHeight = GetFont().FontSize + GetStyle().FramePadding.y * 2.0f;
|
||||
ImGui.Vec2 buttonSize = .(lineHeight + 3.0f, lineHeight);
|
||||
|
||||
PushStyleColor(.Button, Color(230, 25, 45).Value);
|
||||
PushStyleColor(.ButtonHovered, Color(150, 25, 45).Value);
|
||||
PushStyleColor(.ButtonActive, Color(230, 120, 130).Value);
|
||||
|
||||
if (Button("X", buttonSize))
|
||||
{
|
||||
value.X = resetValues.X;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
SameLine();
|
||||
|
||||
if (DragFloat("##X", &value.X, dragSpeed))
|
||||
changed = true;
|
||||
|
||||
PopItemWidth();
|
||||
SameLine();
|
||||
|
||||
PopStyleColor(3);
|
||||
PushStyleColor(.Button, Color(50, 190, 15).Value);
|
||||
PushStyleColor(.ButtonHovered, Color(50, 120, 15).Value);
|
||||
PushStyleColor(.ButtonActive, Color(116, 190, 99).Value);
|
||||
|
||||
if (Button("Y", buttonSize))
|
||||
{
|
||||
value.Y = resetValues.Y;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
SameLine();
|
||||
|
||||
if (DragFloat("##Y", &value.Y, dragSpeed))
|
||||
changed = true;
|
||||
|
||||
PopItemWidth();
|
||||
SameLine();
|
||||
|
||||
PopStyleColor(3);
|
||||
PushStyleColor(.Button, Color(55, 55, 230).Value);
|
||||
PushStyleColor(.ButtonHovered, Color(55, 55, 150).Value);
|
||||
PushStyleColor(.ButtonActive, Color(90, 90, 230).Value);
|
||||
|
||||
if (Button("Z", buttonSize))
|
||||
{
|
||||
value.Z = resetValues.Z;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
SameLine();
|
||||
|
||||
if (DragFloat("##Z", &value.Z, dragSpeed))
|
||||
changed = true;
|
||||
|
||||
PopItemWidth();
|
||||
|
||||
PopStyleColor(3);
|
||||
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
using System;
|
||||
using ImGui;
|
||||
using GlitchyEngine.Events;
|
||||
using ImGuizmo;
|
||||
using GlitchyEngine.Renderer;
|
||||
using System.IO;
|
||||
|
||||
using internal ImGui;
|
||||
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
#endif
|
||||
|
||||
namespace GlitchyEngine.ImGui
|
||||
{
|
||||
public class ImGuiLayer : Layer
|
||||
{
|
||||
ImGui.IO* _io;
|
||||
|
||||
public bool SettingsInvalid;
|
||||
|
||||
public this() : base("ImGuiLayer") { }
|
||||
|
||||
public override void OnAttach()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
//ImGuiImplWin32.EnableDpiAwareness();
|
||||
Log.EngineLogger.Trace("Initializing ImGui...");
|
||||
|
||||
@@ -17,198 +35,114 @@ namespace GlitchyEngine.ImGui
|
||||
ImGui.CreateContext();
|
||||
ImGui.StyleColorsDark();
|
||||
|
||||
ref ImGui.IO io = ref ImGui.GetIO();
|
||||
io.ConfigFlags |= .NavEnableKeyboard;
|
||||
io.ConfigFlags |= .DockingEnable;
|
||||
io.ConfigFlags |= .ViewportsEnable;
|
||||
_io = ImGui.GetIO();
|
||||
_io.ConfigFlags |= .NavEnableKeyboard;
|
||||
_io.ConfigFlags |= .DockingEnable;
|
||||
_io.ConfigFlags |= .ViewportsEnable;
|
||||
|
||||
// Todo: currently broken in ImGui
|
||||
//io.ConfigFlags |= .DpiEnableScaleFonts;
|
||||
//io.ConfigFlags |= .DpiEnableScaleViewports;
|
||||
|
||||
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
|
||||
ref ImGui.Style style = ref ImGui.GetStyle();
|
||||
if(io.ConfigFlags.HasFlag(.ViewportsEnable))
|
||||
ImGui.Style* style = ImGui.GetStyle();
|
||||
if(_io.ConfigFlags.HasFlag(.ViewportsEnable))
|
||||
{
|
||||
style.WindowRounding = 0.0f;
|
||||
style.Colors[(int)ImGui.Col.WindowBg].w = 1.0f;
|
||||
}
|
||||
|
||||
#if GE_WINDOWS
|
||||
// Todo: temporary, needs to be platform independent
|
||||
ImGuiImplWin32.Init((Windows.HWnd)(int)Application.Get().Window.NativeWindow);
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
ImGuiImplWin32.Init(Application.Get().Window.NativeWindow);
|
||||
#endif
|
||||
|
||||
var context = Application.Get().Window.Context;
|
||||
|
||||
ImGuiImplDX11.Init(context.[Friend]nativeDevice, context.[Friend]nativeContext);
|
||||
#if GE_GRAPHICS_DX11
|
||||
ImGuiImplDX11.Init(NativeDevice, NativeContext);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
public override void OnDetach()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
#if GE_GRAPHICS_DX11
|
||||
ImGuiImplDX11.Shutdown();
|
||||
#endif
|
||||
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
ImGuiImplWin32.Shutdown();
|
||||
#endif
|
||||
|
||||
ImGui.DestroyContext();
|
||||
}
|
||||
|
||||
public override void OnEvent(Event event)
|
||||
{
|
||||
/*
|
||||
EventDispatcher dispatcher = scope EventDispatcher(event);
|
||||
|
||||
dispatcher.Dispatch<WindowResizeEvent>(scope (e) =>
|
||||
{
|
||||
//ref ImGui.IO io = ref ImGui.GetIO();
|
||||
//io.DisplaySize = .(e.Width, e.Height);
|
||||
//io.DisplayFramebufferScale = .(1.0f, 1.0f);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
dispatcher.Dispatch<MouseMovedEvent>(scope (e) =>
|
||||
{
|
||||
ref ImGui.IO io = ref ImGui.GetIO();
|
||||
io.MousePos = ImGui.Vec2(e.PositionX, e.PositionY);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
dispatcher.Dispatch<MouseButtonPressedEvent>(scope (e) =>
|
||||
{
|
||||
int button = 0;
|
||||
switch(e.MouseButton)
|
||||
{
|
||||
case .LeftButton:
|
||||
button = (uint)ImGui.MouseButton.Left;
|
||||
case .RightButton:
|
||||
button = (uint)ImGui.MouseButton.Right;
|
||||
case .MiddleButton:
|
||||
button = (uint)ImGui.MouseButton.Middle;
|
||||
case .XButton1:
|
||||
button = (uint)3;
|
||||
case .XButton2:
|
||||
button = (uint)4;
|
||||
default:
|
||||
}
|
||||
|
||||
ref ImGui.IO io = ref ImGui.GetIO();
|
||||
io.MouseDown[button] = true;
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
dispatcher.Dispatch<MouseButtonReleasedEvent>(scope (e) =>
|
||||
{
|
||||
int button = 0;
|
||||
switch(e.MouseButton)
|
||||
{
|
||||
case .LeftButton:
|
||||
button = (uint)ImGui.MouseButton.Left;
|
||||
case .RightButton:
|
||||
button = (uint)ImGui.MouseButton.Right;
|
||||
case .MiddleButton:
|
||||
button = (uint)ImGui.MouseButton.Middle;
|
||||
case .XButton1:
|
||||
button = (uint)3;
|
||||
case .XButton2:
|
||||
button = (uint)4;
|
||||
default:
|
||||
}
|
||||
|
||||
ref ImGui.IO io = ref ImGui.GetIO();
|
||||
io.MouseDown[button] = false;
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
dispatcher.Dispatch<MouseScrolledEvent>(scope (e) =>
|
||||
{
|
||||
ref ImGui.IO io = ref ImGui.GetIO();
|
||||
io.MouseWheel += e.YOffset;
|
||||
io.MouseWheelH += e.XOffset; // Todo: horizontal mousewheel inverted?!
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
dispatcher.Dispatch<KeyPressedEvent>(scope (e) =>
|
||||
{
|
||||
if(e.KeyCode >= (.)256)
|
||||
return false;
|
||||
|
||||
ref ImGui.IO io = ref ImGui.GetIO();
|
||||
io.KeysDown[(int32)e.KeyCode] = true;
|
||||
|
||||
io.KeyCtrl = io.KeysDown[(int32)Key.Control];
|
||||
io.KeyShift = io.KeysDown[(int32)Key.Shift];
|
||||
io.KeyAlt = io.KeysDown[(int32)Key.Alt];
|
||||
io.KeySuper = io.KeysDown[(int32)Key.LeftSuper] || io.KeysDown[(int32)Key.RightSuper];
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
dispatcher.Dispatch<KeyReleasedEvent>(scope (e) =>
|
||||
{
|
||||
if(e.KeyCode >= (.)256)
|
||||
return false;
|
||||
|
||||
ref ImGui.IO io = ref ImGui.GetIO();
|
||||
io.KeysDown[(int32)e.KeyCode] = false;
|
||||
|
||||
io.KeyCtrl = io.KeysDown[(int32)Key.Control];
|
||||
io.KeyShift = io.KeysDown[(int32)Key.Shift];
|
||||
io.KeyAlt = io.KeysDown[(int32)Key.Alt];
|
||||
io.KeySuper = io.KeysDown[(int32)Key.LeftSuper] || io.KeysDown[(int32)Key.RightSuper];
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
dispatcher.Dispatch<KeyTypedEvent>(scope (e) =>
|
||||
{
|
||||
ref ImGui.IO io = ref ImGui.GetIO();
|
||||
io.AddInputCharacterUTF16((uint16)e.Char);
|
||||
|
||||
return false;
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
public void Begin()
|
||||
{
|
||||
// Todo:
|
||||
//var v = DirectX.ImmediateContext;
|
||||
//v.OutputMerger.SetRenderTargets(1, &DirectX.BackBufferTarget, null);
|
||||
Application.Get().Window.Context.SetRenderTarget(null);
|
||||
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
ImGuiImplDX11.NewFrame();
|
||||
ImGuiImplWin32.NewFrame();
|
||||
ImGui.NewFrame();
|
||||
ImGuizmo.BeginFrame();
|
||||
}
|
||||
|
||||
bool showDemo = true;
|
||||
public void ImGuiRender()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
if (SettingsInvalid)
|
||||
{
|
||||
var settings = Application.Get().Settings.ImGuiSettings;
|
||||
|
||||
ImGui.GetIO().Fonts.Clear();
|
||||
|
||||
String fullpath = scope String();
|
||||
Application.Get().ContentManager.GetFilePath(fullpath, settings.FontName);
|
||||
if (File.Exists(fullpath))
|
||||
{
|
||||
ImGui.GetIO().Fonts.AddFontFromFileTTF(fullpath, settings.FontSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.GetIO().Fonts.AddFontDefault();
|
||||
}
|
||||
|
||||
#if GE_GRAPHICS_DX11
|
||||
ImGuiImplDX11.CreateDeviceObjects();
|
||||
#endif
|
||||
|
||||
SettingsInvalid = false;
|
||||
}
|
||||
|
||||
Begin();
|
||||
|
||||
var event = scope ImGuiRenderEvent();
|
||||
Application.Get().OnEvent(event);
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("ImGuiRenderEvent");
|
||||
|
||||
ImGui.ShowDemoWindow(&showDemo);
|
||||
var event = scope ImGuiRenderEvent();
|
||||
Application.Get().OnEvent(event);
|
||||
}
|
||||
|
||||
End();
|
||||
}
|
||||
|
||||
public void End()
|
||||
{
|
||||
ref ImGui.IO io = ref ImGui.GetIO();
|
||||
|
||||
let window = Application.Get().Window;
|
||||
io.DisplaySize = .(window.Width, window.Height);
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
ImGui.Render();
|
||||
ImGuiImplDX11.RenderDrawData(ref ImGui.GetDrawData());
|
||||
|
||||
RenderCommand.SetDepthStencilTarget(null);
|
||||
RenderCommand.SetRenderTarget(null);
|
||||
RenderCommand.BindRenderTargets();
|
||||
|
||||
if(io.ConfigFlags.HasFlag(.ViewportsEnable))
|
||||
#if GE_GRAPHICS_DX11
|
||||
ImGuiImplDX11.RenderDrawData(ImGui.GetDrawData());
|
||||
#endif
|
||||
|
||||
ImGui.CleanupFrame();
|
||||
|
||||
if(_io.ConfigFlags.HasFlag(.ViewportsEnable))
|
||||
{
|
||||
ImGui.UpdatePlatformWindows();
|
||||
ImGui.RenderPlatformWindowsDefault();
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace GlitchyEngine
|
||||
public static extern int32 GetLastMouseY();
|
||||
|
||||
public static extern bool IsMouseButtonPressing(MouseButton button);
|
||||
public static extern bool IsMouseButtonReleasiong(MouseButton button);
|
||||
public static extern bool IsMouseButtonReleasing(MouseButton button);
|
||||
public static extern Point GetMouseMovement();
|
||||
|
||||
public static extern void NewFrame();
|
||||
|
||||
@@ -68,6 +68,7 @@ namespace GlitchyEngine
|
||||
case Z = 90; // Z key
|
||||
case LeftSuper = 91; // Left Windows key(Microsoft® Natural® keyboard)
|
||||
case RightSuper = 92; // Right Windows key(Natural keyboard)
|
||||
case ContextMenu = 93; // Context Menu key (VK_APPS)
|
||||
case Numpad0 = 96; // Numeric keypad 0 key
|
||||
case Numpad1 = 97; // Numeric keypad 1 key
|
||||
case Numpad2 = 98; // Numeric keypad 2 key
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace GlitchyEngine
|
||||
public void PushLayer(Layer ownLayer)
|
||||
{
|
||||
_layers.Insert(_insertIndex++, ownLayer);
|
||||
ownLayer.OnAttach();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,7 +39,6 @@ namespace GlitchyEngine
|
||||
public void PushOverlay(Layer ownOverlay)
|
||||
{
|
||||
_layers.Add(ownOverlay);
|
||||
ownOverlay.OnAttach();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
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))
|
||||
{
|
||||
var initialCapacity;
|
||||
|
||||
if (initialCapacity < sizeof(uint))
|
||||
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 Int2 XX => Int2((int32)this);
|
||||
|
||||
[Inline]
|
||||
public Int3 XXX => Int3((int32)this);
|
||||
|
||||
[Inline]
|
||||
public Int4 XXXX => Int4((int32)this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
|
||||
namespace GlitchyEngine.Math
|
||||
{
|
||||
public static class MathHelper
|
||||
{
|
||||
/// An optimal representation of π.
|
||||
public const float Pi = 3.141592654f;
|
||||
/// An optimal representation of 2*π.
|
||||
public const float TwoPi = 6.283185307f;
|
||||
/// An optimal representation of 1/π.
|
||||
public const float OneOverPi = 0.318309886f;
|
||||
/// An optimal representation of 2/π.
|
||||
public const float OneOverTwoPi = 0.159154943f;
|
||||
/// An optimal representation of π/2.
|
||||
public const float PiOverTwo = 1.570796327f;
|
||||
/// An optimal representation of π/4.
|
||||
public const float PiOverFour = 0.785398163f;
|
||||
|
||||
/// Converts radians to degrees
|
||||
public const float RadToDeg = 180.0f / Pi;
|
||||
|
||||
/// Converts radians to degrees
|
||||
public const float DegToRad = Pi / 180.0f;
|
||||
|
||||
// Converts the given radians to degrees
|
||||
public static float ToDegrees(float radians)
|
||||
{
|
||||
return radians * RadToDeg;
|
||||
}
|
||||
|
||||
// Converts the given radians to degrees
|
||||
public static Vector2 ToDegrees(Vector2 radians)
|
||||
{
|
||||
return radians * RadToDeg;
|
||||
}
|
||||
|
||||
// Converts the given radians to degrees
|
||||
public static Vector3 ToDegrees(Vector3 radians)
|
||||
{
|
||||
return radians * RadToDeg;
|
||||
}
|
||||
|
||||
// Converts the given radians to degrees
|
||||
public static Vector4 ToDegrees(Vector4 radians)
|
||||
{
|
||||
return radians * RadToDeg;
|
||||
}
|
||||
|
||||
// Converts the given degrees to radians
|
||||
public static float ToRadians(float degrees)
|
||||
{
|
||||
return degrees * DegToRad;
|
||||
}
|
||||
|
||||
// Converts the given degrees to radians
|
||||
public static Vector2 ToRadians(Vector2 degrees)
|
||||
{
|
||||
return degrees * DegToRad;
|
||||
}
|
||||
|
||||
// Converts the given degrees to radians
|
||||
public static Vector3 ToRadians(Vector3 degrees)
|
||||
{
|
||||
return degrees * DegToRad;
|
||||
}
|
||||
|
||||
// Converts the given degrees to radians
|
||||
public static Vector4 ToRadians(Vector4 degrees)
|
||||
{
|
||||
return degrees * DegToRad;
|
||||
}
|
||||
|
||||
const float epsilon = 0.0000001f;
|
||||
|
||||
public static bool IsZero(float value)
|
||||
{
|
||||
return Math.Abs(value) < epsilon;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using GlitchyEngine.Math;
|
||||
using GlitchyEngine;
|
||||
|
||||
namespace DirectX.Math
|
||||
{
|
||||
extension Matrix
|
||||
{
|
||||
typealias Vec3 = GlitchyEngine.Math.Vector3;
|
||||
|
||||
public static Self RotationQuaternion(Quaternion rotation)
|
||||
{
|
||||
float xSq = 2 * rotation.X * rotation.X;
|
||||
float ySq = 2 * rotation.Y * rotation.Y;
|
||||
float zSq = 2 * rotation.Z * rotation.Z;
|
||||
|
||||
float xy = 2 * rotation.X * rotation.Y;
|
||||
float xz = 2 * rotation.X * rotation.Z;
|
||||
float xw = 2 * rotation.X * rotation.W;
|
||||
float yz = 2 * rotation.Y * rotation.Z;
|
||||
float yw = 2 * rotation.Y * rotation.W;
|
||||
float zw = 2 * rotation.Z * rotation.W;
|
||||
|
||||
Self result = ?;
|
||||
|
||||
result.V._11 = 1 - ySq - zSq;
|
||||
result.V._21 = xy + zw;
|
||||
result.V._31 = xz - yw;
|
||||
result.V._41 = 0;
|
||||
|
||||
result.V._12 = xy - zw;
|
||||
result.V._22 = 1 - xSq - zSq;
|
||||
result.V._32 = yz + xw;
|
||||
result.V._42 = 0;
|
||||
|
||||
result.V._13 = xz + yw;
|
||||
result.V._23 = yz - xw;
|
||||
result.V._33 = 1 - xSq - ySq;
|
||||
result.V._43 = 0;
|
||||
|
||||
result.V._14 = 0;
|
||||
result.V._24 = 0;
|
||||
result.V._34 = 0;
|
||||
result.V._44 = 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void Decompose(Self matrix, out Vec3 position, out Quaternion rotation, out Vec3 scale)
|
||||
{
|
||||
var matrix;
|
||||
|
||||
// Translation -> get last column
|
||||
position = matrix.Translation;
|
||||
// Zero translation for next step
|
||||
matrix.Translation = .Zero;
|
||||
|
||||
// TODO: this doesn't detect mirroring
|
||||
|
||||
// Extract scaling from matrix
|
||||
scale.X = (*(Vec3*)&matrix.Columns[0]).Magnitude();
|
||||
scale.Y = (*(Vec3*)&matrix.Columns[1]).Magnitude();
|
||||
scale.Z = (*(Vec3*)&matrix.Columns[2]).Magnitude();
|
||||
|
||||
if(MathHelper.IsZero(scale.X) || MathHelper.IsZero(scale.Y) || MathHelper.IsZero(scale.Z))
|
||||
{
|
||||
rotation = .Identity;
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove scale from matrix (normalize the columns)
|
||||
matrix.Columns[0] /= scale.X;
|
||||
matrix.Columns[1] /= scale.Y;
|
||||
matrix.Columns[2] /= scale.Z;
|
||||
|
||||
rotation = Quaternion.FromMatrix(matrix);
|
||||
}
|
||||
|
||||
/*[Test]
|
||||
static void TestQuaternionToMatrix()
|
||||
{
|
||||
// Rotation around Z-Axis by 90°
|
||||
Quaternion quat = .(0, 0, 0.707107f, 0.707107f);
|
||||
quat.Normalize();
|
||||
|
||||
|
||||
}*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,378 @@
|
||||
using System;
|
||||
|
||||
namespace GlitchyEngine.Math
|
||||
{
|
||||
public struct Quaternion
|
||||
{
|
||||
public const Quaternion Zero = .();
|
||||
public const Quaternion One = .(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
public const Quaternion Identity = .(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
public float X, Y, Z, W;
|
||||
|
||||
public this() => this = default;
|
||||
|
||||
public this(float x, float y, float z, float w)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
public this(Vector3 xy, float z, float w)
|
||||
{
|
||||
X = xy.X;
|
||||
Y = xy.Y;
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
public this(Vector3 xyz, float w)
|
||||
{
|
||||
X = xyz.X;
|
||||
Y = xyz.Y;
|
||||
Z = xyz.Z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
public this(Vector4 vector)
|
||||
{
|
||||
X = vector.X;
|
||||
Y = vector.Y;
|
||||
Z = vector.Z;
|
||||
W = vector.W;
|
||||
}
|
||||
|
||||
public Vector3 Vector
|
||||
{
|
||||
get => .(X, Y, Z);
|
||||
set mut
|
||||
{
|
||||
X = value.X;
|
||||
Y = value.Y;
|
||||
Z = value.Z;
|
||||
}
|
||||
}
|
||||
|
||||
public float Scalar
|
||||
{
|
||||
get => W;
|
||||
set mut => W = value;
|
||||
}
|
||||
|
||||
public void Normalize() mut
|
||||
{
|
||||
float invLength = 1.0f / Length();
|
||||
|
||||
X *= invLength;
|
||||
Y *= invLength;
|
||||
Z *= invLength;
|
||||
W *= invLength;
|
||||
}
|
||||
|
||||
public static Quaternion Normalize(Quaternion q)
|
||||
{
|
||||
float invLength = 1.0f / Length(q);
|
||||
|
||||
return Quaternion(q.X * invLength, q.Y * invLength, q.Z * invLength, q.W * invLength);
|
||||
}
|
||||
|
||||
public float Length()
|
||||
{
|
||||
return Math.Sqrt([Inline]LengthSquared());
|
||||
}
|
||||
|
||||
public static float Length(Quaternion q)
|
||||
{
|
||||
return Math.Sqrt([Inline]LengthSquared(q));
|
||||
}
|
||||
|
||||
public float LengthSquared()
|
||||
{
|
||||
return X * X + Y * Y + Z * Z + W * W;
|
||||
}
|
||||
|
||||
public static float LengthSquared(Quaternion q)
|
||||
{
|
||||
return q.X * q.X + q.Y * q.Y + q.Z * q.Z + q.W * q.W;
|
||||
}
|
||||
|
||||
public static float Dot(Self l, Self r)
|
||||
{
|
||||
return l.X * r.X + l.Y * r.Y + l.Z * r.Z + l.W * r.W;
|
||||
}
|
||||
|
||||
public static Quaternion Lerp(Quaternion a, Quaternion b, float interpolationValue)
|
||||
{
|
||||
return a + interpolationValue * (b - a);
|
||||
}
|
||||
|
||||
public static Quaternion Slerp(Quaternion previousQuaternion, Quaternion nextQuaternion, float interpolationValue)
|
||||
{
|
||||
// Based on https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_007_Animations.md
|
||||
|
||||
var nextQuaternion;
|
||||
|
||||
float dot = Dot(previousQuaternion, nextQuaternion);
|
||||
|
||||
//make sure we take the shortest path in case dot Product is negative
|
||||
if(dot < 0.0f)
|
||||
{
|
||||
nextQuaternion = -nextQuaternion;
|
||||
dot = -dot;
|
||||
}
|
||||
|
||||
//if the two quaternions are too close to each other, just linear interpolate between the 4D vector
|
||||
if(dot > 0.9995f)
|
||||
return Normalize(previousQuaternion + interpolationValue * (nextQuaternion - previousQuaternion));
|
||||
|
||||
//perform the spherical linear interpolation
|
||||
var theta_0 = Math.Acos(dot);
|
||||
var theta = interpolationValue * theta_0;
|
||||
var sin_theta = Math.Sin(theta);
|
||||
var sin_theta_0 = Math.Sin(theta_0);
|
||||
|
||||
var scalePreviousQuat = Math.Cos(theta) - dot * sin_theta / sin_theta_0;
|
||||
var scaleNextQuat = sin_theta / sin_theta_0;
|
||||
return scalePreviousQuat * previousQuaternion + scaleNextQuat * nextQuaternion;
|
||||
}
|
||||
|
||||
public static Quaternion FromMatrix(Matrix matrix)
|
||||
{
|
||||
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
|
||||
|
||||
var m = matrix.V;
|
||||
|
||||
Quaternion result = ?;
|
||||
|
||||
float tr = m._11 + m._22 + m._33;
|
||||
|
||||
if (tr > 0) {
|
||||
float S = Math.Sqrt(tr + 1.0f) * 2f; // S=4*result.W
|
||||
result.W = 0.25f * S;
|
||||
result.X = (m._32 - m._23) / S;
|
||||
result.Y = (m._13 - m._31) / S;
|
||||
result.Z = (m._21 - m._12) / S;
|
||||
} else if ((m._11 > m._22)&(m._11 > m._33)) {
|
||||
float S = Math.Sqrt(1.0f + m._11 - m._22 - m._33) * 2f; // S=4*result.X
|
||||
result.W = (m._32 - m._23) / S;
|
||||
result.X = 0.25f * S;
|
||||
result.Y = (m._12 + m._21) / S;
|
||||
result.Z = (m._13 + m._31) / S;
|
||||
} else if (m._22 > m._33) {
|
||||
float S = Math.Sqrt(1.0f + m._22 - m._11 - m._33) * 2f; // S=4*result.Y
|
||||
result.W = (m._13 - m._31) / S;
|
||||
result.X = (m._12 + m._21) / S;
|
||||
result.Y = 0.25f * S;
|
||||
result.Z = (m._23 + m._32) / S;
|
||||
} else {
|
||||
float S = Math.Sqrt(1.0f + m._33 - m._11 - m._22) * 2f; // S=4*result.Z
|
||||
result.W = (m._21 - m._12) / S;
|
||||
result.X = (m._13 + m._31) / S;
|
||||
result.Y = (m._23 + m._32) / S;
|
||||
result.Z = 0.25f * S;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Self operator +(Self value) => value;
|
||||
|
||||
public static Self operator +(Self l, Self r) => Self(l.X + r.X, l.Y + r.Y, l.Z + r.Z, l.W + r.W);
|
||||
|
||||
public static Self operator -(Self value) => Self(-value.X, -value.Y, -value.Z, -value.W);
|
||||
|
||||
public static Self operator -(Self l, Self r) => Self(l.X - r.X, l.Y - r.Y, l.Z - r.Z, l.W - r.W);
|
||||
|
||||
public static Self operator *(float l, Self r) => Self(l * r.X, l * r.Y, l * r.Z, l * r.W);
|
||||
|
||||
public static Self operator /(Self l, float r) => Self(l.X * r, l.Y * r, l.Z * r, l.W * r);
|
||||
|
||||
public static Self operator *(Self l, Self r)
|
||||
{
|
||||
Quaternion result;
|
||||
|
||||
Vector3 v = l.Vector * r.Vector + (l.W * r.Vector) + (r.W * l.Vector);
|
||||
result.X = v.X;
|
||||
result.Y = v.Y;
|
||||
result.Z = v.Z;
|
||||
result.W = (l.W * r.W) - Vector3.Dot(l.Vector, r.Vector);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Quaternion Conjugate(Quaternion q)
|
||||
{
|
||||
return Quaternion(-q.Vector, q.Scalar);
|
||||
}
|
||||
|
||||
public static Quaternion Inverse(Quaternion q)
|
||||
{
|
||||
Quaternion conjugate = Conjugate(q);
|
||||
|
||||
float magSquared = LengthSquared(q);
|
||||
|
||||
return conjugate / magSquared;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
#unwarn
|
||||
public static implicit operator Vector4(in Self value) => *(Vector4*)&value;
|
||||
|
||||
[Inline]
|
||||
#unwarn
|
||||
public static implicit operator Quaternion(in Vector4 value) => *(Quaternion*)&value;
|
||||
|
||||
public static bool operator ==(Quaternion l, Quaternion r)
|
||||
{
|
||||
return l.X == r.X && l.Y == r.Y && l.Z == r.Z && l.W == r.W;
|
||||
}
|
||||
|
||||
public static bool operator !=(Quaternion l, Quaternion r)
|
||||
{
|
||||
return l.X != r.X && l.Y != r.Y && l.Z != r.Z && l.W != r.W;
|
||||
}
|
||||
|
||||
public (Vector3 Axis, float Angle) ToAxisAngle()
|
||||
{
|
||||
// scalar part = cos(θ/2)
|
||||
// So, we can extract the angle directly.
|
||||
float angle = 2.0f * Math.Acos(W);
|
||||
|
||||
// vector part = axis * sin(θ/2)
|
||||
// In other words, the vector part is the axis, but with length of sin(θ/2).
|
||||
// We assume quaternion is unit length, so subtracting w^2 gives us length of just vector part (aka sin(θ/2)).
|
||||
float length = Math.Sqrt(1.0f - (W * W));
|
||||
|
||||
Vector3 axis;
|
||||
|
||||
// Normalize vector part to get the axis!
|
||||
if(length == 0)
|
||||
{
|
||||
axis = Vector3.Zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = 1.0f / length;
|
||||
axis.X = X * length;
|
||||
axis.Y = Y * length;
|
||||
axis.Z = Z * length;
|
||||
}
|
||||
|
||||
return (axis, angle);
|
||||
}
|
||||
|
||||
public static Quaternion FromAxisAngle(Vector3 axis, float angle)
|
||||
{
|
||||
float lengthSq = axis.MagnitudeSquared();
|
||||
|
||||
if(lengthSq == 0)
|
||||
{
|
||||
return .Identity;
|
||||
}
|
||||
|
||||
float halfAngle = angle * 0.5f;
|
||||
|
||||
float sin = Math.Sin(halfAngle) / Math.Sqrt(lengthSq);
|
||||
|
||||
Quaternion result;
|
||||
result.X = axis.X * sin;
|
||||
result.Y = axis.Y * sin;
|
||||
result.Z = axis.Z * sin;
|
||||
result.W = Math.Cos(halfAngle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Assumes YZX-Order meaning Y applied first, Z second and x last
|
||||
public static Quaternion FromEulerAngles(float yaw, float pitch, float roll)
|
||||
{
|
||||
float halfYaw = yaw / 2.0f;
|
||||
float halfPitch = pitch / 2.0f;
|
||||
float halfRoll = roll / 2.0f;
|
||||
|
||||
float cosYaw = Math.Cos(halfYaw);//heading
|
||||
float sinYaw = Math.Sin(halfYaw);
|
||||
float cosRoll = Math.Cos(halfRoll);//attitude
|
||||
float sinRoll = Math.Sin(halfRoll);
|
||||
float cosPitch = Math.Cos(halfPitch);//bank
|
||||
float sinPitch = Math.Sin(halfPitch);
|
||||
|
||||
float cosYawCosRoll = cosYaw * cosRoll;
|
||||
float sinYawSinRoll = sinYaw * sinRoll;
|
||||
float cosYawSinRoll = cosYaw * sinRoll;
|
||||
float sinYawCosRoll = sinYaw * cosRoll;
|
||||
|
||||
Quaternion result;
|
||||
|
||||
result.W = cosYawCosRoll * cosPitch - sinYawSinRoll * sinPitch;
|
||||
result.X = cosYawCosRoll * sinPitch + sinYawSinRoll * cosPitch;
|
||||
result.Y = sinYawCosRoll * cosPitch + cosYawSinRoll * sinPitch;
|
||||
result.Z = cosYawSinRoll * cosPitch - sinYawCosRoll * sinPitch;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Vector3 ToEulerAngles(Quaternion q)
|
||||
{
|
||||
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
|
||||
|
||||
Vector3 result;
|
||||
|
||||
float sqw = q.W*q.W;
|
||||
float sqx = q.X*q.X;
|
||||
float sqy = q.Y*q.Y;
|
||||
float sqz = q.Z*q.Z;
|
||||
float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
|
||||
float test = q.X*q.Y + q.Z*q.W;
|
||||
if (test > 0.4999f*unit) { // singularity at north pole
|
||||
result.Y = 2.0f * Math.Atan2(q.X,q.W);
|
||||
result.Z = Math.PI_f / 2.0f;
|
||||
result.X = 0.0f;
|
||||
return result;
|
||||
}
|
||||
if (test < -0.4999f*unit) { // singularity at south pole
|
||||
result.Y = -2.0f * Math.Atan2(q.X,q.W);
|
||||
result.Z = -Math.PI_f / 2.0f;
|
||||
result.X = 0.0f;
|
||||
return result;
|
||||
}
|
||||
result.Y = Math.Atan2(2*q.Y*q.W-2*q.X*q.Z , sqx - sqy - sqz + sqw);
|
||||
result.Z = Math.Asin(2*test/unit);
|
||||
result.X = Math.Atan2(2*q.X*q.W-2*q.Y*q.Z , -sqx + sqy - sqz + sqw);
|
||||
|
||||
return result;
|
||||
|
||||
//return .(q.Pitch(), q.Yaw(), q.Roll());
|
||||
}
|
||||
/*
|
||||
public float Pitch()
|
||||
{
|
||||
float y = 2.0f * (Y * Z + W * X);
|
||||
float x = W * W - X * X - Y * Y + Z * Z;
|
||||
|
||||
if (Vector2(x, y).Equals(.Zero)) //avoid atan2(0,0) - handle singularity - Matiis
|
||||
return 2.0f * Math.Atan2(X, W);
|
||||
|
||||
return Math.Atan2(y, x);
|
||||
}
|
||||
|
||||
public float Yaw()
|
||||
{
|
||||
return Math.Asin(Math.Clamp(-2.0f * (X * Z - W * Y), -1.0f, 1.0f));
|
||||
}
|
||||
|
||||
public float Roll()
|
||||
{
|
||||
float y = 2.0f * (X * Y + W * Z);
|
||||
float x = W * W + X * X - Y * Y - Z * Z;
|
||||
|
||||
if (Vector2(x, y).Equals(.Zero)) //avoid atan2(0,0) - handle singularity - Matiis
|
||||
return 0;
|
||||
|
||||
return Math.Atan2(y, x);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
|
||||
namespace GlitchyEngine.Math
|
||||
{
|
||||
public struct ResourceBox
|
||||
{
|
||||
/// The x position of the left hand side of the box.
|
||||
public uint32 Left;
|
||||
/// The y position of the top of the box.
|
||||
public uint32 Top;
|
||||
/// The z position of the front of the box.
|
||||
public uint32 Front;
|
||||
/// The x position of the right hand side of the box.
|
||||
public uint32 Right;
|
||||
/// The y position of the bottom of the box.
|
||||
public uint32 Bottom;
|
||||
/// The z position of the back of the box.
|
||||
public uint32 Back;
|
||||
|
||||
/**
|
||||
* Returns true if the box is empty.
|
||||
* A box is empty if the top value is greater than or equal to the bottom value,
|
||||
* or the left value is greater than or equal to the right value,
|
||||
* or the front value is greater than or equal to the back value.
|
||||
*/
|
||||
public bool IsEmpty => Top >= Bottom || Left >= Right || Front >= Back;
|
||||
|
||||
/**
|
||||
* Instantiates a new instance of an uninitialized Box structure.
|
||||
*/
|
||||
public this()
|
||||
{
|
||||
this = default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new instance of a Box structure that is initialized with the dimensions of a box.
|
||||
*/
|
||||
public this(uint32 left, uint32 top, uint32 front, uint32 right, uint32 bottom, uint32 back)
|
||||
{
|
||||
Left = left;
|
||||
Top = top;
|
||||
Front = front;
|
||||
Right = right;
|
||||
Bottom = bottom;
|
||||
Back = back;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public static bool operator ==(ResourceBox l, ResourceBox r)
|
||||
{
|
||||
return l.Left == r.Left && l.Top == r.Top && l.Front == r.Front &&
|
||||
l.Right == r.Right && l.Bottom == r.Bottom && l.Back == r.Back;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
public static bool operator !=(ResourceBox l, ResourceBox r)
|
||||
{
|
||||
return !(l == r);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,15 +12,18 @@ namespace GlitchyEngine.Math
|
||||
|
||||
public int VectorSize => _vectorSize;
|
||||
|
||||
this(int vectorSize)
|
||||
private String _vectorTypeName;
|
||||
|
||||
this(int vectorSize, String vectorTypeName)
|
||||
{
|
||||
_vectorSize = vectorSize;
|
||||
_vectorTypeName = vectorTypeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not a swizzle operator can have a valid setter (e.g. no component assigned twice)
|
||||
* Returns true if the swizzle operator is invalid (same component assigned twice).
|
||||
*/
|
||||
static bool invalidSetter(int[4] cmp, int vecSize)
|
||||
static bool IsSetterValid(int[4] cmp, int vecSize)
|
||||
{
|
||||
return cmp[0] == cmp[1] || (vecSize >= 3 && cmp[0] == cmp[2]) || (vecSize == 4 && cmp[0] == cmp[3]) ||
|
||||
(vecSize >= 3 && cmp[1] == cmp[2]) || (vecSize == 4 && cmp[1] == cmp[3]) ||
|
||||
@@ -30,8 +33,7 @@ namespace GlitchyEngine.Math
|
||||
[Comptime]
|
||||
public void ApplyToType(Type type)
|
||||
{
|
||||
// TODO: report bug... sized array not working
|
||||
String[] componentNames = scope String[]("X", "Y", "Z", "W");
|
||||
String[4] componentNames = .("X", "Y", "Z", "W");
|
||||
|
||||
for(int swizzleCount = 2; swizzleCount <= 4; swizzleCount++)
|
||||
{
|
||||
@@ -49,7 +51,16 @@ namespace GlitchyEngine.Math
|
||||
String swizzleConstructor = scope String(swizzleCount * 3);
|
||||
String setter = scope String(128);
|
||||
|
||||
bool setterInvalid = invalidSetter(cmp, swizzleCount);
|
||||
bool setterInvalid = IsSetterValid(cmp, swizzleCount);
|
||||
|
||||
if (!setterInvalid)
|
||||
{
|
||||
setter.Append(
|
||||
"""
|
||||
set mut
|
||||
{
|
||||
""");
|
||||
}
|
||||
|
||||
for(int c = 0; c < swizzleCount; c++)
|
||||
{
|
||||
@@ -61,21 +72,26 @@ namespace GlitchyEngine.Math
|
||||
}
|
||||
swizzleConstructor.Append(componentNames[cmp[c]]);
|
||||
|
||||
if(!setterInvalid && c < _vectorSize) //
|
||||
if(!setterInvalid && c < _vectorSize)
|
||||
{
|
||||
setter.AppendF($"\n\t\t{componentNames[cmp[c]]} = value.{componentNames[c]};");
|
||||
}
|
||||
}
|
||||
|
||||
//{(setterInvalid ? "[Error(\"Cannot assign multiple values to same component.\")]" : String.Empty)}
|
||||
if (!setterInvalid)
|
||||
{
|
||||
setter.Append(
|
||||
"""
|
||||
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
String swizzleString = scope $"""
|
||||
public Vector{swizzleCount} {swizzleName}
|
||||
public {_vectorTypeName}{swizzleCount} {swizzleName}
|
||||
{{
|
||||
get => .({swizzleConstructor});
|
||||
set mut
|
||||
{{{setter}
|
||||
}}
|
||||
{setter}
|
||||
}}
|
||||
|
||||
""";
|
||||
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace GlitchyEngine.Math
|
||||
{
|
||||
[SwizzleVector(2)]
|
||||
[SwizzleVector(2, "Vector")]
|
||||
public struct Vector2
|
||||
{
|
||||
public const Vector2 Zero = .(0f, 0f);
|
||||
@@ -10,7 +10,7 @@ namespace GlitchyEngine.Math
|
||||
public const Vector2 UnitY = .(0f, 1f);
|
||||
public const Vector2 One = .(1f, 1f);
|
||||
|
||||
public const int Length = 2;
|
||||
public const int ComponentCount = 2;
|
||||
|
||||
public float X, Y;
|
||||
|
||||
@@ -31,18 +31,61 @@ namespace GlitchyEngine.Math
|
||||
public ref float this[int index]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
get mut
|
||||
{
|
||||
if(index < 0 || index >= Length)
|
||||
if(index < 0 || index >= ComponentCount)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
|
||||
#unwarn
|
||||
return ref (&X)[index];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
#unwarn
|
||||
get => ref (&X)[index];
|
||||
get mut => ref (&X)[index];
|
||||
}
|
||||
|
||||
public float this[int index]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
{
|
||||
if(index < 0 || index >= ComponentCount)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
|
||||
if(index == 0)
|
||||
return X;
|
||||
else
|
||||
return Y;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
if(index == 0)
|
||||
return X;
|
||||
else
|
||||
return Y;
|
||||
}
|
||||
|
||||
[Checked]
|
||||
set mut
|
||||
{
|
||||
if(index < 0 || index >= ComponentCount)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
|
||||
if(index == 0)
|
||||
X = value;
|
||||
else
|
||||
Y = value;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
set mut
|
||||
{
|
||||
if(index == 0)
|
||||
X = value;
|
||||
else
|
||||
Y = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +133,7 @@ namespace GlitchyEngine.Math
|
||||
/**
|
||||
* Calculates the projection of a onto b
|
||||
*/
|
||||
public Vector2 Project(Vector2 a, Vector2 b)
|
||||
public static Vector2 Project(Vector2 a, Vector2 b)
|
||||
{
|
||||
return (b * (Dot(a, b) / Dot(b, b)));
|
||||
}
|
||||
@@ -98,11 +141,33 @@ namespace GlitchyEngine.Math
|
||||
/**
|
||||
* Calculates the rejection of a from b
|
||||
*/
|
||||
public Vector2 Reject(Vector2 a, Vector2 b)
|
||||
public static Vector2 Reject(Vector2 a, Vector2 b)
|
||||
{
|
||||
return (a - b * (Dot(a, b) / Dot(b, b)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolates linearly between two given vectors.
|
||||
* @param a The first vector.
|
||||
* @param b The second vector.
|
||||
* @param interpolationValue The value that linearly interpolates between a and b.
|
||||
* (0 means a will be returned, 1 means b will be returned.)
|
||||
* @returns The resulting linear interpolation.
|
||||
*/
|
||||
public static Vector2 Lerp(Vector2 a, Vector2 b, float interpolationValue)
|
||||
{
|
||||
return a + interpolationValue * (b - a);
|
||||
}
|
||||
|
||||
public static Vector2 Min(Vector2 a, Vector2 b)
|
||||
{
|
||||
return .(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y));
|
||||
}
|
||||
|
||||
public static Vector2 Max(Vector2 a, Vector2 b)
|
||||
{
|
||||
return .(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
|
||||
}
|
||||
|
||||
//
|
||||
// Assignment operators
|
||||
@@ -217,11 +282,12 @@ namespace GlitchyEngine.Math
|
||||
|
||||
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1}", X, Y);
|
||||
|
||||
// Todo: move to extension
|
||||
[Inline]
|
||||
public static implicit operator DirectX.Math.Vector2(in Self value) => *(DirectX.Math.Vector2*)&value;
|
||||
public static explicit operator Self(float value) => Self(value);
|
||||
|
||||
[Inline]
|
||||
public static implicit operator Self(in DirectX.Math.Vector2 value) => *(Self*)&value;
|
||||
public bool Equals(Vector2 v, float epsilon = Math.[Friend]sMachineEpsilonFloat)
|
||||
{
|
||||
return (Math.Abs(v.X - X) < epsilon) && (Math.Abs(v.Y - Y) < epsilon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+340
-258
@@ -1,258 +1,340 @@
|
||||
using System;
|
||||
|
||||
namespace GlitchyEngine.Math
|
||||
{
|
||||
[SwizzleVector(3)]
|
||||
public struct Vector3
|
||||
{
|
||||
public const Vector3 Zero = .(0f, 0f, 0f);
|
||||
public const Vector3 UnitX = .(1f, 0f, 0f);
|
||||
public const Vector3 UnitY = .(0f, 1f, 0f);
|
||||
public const Vector3 UnitZ = .(0f, 0f, 1f);
|
||||
public const Vector3 One = .(1f, 1f, 1f);
|
||||
|
||||
public const Vector3 Forward = .(0f, 0f, 1f);
|
||||
public const Vector3 Backward = .(0f, 0f, -1f);
|
||||
public const Vector3 Left = .(-1f, 0f, 0f);
|
||||
public const Vector3 Right = .(1f, 0f, 0f);
|
||||
public const Vector3 Up = .(0f, 1f, 0f);
|
||||
public const Vector3 Down = .(0f, -1f, 0f);
|
||||
|
||||
public float X, Y, Z;
|
||||
|
||||
public this() => this = default;
|
||||
|
||||
public this(float value)
|
||||
{
|
||||
X = value;
|
||||
Y = value;
|
||||
Z = value;
|
||||
}
|
||||
|
||||
public this(Vector2 value, float z)
|
||||
{
|
||||
X = value.X;
|
||||
Y = value.Y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
public this(float x, float y, float z)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
public ref float this[int index]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
{
|
||||
if(index < 0 || index > 2)
|
||||
Internal.ThrowIndexOutOfRange();
|
||||
|
||||
#unwarn
|
||||
return ref (&X)[index];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
#unwarn
|
||||
get => ref (&X)[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the magnitude (length) of this vector.
|
||||
* @remarks MagnitudeSquared might be used if only the relative length is relevant.
|
||||
*/
|
||||
public float Magnitude()
|
||||
{
|
||||
return Math.Sqrt(X * X + Y * Y + Z * Z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the squared magnitude (length) of this vector.
|
||||
*/
|
||||
public float MagnitudeSquared()
|
||||
{
|
||||
return X * X + Y * Y + Z * Z;
|
||||
}
|
||||
|
||||
[Checked]
|
||||
public void Normalize() mut
|
||||
{
|
||||
if(this == .Zero)
|
||||
return;
|
||||
|
||||
this /= Magnitude();
|
||||
}
|
||||
|
||||
public void Normalize() mut
|
||||
{
|
||||
this /= Magnitude();
|
||||
}
|
||||
|
||||
public static Vector3 Normalize(Vector3 v)
|
||||
{
|
||||
return v / v.Magnitude();
|
||||
}
|
||||
|
||||
public static float Dot(Vector3 l, Vector3 r)
|
||||
{
|
||||
return l.X * r.X + l.Y * r.Y + l.Z * r.Z;
|
||||
}
|
||||
|
||||
public static Vector3 Cross(Vector3 l, Vector3 r)
|
||||
{
|
||||
return .(l.Y * r.Z - l.Z * r.Y,
|
||||
l.Z * r.X - l.X * r.Z,
|
||||
l.X * r.Y - l.Y * r.X);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the projection of a onto b
|
||||
*/
|
||||
public static Vector3 Project(Vector3 a, Vector3 b)
|
||||
{
|
||||
return (b * (Dot(a, b) / Dot(b, b)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the rejection of a from b
|
||||
*/
|
||||
public static Vector3 Reject(Vector3 a, Vector3 b)
|
||||
{
|
||||
return (a - b * (Dot(a, b) / Dot(b, b)));
|
||||
}
|
||||
|
||||
//
|
||||
// Assignment operators
|
||||
//
|
||||
|
||||
// Addition
|
||||
|
||||
public void operator +=(Vector3 value) mut
|
||||
{
|
||||
X += value.X;
|
||||
Y += value.Y;
|
||||
Z += value.Z;
|
||||
}
|
||||
|
||||
public void operator +=(float scalar) mut
|
||||
{
|
||||
X += scalar;
|
||||
Y += scalar;
|
||||
Z += scalar;
|
||||
}
|
||||
|
||||
// Subtraction
|
||||
|
||||
|
||||
public void operator -=(Vector3 value) mut
|
||||
{
|
||||
X -= value.X;
|
||||
Y -= value.Y;
|
||||
Z -= value.Z;
|
||||
}
|
||||
|
||||
public void operator -=(float scalar) mut
|
||||
{
|
||||
X -= scalar;
|
||||
Y -= scalar;
|
||||
Z -= scalar;
|
||||
}
|
||||
|
||||
// Multiplication
|
||||
|
||||
public void operator *=(Vector3 value) mut
|
||||
{
|
||||
X *= value.X;
|
||||
Y *= value.Y;
|
||||
Z *= value.Z;
|
||||
}
|
||||
|
||||
public void operator *=(float scalar) mut
|
||||
{
|
||||
X *= scalar;
|
||||
Y *= scalar;
|
||||
Z *= scalar;
|
||||
}
|
||||
|
||||
// Division
|
||||
|
||||
public void operator /=(Vector3 value) mut
|
||||
{
|
||||
X /= value.X;
|
||||
Y /= value.Y;
|
||||
Z /= value.Z;
|
||||
}
|
||||
|
||||
public void operator /=(float scalar) mut
|
||||
{
|
||||
float inv = 1.0f / scalar;
|
||||
X *= inv;
|
||||
Y *= inv;
|
||||
Z *= inv;
|
||||
}
|
||||
|
||||
//
|
||||
// operators
|
||||
//
|
||||
|
||||
// Addition
|
||||
|
||||
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);
|
||||
|
||||
public static Vector3 operator +(Vector3 value) => value;
|
||||
|
||||
// Subtraction
|
||||
|
||||
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);
|
||||
|
||||
public static Vector3 operator -(Vector3 value) => Vector3(-value.X, -value.Y, -value.Z);
|
||||
|
||||
// Multiplication
|
||||
|
||||
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);
|
||||
|
||||
// Division
|
||||
|
||||
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)
|
||||
{
|
||||
float inv = 1.0f / scalar;
|
||||
return Vector3(value.X * inv, value.Y * inv, value.Z * inv);
|
||||
}
|
||||
|
||||
public static Vector3 operator /(float scalar, Vector3 value) => Vector3(scalar / value.X, scalar / value.Y, scalar / value.Z);
|
||||
|
||||
// Equality
|
||||
|
||||
public static bool operator ==(Vector3 left, Vector3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
|
||||
|
||||
public static bool operator !=(Vector3 left, Vector3 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
|
||||
|
||||
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1} Z:{2}", X, Y, Z);
|
||||
|
||||
// Todo: move to extension
|
||||
|
||||
[Inline]
|
||||
public static implicit operator DirectX.Math.Vector3(in Self value) => *(DirectX.Math.Vector3*)&value;
|
||||
|
||||
[Inline]
|
||||
public static implicit operator Self(in DirectX.Math.Vector3 value) => *(Self*)&value;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace GlitchyEngine.Math
|
||||
{
|
||||
[SwizzleVector(3, "Vector")]
|
||||
public struct Vector3
|
||||
{
|
||||
public const Vector3 Zero = .(0f, 0f, 0f);
|
||||
public const Vector3 UnitX = .(1f, 0f, 0f);
|
||||
public const Vector3 UnitY = .(0f, 1f, 0f);
|
||||
public const Vector3 UnitZ = .(0f, 0f, 1f);
|
||||
public const Vector3 One = .(1f, 1f, 1f);
|
||||
|
||||
public const Vector3 Forward = .(0f, 0f, 1f);
|
||||
public const Vector3 Backward = .(0f, 0f, -1f);
|
||||
public const Vector3 Left = .(-1f, 0f, 0f);
|
||||
public const Vector3 Right = .(1f, 0f, 0f);
|
||||
public const Vector3 Up = .(0f, 1f, 0f);
|
||||
public const Vector3 Down = .(0f, -1f, 0f);
|
||||
|
||||
public const int ComponentCount = 3;
|
||||
|
||||
public float X, Y, Z;
|
||||
|
||||
public this() => this = default;
|
||||
|
||||
public this(float value)
|
||||
{
|
||||
X = value;
|
||||
Y = value;
|
||||
Z = value;
|
||||
}
|
||||
|
||||
public this(Vector2 value, float z)
|
||||
{
|
||||
X = value.X;
|
||||
Y = value.Y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
public this(float x, float y, float z)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
public ref float this[int index]
|
||||
{
|
||||
[Checked]
|
||||
get mut
|
||||
{
|
||||
if(index < 0 || index >= ComponentCount)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
|
||||
return ref (&X)[index];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
get mut => ref (&X)[index];
|
||||
}
|
||||
|
||||
public float this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch(index)
|
||||
{
|
||||
case 0: return X;
|
||||
case 1: return Y;
|
||||
case 2: return Z;
|
||||
default: Internal.ThrowIndexOutOfRange(1);
|
||||
}
|
||||
}
|
||||
|
||||
set mut
|
||||
{
|
||||
switch(index)
|
||||
{
|
||||
case 0: X = value;
|
||||
case 1: Y = value;
|
||||
case 2: Z = value;
|
||||
default: Internal.ThrowIndexOutOfRange(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the magnitude (length) of this vector.
|
||||
* @remarks MagnitudeSquared might be used if only the relative length is relevant.
|
||||
*/
|
||||
public float Magnitude()
|
||||
{
|
||||
return Math.Sqrt(X * X + Y * Y + Z * Z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the squared magnitude (length) of this vector.
|
||||
*/
|
||||
public float MagnitudeSquared()
|
||||
{
|
||||
return X * X + Y * Y + Z * Z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes this vector.
|
||||
*/
|
||||
[Checked]
|
||||
public void Normalize() mut
|
||||
{
|
||||
if(this == .Zero)
|
||||
return;
|
||||
|
||||
this /= Magnitude();
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes this vector.
|
||||
*/
|
||||
public void Normalize() mut
|
||||
{
|
||||
this /= Magnitude();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this Vector with a magnitude of 1.
|
||||
*/
|
||||
[Checked]
|
||||
public Vector3 Normalized()
|
||||
{
|
||||
if(this == .Zero)
|
||||
return .Zero;
|
||||
|
||||
return this / Magnitude();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this Vector with a magnitude of 1.
|
||||
*/
|
||||
public Vector3 Normalized()
|
||||
{
|
||||
return this / Magnitude();
|
||||
}
|
||||
|
||||
public static Vector3 Normalize(Vector3 v)
|
||||
{
|
||||
return v / v.Magnitude();
|
||||
}
|
||||
|
||||
public static float Dot(Vector3 l, Vector3 r)
|
||||
{
|
||||
return l.X * r.X + l.Y * r.Y + l.Z * r.Z;
|
||||
}
|
||||
|
||||
public static Vector3 Cross(Vector3 l, Vector3 r)
|
||||
{
|
||||
return .(l.Y * r.Z - l.Z * r.Y,
|
||||
l.Z * r.X - l.X * r.Z,
|
||||
l.X * r.Y - l.Y * r.X);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the projection of a onto b
|
||||
*/
|
||||
public static Vector3 Project(Vector3 a, Vector3 b)
|
||||
{
|
||||
return (b * (Dot(a, b) / Dot(b, b)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the rejection of a from b
|
||||
*/
|
||||
public static Vector3 Reject(Vector3 a, Vector3 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolates linearly between two given vectors.
|
||||
* @param a The first vector.
|
||||
* @param b The second vector.
|
||||
* @param interpolationValue The value that linearly interpolates between a and b.
|
||||
* (0 means a will be returned, 1 means b will be returned.)
|
||||
* @returns The resulting linear interpolation.
|
||||
*/
|
||||
public static Vector3 Lerp(Vector3 a, Vector3 b, float interpolationValue)
|
||||
{
|
||||
return a + interpolationValue * (b - a);
|
||||
}
|
||||
|
||||
public static Vector3 Min(Vector3 a, Vector3 b)
|
||||
{
|
||||
return .(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Min(a.Z, b.Z));
|
||||
}
|
||||
|
||||
public static Vector3 Max(Vector3 a, Vector3 b)
|
||||
{
|
||||
return .(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y), Math.Min(a.Z, b.Z));
|
||||
}
|
||||
|
||||
//
|
||||
// Assignment operators
|
||||
//
|
||||
|
||||
// Addition
|
||||
|
||||
public void operator +=(Vector3 value) mut
|
||||
{
|
||||
X += value.X;
|
||||
Y += value.Y;
|
||||
Z += value.Z;
|
||||
}
|
||||
|
||||
public void operator +=(float scalar) mut
|
||||
{
|
||||
X += scalar;
|
||||
Y += scalar;
|
||||
Z += scalar;
|
||||
}
|
||||
|
||||
// Subtraction
|
||||
|
||||
|
||||
public void operator -=(Vector3 value) mut
|
||||
{
|
||||
X -= value.X;
|
||||
Y -= value.Y;
|
||||
Z -= value.Z;
|
||||
}
|
||||
|
||||
public void operator -=(float scalar) mut
|
||||
{
|
||||
X -= scalar;
|
||||
Y -= scalar;
|
||||
Z -= scalar;
|
||||
}
|
||||
|
||||
// Multiplication
|
||||
|
||||
public void operator *=(Vector3 value) mut
|
||||
{
|
||||
X *= value.X;
|
||||
Y *= value.Y;
|
||||
Z *= value.Z;
|
||||
}
|
||||
|
||||
public void operator *=(float scalar) mut
|
||||
{
|
||||
X *= scalar;
|
||||
Y *= scalar;
|
||||
Z *= scalar;
|
||||
}
|
||||
|
||||
// Division
|
||||
|
||||
public void operator /=(Vector3 value) mut
|
||||
{
|
||||
X /= value.X;
|
||||
Y /= value.Y;
|
||||
Z /= value.Z;
|
||||
}
|
||||
|
||||
public void operator /=(float scalar) mut
|
||||
{
|
||||
float inv = 1.0f / scalar;
|
||||
X *= inv;
|
||||
Y *= inv;
|
||||
Z *= inv;
|
||||
}
|
||||
|
||||
//
|
||||
// operators
|
||||
//
|
||||
|
||||
// Addition
|
||||
|
||||
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);
|
||||
|
||||
public static Vector3 operator +(Vector3 value) => value;
|
||||
|
||||
// Subtraction
|
||||
|
||||
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);
|
||||
|
||||
public static Vector3 operator -(Vector3 value) => Vector3(-value.X, -value.Y, -value.Z);
|
||||
|
||||
// Multiplication
|
||||
|
||||
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);
|
||||
|
||||
// Division
|
||||
|
||||
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)
|
||||
{
|
||||
float inv = 1.0f / scalar;
|
||||
return Vector3(value.X * inv, value.Y * inv, value.Z * inv);
|
||||
}
|
||||
|
||||
public static Vector3 operator /(float scalar, Vector3 value) => Vector3(scalar / value.X, scalar / value.Y, scalar / value.Z);
|
||||
|
||||
// Modulo
|
||||
|
||||
public static Vector3 operator %(Vector3 left, Vector3 right) => Vector3(left.X % right.X, left.Y % right.Y, left.Z % right.Z);
|
||||
|
||||
public static Vector3 operator %(Vector3 value, float scalar) => Vector3(value.X % scalar, value.Y % scalar, value.Z % scalar);
|
||||
|
||||
public static Vector3 operator %(float scalar, Vector3 value) => Vector3(scalar % value.X, scalar % value.Y, scalar % value.Z);
|
||||
|
||||
// Equality
|
||||
|
||||
public static bool operator ==(Vector3 left, Vector3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
|
||||
|
||||
public static bool operator !=(Vector3 left, Vector3 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
|
||||
|
||||
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1} Z:{2}", X, Y, Z);
|
||||
|
||||
[Inline]
|
||||
public static explicit operator Self(float value) => Self(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace GlitchyEngine.Math
|
||||
{
|
||||
[SwizzleVector(4)]
|
||||
[SwizzleVector(4, "Vector")]
|
||||
public struct Vector4
|
||||
{
|
||||
public const Vector4 Zero = .(0f, 0f, 0f, 0f);
|
||||
@@ -11,6 +11,8 @@ namespace GlitchyEngine.Math
|
||||
public const Vector4 UnitZ = .(0f, 0f, 1f, 0f);
|
||||
public const Vector4 UnitW = .(0f, 0f, 0f, 1f);
|
||||
public const Vector4 One = .(1f, 1f, 1f, 1f);
|
||||
|
||||
public const int ComponentCount = 4;
|
||||
|
||||
public float X, Y, Z, W;
|
||||
|
||||
@@ -55,21 +57,47 @@ namespace GlitchyEngine.Math
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
|
||||
public ref float this[int index]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
get mut
|
||||
{
|
||||
if(index < 0 || index > 3)
|
||||
Internal.ThrowIndexOutOfRange();
|
||||
#unwarn
|
||||
if(index < 0 || index >= ComponentCount)
|
||||
Internal.ThrowIndexOutOfRange(1);
|
||||
|
||||
return ref (&X)[index];
|
||||
}
|
||||
|
||||
[Inline]
|
||||
#unwarn
|
||||
get => ref (&X)[index];
|
||||
get mut => ref (&X)[index];
|
||||
}
|
||||
|
||||
public float this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch(index)
|
||||
{
|
||||
case 0: return X;
|
||||
case 1: return Y;
|
||||
case 2: return Z;
|
||||
case 3: return W;
|
||||
default: Internal.ThrowIndexOutOfRange(1);
|
||||
}
|
||||
}
|
||||
|
||||
set mut
|
||||
{
|
||||
switch(index)
|
||||
{
|
||||
case 0: X = value;
|
||||
case 1: Y = value;
|
||||
case 2: Z = value;
|
||||
case 3: W = value;
|
||||
default: Internal.ThrowIndexOutOfRange(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,6 +166,29 @@ namespace GlitchyEngine.Math
|
||||
return (a - b * (Dot(a, b) / Dot(b, b)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolates linearly between two given vectors.
|
||||
* @param a The first vector.
|
||||
* @param b The second vector.
|
||||
* @param interpolationValue The value that linearly interpolates between a and b.
|
||||
* (0 means a will be returned, 1 means b will be returned.)
|
||||
* @returns The resulting linear interpolation.
|
||||
*/
|
||||
public static Vector4 Lerp(Vector4 a, Vector4 b, float interpolationValue)
|
||||
{
|
||||
return a + interpolationValue * (b - a);
|
||||
}
|
||||
|
||||
public static Vector4 Min(Vector4 a, Vector4 b)
|
||||
{
|
||||
return .(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Min(a.Z, b.Z), Math.Min(a.W, b.W));
|
||||
}
|
||||
|
||||
public static Vector4 Max(Vector4 a, Vector4 b)
|
||||
{
|
||||
return .(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y), Math.Min(a.Z, b.Z), Math.Min(a.W, b.W));
|
||||
}
|
||||
|
||||
//
|
||||
// Assignment operators
|
||||
//
|
||||
@@ -267,12 +318,7 @@ namespace GlitchyEngine.Math
|
||||
|
||||
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W);
|
||||
|
||||
// Todo: move to extension
|
||||
|
||||
[Inline]
|
||||
public static implicit operator DirectX.Math.Vector4(in Self value) => *(DirectX.Math.Vector4*)&value;
|
||||
|
||||
[Inline]
|
||||
public static implicit operator Self(in DirectX.Math.Vector4 value) => *(Self*)&value;
|
||||
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, "Int")]
|
||||
public struct Int2 : IHashable
|
||||
{
|
||||
public const Int2 Zero = .(0, 0);
|
||||
public const Int2 UnitX = .(1, 0);
|
||||
public const Int2 UnitY = .(0, 1);
|
||||
public const Int2 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 Int2 Abs() => .(Math.Abs(X), Math.Abs(Y));
|
||||
|
||||
//
|
||||
// Assignment operators
|
||||
//
|
||||
|
||||
public void operator +=(Int2 value) mut
|
||||
{
|
||||
X += value.X;
|
||||
Y += value.Y;
|
||||
}
|
||||
|
||||
public void operator +=(int32 value) mut
|
||||
{
|
||||
X += value;
|
||||
Y += value;
|
||||
}
|
||||
|
||||
public void operator -=(Int2 value) mut
|
||||
{
|
||||
X -= value.X;
|
||||
Y -= value.Y;
|
||||
}
|
||||
|
||||
public void operator -=(int32 value) mut
|
||||
{
|
||||
X -= value;
|
||||
Y -= value;
|
||||
}
|
||||
|
||||
public void operator *=(Int2 value) mut
|
||||
{
|
||||
X *= value.X;
|
||||
Y *= value.Y;
|
||||
}
|
||||
|
||||
public void operator *=(int32 value) mut
|
||||
{
|
||||
X *= value;
|
||||
Y *= value;
|
||||
}
|
||||
|
||||
public void operator /=(Int2 value) mut
|
||||
{
|
||||
X /= value.X;
|
||||
Y /= value.Y;
|
||||
}
|
||||
|
||||
public void operator /=(int32 value) mut
|
||||
{
|
||||
X /= value;
|
||||
Y /= value;
|
||||
}
|
||||
|
||||
// Operators
|
||||
|
||||
public static Int2 operator +(Int2 value) => value;
|
||||
public static Int2 operator +(Int2 left, Int2 right) => .(left.X + right.X, left.Y + right.Y);
|
||||
public static Int2 operator +(Int2 left, int32 right) => .(left.X + right, left.Y + right);
|
||||
public static Int2 operator +(int32 left, Int2 right) => .(left + right.X, left + right.Y);
|
||||
|
||||
public static Int2 operator -(Int2 value) => .(-value.X, -value.Y);
|
||||
public static Int2 operator -(Int2 left, Int2 right) => .(left.X - right.X, left.Y - right.Y);
|
||||
public static Int2 operator -(Int2 left, int32 right) => .(left.X - right, left.Y - right);
|
||||
public static Int2 operator -(int32 left, Int2 right) => .(left - right.X, left - right.Y);
|
||||
|
||||
public static Int2 operator *(Int2 left, Int2 right) => .(left.X * right.X, left.Y * right.Y);
|
||||
public static Int2 operator *(Int2 left, int32 right) => .(left.X * right, left.Y * right);
|
||||
public static Int2 operator *(int32 left, Int2 right) => .(left * right.X, left * right.Y);
|
||||
|
||||
public static Int2 operator /(Int2 left, Int2 right) => .(left.X / right.X, left.Y / right.Y);
|
||||
public static Int2 operator /(Int2 left, int32 right) => .(left.X / right, left.Y / right);
|
||||
public static Int2 operator /(int32 left, Int2 right) => .(left / right.X, left / right.Y);
|
||||
|
||||
public static Int2 operator %(Int2 left, Int2 right) => .(left.X % right.X, left.Y % right.Y);
|
||||
public static Int2 operator %(Int2 left, int32 right) => .(left.X % right, left.Y % right);
|
||||
public static Int2 operator %(int32 left, Int2 right) => .(left % right.X, left % right.Y);
|
||||
|
||||
public static bool operator ==(Int2 left, Int2 right) => left.X == right.X && left.Y == right.Y;
|
||||
public static bool operator ==(Int2 left, int32 right) => left.X == right && left.Y == right;
|
||||
public static bool operator ==(int32 left, Int2 right) => left == right.X && left == right.Y;
|
||||
|
||||
public static bool operator !=(Int2 left, Int2 right) => left.X != right.X || left.Y != right.Y;
|
||||
public static bool operator !=(Int2 left, int32 right) => left.X != right || left.Y != right;
|
||||
public static bool operator !=(int32 left, Int2 right) => left != right.X || left != right.Y;
|
||||
|
||||
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y}");
|
||||
|
||||
public static explicit operator Vector2(Int2 point) => .(point.X, point.Y);
|
||||
|
||||
public static explicit operator Int2(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, "Int")]
|
||||
public struct Int3 : IHashable
|
||||
{
|
||||
public const Int3 Zero = .(0, 0, 0);
|
||||
public const Int3 UnitX = .(1, 0, 0);
|
||||
public const Int3 UnitY = .(0, 1, 0);
|
||||
public const Int3 UnitZ = .(0, 0, 1);
|
||||
public const Int3 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(Int2 xy, int32 z)
|
||||
{
|
||||
X = xy.X;
|
||||
Y = xy.Y;
|
||||
Z = z;
|
||||
}
|
||||
|
||||
public this(int32 x, Int2 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(Int2 xy, int z)
|
||||
{
|
||||
X = xy.X;
|
||||
Y = xy.Y;
|
||||
Z = (.)z;
|
||||
}
|
||||
|
||||
public this(int x, Int2 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 Int3 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z));
|
||||
|
||||
//
|
||||
// Assignment operators
|
||||
//
|
||||
|
||||
public void operator +=(Int3 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 -=(Int3 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 *=(Int3 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 /=(Int3 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 Int3 operator +(Int3 value) => value;
|
||||
public static Int3 operator +(Int3 left, Int3 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
|
||||
public static Int3 operator +(Int3 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right);
|
||||
public static Int3 operator +(int32 left, Int3 right) => .(left + right.X, left + right.Y, left + right.Z);
|
||||
|
||||
public static Int3 operator -(Int3 value) => .(-value.X, -value.Y, -value.Z);
|
||||
public static Int3 operator -(Int3 left, Int3 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
|
||||
public static Int3 operator -(Int3 left, int32 right) => .(left.X - right, left.Y - right, left.Z- right);
|
||||
public static Int3 operator -(int32 left, Int3 right) => .(left - right.X, left - right.Y, left - right.Z);
|
||||
|
||||
public static Int3 operator *(Int3 left, Int3 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
|
||||
public static Int3 operator *(Int3 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right);
|
||||
public static Int3 operator *(int32 left, Int3 right) => .(left * right.X, left * right.Y, left * right.Z);
|
||||
|
||||
public static Int3 operator /(Int3 left, Int3 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
|
||||
public static Int3 operator /(Int3 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right);
|
||||
public static Int3 operator /(int32 left, Int3 right) => .(left / right.X, left / right.Y, left / right.Z);
|
||||
|
||||
public static Int3 operator %(Int3 left, Int3 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z);
|
||||
public static Int3 operator %(Int3 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right);
|
||||
public static Int3 operator %(int32 left, Int3 right) => .(left % right.X, left % right.Y, left % right.Z);
|
||||
|
||||
public static bool operator ==(Int3 left, Int3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
|
||||
public static bool operator ==(Int3 left, int32 right) => left.X == right && left.Y == right && left.Z == right;
|
||||
public static bool operator ==(int32 left, Int3 right) => left == right.X && left == right.Y && left == right.Z;
|
||||
|
||||
public static bool operator !=(Int3 left, Int3 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
|
||||
public static bool operator !=(Int3 left, int32 right) => left.X != right || left.Y != right || left.Z != right;
|
||||
public static bool operator !=(int32 left, Int3 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(Int3 point) => .(point.X, point.Y, point.Z);
|
||||
|
||||
public static explicit operator Int3(Vector3 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z);
|
||||
|
||||
[Inline]
|
||||
public static explicit operator Int2(in Int3 point) => *(Int2*)&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, "Int")]
|
||||
public struct Int4 : IHashable
|
||||
{
|
||||
public const Int4 Zero = .(0, 0, 0, 0);
|
||||
public const Int4 UnitX = .(1, 0, 0, 0);
|
||||
public const Int4 UnitY = .(0, 1, 0, 0);
|
||||
public const Int4 UnitZ = .(0, 0, 1, 0);
|
||||
public const Int4 UnitW = .(0, 0, 0, 1);
|
||||
public const Int4 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(Int2 xy, int32 z, int32 w)
|
||||
{
|
||||
X = xy.X;
|
||||
Y = xy.Y;
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
public this(int32 x, Int2 yz, int32 w)
|
||||
{
|
||||
X = x;
|
||||
Y = yz.X;
|
||||
Z = yz.Y;
|
||||
W = w;
|
||||
}
|
||||
|
||||
public this(int32 x, int32 y, Int2 zw)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = zw.X;
|
||||
W = zw.Y;
|
||||
}
|
||||
|
||||
public this(Int2 xy, Int2 zw)
|
||||
{
|
||||
X = xy.X;
|
||||
Y = xy.Y;
|
||||
Z = zw.X;
|
||||
W = zw.Y;
|
||||
}
|
||||
|
||||
public this(Int3 xyz, int32 w)
|
||||
{
|
||||
X = xyz.X;
|
||||
Y = xyz.Y;
|
||||
Z = xyz.Z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
public this(int32 x, Int3 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 Int4 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z), Math.Abs(W));
|
||||
|
||||
//
|
||||
// Assignment operators
|
||||
//
|
||||
|
||||
public void operator +=(Int4 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 -=(Int4 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 *=(Int4 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 /=(Int4 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 Int4 operator +(Int4 value) => value;
|
||||
public static Int4 operator +(Int4 left, Int4 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
|
||||
public static Int4 operator +(Int4 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right, left.W + right);
|
||||
public static Int4 operator +(int32 left, Int4 right) => .(left + right.X, left + right.Y, left + right.Z, left + right.W);
|
||||
|
||||
public static Int4 operator -(Int4 value) => .(-value.X, -value.Y, -value.Z, -value.W);
|
||||
public static Int4 operator -(Int4 left, Int4 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W);
|
||||
public static Int4 operator -(Int4 left, int32 right) => .(left.X - right, left.Y - right, left.Z - right, left.W - right);
|
||||
public static Int4 operator -(int32 left, Int4 right) => .(left - right.X, left - right.Y, left - right.Z, left - right.W);
|
||||
|
||||
public static Int4 operator *(Int4 left, Int4 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W);
|
||||
public static Int4 operator *(Int4 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right, left.W * right);
|
||||
public static Int4 operator *(int32 left, Int4 right) => .(left * right.X, left * right.Y, left * right.Z, left * right.W);
|
||||
|
||||
public static Int4 operator /(Int4 left, Int4 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W);
|
||||
public static Int4 operator /(Int4 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right, left.W / right);
|
||||
public static Int4 operator /(int32 left, Int4 right) => .(left / right.X, left / right.Y, left / right.Z, left / right.W);
|
||||
|
||||
public static Int4 operator %(Int4 left, Int4 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z, left.W % right.W);
|
||||
public static Int4 operator %(Int4 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right, left.W % right);
|
||||
public static Int4 operator %(int32 left, Int4 right) => .(left % right.X, left % right.Y, left % right.Z, left % right.W);
|
||||
|
||||
public static bool operator ==(Int4 left, Int4 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z && left.W == right.W;
|
||||
public static bool operator ==(Int4 left, int32 right) => left.X == right && left.Y == right && left.Z == right && left.W == right;
|
||||
public static bool operator ==(int32 left, Int4 right) => left == right.X && left == right.Y && left == right.Z && left == right.W;
|
||||
|
||||
public static bool operator !=(Int4 left, Int4 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z || left.W != right.W;
|
||||
public static bool operator !=(Int4 left, int32 right) => left.X != right || left.Y != right || left.Z != right || left.W != right;
|
||||
public static bool operator !=(int32 left, Int4 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(Int4 point) => .(point.X, point.Y, point.Z, point.W);
|
||||
|
||||
public static explicit operator Int4(Vector4 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z, (int32)point.W);
|
||||
|
||||
[Inline]
|
||||
public static explicit operator Int2(in Int4 point) => *(Int2*)&point;
|
||||
|
||||
[Inline]
|
||||
public static explicit operator Int3(in Int4 point) => *(Int3*)&point;
|
||||
|
||||
public int GetHashCode()
|
||||
{
|
||||
return (((((X * 39) ^ Y) * 39) ^ Z) * 39) ^ W;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
using GlitchyEngine.Renderer;
|
||||
using GlitchyEngine.Events;
|
||||
using GlitchyEngine.Math;
|
||||
using System;
|
||||
|
||||
namespace GlitchyEngine
|
||||
{
|
||||
/// A simple controller for an orthographic camera
|
||||
public class OrthographicCameraController
|
||||
{
|
||||
private OrthographicCamera _camera ~ delete _;
|
||||
private float _aspectRatio;
|
||||
private float _zoomLevel = 1.0f;
|
||||
|
||||
private bool _rotation;
|
||||
|
||||
private Vector3 _cameraPosition;
|
||||
private float _cameraRotation;
|
||||
private float _cameraTranslationSpeed = 1.0f;
|
||||
private float _cameraRotationSpeed = 1.0f;
|
||||
|
||||
public OrthographicCamera Camera => _camera;
|
||||
|
||||
public this(float aspectRatio, bool rotation = false)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
_aspectRatio = aspectRatio;
|
||||
_rotation = rotation;
|
||||
|
||||
_camera = new OrthographicCamera();
|
||||
_camera.NearPlane = -10f;
|
||||
_camera.FarPlane = 10f;
|
||||
|
||||
UpdateCamera();
|
||||
}
|
||||
|
||||
public void Update(GameTime gameTime)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
if(Application.Get().Window.IsActive)
|
||||
{
|
||||
Vector3 movement = .();
|
||||
|
||||
if(Input.IsKeyPressed(Key.W))
|
||||
{
|
||||
movement.Y += 1;
|
||||
}
|
||||
if(Input.IsKeyPressed(Key.S))
|
||||
{
|
||||
movement.Y -= 1;
|
||||
}
|
||||
|
||||
if(Input.IsKeyPressed(Key.A))
|
||||
{
|
||||
movement.X -= 1;
|
||||
}
|
||||
if(Input.IsKeyPressed(Key.D))
|
||||
{
|
||||
movement.X += 1;
|
||||
}
|
||||
|
||||
if(movement != .Zero)
|
||||
movement.Normalize();
|
||||
|
||||
movement *= (float)(gameTime.FrameTime.TotalSeconds) * _cameraTranslationSpeed * _zoomLevel;
|
||||
|
||||
_cameraPosition += movement;
|
||||
|
||||
if(_rotation)
|
||||
{
|
||||
if(Input.IsKeyPressed(Key.Q))
|
||||
{
|
||||
_cameraRotation += (float)(gameTime.FrameTime.TotalSeconds) * _cameraRotationSpeed;
|
||||
}
|
||||
if(Input.IsKeyPressed(Key.E))
|
||||
{
|
||||
_cameraRotation -= (float)(gameTime.FrameTime.TotalSeconds) * _cameraRotationSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateCamera();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEvent(Event e)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
EventDispatcher dispatcher = EventDispatcher(e);
|
||||
dispatcher.Dispatch<MouseScrolledEvent>(scope => OnMouseScrolled);
|
||||
dispatcher.Dispatch<WindowResizeEvent>(scope => OnWindowResized);
|
||||
}
|
||||
|
||||
private bool OnMouseScrolled(MouseScrolledEvent e)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
_zoomLevel -= e.YOffset * 0.25f;
|
||||
|
||||
_zoomLevel = Math.Max(_zoomLevel, 0.25f);
|
||||
|
||||
UpdateCamera();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool OnWindowResized(WindowResizeEvent e)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
_aspectRatio = (float)e.Width / (float)e.Height;
|
||||
|
||||
UpdateCamera();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UpdateCamera()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
_camera.Left = -_aspectRatio * _zoomLevel;
|
||||
_camera.Right = _aspectRatio * _zoomLevel;
|
||||
_camera.Top = _zoomLevel;
|
||||
_camera.Bottom = -_zoomLevel;
|
||||
_camera.Rotation = .(_camera.Rotation.X, _camera.Rotation.Y, _cameraRotation);
|
||||
_camera.Position = _cameraPosition;
|
||||
|
||||
_camera.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using GlitchyEngine.Events;
|
||||
using GlitchyEngine.Math;
|
||||
using GlitchyEngine.Renderer;
|
||||
|
||||
namespace GlitchyEngine
|
||||
{
|
||||
/// A simple controller for a perspective camera
|
||||
public class PerspectiveCameraController
|
||||
{
|
||||
private PerspectiveCamera _camera ~ delete _;
|
||||
private float _aspectRatio;
|
||||
private float _fovY = Math.PI_f / 4;
|
||||
|
||||
private Vector3 _cameraPosition;
|
||||
private Vector3 _cameraRotation;
|
||||
|
||||
private float _cameraTranslationSpeed = 1.0f;
|
||||
private float _cameraRotationSpeedX = 0.001f;
|
||||
private float _cameraRotationSpeedY = 0.001f;
|
||||
|
||||
public PerspectiveCamera Camera => _camera;
|
||||
|
||||
public float TranslationSpeed
|
||||
{
|
||||
get => _cameraTranslationSpeed;
|
||||
set => _cameraTranslationSpeed = value;
|
||||
}
|
||||
|
||||
public Vector2 RotationSpeed
|
||||
{
|
||||
get => .(_cameraRotationSpeedX, _cameraRotationSpeedY);
|
||||
set
|
||||
{
|
||||
_cameraRotationSpeedX = value.X;
|
||||
_cameraRotationSpeedY = value.Y;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 CameraPosition
|
||||
{
|
||||
get => _cameraPosition;
|
||||
set
|
||||
{
|
||||
_cameraPosition = value;
|
||||
|
||||
UpdateCamera();
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 CameraRotation
|
||||
{
|
||||
get => _cameraRotation;
|
||||
set
|
||||
{
|
||||
_cameraRotation = value;
|
||||
|
||||
UpdateCamera();
|
||||
}
|
||||
}
|
||||
|
||||
public float FovY
|
||||
{
|
||||
get => _fovY;
|
||||
set
|
||||
{
|
||||
_fovY = value;
|
||||
|
||||
UpdateCamera();
|
||||
}
|
||||
}
|
||||
|
||||
public float AspectRatio
|
||||
{
|
||||
get => _aspectRatio;
|
||||
set
|
||||
{
|
||||
_aspectRatio = value;
|
||||
|
||||
UpdateCamera();
|
||||
}
|
||||
}
|
||||
|
||||
public this(float aspectRatio)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
_aspectRatio = aspectRatio;
|
||||
|
||||
_camera = new PerspectiveCamera();
|
||||
_camera.ProjectionType = .Infinite;
|
||||
_camera.NearPlane = 0.01f;
|
||||
|
||||
UpdateCamera();
|
||||
}
|
||||
|
||||
public void Update(GameTime gameTime)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
if(Application.Get().Window.IsActive)
|
||||
{
|
||||
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) * _cameraTranslationSpeed;
|
||||
|
||||
Vector4 delta = Vector4(movement, 1.0f) * _camera.View;
|
||||
|
||||
_cameraPosition += Vector3(delta.X, delta.Y, delta.Z);
|
||||
//_cameraPosition += movement;
|
||||
|
||||
// Camera rotation
|
||||
var mouseDelta = Input.GetMouseMovement();
|
||||
|
||||
float rotY = mouseDelta.X * _cameraRotationSpeedX;
|
||||
float rotX = mouseDelta.Y * _cameraRotationSpeedY;
|
||||
|
||||
_cameraRotation.X += rotX;
|
||||
_cameraRotation.Y += rotY;
|
||||
|
||||
UpdateCamera();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEvent(Event e)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
EventDispatcher dispatcher = EventDispatcher(e);
|
||||
dispatcher.Dispatch<WindowResizeEvent>(scope => OnWindowResized);
|
||||
}
|
||||
|
||||
private bool OnWindowResized(WindowResizeEvent e)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
_aspectRatio = (float)e.Width / (float)e.Height;
|
||||
|
||||
UpdateCamera();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UpdateCamera()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
_camera.AspectRatio = _aspectRatio;
|
||||
_camera.FovY = _fovY;
|
||||
_camera.Rotation = _cameraRotation;
|
||||
_camera.Position = _cameraPosition;
|
||||
|
||||
_camera.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
#if !BF_PLATFORM_WINDOWS
|
||||
#error DirectX 11 (GE_GRAPHICS_DX11) can only be used on Windows.
|
||||
#endif
|
||||
|
||||
using DirectX.Common;
|
||||
using DirectX.D3D11;
|
||||
using DirectX.D3D11.SDKLayers;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace GlitchyEngine.Platform.DX11
|
||||
{
|
||||
static
|
||||
{
|
||||
/// Gets whether or not the DX11 device is initialized.
|
||||
protected internal static bool IsDx11Initialized => NativeDevice != null;
|
||||
protected internal static ID3D11Device* NativeDevice;
|
||||
protected internal static ID3D11DeviceContext* NativeContext;
|
||||
|
||||
#if DEBUG
|
||||
protected internal static ID3D11Debug* DebugDevice;
|
||||
#endif
|
||||
|
||||
internal static void Dx11Init()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
if(!IsDx11Initialized)
|
||||
{
|
||||
Log.EngineLogger.Trace("Creating D3D11 Device and Context...");
|
||||
|
||||
DeviceCreationFlags deviceFlags = .None;
|
||||
#if DEBUG
|
||||
deviceFlags |= .Debug;
|
||||
#endif
|
||||
|
||||
FeatureLevel[] levels = scope .(.Level_11_0);
|
||||
|
||||
FeatureLevel deviceLevel = ?;
|
||||
|
||||
HResult deviceResult;
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("D3D11.CreateDevice");
|
||||
deviceResult = D3D11.CreateDevice(null, .Hardware, 0, deviceFlags, levels, &NativeDevice, &deviceLevel, &NativeContext);
|
||||
}
|
||||
|
||||
Log.EngineLogger.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message({(int32)deviceResult}): {deviceResult}");
|
||||
|
||||
#if DEBUG
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("Query for ID3D11Debug");
|
||||
if(NativeDevice.QueryInterface<ID3D11Debug>(out DebugDevice).Succeeded)
|
||||
{
|
||||
ID3D11InfoQueue* infoQueue;
|
||||
if(NativeDevice.QueryInterface<ID3D11InfoQueue>(out infoQueue).Succeeded)
|
||||
{
|
||||
infoQueue.SetBreakOnSeverity(.Corruption, true);
|
||||
infoQueue.SetBreakOnSeverity(.Error, true);
|
||||
|
||||
infoQueue.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Log.EngineLogger.Trace($"D3D11 Device and Context created (Feature level: {deviceLevel})");
|
||||
}
|
||||
else
|
||||
{
|
||||
// We created a second GraphicsContext (for some reason?) just increment references.
|
||||
NativeDevice.AddRef();
|
||||
NativeContext.AddRef();
|
||||
#if DEBUG
|
||||
DebugDevice.AddRef();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Dx11Release()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
NativeDevice.Release();
|
||||
NativeContext.Release();
|
||||
#if DEBUG
|
||||
Debug.WriteLine("""
|
||||
|
||||
-------------------------
|
||||
Live DX Objects Report:
|
||||
|
||||
""");
|
||||
|
||||
DebugDevice.ReportLiveDeviceObjects(.Detail | .IgnoreInternal);
|
||||
|
||||
Debug.WriteLine("""
|
||||
|
||||
-------------------------
|
||||
|
||||
""");
|
||||
|
||||
DebugDevice.Release();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using GlitchyEngine.Renderer;
|
||||
using System.Collections;
|
||||
using DirectX.D3D11;
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
using internal GlitchyEngine.Renderer;
|
||||
|
||||
extension ImGui
|
||||
{
|
||||
static List<ID3D11ShaderResourceView*> _resourceViews = new .() ~ delete _;
|
||||
|
||||
public static override void Image(Texture2D texture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec4 tint_col = Vec4.Ones, Vec4 border_col = Vec4.Zero)
|
||||
{
|
||||
var view = texture.nativeResourceView..AddRef();
|
||||
_resourceViews.Add(view);
|
||||
|
||||
ImGui.Image(view, size, uv0, uv1, tint_col, border_col);
|
||||
}
|
||||
|
||||
public static override void Image(RenderTarget2D texture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec4 tint_col = Vec4.Ones, Vec4 border_col = Vec4.Zero)
|
||||
{
|
||||
var view = texture._nativeResourceView..AddRef();
|
||||
_resourceViews.Add(view);
|
||||
|
||||
ImGui.Image(view, size, uv0, uv1, tint_col, border_col);
|
||||
}
|
||||
|
||||
protected internal static override void CleanupFrame()
|
||||
{
|
||||
for(var view in _resourceViews)
|
||||
{
|
||||
view.Release();
|
||||
}
|
||||
|
||||
_resourceViews.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,9 +1,12 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
// Disable warning when taking pointer of in-Parameter
|
||||
#pragma warning disable 4204
|
||||
|
||||
using System;
|
||||
|
||||
namespace GlitchyEngine.Math
|
||||
{
|
||||
/*
|
||||
extension Vector2
|
||||
{
|
||||
[Inline]
|
||||
@@ -30,5 +33,6 @@ namespace GlitchyEngine.Math
|
||||
[Inline]
|
||||
public static implicit operator Self(in DirectX.Math.Vector4 value) => *(Self*)&value;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using DirectX.D3D11;
|
||||
using DirectX.Common;
|
||||
using GlitchyEngine.Math;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
extension BlendState
|
||||
{
|
||||
internal ID3D11BlendState* nativeBlendState ~ _?.Release();
|
||||
|
||||
public override void Bind(ColorRGBA blendFactor)
|
||||
{
|
||||
_context.nativeContext.OutputMerger.SetBlendState(nativeBlendState, blendFactor);
|
||||
}
|
||||
|
||||
protected override void PlatformCreateBlendState()
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
BlendDescription nativeDesc = .Default;
|
||||
nativeDesc.IndependentBlendEnable = _desc.IndependentBlendEnable;
|
||||
nativeDesc.AlphaToCoverageEnable = _desc.AlphaToCoverageEnable;
|
||||
@@ -33,9 +35,11 @@ namespace GlitchyEngine.Renderer
|
||||
nativeDesc.RenderTarget[i].RenderTargetWriteMask = _desc.RenderTarget[i].RenderTargetWriteMask;
|
||||
}
|
||||
|
||||
HResult result = _context.nativeDevice.CreateBlendState(ref nativeDesc, &nativeBlendState);
|
||||
HResult result = NativeDevice.CreateBlendState(ref nativeDesc, &nativeBlendState);
|
||||
|
||||
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create blend state. Error({(int)result}): {result}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,27 +1,16 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
using DirectX.D3D11;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
extension CPUAccessFlags
|
||||
{
|
||||
public static explicit operator DirectX.D3D11.CpuAccessFlags(Self cpuAccessFlags)
|
||||
{
|
||||
DirectX.D3D11.CpuAccessFlags flags = .None;
|
||||
|
||||
if(cpuAccessFlags.HasFlag(.Read))
|
||||
flags |= .Read;
|
||||
|
||||
if(cpuAccessFlags.HasFlag(.Write))
|
||||
flags |= .Write;
|
||||
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
|
||||
extension BufferDescription
|
||||
{
|
||||
public static operator DirectX.D3D11.BufferDescription(Self desc)
|
||||
@@ -71,6 +60,8 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
private Result<void> InternalCreateBuffer(void* data, uint32 byteLength, uint32 dstByteOffset)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
nativeDescription = (.)_description;
|
||||
|
||||
uint8* byteData = (.)data;
|
||||
@@ -79,11 +70,11 @@ namespace GlitchyEngine.Renderer
|
||||
{
|
||||
byteData = new uint8[nativeDescription.ByteWidth]*;
|
||||
defer:: delete byteData;
|
||||
Internal.MemCpy(byteData + dstByteOffset, data, byteLength);
|
||||
Internal.MemCpy(byteData + dstByteOffset, data, Math.Min(nativeDescription.ByteWidth, byteLength));
|
||||
}
|
||||
|
||||
SubresourceData srData = .(byteData, byteLength, 0);
|
||||
var result = _context.nativeDevice.CreateBuffer(ref nativeDescription, &srData, &nativeBuffer);
|
||||
var result = NativeDevice.CreateBuffer(ref nativeDescription, &srData, &nativeBuffer);
|
||||
if(result.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to create buffer. Message({(int)result}):{result}");
|
||||
@@ -95,6 +86,8 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
protected override Result<void> PlatformSetData(void* data, uint32 byteLength, uint32 dstByteOffset, GlitchyEngine.Renderer.MapType mapType)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
if(nativeBuffer == null)
|
||||
{
|
||||
// We can pass the data while creating the buffer, so we can return here.
|
||||
@@ -107,14 +100,14 @@ namespace GlitchyEngine.Renderer
|
||||
{
|
||||
case .Default:
|
||||
Box dataBox = .(dstByteOffset, 0, 0, dstByteOffset + byteLength, 1, 1);
|
||||
_context.nativeContext.UpdateSubresource(nativeBuffer, 0, &dataBox, data, byteLength, byteLength);
|
||||
NativeContext.UpdateSubresource(nativeBuffer, 0, &dataBox, data, byteLength, byteLength);
|
||||
case .Dynamic:
|
||||
Debug.Assert(mapType.CanWrite, "The map type has to have write access.");
|
||||
// Todo: DoNotWaitFlag
|
||||
MappedSubresource map = ?;
|
||||
_context.nativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map);
|
||||
NativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map);
|
||||
Internal.MemCpy(((uint8*)map.Data) + dstByteOffset, data, byteLength);
|
||||
_context.nativeContext.Unmap(nativeBuffer, 0);
|
||||
NativeContext.Unmap(nativeBuffer, 0);
|
||||
case .Immutable:
|
||||
Log.EngineLogger.Error("Can't set the data of an immutable resource.");
|
||||
return .Err;
|
||||
@@ -127,3 +120,5 @@ namespace GlitchyEngine.Renderer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using DirectX.D3D11;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
@@ -10,6 +12,8 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
internal void PlatformFetchNativeBuffers()
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
for(let buffer in _buffers)
|
||||
{
|
||||
nativeBuffers[buffer.Index] = buffer.Buffer.nativeBuffer;
|
||||
@@ -17,3 +21,5 @@ namespace GlitchyEngine.Renderer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
extension CPUAccessFlags
|
||||
{
|
||||
public static explicit operator DirectX.D3D11.CpuAccessFlags(Self cpuAccessFlags)
|
||||
{
|
||||
DirectX.D3D11.CpuAccessFlags flags = .None;
|
||||
|
||||
if(cpuAccessFlags.HasFlag(.Read))
|
||||
flags |= .Read;
|
||||
|
||||
if(cpuAccessFlags.HasFlag(.Write))
|
||||
flags |= .Write;
|
||||
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,5 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using DirectX.Common;
|
||||
using DirectX.D3D11Shader;
|
||||
using System;
|
||||
@@ -10,6 +12,8 @@ namespace GlitchyEngine.Renderer
|
||||
{
|
||||
internal this(ConstantBuffer constantBuffer, ID3D11ShaderReflectionVariable* variableReflection)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
_constantBuffer = constantBuffer;
|
||||
|
||||
HResult result = variableReflection.GetDescription(let variableDescription);
|
||||
@@ -27,14 +31,19 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
switch(shaderTypeDescription.Type)
|
||||
{
|
||||
case .Bool:
|
||||
_type = .Bool;
|
||||
case .Float:
|
||||
_type = .Float;
|
||||
case .Int:
|
||||
_type = .Int;
|
||||
default:
|
||||
Log.EngineLogger.Assert(false, scope $"Unhandled shader variable type: {shaderTypeDescription.Type}");
|
||||
}
|
||||
|
||||
_columns = shaderTypeDescription.Columns;
|
||||
_rows = shaderTypeDescription.Rows;
|
||||
_elements = shaderTypeDescription.Elements;
|
||||
|
||||
SetRawData(variableDescription.DefaultValue);
|
||||
}
|
||||
@@ -42,8 +51,10 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
extension ConstantBuffer
|
||||
{
|
||||
internal this(GraphicsContext context, ID3D11ShaderReflectionConstantBuffer* bufferReflection) : base(context)
|
||||
internal this(ID3D11ShaderReflectionConstantBuffer* bufferReflection)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
Reflect(bufferReflection);
|
||||
|
||||
ConstructBuffer();
|
||||
@@ -53,6 +64,8 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
private void Reflect(ID3D11ShaderReflectionConstantBuffer* bufferReflection)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
HResult result = bufferReflection.GetDescription(let bufferDescription);
|
||||
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to get buffer description. Error({(int)result}): {result}");
|
||||
|
||||
@@ -75,3 +88,5 @@ namespace GlitchyEngine.Renderer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using DirectX.D3D11;
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
internal typealias D3DDepthStencilStateDesc = DirectX.D3D11.DepthStencilStateDescription;
|
||||
internal typealias DxDSSDesc = DirectX.D3D11.DepthStencilStateDescription;
|
||||
internal typealias GEDSSDesc = GlitchyEngine.Renderer.DepthStencilStateDescription;
|
||||
|
||||
internal typealias DxDepthStencilOpDesc = DirectX.D3D11.DepthStencilOperationDescription;
|
||||
|
||||
extension StencilOperation
|
||||
{
|
||||
public static explicit operator DirectX.D3D11.StencilOperation(Self self)
|
||||
{
|
||||
return (.)self.Underlying;
|
||||
}
|
||||
}
|
||||
|
||||
extension DepthStencilOperationDescription
|
||||
{
|
||||
public static explicit operator DxDepthStencilOpDesc(Self desc)
|
||||
{
|
||||
return DxDepthStencilOpDesc((.)desc.StencilFailOperation, (.)desc.StencilDepthFailOperation,
|
||||
(.)desc.StencilPassOperation, (.)desc.StencilFunction);
|
||||
}
|
||||
}
|
||||
|
||||
extension DepthStencilStateDescription
|
||||
{
|
||||
public static explicit operator DxDSSDesc(Self desc)
|
||||
{
|
||||
return DxDSSDesc(desc.DepthEnabled, desc.WriteDepth ? .All : .Zero, (.)desc.DepthFunction, desc.StencilEnabled,
|
||||
desc.StencilReadMask, desc.StencilWriteMask, (.)desc.FrontFace, (.)desc.BackFace);
|
||||
}
|
||||
}
|
||||
|
||||
extension DepthStencilState
|
||||
{
|
||||
internal DxDSSDesc nativeDescription;
|
||||
internal ID3D11DepthStencilState* nativeDepthStencilState ~ _?.Release();
|
||||
|
||||
public this(GEDSSDesc description)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
_description = description;
|
||||
nativeDescription = (.)description;
|
||||
|
||||
var result = NativeDevice.CreateDepthStencilState(ref nativeDescription, &nativeDepthStencilState);
|
||||
if (result.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to create depth stencil state. Message({(int)result}): {result}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,59 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using DirectX.D3D11;
|
||||
using internal GlitchyEngine.Renderer;
|
||||
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
extension DepthStencilFormat
|
||||
{
|
||||
public static explicit operator Format(Self self)
|
||||
{
|
||||
switch(self)
|
||||
{
|
||||
case .D16_UNorm:
|
||||
return .D16_UNorm;
|
||||
case .D24_UNorm_S8_UInt:
|
||||
return .D24_UNorm_S8_UInt;
|
||||
case .D32_Float:
|
||||
return .D32_Float;
|
||||
case .D32_Float_S8X24_UInt:
|
||||
return .D32_Float_S8X24_UInt;
|
||||
case .None:
|
||||
Log.EngineLogger.Assert(false, "None is not a valid depth stencil format");
|
||||
return .Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Depth stencil target is technically just a RenderTarget
|
||||
extension DepthStencilTarget
|
||||
{
|
||||
internal ID3D11DepthStencilView* nativeView ~ _?.Release();
|
||||
|
||||
protected override void PlatformCreate()
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
Texture2DDescription desc = .();
|
||||
desc.Format = (.)_format;
|
||||
desc.ArraySize = 1;
|
||||
desc.BindFlags = .DepthStencil;
|
||||
desc.Width = _width;
|
||||
desc.Height = _height;
|
||||
desc.SampleDesc = .(1, 0);
|
||||
|
||||
ID3D11Texture2D* tex = ?;
|
||||
NativeDevice.CreateTexture2D(ref desc, null, &tex);
|
||||
|
||||
NativeDevice.CreateDepthStencilView(tex, null, &nativeView);
|
||||
|
||||
tex.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,5 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using System;
|
||||
using DirectX;
|
||||
using DirectX.D3D11;
|
||||
@@ -10,9 +12,11 @@ namespace GlitchyEngine.Renderer
|
||||
{
|
||||
protected override void Compile(String vsPath, String vsEntry, String psPath, String psEntry)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
// Todo: macros
|
||||
VertexShader = new VertexShader(_context, vsPath, vsEntry);
|
||||
PixelShader = new PixelShader(_context, psPath, psEntry);
|
||||
VertexShader = new VertexShader(vsPath, vsEntry);
|
||||
PixelShader = new PixelShader(psPath, psEntry);
|
||||
|
||||
Reflect();
|
||||
}
|
||||
@@ -23,3 +27,5 @@ namespace GlitchyEngine.Renderer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using System;
|
||||
using DirectX.D3D11;
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
using internal GlitchyEngine.Renderer;
|
||||
|
||||
extension GeometryBinding
|
||||
{
|
||||
internal ID3D11Buffer*[DirectX.D3D11.D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT] nativeBuffers;
|
||||
@@ -27,6 +31,8 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
protected override void PlatformSetVertexBuffer(VertexBufferBinding binding, uint32 slot)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
Log.EngineLogger.Assert(slot < nativeBuffers.Count, "The buffer slot has to be in the range from 0 to 31.");
|
||||
|
||||
VertexBuffer vertexBuffer = binding.Buffer;
|
||||
@@ -40,8 +46,8 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
nativeBuffers[slot] = vertexBuffer.nativeBuffer..AddRef();
|
||||
|
||||
bufferStrides[slot] = vertexBuffer.Binding.Stride;
|
||||
bufferOffsets[slot] = vertexBuffer.Binding.Offset;
|
||||
bufferStrides[slot] = binding.Stride;
|
||||
bufferOffsets[slot] = binding.Offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -55,12 +61,16 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
protected override void PlatformSetVertexLayout(VertexLayout vertexLayout)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
nativeVertexLayout?.Release();
|
||||
nativeVertexLayout = vertexLayout?.nativeLayout..AddRef();
|
||||
}
|
||||
|
||||
protected override void PlatformSetIndexBuffer(IndexBuffer indexBuffer)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
Log.EngineLogger.Assert(indexBuffer.nativeDescription.BindFlags.HasFlag(.IndexBuffer),
|
||||
scope $"Buffer ({indexBuffer.nativeBuffer.GetDebugName(.. scope .())}) must have IndexBuffer-flag set to be bound as an index buffer.");
|
||||
|
||||
@@ -73,32 +83,33 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
}
|
||||
|
||||
public override void Bind(GraphicsContext context = null)
|
||||
public override void Bind()
|
||||
{
|
||||
var context;
|
||||
context ??= _context;
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
context.nativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nativeBuffers, &bufferStrides, &bufferOffsets);
|
||||
context.nativeContext.InputAssembler.SetInputLayout(_vertexLayout.nativeLayout);
|
||||
context.nativeContext.InputAssembler.SetPrimitiveTopology((.)_primitiveTopology);
|
||||
|
||||
context.nativeContext.InputAssembler.SetIndexBuffer(_indexBuffer.nativeBuffer, _indexBuffer.Format == .Index32Bit ? .R32_UInt : .R16_UInt, _indexByteOffset);
|
||||
NativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nativeBuffers, &bufferStrides, &bufferOffsets);
|
||||
NativeContext.InputAssembler.SetInputLayout(_vertexLayout.nativeLayout);
|
||||
NativeContext.InputAssembler.SetPrimitiveTopology((.)_primitiveTopology);
|
||||
|
||||
if(_indexBuffer != null)
|
||||
NativeContext.InputAssembler.SetIndexBuffer(_indexBuffer.nativeBuffer, _indexBuffer.Format == .Index32Bit ? .R32_UInt : .R16_UInt, _indexByteOffset);
|
||||
}
|
||||
|
||||
public void Unbind(GraphicsContext context = null)
|
||||
public override void Unbind()
|
||||
{
|
||||
var context;
|
||||
context ??= _context;
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
ID3D11Buffer*[nativeBuffers.Count] nullBuffers = .();
|
||||
uint32[nativeBuffers.Count] zeroStrides = .();
|
||||
uint32[nativeBuffers.Count] zeroOffsets = .();
|
||||
|
||||
context.nativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nullBuffers, &zeroStrides, &zeroOffsets);
|
||||
context.nativeContext.InputAssembler.SetInputLayout(null);
|
||||
context.nativeContext.InputAssembler.SetPrimitiveTopology(.Undefined);
|
||||
NativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nullBuffers, &zeroStrides, &zeroOffsets);
|
||||
NativeContext.InputAssembler.SetInputLayout(null);
|
||||
NativeContext.InputAssembler.SetPrimitiveTopology(.Undefined);
|
||||
|
||||
context.nativeContext.InputAssembler.SetIndexBuffer(null, .R16_UNorm, 0);
|
||||
NativeContext.InputAssembler.SetIndexBuffer(null, .R16_UNorm, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using DirectX.Common;
|
||||
@@ -6,9 +8,12 @@ using DirectX.D3D11.SDKLayers;
|
||||
using DirectX.DXGI.DXGI1_2;
|
||||
using GlitchyEngine.Renderer;
|
||||
using GlitchyEngine.Math;
|
||||
|
||||
using DirectX.D3D11.DeviceContextStages;
|
||||
using internal GlitchyEngine.Renderer;
|
||||
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
/// DirectX 11 specific implementation of the GraphicsContext
|
||||
@@ -18,10 +23,12 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
internal Windows.HWnd nativeWindowHandle;
|
||||
|
||||
internal ID3D11Device* nativeDevice;
|
||||
internal ID3D11DeviceContext* nativeContext;
|
||||
|
||||
private ID3D11Debug* _debugDevice;
|
||||
/// Gets whether or not the DX11 device is initialized.
|
||||
//internal bool IsDx11Initialized => nativeDevice != null;
|
||||
//internal ID3D11Device* nativeDevice => NativeDevice;
|
||||
//internal ID3D11DeviceContext* nativeContext => NativeContext;
|
||||
|
||||
//internal ID3D11Debug* debugDevice => DebugDevice;
|
||||
|
||||
public override SwapChain SwapChain => _swapChain;
|
||||
|
||||
@@ -31,24 +38,29 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
public this(Windows.HWnd windowHandle)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
nativeWindowHandle = windowHandle;
|
||||
|
||||
_swapChain = new SwapChain(this);
|
||||
|
||||
s_GraphicsContext = this;
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
delete _swapChain;
|
||||
|
||||
nativeDevice?.Release();
|
||||
nativeContext?.Release();
|
||||
_debugDevice.ReportLiveDeviceObjects(.Detail);
|
||||
_debugDevice?.Release();
|
||||
Dx11Release();
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
InitDevice();
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
Dx11Init();
|
||||
|
||||
SwapChain.Init();
|
||||
}
|
||||
@@ -56,132 +68,183 @@ namespace GlitchyEngine.Renderer
|
||||
/**
|
||||
* Initializes the Device and ImmediateContext.
|
||||
*/
|
||||
/*
|
||||
void InitDevice()
|
||||
{
|
||||
nativeDevice?.Release();
|
||||
nativeContext?.Release();
|
||||
|
||||
Log.EngineLogger.Trace("Creating D3D11 Device and Context...");
|
||||
|
||||
DeviceCreationFlags deviceFlags = .None;
|
||||
if(!IsDx11Initialized)
|
||||
{
|
||||
Log.EngineLogger.Trace("Creating D3D11 Device and Context...");
|
||||
|
||||
DeviceCreationFlags deviceFlags = .None;
|
||||
#if DEBUG
|
||||
deviceFlags |= .Debug;
|
||||
deviceFlags |= .Debug;
|
||||
#endif
|
||||
|
||||
FeatureLevel[] levels = scope .(.Level_11_0);
|
||||
FeatureLevel[] levels = scope .(.Level_11_0);
|
||||
|
||||
FeatureLevel deviceLevel = ?;
|
||||
var deviceResult = D3D11.CreateDevice(null, .Hardware, 0, deviceFlags, levels, &nativeDevice, &deviceLevel, &nativeContext);
|
||||
Debug.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message(0x{(int32)deviceResult}): {deviceResult}");
|
||||
FeatureLevel deviceLevel = ?;
|
||||
var deviceResult = D3D11.CreateDevice(null, .Hardware, 0, deviceFlags, levels, &nativeDevice, &deviceLevel, &nativeContext);
|
||||
Log.EngineLogger.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message({(int32)deviceResult}): {deviceResult}");
|
||||
|
||||
#if DEBUG
|
||||
if(nativeDevice.QueryInterface<ID3D11Debug>(out _debugDevice).Succeeded)
|
||||
{
|
||||
ID3D11InfoQueue* infoQueue;
|
||||
if(nativeDevice.QueryInterface<ID3D11InfoQueue>(out infoQueue).Succeeded)
|
||||
if(nativeDevice.QueryInterface<ID3D11Debug>(out debugDevice).Succeeded)
|
||||
{
|
||||
infoQueue.SetBreakOnSeverity(.Corruption, true);
|
||||
infoQueue.SetBreakOnSeverity(.Error, true);
|
||||
ID3D11InfoQueue* infoQueue;
|
||||
if(nativeDevice.QueryInterface<ID3D11InfoQueue>(out infoQueue).Succeeded)
|
||||
{
|
||||
infoQueue.SetBreakOnSeverity(.Corruption, true);
|
||||
infoQueue.SetBreakOnSeverity(.Error, true);
|
||||
|
||||
infoQueue.Release();
|
||||
infoQueue.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Log.EngineLogger.Trace($"D3D11 Device and Context created (Feature level: {deviceLevel})");
|
||||
}
|
||||
|
||||
|
||||
|
||||
private ID3D11RenderTargetView*[MaxRTVCount] _renderTargets;
|
||||
|
||||
public override void SetRenderTarget(RenderTarget renderTarget, int slot = 0)
|
||||
{
|
||||
if(renderTarget == null)
|
||||
{
|
||||
_renderTargets[slot] = _swapChain.nativeBackBufferTarget;
|
||||
Log.EngineLogger.Trace($"D3D11 Device and Context created (Feature level: {deviceLevel})");
|
||||
}
|
||||
else
|
||||
{
|
||||
Runtime.NotImplemented();
|
||||
// We created a second GraphicsContext (for some reason?) just increment references.
|
||||
nativeDevice.AddRef();
|
||||
nativeContext.AddRef();
|
||||
debugDevice.AddRef();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private ID3D11DepthStencilView* _depthStencilTarget;
|
||||
private ID3D11RenderTargetView*[MaxRTVCount] _renderTargets;
|
||||
|
||||
public override void SetDepthStencilTarget(DepthStencilTarget target)
|
||||
{
|
||||
_depthStencilTarget = target?.nativeView;
|
||||
}
|
||||
|
||||
public override void SetRenderTarget(RenderTarget2D renderTarget, int slot, bool setDepthTarget)
|
||||
{
|
||||
_renderTargets[slot] = (renderTarget ?? _swapChain.BackBuffer)._nativeRenderTargetView;
|
||||
|
||||
if(slot == 0 && setDepthTarget)
|
||||
{
|
||||
SetDepthStencilTarget((renderTarget ?? _swapChain.BackBuffer).DepthStencilTarget);
|
||||
}
|
||||
}
|
||||
|
||||
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(RenderTarget2D renderTarget, ColorRGBA color)
|
||||
{
|
||||
if(renderTarget == null)
|
||||
{
|
||||
nativeContext.ClearRenderTargetView(_swapChain.nativeBackBufferTarget, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
Runtime.NotImplemented();
|
||||
}
|
||||
NativeContext.ClearRenderTargetView((renderTarget ?? _swapChain.BackBuffer)._nativeRenderTargetView, color);
|
||||
}
|
||||
|
||||
public override void SetVertexBuffer(uint32 slot, Buffer buffer, uint32 stride, uint32 offset = 0)
|
||||
{
|
||||
// make stride and offset mutable so that we can take their pointers.
|
||||
var stride, offset;
|
||||
nativeContext.InputAssembler.SetVertexBuffers(slot, 1, &buffer.nativeBuffer, &stride, &offset);
|
||||
NativeContext.InputAssembler.SetVertexBuffers(slot, 1, &buffer.nativeBuffer, &stride, &offset);
|
||||
}
|
||||
|
||||
public override void Draw(uint32 vertexCount, uint32 startVertexIndex = 0)
|
||||
{
|
||||
nativeContext.Draw(vertexCount, startVertexIndex);
|
||||
NativeContext.Draw(vertexCount, startVertexIndex);
|
||||
}
|
||||
|
||||
public override void DrawIndexed(uint32 indexCount, uint32 startIndexLocation = 0, int32 vertexOffset = 0)
|
||||
{
|
||||
nativeContext.DrawIndexed(indexCount, startIndexLocation, vertexOffset);
|
||||
NativeContext.DrawIndexed(indexCount, startIndexLocation, vertexOffset);
|
||||
}
|
||||
|
||||
public override void SetIndexBuffer(Buffer buffer, IndexFormat indexFormat = .Index16Bit, uint32 byteOffset = 0)
|
||||
{
|
||||
nativeContext.InputAssembler.SetIndexBuffer(buffer.nativeBuffer, indexFormat == .Index32Bit ? .R32_UInt : .R16_UInt, byteOffset);
|
||||
}
|
||||
|
||||
protected override void SetRasterizerStateImpl()
|
||||
{
|
||||
nativeContext.Rasterizer.SetState(_currentRasterizerState.nativeRasterizerState);
|
||||
NativeContext.InputAssembler.SetIndexBuffer(buffer.nativeBuffer, indexFormat == .Index32Bit ? .R32_UInt : .R16_UInt, byteOffset);
|
||||
}
|
||||
|
||||
public override void SetViewports(uint32 viewportsCount, GlitchyEngine.Renderer.Viewport* viewports)
|
||||
{
|
||||
nativeContext.Rasterizer.SetViewports(viewportsCount, (.)viewports);
|
||||
NativeContext.Rasterizer.SetViewports(viewportsCount, (.)viewports);
|
||||
}
|
||||
|
||||
public override void SetVertexLayout(VertexLayout vertexLayout)
|
||||
{
|
||||
nativeContext.InputAssembler.SetInputLayout(vertexLayout.nativeLayout);
|
||||
NativeContext.InputAssembler.SetInputLayout(vertexLayout.nativeLayout);
|
||||
}
|
||||
|
||||
public override void SetPrimitiveTopology(GlitchyEngine.Renderer.PrimitiveTopology primitiveTopology)
|
||||
{
|
||||
nativeContext.InputAssembler.SetPrimitiveTopology((DirectX.Common.PrimitiveTopology)primitiveTopology);
|
||||
NativeContext.InputAssembler.SetPrimitiveTopology((DirectX.Common.PrimitiveTopology)primitiveTopology);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the given shader to the corresponding shader stage.
|
||||
* @param shader The shader that will be bound to the graphics context.
|
||||
*/
|
||||
private void BindShaderToStage<TShader>(TShader shader) where TShader : Shader
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
uint32 _firstTexture = D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
uint32 _textureCount = 0;
|
||||
|
||||
ID3D11ShaderResourceView*[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] _textures = .();
|
||||
ID3D11SamplerState*[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] _samplers = .();
|
||||
|
||||
for(let entry in shader.Textures)
|
||||
{
|
||||
_textures[entry.Index] = entry.Texture?.nativeResourceView;
|
||||
_samplers[entry.Index] = entry.Texture?.SamplerState?.nativeSamplerState;
|
||||
|
||||
if(entry.Index >= _textureCount)
|
||||
_textureCount = entry.Index + 1;
|
||||
if(entry.Index < _firstTexture)
|
||||
_firstTexture = entry.Index;
|
||||
}
|
||||
|
||||
if(_textureCount > 0)
|
||||
{
|
||||
switch(typeof(TShader))
|
||||
{
|
||||
// TODO: Add remaining shader stages
|
||||
case typeof(PixelShader):
|
||||
// TODO: bind uavs
|
||||
NativeContext.PixelShader.SetShaderResources(_firstTexture, _textureCount, &_textures[_firstTexture]);
|
||||
NativeContext.PixelShader.SetSamplers(_firstTexture, _textureCount, &_samplers[_firstTexture]);
|
||||
case typeof(VertexShader):
|
||||
NativeContext.VertexShader.SetShaderResources(_firstTexture, _textureCount, &_textures[_firstTexture]);
|
||||
NativeContext.VertexShader.SetSamplers(_firstTexture, _textureCount, &_samplers[_firstTexture]);
|
||||
default:
|
||||
Runtime.FatalError(scope $"Shader stage \"{typeof(TShader)}\" not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
shader.Buffers.PlatformFetchNativeBuffers();
|
||||
|
||||
switch(typeof(TShader))
|
||||
{
|
||||
// TODO: Add remaining shader stages
|
||||
case typeof(PixelShader):
|
||||
NativeContext.PixelShader.SetConstantBuffers(0, shader.Buffers.nativeBuffers.Count, &shader.Buffers.nativeBuffers);
|
||||
[IgnoreErrors]{ NativeContext.PixelShader.SetShader(((PixelShader)shader).nativeShader); }
|
||||
case typeof(VertexShader):
|
||||
NativeContext.VertexShader.SetConstantBuffers(0, shader.Buffers.nativeBuffers.Count, &shader.Buffers.nativeBuffers);
|
||||
[IgnoreErrors]{ NativeContext.VertexShader.SetShader(((VertexShader)shader).nativeShader); }
|
||||
default:
|
||||
Runtime.FatalError(scope $"Shader stage \"{typeof(TShader)}\" not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetVertexShader(VertexShader vertexShader)
|
||||
{
|
||||
//Todo: nativeContext.VertexShader.SetSamplers();
|
||||
vertexShader.Buffers.PlatformFetchNativeBuffers();
|
||||
nativeContext.VertexShader.SetConstantBuffers(0, vertexShader.Buffers.nativeBuffers.Count, &vertexShader.Buffers.nativeBuffers);
|
||||
//Todo: nativeContext.VertexShader.SetShaderResources();
|
||||
nativeContext.VertexShader.SetShader(vertexShader.nativeShader);
|
||||
BindShaderToStage(vertexShader);
|
||||
}
|
||||
|
||||
public override void SetPixelShader(PixelShader pixelShader)
|
||||
{
|
||||
//Todo: nativeContext.PixelShader.SetSamplers();
|
||||
pixelShader.Buffers.PlatformFetchNativeBuffers();
|
||||
nativeContext.PixelShader.SetConstantBuffers(0, pixelShader.Buffers.nativeBuffers.Count, &pixelShader.Buffers.nativeBuffers);
|
||||
//Todo: nativeContext.PixelShader.SetShaderResources();
|
||||
nativeContext.PixelShader.SetShader(pixelShader.nativeShader);
|
||||
BindShaderToStage(pixelShader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using System;
|
||||
using DirectX.D3D11;
|
||||
using DirectX.D3DCompiler;
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
@@ -12,15 +16,23 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
public override void CompileFromSource(String code, String entryPoint, ShaderDefine[] macros = null)
|
||||
{
|
||||
Shader.PlattformCompileShaderFromSource(code, macros, entryPoint, "ps_5_0", DefaultCompileFlags, out nativeCode);
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
var result = _context.nativeDevice.CreatePixelShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
|
||||
if(result.Failed)
|
||||
Shader.PlattformCompileShaderFromSource(code, macros, entryPoint, "ps_5_0", DefaultCompileFlags, out nativeCode);
|
||||
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to create pixel shader: Message ({(int)result}): {result}");
|
||||
Debug.Profiler.ProfileResourceScope!("CreateNativePixelShader");
|
||||
|
||||
var result = NativeDevice.CreatePixelShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
|
||||
if(result.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to create pixel shader: Message ({(int)result}): {result}");
|
||||
}
|
||||
}
|
||||
|
||||
Reflect(nativeCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using DirectX.D3D11;
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
@@ -48,12 +52,14 @@ namespace GlitchyEngine.Renderer
|
||||
internal DirectX.D3D11.RasterizerStateDescription nativeDescription;
|
||||
internal ID3D11RasterizerState* nativeRasterizerState ~ _?.Release();
|
||||
|
||||
public override this(GraphicsContext context, GlitchyEngine.Renderer.RasterizerStateDescription description) : this(context)
|
||||
public override this(GlitchyEngine.Renderer.RasterizerStateDescription description)
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
_description = description;
|
||||
nativeDescription = (.)_description;
|
||||
|
||||
var result = context.nativeDevice.CreateRasterizerState(ref nativeDescription, &nativeRasterizerState);
|
||||
var result = NativeDevice.CreateRasterizerState(ref nativeDescription, &nativeRasterizerState);
|
||||
if(result.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to create rasterizer state. Message({(int)result}): {result}");
|
||||
@@ -61,3 +67,5 @@ namespace GlitchyEngine.Renderer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using DirectX.Common;
|
||||
using DirectX.D3D11;
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
using DirectX.DXGI.DXGI1_2;
|
||||
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
using internal GlitchyEngine.Renderer;
|
||||
|
||||
extension RenderTarget2D
|
||||
{
|
||||
protected internal ID3D11Texture2D* _nativeTexture ~ _?.Release();
|
||||
protected internal ID3D11ShaderResourceView* _nativeResourceView ~ _?.Release();
|
||||
protected internal ID3D11RenderTargetView* _nativeRenderTargetView ~ _?.Release();
|
||||
|
||||
private void ReleaseAndNullify()
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
ReleaseAndNullify!(_nativeTexture);
|
||||
ReleaseAndNullify!(_nativeResourceView);
|
||||
ReleaseAndNullify!(_nativeRenderTargetView);
|
||||
|
||||
ReleaseRefAndNullify!(_depthStenilTarget);
|
||||
}
|
||||
|
||||
public override void Resize(uint32 width, uint32 height)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
ReleaseAndNullify();
|
||||
|
||||
_description.Width = width;
|
||||
_description.Height = height;
|
||||
|
||||
ApplyChanges();
|
||||
}
|
||||
|
||||
protected override void PlatformApplyChanges()
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
ReleaseAndNullify();
|
||||
|
||||
if(_description.IsSwapchainTarget)
|
||||
{
|
||||
// TODO: if the engine supports multiple windows it has to support multiple swap chains.
|
||||
|
||||
// kinda dirty...
|
||||
var context = GraphicsContext.Get();
|
||||
|
||||
context.SwapChain.GetBackbuffer(out _nativeTexture);
|
||||
|
||||
CreateViews();
|
||||
}
|
||||
else
|
||||
{
|
||||
PlatformCreateTexture();
|
||||
}
|
||||
|
||||
if(_description.DepthStencilFormat != .None)
|
||||
{
|
||||
_depthStenilTarget = new DepthStencilTarget(_description.Width, _description.Height, _description.DepthStencilFormat);
|
||||
}
|
||||
}
|
||||
|
||||
private void PlatformCreateTexture()
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
Texture2DDescription desc = .()
|
||||
{
|
||||
Width = _description.Width,
|
||||
Height = _description.Height,
|
||||
ArraySize = _description.ArraySize,
|
||||
MipLevels = _description.MipLevels,
|
||||
Format = _description.PixelFormat,
|
||||
// Always bindable as ShaderResource and RenderTarget
|
||||
BindFlags = .ShaderResource | .RenderTarget,
|
||||
CpuAccessFlags = (.)_description.CpuAccess,
|
||||
// 2D RenderTarget never has misc flags
|
||||
MiscFlags = .None,
|
||||
SampleDesc = .(_description.SampleCount, _description.SampleQuality),
|
||||
// RenderTarget has always Default usage
|
||||
Usage = .Default
|
||||
};
|
||||
|
||||
var result = NativeDevice.CreateTexture2D(ref desc, null, &_nativeTexture);
|
||||
Log.EngineLogger.Assert(result.Succeeded, "Failed to create RenderTarget2D");
|
||||
|
||||
CreateViews();
|
||||
}
|
||||
|
||||
private void CreateViews()
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
if(_description.IsSwapchainTarget)
|
||||
{
|
||||
var result = NativeDevice.CreateShaderResourceView(_nativeTexture, null, &_nativeResourceView);
|
||||
Log.EngineLogger.Assert(result.Succeeded, "Failed to create resource view");
|
||||
|
||||
// TODO: SRGB...
|
||||
//RenderTargetViewDescription rtvDesc = .(_nativeTexture, .Texture2D, .R8G8B8A8_UNorm_SRGB);
|
||||
//result = NativeDevice.CreateRenderTargetView(_nativeTexture, &rtvDesc, &_nativeRenderTargetView);
|
||||
result = NativeDevice.CreateRenderTargetView(_nativeTexture, null, &_nativeRenderTargetView);
|
||||
Log.EngineLogger.Assert(result.Succeeded, "Failed to create render target view");
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = NativeDevice.CreateShaderResourceView(_nativeTexture, null, &_nativeResourceView);
|
||||
Log.EngineLogger.Assert(result.Succeeded, "Failed to create resource view");
|
||||
|
||||
result = NativeDevice.CreateRenderTargetView(_nativeTexture, null, &_nativeRenderTargetView);
|
||||
Log.EngineLogger.Assert(result.Succeeded, "Failed to create render target view");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,32 +1,141 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using GlitchyEngine.Math;
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
using internal GlitchyEngine.Renderer;
|
||||
|
||||
extension RendererAPI
|
||||
{
|
||||
private GraphicsContext _context;
|
||||
private GraphicsContext _context ~ _?.ReleaseRef();
|
||||
|
||||
public GraphicsContext Context
|
||||
{
|
||||
get => _context;
|
||||
set => _context = value;
|
||||
set => SetReference!(_context, value);
|
||||
}
|
||||
|
||||
public static override API Api => .D3D11;
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
}
|
||||
|
||||
public override void Clear(RenderTarget renderTarget, ColorRGBA clearColor)
|
||||
private mixin RtOrBackbuffer(RenderTarget2D renderTarget)
|
||||
{
|
||||
_context.ClearRenderTarget(renderTarget, clearColor);
|
||||
renderTarget ?? _context.SwapChain.BackBuffer
|
||||
}
|
||||
|
||||
public override void Clear(RenderTarget2D renderTarget, ColorRGBA color)
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
NativeContext.ClearRenderTargetView(RtOrBackbuffer!(renderTarget)._nativeRenderTargetView, color);
|
||||
}
|
||||
|
||||
public override void Clear(DepthStencilTarget target, ClearOptions clearOptions, float depth, uint8 stencil)
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
if(target == null)
|
||||
return;
|
||||
|
||||
DirectX.D3D11.ClearFlag flags = default;
|
||||
|
||||
if(clearOptions.HasFlag(.Depth))
|
||||
{
|
||||
flags |= .Depth;
|
||||
}
|
||||
|
||||
if(clearOptions.HasFlag(.Stencil))
|
||||
{
|
||||
flags |= .Stencil;
|
||||
}
|
||||
|
||||
NativeContext.ClearDepthStencilView(target.nativeView, flags, depth, stencil);
|
||||
}
|
||||
|
||||
public override void SetRenderTarget(RenderTarget2D renderTarget, int slot, bool setDepthBuffer)
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
_context.SetRenderTarget(renderTarget, slot, setDepthBuffer);
|
||||
}
|
||||
|
||||
public override void SetDepthStencilTarget(DepthStencilTarget target)
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
_context.SetDepthStencilTarget(target);
|
||||
}
|
||||
|
||||
public override void BindRenderTargets()
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
_context.BindRenderTargets();
|
||||
}
|
||||
|
||||
private RasterizerState _currentRasterizerState ~ _?.ReleaseRef();
|
||||
|
||||
public override void SetRasterizerState(RasterizerState rasterizerState)
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
SetReference!(_currentRasterizerState, rasterizerState);
|
||||
NativeContext.Rasterizer.SetState(_currentRasterizerState.nativeRasterizerState);
|
||||
}
|
||||
|
||||
private BlendState _currentBlendState ~ _?.ReleaseRef();
|
||||
|
||||
public override void SetBlendState(BlendState blendState, ColorRGBA blendFactor)
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
SetReference!(_currentBlendState, blendState);
|
||||
NativeContext.OutputMerger.SetBlendState(_currentBlendState.nativeBlendState, blendFactor);
|
||||
}
|
||||
|
||||
private DepthStencilState _currentDepthStencilState ~ _?.ReleaseRef();
|
||||
|
||||
public override void SetDepthStencilState(DepthStencilState depthStencilState, uint8 stencilReference)
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
SetReference!(_currentDepthStencilState, depthStencilState);
|
||||
NativeContext.OutputMerger.SetDepthStencilState(_currentDepthStencilState.nativeDepthStencilState, stencilReference);
|
||||
}
|
||||
|
||||
public override void DrawIndexed(GeometryBinding geometry)
|
||||
{
|
||||
_context.DrawIndexed(geometry.IndexCount, geometry.IndexByteOffset, 0);
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
if(geometry.IsIndexed)
|
||||
_context.DrawIndexed(geometry.IndexCount, geometry.IndexByteOffset, 0);
|
||||
else
|
||||
_context.Draw(geometry.VertexCount, 0);
|
||||
}
|
||||
|
||||
public override void DrawIndexedInstanced(GeometryBinding geometry)
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
NativeContext.DrawIndexedInstanced(geometry.IndexCount, geometry.InstanceCount, geometry.IndexByteOffset, 0, 0);
|
||||
}
|
||||
|
||||
public override void SetViewport(Viewport viewport)
|
||||
{
|
||||
Debug.Profiler.ProfileRendererFunction!();
|
||||
|
||||
_context.SetViewport(viewport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
using DirectX.D3D11;
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using DirectX.Common;
|
||||
using DirectX.D3D11;
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace DirectX.D3D11
|
||||
{
|
||||
@@ -73,8 +79,6 @@ namespace DirectX.D3D11
|
||||
}
|
||||
}
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
extension SamplerStateDescription
|
||||
@@ -107,17 +111,21 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
protected override void PlatformCreateSamplerState()
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
_desc.ToNative(var nativeDesc);
|
||||
|
||||
HResult result = _context.nativeDevice.CreateSamplerState(ref nativeDesc, &nativeSamplerState);
|
||||
HResult result = NativeDevice.CreateSamplerState(ref nativeDesc, &nativeSamplerState);
|
||||
|
||||
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create SamplerState. Error({(int)result}): {result}");
|
||||
}
|
||||
|
||||
public override void Bind(uint32 slot)
|
||||
{
|
||||
_context.nativeContext.VertexShader.SetSamplers(slot, 1, &nativeSamplerState);
|
||||
_context.nativeContext.PixelShader.SetSamplers(slot, 1, &nativeSamplerState);
|
||||
NativeContext.VertexShader.SetSamplers(slot, 1, &nativeSamplerState);
|
||||
NativeContext.PixelShader.SetSamplers(slot, 1, &nativeSamplerState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using System;
|
||||
using GlitchyEngine.Renderer;
|
||||
using DirectX.Common;
|
||||
@@ -15,8 +17,8 @@ namespace GlitchyEngine.Renderer
|
||||
* Internal compiled code of the shader.
|
||||
*/
|
||||
internal ID3DBlob* nativeCode ~ _?.Release();
|
||||
|
||||
protected const ShaderCompileFlags DefaultCompileFlags =
|
||||
|
||||
protected const ShaderCompileFlags DefaultCompileFlags = .EnableStrictness |
|
||||
#if DEBUG
|
||||
.Debug;
|
||||
#else
|
||||
@@ -25,6 +27,8 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
internal static void PlattformCompileShaderFromSource(String code, ShaderDefine[] macros, String entryPoint, String target, ShaderCompileFlags compileFlags, out ID3DBlob* shaderBlob)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
ShaderMacro* nativeMacros = macros == null ? null : new:ScopedAlloc! ShaderMacro[macros.Count]*;
|
||||
|
||||
for(int i < macros?.Count ?? 0)
|
||||
@@ -42,72 +46,67 @@ namespace GlitchyEngine.Renderer
|
||||
var result = D3DCompiler.D3DCompile(code.CStr(), (.)code.Length, null, nativeMacros, null, entryPoint, target, compileFlags, .None, &shaderBlob, &errorBlob);
|
||||
if(result.Failed)
|
||||
{
|
||||
StringView str = StringView((char8*)errorBlob.GetBufferPointer(), errorBlob.GetBufferSize());
|
||||
StringView str = StringView((char8*)errorBlob.GetBufferPointer(), (int)errorBlob.GetBufferSize());
|
||||
Log.EngineLogger.Error($"Failed to compile Shader: Error Code({(int)result}): {result} | Error Message: {str}");
|
||||
}
|
||||
}
|
||||
|
||||
protected internal void Reflect(ID3DBlob* shaderCode)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
ID3D11ShaderReflection* reflection = null;
|
||||
var result = D3DCompiler.D3DReflect(shaderCode.GetBufferPointer(), shaderCode.GetBufferSize(), &reflection);
|
||||
if(result.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to reflect shader: Message ({(int)result}): {result}");
|
||||
Debug.Profiler.ProfileResourceScope!("D3DReflect");
|
||||
|
||||
var result = D3DCompiler.D3DReflect(shaderCode.GetBufferPointer(), shaderCode.GetBufferSize(), &reflection);
|
||||
if(result.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to reflect shader: Message ({(int)result}): {result}");
|
||||
}
|
||||
}
|
||||
|
||||
reflection.GetDescription(let desc);
|
||||
uint32 cBufferCount = desc.ConstantBuffers;
|
||||
|
||||
for(uint32 i < cBufferCount)
|
||||
uint32 resourceCount = desc.BoundResources;
|
||||
for (uint32 i < resourceCount)
|
||||
{
|
||||
reflection.GetResourceBindingDescription(i, let bindDesc);
|
||||
var res = reflection.GetResourceBindingDescription(i, let bindDesc);
|
||||
|
||||
var bufferReflection = reflection.GetConstantBufferByIndex(i);
|
||||
|
||||
bufferReflection.GetDescription(let bufferDesc);
|
||||
|
||||
// ConstantBuffer
|
||||
if(bufferDesc.Type == .D3D11_CT_CBUFFER)
|
||||
if (res.Failed)
|
||||
{
|
||||
let buffer = new ConstantBuffer(_context, bufferReflection);
|
||||
|
||||
_buffers.Add(bindDesc.BindPoint, buffer.Name, buffer);
|
||||
|
||||
buffer.ReleaseRef();
|
||||
Log.EngineLogger.Error($"Error({(int)res}) {res}: Failed to get resource binding desc for resource {i}");
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
bufferReflection.GetDescription(let bufferDesc);
|
||||
|
||||
// ConstantBuffer
|
||||
if(bufferDesc.Type == .D3D11_CT_CBUFFER)
|
||||
switch(bindDesc.Type)
|
||||
{
|
||||
GlitchyEngine.Renderer.BufferDescription cBufferDesc = .(bufferDesc.Size, .Constant, .Dynamic, .Write);
|
||||
case .ConstantBuffer:
|
||||
var bufferReflection = reflection.GetConstantBufferByName(bindDesc.Name);
|
||||
|
||||
let buffer = new Buffer(_context, cBufferDesc);
|
||||
bufferReflection.GetDescription(let bufferDesc);
|
||||
|
||||
_buffers.Add(bindDesc.BindPoint, StringView(bufferDesc.Name), buffer);//, true
|
||||
buffer.ReleaseRef();
|
||||
|
||||
// Buffer for default values
|
||||
uint8* rawData = new:ScopedAlloc! uint8[bufferDesc.Size]*;
|
||||
|
||||
// Get default values
|
||||
for(uint32 variable < bufferDesc.Variables)
|
||||
// ConstantBuffer
|
||||
if(bufferDesc.Type == .D3D11_CT_CBUFFER)
|
||||
{
|
||||
let variableReflection = bufferReflection.GetVariableByIndex(variable);
|
||||
let buffer = new ConstantBuffer(bufferReflection);
|
||||
|
||||
variableReflection.GetDescription(let varDesc);
|
||||
_buffers.Add(bindDesc.BindPoint, buffer.Name, buffer);
|
||||
|
||||
if(varDesc.DefaultValue != null)
|
||||
Internal.MemCpy(rawData + varDesc.StartOffset, varDesc.DefaultValue, varDesc.Size);
|
||||
buffer.ReleaseRef();
|
||||
}
|
||||
|
||||
buffer.SetData(Span<uint8>(rawData, bufferDesc.Size));
|
||||
case .Texture:
|
||||
_textures.Add(scope String(bindDesc.Name), bindDesc.BindPoint, null);
|
||||
case .Sampler:
|
||||
// TODO: do we have to do something for samplers?
|
||||
default:
|
||||
Log.EngineLogger.Warning($"Unhandled shader resource type: \"{bindDesc.Type}\"");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
reflection.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,56 +1,24 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using DirectX.D3D11;
|
||||
using DirectX.DXGI.DXGI1_2;
|
||||
using System.Diagnostics;
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
public extension SwapChain
|
||||
{
|
||||
private GraphicsContext _context;
|
||||
|
||||
private bool _changed;
|
||||
|
||||
private uint32 _width, _height;
|
||||
|
||||
internal DirectX.DXGI.IDXGIDevice* nativeDxgiDevice;
|
||||
internal IDXGISwapChain1* nativeSwapChain;
|
||||
|
||||
internal ID3D11RenderTargetView* nativeBackBufferTarget;
|
||||
|
||||
public override uint32 Width
|
||||
{
|
||||
get => _width;
|
||||
|
||||
set
|
||||
{
|
||||
if(_width == value)
|
||||
return;
|
||||
|
||||
_width = value;
|
||||
_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override uint32 Height
|
||||
{
|
||||
get => _height;
|
||||
|
||||
set
|
||||
{
|
||||
if(_height == value)
|
||||
return;
|
||||
|
||||
_height = value;
|
||||
_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override GraphicsContext Context => _context;
|
||||
|
||||
public this(GraphicsContext context)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
_context = context;
|
||||
|
||||
SetResolutionFromWindow();
|
||||
@@ -58,9 +26,10 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
public ~this()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
nativeDxgiDevice?.Release();
|
||||
nativeSwapChain?.Release();
|
||||
nativeBackBufferTarget?.Release();
|
||||
}
|
||||
|
||||
void SetResolutionFromWindow()
|
||||
@@ -73,11 +42,15 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
ApplyChanges();
|
||||
}
|
||||
|
||||
public override void ApplyChanges()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
if(!_changed)
|
||||
return;
|
||||
|
||||
@@ -89,6 +62,8 @@ namespace GlitchyEngine.Renderer
|
||||
*/
|
||||
public void UpdateSwapchain()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
Log.EngineLogger.Trace($"Updating swap chain ({_width}, {_height})");
|
||||
|
||||
uint32 backBufferCount = 2;
|
||||
@@ -97,7 +72,8 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
if(nativeSwapChain != null)
|
||||
{
|
||||
nativeBackBufferTarget.Release();
|
||||
//nativeBackBufferTarget.Release();
|
||||
_backBuffer.ReleaseRef();
|
||||
|
||||
var resizeResult = nativeSwapChain.ResizeBuffers(backBufferCount, _width, _height, backBufferFormat, .None);
|
||||
if(resizeResult.Failed)
|
||||
@@ -107,7 +83,7 @@ namespace GlitchyEngine.Renderer
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.nativeDevice.QueryInterface(out nativeDxgiDevice);
|
||||
NativeDevice.QueryInterface(out nativeDxgiDevice);
|
||||
|
||||
nativeDxgiDevice.GetAdapter(let adapter);
|
||||
adapter.GetParent<DirectX.DXGI.IDXGIFactory2>(let factory);
|
||||
@@ -118,14 +94,14 @@ namespace GlitchyEngine.Renderer
|
||||
swDesc.Height = _height;
|
||||
swDesc.Format = backBufferFormat;
|
||||
swDesc.SampleDescription = .(1, 0);
|
||||
swDesc.BufferUsage = .RenderTargetOutput;
|
||||
swDesc.BufferUsage = .RenderTargetOutput | .ShaderInput;
|
||||
swDesc.BufferCount = backBufferCount;
|
||||
swDesc.SwapEffect = .FlipDiscard;
|
||||
|
||||
SwapChainFullscreenDescription fsSwapChainDesc = .();
|
||||
fsSwapChainDesc.Windowed = true;
|
||||
|
||||
var createResult = factory.CreateSwapChainForHwnd((.)_context.nativeDevice, _context.nativeWindowHandle, ref swDesc, &fsSwapChainDesc, null, &nativeSwapChain);
|
||||
var createResult = factory.CreateSwapChainForHwnd((.)NativeDevice, _context.nativeWindowHandle, ref swDesc, &fsSwapChainDesc, null, &nativeSwapChain);
|
||||
if(createResult.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to create swap chain. Message({(int)createResult}):{createResult}");
|
||||
@@ -134,19 +110,31 @@ namespace GlitchyEngine.Renderer
|
||||
factory.Release();
|
||||
}
|
||||
|
||||
nativeSwapChain.GetBuffer<ID3D11Texture2D>(0, let backBuffer);
|
||||
|
||||
RenderTargetViewDescription rtvDesc = .(backBuffer, .Texture2D, backBufferViewFormat);
|
||||
_context.nativeDevice.CreateRenderTargetView(backBuffer, &rtvDesc, &nativeBackBufferTarget);
|
||||
RenderTarget2DDescription desc = .(backBufferFormat, _width, _height);
|
||||
desc.DepthStencilFormat = _depthStencilFormat;
|
||||
desc.IsSwapchainTarget = true;
|
||||
|
||||
// Todo: perhaps just update the render target
|
||||
_backBuffer = new RenderTarget2D(desc);
|
||||
|
||||
_backBufferViewport = GlitchyEngine.Renderer.Viewport(0, 0, _width, _height, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
backBuffer.Release();
|
||||
internal void GetBackbuffer(out ID3D11Texture2D* texture)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
var result = nativeSwapChain.GetBuffer<ID3D11Texture2D>(0, out texture);
|
||||
Log.EngineLogger.Assert(result.Succeeded, "Failed to get backbuffer.");
|
||||
}
|
||||
|
||||
public override void Present()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
nativeSwapChain.Present(Application.Get().Window.IsVSync ? 1 : 0, .None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,48 +1,288 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using DirectX.D3D11;
|
||||
using DirectX.Common;
|
||||
using DirectXTK;
|
||||
using GlitchyEngine.Math;
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
using System.Collections;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
internal typealias NativeTex2DDesc = DirectX.D3D11.Texture2DDescription;
|
||||
|
||||
extension Texture
|
||||
{
|
||||
protected internal ID3D11ShaderResourceView* nativeResourceView ~ _?.Release();
|
||||
|
||||
/** \brief Loads the texture from the specified path.
|
||||
* @param path The path of the texture to load.
|
||||
* @param texture The reference to the pointer that will hold the texture.
|
||||
* @returns true if the texture was loaded successfully; false otherwise.
|
||||
*/
|
||||
protected bool LoadDdsResourcePlatform<T>(Stream stream, ref T* texture) where T : ID3D11Resource
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
uint8[] ddsData = new:ScopedAlloc! uint8[stream.Length];
|
||||
|
||||
var result = stream.TryRead(ddsData);
|
||||
|
||||
if (result case .Err(let err))
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to read texture data from stream. Error: {err}");
|
||||
}
|
||||
|
||||
((ID3D11Resource*)texture)?.Release();
|
||||
nativeResourceView?.Release();
|
||||
|
||||
HResult loadResult = DDSTextureLoader.CreateDDSTextureFromMemory(NativeDevice,
|
||||
ddsData.Ptr, (uint)ddsData.Count, (.)&texture, &nativeResourceView);
|
||||
|
||||
if(loadResult.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to load texture. Error({(int)loadResult}): {loadResult}");
|
||||
|
||||
ReleaseAndNullify!(texture);
|
||||
ReleaseAndNullify!(nativeResourceView);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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 = (.)Usage;
|
||||
desc.BindFlags = .ShaderResource;
|
||||
|
||||
desc.CpuAccessFlags = (.)CpuAccess;
|
||||
desc.MiscFlags = .None;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
public static explicit operator NativeTex2DDesc(Self desc) => desc.ToNative();
|
||||
}
|
||||
|
||||
extension Texture2D
|
||||
{
|
||||
internal ID3D11Texture2D* nativeTexture ~ _?.Release();
|
||||
internal ID3D11ShaderResourceView* nativeView ~ _?.Release();
|
||||
internal Texture2DDescription nativeDesc;
|
||||
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;
|
||||
public override uint32 MipLevels => nativeDesc.MipLevels;
|
||||
|
||||
protected override void LoadTexturePlatform()
|
||||
protected override void LoadDdsPlatform(Stream stream)
|
||||
{
|
||||
nativeTexture?.Release();
|
||||
nativeView?.Release();
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
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();
|
||||
LoadDdsResourcePlatform(stream, ref nativeTexture);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
protected override void ImplBind(uint32 slot)
|
||||
protected override void CreateTexturePlatform(Texture2DDesc desc, bool isRenderTarget, void* data, uint32 linePitch)
|
||||
{
|
||||
_context.nativeContext.VertexShader.SetShaderResources(slot, 1, &nativeView);
|
||||
_context.nativeContext.PixelShader.SetShaderResources(slot, 1, &nativeView);
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
PrepareTexturePlatform(desc, isRenderTarget);
|
||||
InternalCreateTexture(data, linePitch, 0);
|
||||
}
|
||||
|
||||
private void InternalCreateTexture(void* data, uint32 linePitch, uint32 slicePitch)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
SubresourceData resData = .(data, linePitch, slicePitch);
|
||||
|
||||
// If data is null, set subresourceData null
|
||||
SubresourceData* resDataPtr = data == null ? null : &resData;
|
||||
|
||||
var result = NativeDevice.CreateTexture2D(ref nativeDesc, resDataPtr, &nativeTexture);
|
||||
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture 2D. Error ({result.Underlying}): {result}");
|
||||
|
||||
result = NativeDevice.CreateShaderResourceView(nativeTexture, null, &nativeResourceView);
|
||||
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture view. Error ({result.Underlying}): {result}");
|
||||
}
|
||||
|
||||
protected override void PrepareTexturePlatform(Texture2DDesc desc, bool isRenderTarget)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
nativeTexture?.Release();
|
||||
nativeTexture = null;
|
||||
nativeResourceView?.Release();
|
||||
nativeResourceView = null;
|
||||
|
||||
nativeDesc = (NativeTex2DDesc)desc;
|
||||
|
||||
if(isRenderTarget)
|
||||
nativeDesc.BindFlags |= .RenderTarget;
|
||||
}
|
||||
|
||||
// TODO: Update Texture Arrays!
|
||||
protected override System.Result<void> PlatformSetData(void* data, uint32 elementSize, uint32 destX,
|
||||
uint32 destY, uint32 destWidth, uint32 destHeight, uint32 arraySlice, uint32 mipLevel, GlitchyEngine.Renderer.MapType mapType)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
if(nativeTexture == null)
|
||||
{
|
||||
if(destX == 0 && destY == 0 && destWidth == nativeDesc.Width && destHeight == nativeDesc.Height)
|
||||
{
|
||||
// We can pass the data while creating the buffer, so we can return here.
|
||||
var rowPitch = elementSize * destWidth;
|
||||
return InternalCreateTexture(data, rowPitch, rowPitch * destHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we don't set the entire texture we need to create the texture with unknown content and set it manually
|
||||
Log.EngineLogger.Assert(nativeDesc.Usage != .Immutable, "The entire immutable texture has to be initialized.");
|
||||
InternalCreateTexture(null, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
//Log.EngineLogger.AssertDebug();
|
||||
// TODO: Debug.Assert(dstByteOffset + byteLength <= _description.Size, "The destination offset and byte length are too long for the target buffer.");
|
||||
|
||||
uint32 subresourceIndex = D3D11.CalcSubresource(mipLevel, arraySlice, nativeDesc.MipLevels);
|
||||
|
||||
switch(nativeDesc.Usage)
|
||||
{
|
||||
case .Default:
|
||||
Box dataBox = .(destX, destY, 0, destX + destWidth, destY + destHeight, 1);
|
||||
var rowPitch = elementSize * destWidth;
|
||||
NativeContext.UpdateSubresource(nativeTexture, subresourceIndex, &dataBox, data, rowPitch, rowPitch * destHeight);
|
||||
case .Dynamic:
|
||||
Runtime.NotImplemented();
|
||||
/*
|
||||
Log.EngineLogger.Assert(mapType.CanWrite, "The map type has to have write access.");
|
||||
// Todo: DoNotWaitFlag
|
||||
MappedSubresource map = ?;
|
||||
_context.nativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map);
|
||||
Internal.MemCpy(((uint8*)map.Data) + dstByteOffset, data, byteLength);
|
||||
_context.nativeContext.Unmap(nativeBuffer, 0);
|
||||
*/
|
||||
case .Immutable:
|
||||
Log.EngineLogger.Error("Can't set the data of an immutable resource.");
|
||||
return .Err;
|
||||
default:
|
||||
Log.EngineLogger.Error($"Unknown resource usage: {nativeDesc.Usage}");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public static override void CopySubresourceRegion(Texture2D source, Texture2D destination,
|
||||
ResourceBox sourceBox = default, uint32 destX = 0, uint32 destY = 0,
|
||||
uint32 srcArraySlice = 0, uint32 srcMipSlice = 0, uint32 destArraySlice = 0, uint32 destMipSlice = 0)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
Log.EngineLogger.AssertDebug(source != destination || srcArraySlice != destArraySlice
|
||||
|| srcMipSlice != destMipSlice, "Cannot copy from and to the same sub resource.");
|
||||
|
||||
Log.EngineLogger.AssertDebug(source.nativeDesc.Format == destination.nativeDesc.Format,
|
||||
"Source and destination resources must have the same texel-format.");
|
||||
|
||||
var sourceBox;
|
||||
|
||||
if(sourceBox.Right == 0)
|
||||
{
|
||||
sourceBox.Right = source.Width;
|
||||
sourceBox.Bottom = source.Height;
|
||||
sourceBox.Back = 1;
|
||||
}
|
||||
|
||||
// Make sure the destination was initialized
|
||||
if(destination.nativeTexture == null)
|
||||
destination.InternalCreateTexture(null, 0, 0);
|
||||
|
||||
NativeContext.CopySubresourceRegion(destination.nativeTexture,
|
||||
D3D11.CalcSubresource(destMipSlice, destArraySlice, destination.MipLevels), destX, destY, 0,
|
||||
source.nativeTexture, D3D11.CalcSubresource(srcMipSlice, srcArraySlice, source.MipLevels), (.)&sourceBox);
|
||||
}
|
||||
|
||||
public override void CopyTo(Texture2D destination)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
if(nativeTexture == null)
|
||||
return;
|
||||
|
||||
Log.EngineLogger.AssertDebug(nativeDesc.Format == destination.nativeDesc.Format,
|
||||
"Source and destination resources must have the same texel-format.");
|
||||
|
||||
// Make sure the destination was initialized
|
||||
if(destination.nativeTexture == null)
|
||||
destination.InternalCreateTexture(null, 0, 0);
|
||||
|
||||
uint32 arraySlices = Math.Min(ArraySize, destination.ArraySize);
|
||||
uint32 mipSlices = Math.Min(MipLevels, destination.MipLevels);
|
||||
|
||||
for(uint32 arraySlice < arraySlices)
|
||||
for(uint32 mipSlice < mipSlices)
|
||||
{
|
||||
Box sourceBox = .(0, 0, 0, Width, Height, 1);
|
||||
|
||||
NativeContext.CopySubresourceRegion(destination.nativeTexture,
|
||||
D3D11.CalcSubresource(mipSlice, arraySlice, destination.MipLevels), 0, 0, 0,
|
||||
nativeTexture, D3D11.CalcSubresource(mipSlice, arraySlice, MipLevels), (.)&sourceBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(Stream stream)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
LoadDdsResourcePlatform(stream, ref nativeTexture);
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using System;
|
||||
using DirectX.D3D11;
|
||||
using System.Diagnostics;
|
||||
using DirectX.Common;
|
||||
using DirectX.D3D11;
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
@@ -13,12 +17,12 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
public ID3DBlob* nativeShaderCode ~ _?.Release();
|
||||
|
||||
public this(GraphicsContext context, VertexElement[] elements, VertexShader vertexShader)
|
||||
public this(VertexElement[] elements, bool ownsElements, VertexShader vertexShader)
|
||||
{
|
||||
nativeShaderCode = vertexShader.nativeCode..AddRef();
|
||||
|
||||
_context = context..AddRef();
|
||||
_elements = elements;
|
||||
_ownsElements = ownsElements;
|
||||
|
||||
CreateNativeLayout();
|
||||
}
|
||||
@@ -28,16 +32,18 @@ namespace GlitchyEngine.Renderer
|
||||
Debug.Assert(input.Count == output.Count);
|
||||
|
||||
for(int i < input.Count)
|
||||
output[i] = .(input[i].SemanticName, input[i].SemanticIndex, input[i].Format, input[i].InputSlot, input[i].AlignedByteOffset, (.)input[i].InputSlotClass);
|
||||
output[i] = .(input[i].SemanticName, input[i].SemanticIndex, input[i].Format, input[i].InputSlot, input[i].AlignedByteOffset, (.)input[i].InputSlotClass, input[i].InstanceDataStepRate);
|
||||
}
|
||||
|
||||
protected override void CreateNativeLayout()
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
var nativeElements = scope InputElementDescription[_elements.Count];
|
||||
|
||||
ToNativeLayout(_elements, nativeElements);
|
||||
|
||||
var result = _context.nativeDevice.CreateInputLayout(nativeElements.CArray(), (.)nativeElements.Count, nativeShaderCode.GetBufferPointer(), nativeShaderCode.GetBufferSize(), &nativeLayout);
|
||||
var result = NativeDevice.CreateInputLayout(nativeElements.CArray(), (.)nativeElements.Count, nativeShaderCode.GetBufferPointer(), nativeShaderCode.GetBufferSize(), &nativeLayout);
|
||||
if(result.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to create D3D11 input layout: Message({(int)result}): {result}");
|
||||
@@ -45,3 +51,5 @@ namespace GlitchyEngine.Renderer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#if GE_GRAPHICS_DX11
|
||||
|
||||
using System;
|
||||
using GlitchyEngine.Renderer;
|
||||
using DirectX.Common;
|
||||
using DirectX.D3D11;
|
||||
using DirectX.D3DCompiler;
|
||||
using GlitchyEngine.Platform.DX11;
|
||||
|
||||
using internal GlitchyEngine.Renderer;
|
||||
using internal GlitchyEngine.Platform.DX11;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
@@ -14,15 +17,23 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
public override void CompileFromSource(String code, String entryPoint, ShaderDefine[] macros = null)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
Shader.PlattformCompileShaderFromSource(code, macros, entryPoint, "vs_5_0", DefaultCompileFlags, out nativeCode);
|
||||
|
||||
var result = _context.nativeDevice.CreateVertexShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
|
||||
if(result.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to create vertex shader: Message ({(int)result}): {result}");
|
||||
Debug.Profiler.ProfileResourceScope!("CreateNativeVertexShader");
|
||||
|
||||
var result = NativeDevice.CreateVertexShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
|
||||
if(result.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to create vertex shader: Message ({(int)result}): {result}");
|
||||
}
|
||||
}
|
||||
|
||||
Reflect(nativeCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
|
||||
using System;
|
||||
using GlitchyEngine.Events;
|
||||
using DirectX.Windows.VirtualKeyCodes;
|
||||
@@ -7,8 +9,6 @@ using static System.Windows;
|
||||
|
||||
namespace GlitchyEngine
|
||||
{
|
||||
|
||||
#if GE_WINDOWS
|
||||
/// Windows (WinApi) specific implementation of the Input-class
|
||||
extension Input
|
||||
{
|
||||
@@ -20,8 +20,10 @@ namespace GlitchyEngine
|
||||
public Point CursorPositionDifference;
|
||||
}
|
||||
|
||||
static WindowsInputState* CurrentState = new WindowsInputState() ~ delete _;
|
||||
static WindowsInputState* LastState = new WindowsInputState() ~ delete _;
|
||||
static WindowsInputState[2] IputStates;
|
||||
|
||||
static WindowsInputState* CurrentState = &IputStates[0];
|
||||
static WindowsInputState* LastState = &IputStates[1];
|
||||
|
||||
//
|
||||
// Current Keyboard state
|
||||
@@ -49,13 +51,13 @@ namespace GlitchyEngine
|
||||
//
|
||||
public override static bool WasKeyPressed(Key keycode)
|
||||
{
|
||||
int8 state = CurrentState.KeyStates[(int)keycode];
|
||||
int8 state = LastState.KeyStates[(int)keycode];
|
||||
return state < 0;
|
||||
}
|
||||
|
||||
public override static bool WasKeyReleased(Key keycode)
|
||||
{
|
||||
int8 state = CurrentState.KeyStates[(int)keycode];
|
||||
int8 state = LastState.KeyStates[(int)keycode];
|
||||
return state >= 0;
|
||||
}
|
||||
|
||||
@@ -148,11 +150,10 @@ namespace GlitchyEngine
|
||||
//
|
||||
public override static bool IsMouseButtonPressing(MouseButton button) => IsMouseButtonPressed(button) && WasMouseButtonReleased(button);
|
||||
|
||||
public override static bool IsMouseButtonReleasiong(MouseButton button) => IsMouseButtonReleased(button) && WasMouseButtonPressed(button);
|
||||
public override static bool IsMouseButtonReleasing(MouseButton button) => IsMouseButtonReleased(button) && WasMouseButtonPressed(button);
|
||||
|
||||
public override static Point GetMouseMovement() => CurrentState.CursorPositionDifference;
|
||||
|
||||
|
||||
//[CLink, CallingConvention(.Stdcall)]
|
||||
//static extern int16 GetKeyState(int32 keycode);
|
||||
[CLink, CallingConvention(.Stdcall)]
|
||||
@@ -162,11 +163,10 @@ namespace GlitchyEngine
|
||||
|
||||
public override static void NewFrame()
|
||||
{
|
||||
// Switch last and current states
|
||||
var buffer = LastState;
|
||||
LastState = CurrentState;
|
||||
CurrentState = buffer;
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
Swap!(CurrentState, LastState);
|
||||
|
||||
// Get current keyboard state
|
||||
if(DirectX.Windows.Winuser.GetKeyboardState((uint8*)&CurrentState.KeyStates) == 0)
|
||||
{
|
||||
@@ -194,5 +194,6 @@ namespace GlitchyEngine
|
||||
CurrentState.CursorPositionDifference = CurrentState.CursorPosition - LastState.CursorPosition;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using DirectX.Windows.Winuser;
|
||||
using DirectX.Common;
|
||||
|
||||
namespace GlitchyEngine
|
||||
{
|
||||
extension Key
|
||||
{
|
||||
// Todo: make overriding method
|
||||
public void GetKeyName(String strBuffer)
|
||||
{
|
||||
var scanCode = MapVirtualKeyW((.)this, MAPVK_VK_TO_VSC);
|
||||
|
||||
char16[128] name = ?;
|
||||
int result;
|
||||
|
||||
switch (this)
|
||||
{
|
||||
case .Left, .Up, .Right, .Down, .RightControl, .RightAlt,
|
||||
.LeftSuper, .RightSuper, ContextMenu,
|
||||
.Prior, .Next, .End, .Home, .Insert, .Delete, .Divide, .Numlock:
|
||||
// set extended flag
|
||||
scanCode |= 0x0100;
|
||||
fallthrough;
|
||||
default:
|
||||
result = GetKeyNameTextW((.)(scanCode << 16), &name, name.Count);
|
||||
}
|
||||
|
||||
if(result == 0)
|
||||
{
|
||||
HResult error = (.)DirectX.Windows.Kernel32.GetLastError();
|
||||
|
||||
Log.EngineLogger.Error($"Failed to convert keycode {(int)this}({this}) to string. WinAPI error({(int)error}): {error}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
strBuffer.Append(Span<char16>(&name, result));
|
||||
}
|
||||
|
||||
/// Converts the given windows specific virtual key code to a GlitchyEngine Key.
|
||||
[Inline]
|
||||
internal static Key FromWindowsKeyCode(int32 vKeyCode)
|
||||
{
|
||||
return (.)vKeyCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#if GE_WINDOWS
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
|
||||
using System;
|
||||
using DirectX.Common;
|
||||
using DirectX.Windows;
|
||||
@@ -11,6 +12,8 @@ using GlitchyEngine.Math;
|
||||
using GlitchyEngine.Renderer;
|
||||
using static System.Windows;
|
||||
|
||||
using internal GlitchyEngine;
|
||||
|
||||
namespace GlitchyEngine
|
||||
{
|
||||
/// The windows specific Window implementation
|
||||
@@ -31,6 +34,8 @@ namespace GlitchyEngine
|
||||
|
||||
private bool _isVSync = true;
|
||||
|
||||
private bool _isActive;
|
||||
|
||||
private GraphicsContext _graphicsContext ~ _?.ReleaseRef();
|
||||
|
||||
public override GraphicsContext Context => _graphicsContext;
|
||||
@@ -149,10 +154,14 @@ namespace GlitchyEngine
|
||||
set => _isVSync = value;
|
||||
}
|
||||
|
||||
public override bool IsActive => _isActive;
|
||||
|
||||
public override void* NativeWindow => (void*)(int)_windowHandle;
|
||||
|
||||
public override this(WindowDescription desc)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
_minMaxInfo.MaximumTrackingSize.x = int32.MaxValue;
|
||||
_minMaxInfo.MaximumTrackingSize.y = int32.MaxValue;
|
||||
|
||||
@@ -167,6 +176,8 @@ namespace GlitchyEngine
|
||||
|
||||
public ~this()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
if(!DestroyWindow(_windowHandle))
|
||||
{
|
||||
HResult res = (.)GetLastError();
|
||||
@@ -176,8 +187,13 @@ namespace GlitchyEngine
|
||||
UnregisterClass(WindowClassName.ToScopedNativeWChar!(), _instanceHandle);
|
||||
}
|
||||
|
||||
[LinkName(.C)]
|
||||
static extern HWnd GetActiveWindow();
|
||||
|
||||
private void Init(WindowDescription desc)
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
Log.EngineLogger.Trace($"Creating window \"{desc.Title}\" ({desc.Width}, {desc.Height})...");
|
||||
|
||||
_instanceHandle = (.)GetModuleHandleW(null);
|
||||
@@ -188,7 +204,11 @@ namespace GlitchyEngine
|
||||
_windowClass.HInstance = (.)_instanceHandle;
|
||||
|
||||
if (desc.Icon.Ptr != null)
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("LoadIcon");
|
||||
|
||||
_windowClass.Icon = LoadImageW(0, desc.Icon.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile);
|
||||
}
|
||||
else
|
||||
_windowClass.Icon = 0;
|
||||
|
||||
@@ -203,8 +223,12 @@ namespace GlitchyEngine
|
||||
Runtime.FatalError("Failed to register window class");
|
||||
}
|
||||
|
||||
_windowHandle = CreateWindowExW(.None, _windowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, (.)_instanceHandle, null);
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("CreateWindow");
|
||||
|
||||
_windowHandle = CreateWindowExW(.None, _windowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, (.)_instanceHandle, null);
|
||||
}
|
||||
|
||||
_graphicsContext = new GraphicsContext(_windowHandle);
|
||||
_graphicsContext.Init();
|
||||
@@ -215,6 +239,9 @@ namespace GlitchyEngine
|
||||
LoadWindowRectangle();
|
||||
|
||||
Log.EngineLogger.Trace($"Created window \"{Title}\" ({Width}, {Height})");
|
||||
|
||||
// Determine whether or not the window is currently active
|
||||
_isActive = GetActiveWindow() == _windowHandle;
|
||||
}
|
||||
|
||||
private bool _isResizingOrMoving;
|
||||
@@ -223,6 +250,18 @@ namespace GlitchyEngine
|
||||
[CLink]
|
||||
static extern IntBool IsWindowUnicode(HWND whnd);
|
||||
|
||||
public Result<void> SetIcon(StringView filePath)
|
||||
{
|
||||
HICON hIcon = LoadImageW(0, filePath.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile);
|
||||
if (hIcon == 0)
|
||||
return .Err;
|
||||
|
||||
// WM_SETICON 0x0080
|
||||
SendMessageW(_windowHandle, 0x0080, 1 /* ICON_BIG */, (int)hIcon);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static LRESULT MessageHandler(HWND hwnd, uint32 uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
void* windowPtr = (void*)GetWindowLongPtrW(hwnd, GWL_USERDATA);
|
||||
@@ -251,26 +290,21 @@ namespace GlitchyEngine
|
||||
// Window size changed
|
||||
case WM_SIZE:
|
||||
{
|
||||
if (wParam == (.)ResizingType.Minimized)
|
||||
{
|
||||
window._isMinimized = true;
|
||||
}
|
||||
else if (window._isMinimized)
|
||||
{
|
||||
window._isMinimized = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SplitHighAndLowOrder!(lParam, out window._clientRect.Width, out window._clientRect.Height);
|
||||
window._isMinimized = wParam == (.)ResizingType.Minimized;
|
||||
|
||||
|
||||
SplitHighAndLowOrder!(lParam, out window._clientRect.Width, out window._clientRect.Height);
|
||||
|
||||
if(window._clientRect.Width != 0 && window._clientRect.Height != 0)
|
||||
{
|
||||
window._graphicsContext.SwapChain.Width = (.)window._clientRect.Width;
|
||||
window._graphicsContext.SwapChain.Height = (.)window._clientRect.Height;
|
||||
|
||||
|
||||
window._graphicsContext.SwapChain.ApplyChanges();
|
||||
|
||||
WindowResizeEvent event = scope WindowResizeEvent(window._clientRect.Width, window._clientRect.Height, window._isResizingOrMoving);
|
||||
window._eventCallback(event);
|
||||
}
|
||||
|
||||
WindowResizeEvent event = scope WindowResizeEvent(window._clientRect.Width, window._clientRect.Height, window._isResizingOrMoving);
|
||||
window._eventCallback(event);
|
||||
}
|
||||
// Window position changed
|
||||
case WM_MOVE:
|
||||
@@ -304,19 +338,40 @@ namespace GlitchyEngine
|
||||
var moveEvent = scope WindowMoveEvent(window._clientRect.X, window._clientRect.Y, false);
|
||||
window._eventCallback(moveEvent);
|
||||
}
|
||||
|
||||
case 0x0006: //WM_ACTIVATE
|
||||
{
|
||||
SplitHighAndLowOrder!((int64)wParam, let lowWParam, let highWParam);
|
||||
|
||||
bool isActivate = lowWParam > 0;
|
||||
|
||||
//HWnd windowHandle = (.)lParam;
|
||||
|
||||
if(window._isActive != isActivate)
|
||||
{
|
||||
window._isActive = isActivate;
|
||||
|
||||
if(window._isActive)
|
||||
window._eventCallback(scope WindowActivateEvent());
|
||||
else
|
||||
window._eventCallback(scope WindowDeactivateEvent());
|
||||
|
||||
}
|
||||
}
|
||||
////
|
||||
//// Keyboard input
|
||||
////
|
||||
|
||||
case WM_KEYDOWN:
|
||||
case WM_KEYDOWN, WM_SYSKEYDOWN:
|
||||
{
|
||||
var event = scope KeyPressedEvent((Key)wParam, (int32)lParam & 0xFFFF);
|
||||
Key key = WindowMessageToKey(wParam, lParam);
|
||||
var event = scope KeyPressedEvent(key, (int32)lParam & 0xFFFF);
|
||||
window._eventCallback(event);
|
||||
}
|
||||
case WM_KEYUP:
|
||||
case WM_KEYUP, WM_SYSKEYUP:
|
||||
{
|
||||
var event = scope KeyReleasedEvent((Key)wParam);
|
||||
Key key = WindowMessageToKey(wParam, lParam);
|
||||
var event = scope KeyReleasedEvent(key);
|
||||
window._eventCallback(event);
|
||||
}
|
||||
|
||||
@@ -443,8 +498,33 @@ namespace GlitchyEngine
|
||||
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the wParam and lParam of a WM_KEYDOWN or WM_KEYUP message to the corresponding engine keycode.
|
||||
*/
|
||||
static Key WindowMessageToKey(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
Key key = Key.FromWindowsKeyCode((.)wParam);
|
||||
|
||||
var scancode = (lParam & 0x00ff0000) >> 16;
|
||||
bool isExtended = (lParam & 0x01000000) > 0;
|
||||
|
||||
switch(key)
|
||||
{
|
||||
case .Shift:
|
||||
return Key.FromWindowsKeyCode((.)MapVirtualKeyW((.)scancode, MAPVK_VSC_TO_VK_EX));
|
||||
case .Control:
|
||||
return isExtended ? .RightControl : .LeftControl;
|
||||
case .Alt:
|
||||
return isExtended ? .RightAlt : .LeftAlt;
|
||||
default:
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
Debug.Profiler.ProfileFunction!();
|
||||
|
||||
Message message = .();
|
||||
while (PeekMessageW(&message, 0, 0, 0, .Remove))
|
||||
{
|
||||
@@ -488,4 +568,5 @@ namespace GlitchyEngine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using GlitchLog;
|
||||
using GlitchyEngine.Debug;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace GlitchyEngine
|
||||
@@ -11,24 +12,38 @@ namespace GlitchyEngine
|
||||
|
||||
public static int Main(String[] args)
|
||||
{
|
||||
Log.EngineLogger.Info("Initializing Application...");
|
||||
Debug.Profiler.BeginProfiling();
|
||||
|
||||
Stopwatch initWatch = scope Stopwatch();
|
||||
Application app;
|
||||
|
||||
var app = CreateApplication();
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("Initialize");
|
||||
|
||||
Log.EngineLogger.Info("Initializing Application...");
|
||||
|
||||
initWatch.Stop();
|
||||
Stopwatch initWatch = scope Stopwatch();
|
||||
|
||||
Log.EngineLogger.Info($"Application initialized ({initWatch.ElapsedMilliseconds}ms).");
|
||||
Log.EngineLogger.Info("Running App...");
|
||||
app = CreateApplication();
|
||||
|
||||
initWatch.Stop();
|
||||
|
||||
Log.EngineLogger.Info($"Application initialized ({initWatch.ElapsedMilliseconds}ms).");
|
||||
Log.EngineLogger.Info("Running App...");
|
||||
}
|
||||
|
||||
app.Run();
|
||||
|
||||
Log.EngineLogger.Info("Uninitializing Application...");
|
||||
{
|
||||
Debug.Profiler.ProfileScope!("Shutdown");
|
||||
|
||||
Log.EngineLogger.Info("Uninitializing Application...");
|
||||
|
||||
delete app;
|
||||
|
||||
Log.EngineLogger.Info("Application uninitialized.");
|
||||
}
|
||||
|
||||
delete app;
|
||||
|
||||
Log.EngineLogger.Info("Application uninitialized.");
|
||||
Debug.Profiler.EndProfiling();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
using System;
|
||||
using GlitchyEngine.Core;
|
||||
using GlitchyEngine.Math;
|
||||
|
||||
namespace GlitchyEngine.Renderer.Animation
|
||||
{
|
||||
class AnimationPlayer
|
||||
{
|
||||
public Skeleton Skeleton ~ _.ReleaseRef();
|
||||
public AnimationClip CurrentClip ~ _.ReleaseRef();
|
||||
public float TimeStamp;
|
||||
|
||||
public SkeletonPose Pose ~ delete _;
|
||||
|
||||
public Matrix[] SkinningMatricies ~ delete _;
|
||||
public Matrix3x3[] InvTransSkinningMatricies ~ delete _;
|
||||
|
||||
public this(Skeleton skeleton, AnimationClip clip)
|
||||
{
|
||||
Skeleton = skeleton;
|
||||
CurrentClip = clip;
|
||||
|
||||
Pose = new SkeletonPose(Skeleton);
|
||||
|
||||
SkinningMatricies = new Matrix[Skeleton.Joints.Count];
|
||||
InvTransSkinningMatricies = new Matrix3x3[Skeleton.Joints.Count];
|
||||
}
|
||||
|
||||
public void Update(GameTime gameTime)
|
||||
{
|
||||
TimeStamp += (float)gameTime.FrameTime.TotalSeconds;
|
||||
|
||||
if(CurrentClip.IsLooping && CurrentClip.Duration != 0)
|
||||
{
|
||||
TimeStamp %= CurrentClip.Duration;
|
||||
}
|
||||
|
||||
for(int i < Skeleton.Joints.Count)
|
||||
{
|
||||
ref JointPose localPose = ref Pose.LocalPose[i];
|
||||
|
||||
localPose = CurrentClip.JointAnimations[i].GetCurrentPose(TimeStamp);
|
||||
|
||||
Matrix jointToParent =
|
||||
Matrix.Translation(localPose.Translation) *
|
||||
Matrix.RotationQuaternion(localPose.Rotation) *
|
||||
Matrix.Scaling(localPose.Scale);
|
||||
|
||||
uint8 parentIndex = Skeleton.Joints[i].ParentID;
|
||||
|
||||
ref Matrix globalPose = ref Pose.GlobalPose[i];
|
||||
|
||||
if(parentIndex == uint8.MaxValue)
|
||||
{
|
||||
globalPose = jointToParent;
|
||||
}
|
||||
else
|
||||
{
|
||||
globalPose = Pose.GlobalPose[parentIndex] * jointToParent;
|
||||
}
|
||||
|
||||
SkinningMatricies[i] = globalPose * Skeleton.Joints[i].InverseBindPose;
|
||||
InvTransSkinningMatricies[i] = ((Matrix3x3)SkinningMatricies[i]).Inverse().Transpose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AnimationClip : RefCounter
|
||||
{
|
||||
private Skeleton _skeleton ~ _.ReleaseRef();
|
||||
public JointAnimation[] JointAnimations ~ delete _;
|
||||
public bool IsLooping;
|
||||
public float Duration;
|
||||
|
||||
public Skeleton Skeleton => _skeleton;
|
||||
|
||||
[AllowAppend]
|
||||
public this(Skeleton skeleton)
|
||||
{
|
||||
var jointAnimations = new JointAnimation[skeleton.Joints.Count];
|
||||
|
||||
Log.EngineLogger.AssertDebug(skeleton != null);
|
||||
|
||||
_skeleton = skeleton..AddRef();
|
||||
JointAnimations = jointAnimations;
|
||||
}
|
||||
|
||||
// TODO: check
|
||||
public ~this()
|
||||
{
|
||||
for(var jointAnimation in JointAnimations)
|
||||
{
|
||||
//jointAnimation.Dispose();
|
||||
delete jointAnimation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum InterpolationMode
|
||||
{
|
||||
/**
|
||||
* No keyframe interpolation.
|
||||
* The keyframe with the greates timestamp that is smaller than the current time will be used.
|
||||
*/
|
||||
Step,
|
||||
/**
|
||||
* Linear interpolation between the two keyframes that are closest to the current time.
|
||||
*/
|
||||
Linear,
|
||||
/**
|
||||
* Cubic spline interpolation between the two keyframes that are closest to the current time.
|
||||
*/
|
||||
CubicSpline
|
||||
}
|
||||
|
||||
class JointAnimationChannel<T> where T : struct // : IDisposable
|
||||
{
|
||||
public float[] TimeStamps;
|
||||
public T[] Values;
|
||||
public InterpolationMode InterpolationMode = .Step;
|
||||
|
||||
public float Duration => TimeStamps[TimeStamps.Count - 1];
|
||||
|
||||
public this(int samples, InterpolationMode interpolationMode)
|
||||
{
|
||||
TimeStamps = new float[samples];
|
||||
Values = new T[samples];
|
||||
InterpolationMode = interpolationMode;
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
delete TimeStamps;
|
||||
delete Values;
|
||||
}
|
||||
|
||||
|
||||
public (T, T, float) GetSample(float currentTime)
|
||||
{
|
||||
let (previousIndex, previousTime) = FindPreviousTimeStamp(currentTime);
|
||||
|
||||
T previousSample = Values[previousIndex];
|
||||
|
||||
if(InterpolationMode == .Step)
|
||||
{
|
||||
return (previousSample, default(T), 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
int nextIndex = previousIndex + 1;
|
||||
if(nextIndex >= Values.Count)
|
||||
nextIndex = Values.Count - 1;
|
||||
|
||||
float nextTime = TimeStamps[nextIndex];
|
||||
|
||||
T nextSample = Values[nextIndex];
|
||||
|
||||
float interpolationValue = (currentTime - previousTime) / (nextTime - previousTime);
|
||||
|
||||
return (previousSample, nextSample, interpolationValue);
|
||||
}
|
||||
}
|
||||
|
||||
public (int Index, float timeStamp) FindPreviousTimeStamp(float currentTime)
|
||||
{
|
||||
// TODO use binary search to speed things up?
|
||||
|
||||
int index = TimeStamps.Count - 1;
|
||||
|
||||
for(int i < TimeStamps.Count)
|
||||
{
|
||||
if(TimeStamps[i] > currentTime)
|
||||
{
|
||||
index = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(index == -1)
|
||||
index = 0;
|
||||
|
||||
// Don't return -1 but return first frame
|
||||
return (index, TimeStamps[index]);
|
||||
|
||||
//return array.Count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
class JointAnimation// : IDisposable
|
||||
{
|
||||
public JointAnimationChannel<Vector3> TranslationChannel ~ delete _;
|
||||
public JointAnimationChannel<Quaternion> RotationChannel ~ delete _;
|
||||
public JointAnimationChannel<Vector3> ScaleChannel ~ delete _;
|
||||
|
||||
public float Duration
|
||||
{
|
||||
get
|
||||
{
|
||||
return Math.Max(Math.Max(
|
||||
TranslationChannel?.Duration ?? 0, RotationChannel?.Duration ?? 0), ScaleChannel?.Duration ?? 0);
|
||||
}
|
||||
}
|
||||
/*
|
||||
public void Dispose()
|
||||
{
|
||||
delete TranslationChannel;
|
||||
delete RotationChannel;
|
||||
delete ScaleChannel;
|
||||
}
|
||||
*/
|
||||
public JointPose GetCurrentPose(float timeStamp)
|
||||
{
|
||||
JointPose result;
|
||||
|
||||
if(TranslationChannel != null)
|
||||
{
|
||||
let (previous, next, interpolationValue) = TranslationChannel.GetSample(timeStamp);
|
||||
|
||||
switch(TranslationChannel.InterpolationMode)
|
||||
{
|
||||
case .Step:
|
||||
result.Translation = previous;
|
||||
case .Linear:
|
||||
result.Translation = Vector3.Lerp(previous, next, interpolationValue);
|
||||
default:
|
||||
result.Rotation = ?;
|
||||
Runtime.NotImplemented();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Translation = .Zero;
|
||||
}
|
||||
|
||||
if(RotationChannel != null)
|
||||
{
|
||||
let (previous, next, interpolationValue) = RotationChannel.GetSample(timeStamp);
|
||||
|
||||
switch(RotationChannel.InterpolationMode)
|
||||
{
|
||||
case .Step:
|
||||
result.Rotation = previous;
|
||||
case .Linear:
|
||||
result.Rotation = Quaternion.Slerp(previous, next, interpolationValue);
|
||||
default:
|
||||
result.Rotation = ?;
|
||||
Runtime.NotImplemented();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Rotation = .Identity;
|
||||
}
|
||||
|
||||
if(ScaleChannel != null)
|
||||
{
|
||||
let (previous, next, interpolationValue) = ScaleChannel.GetSample(timeStamp);
|
||||
|
||||
//switch(ScaleChannel.InterpolationMode)
|
||||
switch(InterpolationMode.Step)
|
||||
{
|
||||
case .Step:
|
||||
result.Scale = previous;
|
||||
case .Linear:
|
||||
result.Scale = Vector3.Lerp(previous, next, interpolationValue);
|
||||
default:
|
||||
result.Rotation = ?;
|
||||
Runtime.NotImplemented();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Scale = .One;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using GlitchyEngine.Math;
|
||||
using System;
|
||||
|
||||
namespace GlitchyEngine.Renderer.Animation
|
||||
{
|
||||
public struct Joint
|
||||
{
|
||||
/// Converts vertices from model space to joint space
|
||||
public Matrix InverseBindPose;
|
||||
public String Name;
|
||||
public uint8 ParentID;
|
||||
}
|
||||
|
||||
public struct JointPose
|
||||
{
|
||||
public Quaternion Rotation;
|
||||
public Vector3 Translation;
|
||||
public Vector3 Scale;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using GlitchyEngine.Core;
|
||||
using GlitchyEngine.Math;
|
||||
|
||||
namespace GlitchyEngine.Renderer.Animation
|
||||
{
|
||||
public class Skeleton : RefCounter
|
||||
{
|
||||
public Joint[] Joints ~ delete _;
|
||||
|
||||
public Joint? GetParent(Joint joint)
|
||||
{
|
||||
if(joint.ParentID == uint8.MaxValue)
|
||||
return null;
|
||||
|
||||
return Joints[joint.ParentID];
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
for(var joint in Joints)
|
||||
{
|
||||
delete joint.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SkeletonPose
|
||||
{
|
||||
private Skeleton _skeleton ~ _.ReleaseRef();
|
||||
public JointPose[] LocalPose ~ delete _;
|
||||
public Matrix[] GlobalPose ~ delete _;
|
||||
|
||||
public Matrix[] SkinningMatricies ~ delete _;
|
||||
public Matrix3x3[] InvTransSkinningMatricies ~ delete _;
|
||||
|
||||
public Skeleton Skeleton => _skeleton;
|
||||
|
||||
public this(Skeleton skeleton)
|
||||
{
|
||||
_skeleton = skeleton..AddRef();
|
||||
|
||||
int jointCount = _skeleton.Joints.Count;
|
||||
|
||||
LocalPose = new JointPose[jointCount];
|
||||
GlobalPose = new Matrix[jointCount];
|
||||
|
||||
SkinningMatricies = new Matrix[jointCount];
|
||||
InvTransSkinningMatricies = new Matrix3x3[jointCount];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using GlitchyEngine.Core;
|
||||
using GlitchyEngine.Math;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
public struct BlendStateDescription
|
||||
@@ -70,23 +72,25 @@ namespace GlitchyEngine.Renderer
|
||||
public static readonly Self Default = .(false, false, .(.Default, .Default, .Default, .Default, .Default, .Default, .Default, .Default));
|
||||
}
|
||||
|
||||
public class BlendState : RefCounted
|
||||
public class BlendState : RefCounter
|
||||
{
|
||||
protected GraphicsContext _context ~ _?.ReleaseRef();
|
||||
|
||||
protected BlendStateDescription _desc;
|
||||
|
||||
public BlendStateDescription Description => _desc;
|
||||
|
||||
public this(GraphicsContext context, BlendStateDescription desc)
|
||||
public this(BlendStateDescription desc)
|
||||
{
|
||||
_context = context..AddRef();
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
_desc = desc;
|
||||
|
||||
PlatformCreateBlendState();
|
||||
}
|
||||
|
||||
public extern void Bind(ColorRGBA blendFactor = .White);
|
||||
public ~this()
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
}
|
||||
|
||||
protected extern void PlatformCreateBlendState();
|
||||
}
|
||||
|
||||
@@ -1,47 +1,8 @@
|
||||
using System;
|
||||
using GlitchyEngine.Core;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
/**
|
||||
* Identifies expected resource use during rendering.
|
||||
* The usage directly reflects whether a resource is accessible by the CPU and/or the graphics processing unit (GPU).
|
||||
*/
|
||||
public enum Usage
|
||||
{
|
||||
/**
|
||||
* A resource that requires read and write access by the GPU.
|
||||
* This is likely to be the most common usage choice.
|
||||
*/
|
||||
Default = 0,
|
||||
/**
|
||||
* A resource that can only be read by the GPU. It cannot be written by the GPU, and cannot be accessed at all by the CPU.
|
||||
*/
|
||||
Immutable = 1,
|
||||
/**
|
||||
* A resource that is accessible by both the GPU (read only) and the CPU (write only).
|
||||
* A dynamic resource is a good choice for a resource that will be updated by the CPU at least once per frame.
|
||||
* To update a dynamic resource, use a Map method.
|
||||
*/
|
||||
Dynamic = 2,
|
||||
/**
|
||||
* A resource that supports data transfer (copy) from the GPU to the CPU.
|
||||
*/
|
||||
Staging = 3
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines how the CPU can access a resource.
|
||||
*/
|
||||
public enum CPUAccessFlags
|
||||
{
|
||||
/// The CPU has no access to the resource.
|
||||
None = 0,
|
||||
/// The CPU has read access to the resource.
|
||||
Read = 1,
|
||||
/// The CPU has write access to the resource.
|
||||
Write = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines how to bind a buffer to the pipeline.
|
||||
*/
|
||||
@@ -120,26 +81,19 @@ namespace GlitchyEngine.Renderer
|
||||
}
|
||||
|
||||
/// Represents a buffer containing binary data on the GPU.
|
||||
public class Buffer : RefCounted
|
||||
public class Buffer : RefCounter
|
||||
{
|
||||
internal GraphicsContext _context ~ _?.ReleaseRef();
|
||||
|
||||
protected BufferDescription _description;
|
||||
|
||||
public GraphicsContext Context => _context;
|
||||
|
||||
public BufferDescription Description => _description;
|
||||
|
||||
protected this(GraphicsContext context)
|
||||
{
|
||||
_context = context..AddRef();
|
||||
}
|
||||
|
||||
protected this() { }
|
||||
|
||||
/**
|
||||
* Creates a new instance of a Buffer.
|
||||
* @param description The buffer description.
|
||||
*/
|
||||
public this(GraphicsContext context, BufferDescription description) : this(context)
|
||||
public this(BufferDescription description)
|
||||
{
|
||||
_description = description;
|
||||
}
|
||||
@@ -201,10 +155,9 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
/**
|
||||
* Creates a new instance of a Buffer<T>
|
||||
* @param context The graphics context.
|
||||
* @param description Describes the Buffer. Note: description.Size is ignored, as it will always be sizeof(T).
|
||||
*/
|
||||
public this(GraphicsContext context, BufferDescription description) : base(context)
|
||||
public this(BufferDescription description)
|
||||
{
|
||||
_description = description;
|
||||
_description.Size = (uint32)sizeof(T);
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace GlitchyEngine.Renderer
|
||||
[AllowAppend]
|
||||
public this()
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
// Todo: append allocate as soon as it's fixed
|
||||
let buffers = new List<BufferEntry>();
|
||||
let strToBuf = new Dictionary<String, BufferEntry*>();
|
||||
@@ -24,6 +26,11 @@ namespace GlitchyEngine.Renderer
|
||||
_strToBuf = strToBuf;
|
||||
_idxToBuf = idxToBuf;
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
}
|
||||
|
||||
mixin DeleteBufferEntries(List<BufferEntry> entries)
|
||||
{
|
||||
@@ -54,6 +61,8 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
public BufferEntry* TryGetBufferEntry(String name)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
if(_strToBuf.TryGetValue(name, let buffer))
|
||||
{
|
||||
return buffer;
|
||||
@@ -64,6 +73,8 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
public BufferEntry* TryGetBufferEntry(int index)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
if(_idxToBuf.TryGetValue(index, let buffer))
|
||||
{
|
||||
return buffer;
|
||||
@@ -80,6 +91,8 @@ namespace GlitchyEngine.Renderer
|
||||
*/
|
||||
public bool TryReplaceBuffer(int idx, Buffer buffer)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
if(_idxToBuf.TryGetValue(idx, let bufferEntry))
|
||||
{
|
||||
Log.EngineLogger.Assert(idx == bufferEntry.Index);
|
||||
@@ -105,6 +118,8 @@ namespace GlitchyEngine.Renderer
|
||||
*/
|
||||
public bool TryReplaceBuffer(String name, Buffer buffer)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
if(_strToBuf.TryGetValue(name, let bufferEntry))
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(name == bufferEntry.Name);
|
||||
@@ -129,6 +144,8 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
public void Add(BufferEntry entry)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
BufferEntry copy = (new String(entry.Name), entry.Index, entry.Buffer..AddRef());
|
||||
|
||||
_buffers.Add(copy);
|
||||
@@ -146,6 +163,8 @@ namespace GlitchyEngine.Renderer
|
||||
*/
|
||||
int GetIndexOfBuffer(Buffer buffer)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
for(int i < _buffers.Count)
|
||||
{
|
||||
// Only check for reference equality.
|
||||
@@ -165,6 +184,8 @@ namespace GlitchyEngine.Renderer
|
||||
*/
|
||||
String GetNameOfBuffer(Buffer buffer)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
for(int i < _buffers.Count)
|
||||
{
|
||||
// Only check for reference equality.
|
||||
|
||||
@@ -12,10 +12,12 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
private ShaderVariableType _type;
|
||||
|
||||
private uint32 _columns;
|
||||
private uint32 _rows;
|
||||
internal uint32 _columns;
|
||||
internal uint32 _rows;
|
||||
private uint32 _offset;
|
||||
private uint32 _sizeInBytes;
|
||||
internal uint32 _sizeInBytes;
|
||||
// Number of elements in the array
|
||||
internal uint32 _elements;
|
||||
|
||||
private bool _isUsed;
|
||||
|
||||
@@ -25,13 +27,15 @@ namespace GlitchyEngine.Renderer
|
||||
|
||||
public String Name => _name;
|
||||
|
||||
public bool IsUsed => _isUsed;
|
||||
|
||||
/**
|
||||
* Gets a pointer to the start of the variable in the constant buffers backing data.
|
||||
*/
|
||||
[Inline]
|
||||
internal uint8* firstByte => _constantBuffer.rawData.CArray() + _offset;
|
||||
|
||||
public this(ConstantBuffer constantBuffer, ShaderVariableType type, uint32 columns, uint32 rows, uint32 offset, uint32 sizeInBytes, bool isUsed)
|
||||
public this(ConstantBuffer constantBuffer, ShaderVariableType type, uint32 columns, uint32 rows, uint32 offset, uint32 sizeInBytes, uint32 elements, bool isUsed)
|
||||
{
|
||||
_constantBuffer = constantBuffer..AddRef();
|
||||
_type = type;
|
||||
@@ -39,97 +43,140 @@ namespace GlitchyEngine.Renderer
|
||||
_rows = rows;
|
||||
_offset = offset;
|
||||
_sizeInBytes = sizeInBytes;
|
||||
_elements = elements;
|
||||
_isUsed = isUsed;
|
||||
}
|
||||
|
||||
public void EnsureTypeMatch(int rows, int cols, ShaderVariableType type)
|
||||
{
|
||||
#if GE_ERROR_SHADER_MATRIX_MISMATCH
|
||||
#if GE_SHADER_MATRIX_MISMATCH_IS_ERROR
|
||||
Log.EngineLogger.Assert(rows == _rows || cols == _columns, scope $"The matrix-dimensions do not match: Expected {_rows} rows and {_rows} columns but Received {rows} rows and {cols} columns instead. Variable: \"{_name}\" of buffer: \"{_constantBuffer.Name}\"");
|
||||
#elif GE_WARN_SHADER_MATRIX_MISMATCH
|
||||
#elif GE_SHADER_MATRIX_MISMATCH_IS_WARNING
|
||||
if (rows != _rows || cols != _columns)
|
||||
Log.EngineLogger.Warning($"The matrix-dimensions do not match: Expected {_rows} rows and {_rows} columns but Received {rows} rows and {cols} columns instead. Variable: \"{_name}\" of buffer: \"{_constantBuffer.Name}\"");
|
||||
#endif
|
||||
|
||||
#if GE_ERROR_SHADER_VAR_TYPE_MISMATCH
|
||||
#if GE_SHADER_VAR_TYPE_MISMATCH_IS_ERROR
|
||||
Log.EngineLogger.Assert(type == _type, scope $"The types do not match: Expected \"{_type}\" but Received \"{type}\" instead. Variable: \"{_name}\" of buffer: \"{_constantBuffer.Name}\"");
|
||||
#elif GE_WARN_SHADER_VAR_TYPE_MISMATCH
|
||||
#elif GE_SHADER_VAR_TYPE_MISMATCH_IS_WARNING
|
||||
if (type != _type)
|
||||
Log.EngineLogger.Warning($"The types do not match: Expected \"{_type}\" but Received \"{type}\" instead. Variable: \"{_name}\" of buffer: \"{_constantBuffer.Name}\"");
|
||||
#endif
|
||||
}
|
||||
|
||||
public void SetData(float value)
|
||||
public void EnsureTypeMatch<T>()
|
||||
{
|
||||
EnsureTypeMatch(1, 1, .Float);
|
||||
switch(typeof(T))
|
||||
{
|
||||
case typeof(bool):
|
||||
EnsureTypeMatch(1, 1, .Bool);
|
||||
|
||||
*(float*)firstByte = value;
|
||||
case typeof(float):
|
||||
EnsureTypeMatch(1, 1, .Float);
|
||||
case typeof(Vector2):
|
||||
EnsureTypeMatch(1, 2, .Float);
|
||||
case typeof(Vector3):
|
||||
EnsureTypeMatch(1, 3, .Float);
|
||||
case typeof(Vector4):
|
||||
EnsureTypeMatch(1, 4, .Float);
|
||||
|
||||
case typeof(int32):
|
||||
EnsureTypeMatch(1, 1, .Int);
|
||||
case typeof(Int2):
|
||||
EnsureTypeMatch(1, 2, .Int);
|
||||
case typeof(Int3):
|
||||
EnsureTypeMatch(1, 3, .Int);
|
||||
case typeof(Int4):
|
||||
EnsureTypeMatch(1, 4, .Int);
|
||||
|
||||
case typeof(Matrix4x3):
|
||||
EnsureTypeMatch(4, 3, .Float);
|
||||
case typeof(Matrix3x3):
|
||||
EnsureTypeMatch(3, 3, .Float);
|
||||
case typeof(Matrix):
|
||||
EnsureTypeMatch(4, 4, .Float);
|
||||
|
||||
case typeof(ColorRGB):
|
||||
EnsureTypeMatch(1, 3, .Float);
|
||||
case typeof(ColorRGBA):
|
||||
EnsureTypeMatch(1, 4, .Float);
|
||||
case typeof(Color):
|
||||
EnsureTypeMatch(1, 4, .Float);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetData(Vector2 value)
|
||||
[Inline]
|
||||
private void SetData<T>(T value)
|
||||
{
|
||||
EnsureTypeMatch(1, 2, .Float);
|
||||
|
||||
*(Vector2*)firstByte = value;
|
||||
EnsureTypeMatch<T>();
|
||||
|
||||
*(T*)firstByte = value;
|
||||
}
|
||||
|
||||
[Inline]
|
||||
private T GetData<T>()
|
||||
{
|
||||
return *(T*)firstByte;
|
||||
}
|
||||
|
||||
public void SetData(Vector3 value)
|
||||
{
|
||||
EnsureTypeMatch(1, 3, .Float);
|
||||
|
||||
*(Vector3*)firstByte = value;
|
||||
}
|
||||
public void SetData(bool value) => SetData<bool>(value);
|
||||
|
||||
public void SetData(Vector4 value)
|
||||
{
|
||||
EnsureTypeMatch(1, 4, .Float);
|
||||
|
||||
*(Vector4*)firstByte = value;
|
||||
}
|
||||
public void SetData(float value) => SetData<float>(value);
|
||||
public void SetData(Vector2 value) => SetData<Vector2>(value);
|
||||
public void SetData(Vector3 value) => SetData<Vector3>(value);
|
||||
public void SetData(Vector4 value) => SetData<Vector4>(value);
|
||||
|
||||
public void SetData(Matrix4x3 value)
|
||||
{
|
||||
// I think this is right
|
||||
EnsureTypeMatch(4, 3, .Float);
|
||||
public void SetData(int32 value) => SetData<int32>(value);
|
||||
public void SetData(Int2 value) => SetData<Int2>(value);
|
||||
public void SetData(Int3 value) => SetData<Int3>(value);
|
||||
public void SetData(Int4 value) => SetData<Int4>(value);
|
||||
|
||||
public void SetData(ColorRGB value) => SetData<ColorRGB>(value);
|
||||
public void SetData(ColorRGBA value) => SetData<ColorRGBA>(value);
|
||||
public void SetData(Color value) => SetData<ColorRGBA>((ColorRGBA)value);
|
||||
|
||||
*(Matrix4x3*)firstByte = value;
|
||||
|
||||
public void SetData(Matrix4x3 value) => SetData<Matrix4x3>(value);
|
||||
|
||||
public void SetData(Matrix4x3[] value)
|
||||
{
|
||||
EnsureTypeMatch<Matrix4x3>();
|
||||
|
||||
// TODO: assert length
|
||||
|
||||
Internal.MemCpy(firstByte, value.Ptr, sizeof(Matrix4x3) * Math.Min(value.Count, _elements));
|
||||
}
|
||||
|
||||
public void SetData(Matrix3x3 value)
|
||||
{
|
||||
EnsureTypeMatch(3, 3, .Float);
|
||||
EnsureTypeMatch<Matrix3x3>();
|
||||
|
||||
*(Matrix4x3*)firstByte = Matrix4x3(value);
|
||||
|
||||
// Todo: maybe manual copy
|
||||
}
|
||||
|
||||
public void SetData(Matrix value)
|
||||
{
|
||||
EnsureTypeMatch(4, 4, .Float);
|
||||
|
||||
*(Matrix*)firstByte = value;
|
||||
public void SetData(Matrix3x3[] value)
|
||||
{
|
||||
EnsureTypeMatch<Matrix3x3>();
|
||||
|
||||
// TODO: assert length
|
||||
|
||||
for(int i < Math.Min(value.Count, _elements))
|
||||
{
|
||||
((Matrix4x3*)firstByte)[i] = Matrix4x3(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetData(ColorRGB value)
|
||||
public void SetData(Matrix value) => SetData<Matrix>(value);
|
||||
|
||||
public void SetData(Matrix[] value)
|
||||
{
|
||||
EnsureTypeMatch(1, 3, .Float);
|
||||
EnsureTypeMatch<Matrix>();
|
||||
|
||||
*(ColorRGB*)firstByte = value;
|
||||
}
|
||||
// TODO: assert length
|
||||
|
||||
public void SetData(ColorRGBA value)
|
||||
{
|
||||
EnsureTypeMatch(1, 4, .Float);
|
||||
|
||||
*(ColorRGBA*)firstByte = value;
|
||||
}
|
||||
|
||||
public void SetData(Color value)
|
||||
{
|
||||
EnsureTypeMatch(1, 4, .Float);
|
||||
|
||||
*(ColorRGBA*)firstByte = (ColorRGBA)value;
|
||||
Internal.MemCpy(firstByte, value.Ptr, sizeof(Matrix) * Math.Min(value.Count, _elements));
|
||||
}
|
||||
|
||||
// Todo: add all the other SetData-Methods
|
||||
@@ -140,7 +187,9 @@ namespace GlitchyEngine.Renderer
|
||||
*/
|
||||
internal void SetRawData(void* rawData)
|
||||
{
|
||||
#if DEBUG && !GE_IGNORE_UNUSED_VARIABLE
|
||||
#if GE_SHADER_UNUSED_VARIABLE_IS_ERROR
|
||||
Log.EngineLogger.Assert(_isUsed, scope $"Setting data for unused Variable \"{_name}\" of constant buffer \"{_constantBuffer.Name}\".");
|
||||
#elif GE_SHADER_UNUSED_VARIABLE_IS_WARNING
|
||||
if(!_isUsed)
|
||||
{
|
||||
Log.EngineLogger.Warning($"Setting data for unused Variable \"{_name}\" of constant buffer \"{_constantBuffer.Name}\".");
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
/**
|
||||
* Defines how the CPU can access a resource.
|
||||
*/
|
||||
public enum CPUAccessFlags
|
||||
{
|
||||
/// The CPU has no access to the resource.
|
||||
None = 0,
|
||||
/// The CPU has read access to the resource.
|
||||
Read = 1,
|
||||
/// The CPU has write access to the resource.
|
||||
Write = 2
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,25 @@ using GlitchyEngine.Math;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
public abstract class Camera
|
||||
struct Camera
|
||||
{
|
||||
protected Matrix _projection;
|
||||
|
||||
public Matrix Projection => _projection;
|
||||
|
||||
protected this()
|
||||
{
|
||||
_projection = .Identity;
|
||||
}
|
||||
|
||||
public this(Matrix projection)
|
||||
{
|
||||
_projection = projection;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public abstract class OldCamera
|
||||
{
|
||||
protected Matrix _view;
|
||||
protected Matrix _transform;
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
using GlitchyEngine.Math;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
public struct OldCameraComponent
|
||||
{
|
||||
public enum Projection
|
||||
{
|
||||
/**
|
||||
* A perspective projection with near and far plane.
|
||||
*/
|
||||
Perspective,
|
||||
/**
|
||||
* A perspective projection with near and far plane,
|
||||
* except that the closer a pixel is to the far plane the smaller its depth-value gets.
|
||||
*/
|
||||
PerspectiveReversed,
|
||||
/**
|
||||
* A perspective projection with a near plane and a far plane at infinite distance.
|
||||
*/
|
||||
PerspectiveInfinite,
|
||||
/**
|
||||
* A perspective projection with a near plane and a far plane at infinite distance,
|
||||
* except that the closer a pixel is to the far plane the smaller its depth-value gets.
|
||||
*/
|
||||
PerspectiveReversedInfinite,
|
||||
/**
|
||||
* An orthographic projection.
|
||||
*/
|
||||
Orthographic
|
||||
}
|
||||
|
||||
float _nearPlane;
|
||||
float _farPlane;
|
||||
Projection _projectionType;
|
||||
|
||||
float _fovY;
|
||||
float _aspect;
|
||||
|
||||
public float NearPlane
|
||||
{
|
||||
get => _nearPlane;
|
||||
set mut => _nearPlane = value;
|
||||
}
|
||||
|
||||
public float FarPlane
|
||||
{
|
||||
get => _farPlane;
|
||||
set mut => _farPlane = value;
|
||||
}
|
||||
|
||||
public Projection ProjectionType
|
||||
{
|
||||
get => _projectionType;
|
||||
set mut => _projectionType = value;
|
||||
}
|
||||
|
||||
public float FovY
|
||||
{
|
||||
get => _fovY;
|
||||
set mut => _fovY = value;
|
||||
}
|
||||
|
||||
public float FovX
|
||||
{
|
||||
get => _fovY * _aspect;
|
||||
set mut => _fovY = value / _aspect;
|
||||
}
|
||||
|
||||
public float Aspect
|
||||
{
|
||||
get => _aspect;
|
||||
set mut => _aspect = value;
|
||||
}
|
||||
|
||||
public Matrix Projection => CalculateProjection();
|
||||
|
||||
public Matrix CalculateProjection()
|
||||
{
|
||||
Log.EngineLogger.Assert(_nearPlane < _farPlane, "The near plane must be smaller than the far plane.");
|
||||
|
||||
switch(_projectionType)
|
||||
{
|
||||
case .Perspective:
|
||||
return Matrix.PerspectiveProjection(_fovY, _aspect, _nearPlane, _farPlane);
|
||||
case .PerspectiveReversed:
|
||||
return Matrix.ReversedPerspectiveProjection(_fovY, _aspect, _nearPlane, _farPlane);
|
||||
case .PerspectiveInfinite:
|
||||
return Matrix.InfinitePerspectiveProjection(_fovY, _aspect, _nearPlane);
|
||||
case .PerspectiveReversedInfinite:
|
||||
return Matrix.ReversedInfinitePerspectiveProjection(_fovY, _aspect, _nearPlane);
|
||||
default:
|
||||
Log.EngineLogger.Assert(false, "Unknown projection type.");
|
||||
}
|
||||
|
||||
return .Identity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using GlitchyEngine.Math;
|
||||
using System;
|
||||
|
||||
namespace GlitchyEngine.Renderer
|
||||
{
|
||||
struct ColorHSV
|
||||
{
|
||||
public float H;
|
||||
public float S;
|
||||
public float V;
|
||||
|
||||
public this(float h, float s, float v)
|
||||
{
|
||||
H = h;
|
||||
S = s;
|
||||
V = v;
|
||||
}
|
||||
|
||||
public static explicit operator ColorRGB(ColorHSV hsv)
|
||||
{
|
||||
float c = hsv.V * hsv.S;
|
||||
float x = c * (1.0f - Math.Abs((hsv.H / 60.0f) % 2.0f - 1.0f));
|
||||
|
||||
float m = hsv.V - c;
|
||||
|
||||
ColorRGB rgb_ = ?;
|
||||
|
||||
if (hsv.H < 60.0f)
|
||||
rgb_ = ColorRGB(c, x, 0);
|
||||
else if (hsv.H < 120.0f)
|
||||
rgb_ = ColorRGB(x, c, 0);
|
||||
else if (hsv.H < 180.0f)
|
||||
rgb_ = ColorRGB(0, c, x);
|
||||
else if (hsv.H < 240.0f)
|
||||
rgb_ = ColorRGB(0, x, c);
|
||||
else if (hsv.H < 300.0f)
|
||||
rgb_ = ColorRGB(x, 0, c);
|
||||
else if (hsv.H < 360.0f)
|
||||
rgb_ = ColorRGB(c, 0, x);
|
||||
|
||||
return .(rgb_.Red + m, rgb_.Green + m, rgb_.Blue + m);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user