20 Commits
Author SHA1 Message Date
Simon Lübeß 81ae5f1135 Updated submodules 2023-03-25 13:19:59 +01:00
Simon Lübeß b76d4cd558 Merge remote-tracking branch 'origin/asset_handle' into asset_handle 2023-03-25 13:04:52 +01:00
Simon Lübeß 5893cfb74a Refactor 2023-03-25 13:04:02 +01:00
Simon Lübeß ed952155b6 Refactor 2023-03-25 00:41:34 +01:00
Simon Lübeß d54cdf43ee Simulation mode and pause/resume 2023-03-24 23:50:46 +01:00
Simon Lübeß f16fbc3984 Separated window for game and editor 2023-03-24 22:48:55 +01:00
Simon Lübeß 184480243f Collider rendering 2023-03-24 19:13:32 +01:00
Simon Lübeß fc1875ed96 Added Line batch rendering 2023-03-24 12:54:46 +01:00
Simon Lübeß 5733166b74 Fixed uint render targets always being cleared to zero 2023-03-19 12:49:12 +01:00
Simon Lübeß 2ba4b68e09 Split up sprite and circle renderer component 2023-03-19 12:19:22 +01:00
Simon Lübeß edcec02e94 Fixed AssetHandle serialization
- Serialize MeshComponents
- Fixed handling renamed files
2023-03-19 01:04:32 +01:00
Simon Lübeß c164aeecaa Copy scene when entering play mode 2023-03-18 23:30:57 +01:00
Simon Lübeß 793aa40975 Add up button und split scrolling in content browser
- Add Parent to TreeNode
- Start of Rename handling
2023-03-18 19:59:56 +01:00
Simon Lübeß 50714976e9 Start of async asset loading
- Refactored content manager and asset loaders
- Asset placeholder
- Error asset
2023-03-18 18:59:30 +01:00
Simon Lübeß 7b6662cbc4 Fixed Textures in Materials 2023-03-16 23:32:02 +01:00
Simon Lübeß cfa046d2ea Refactored EditorContentManager 2023-03-16 23:31:19 +01:00
Simon Lübeß b22f73a156 Handles barely work now, mostly no crashes 2023-03-16 17:57:23 +01:00
Simon Lübeß c3fb9bbd5d More asset handles 2023-03-13 20:35:36 +01:00
Simon Lübeß c22d122921 Start of asset handles 2023-03-13 20:35:30 +01:00
Simon Lübeß ad14c9f6ed AssetHandle Code snippets for design reference 2023-03-13 15:56:31 +01:00
77 changed files with 4278 additions and 2304 deletions
+1
View File
@@ -1,5 +1,6 @@
FileVersion = 1 FileVersion = 1
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"}, box2d-beef = {Path = "GlitchyEngine/vendor/box2D"}, "Beef.Linq" = {Path = "GlitchyEngine/vendor/Beef.Linq/src"}} 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"}, box2d-beef = {Path = "GlitchyEngine/vendor/box2D"}, "Beef.Linq" = {Path = "GlitchyEngine/vendor/Beef.Linq/src"}}
Unlocked = ["corlib"]
WorkspaceFolders = {GlitchyEngine = ["GlitchyEngine", "GlitchLog", "GlitchyEngineHelper"], "GlitchyEngine/Dependencies" = ["cgltf-beef", "DirectX", "FreeType", "ImGui", "ImGuiImplDX11", "ImGuiImplWin32", "ImGuizmo", "LodePng", "msdfgen-beef", "bon", "box2d-beef", "Beef.Linq"]} WorkspaceFolders = {GlitchyEngine = ["GlitchyEngine", "GlitchLog", "GlitchyEngineHelper"], "GlitchyEngine/Dependencies" = ["cgltf-beef", "DirectX", "FreeType", "ImGui", "ImGuiImplDX11", "ImGuiImplWin32", "ImGuizmo", "LodePng", "msdfgen-beef", "bon", "box2d-beef", "Beef.Linq"]}
[Workspace] [Workspace]
@@ -0,0 +1,92 @@
///
/// Loading and usage of an asset.
///
// Shows the usage of the Content Manager.
// An asset is loaded with Content.LoadAsset(AssetName)
// Loading an asset returns an AssetHandle.
// Usually you want to hold on to the AssetHandle.
// If you need the actual asset you can use Content.GetAsset<AssetType>(AssetHandle) to retrieve the actual asset.
/*
* Only hold on to AssetHandle.
* Do NOT hold actual Assets! The Asset might get invalidated
* (e.g. reloading after the source-file changed).
*/
AssetHandle _lineEffect;
void Start()
{
/*
* Loading an asset returns a handle to that asset.
*/
_lineEffect = Content.LoadAsset("Shaders\\LineEffect.hlsl");
}
void Update()
{
// Use case 1:
{
// Retrieve the actual asset. Note that this Asset is only guaranteed to persist
// for the current frame.
Effect fx = Content.GetAsset<Effect>(_lineEffect);
// Note: Assets are ref counted, however since they are not inteded to be held GetAsset
// does NOT increment the reference counter so you usually should NOT decrement it.
// The exception is when you manually hold on to an asset (see Example below)
// Use asset
fx.Variables["ViewProjection"].SetData(viewProjection * transform);
fx.Variables["Color"].SetData(color);
fx.ApplyChanges();
fx.Bind();
}
// Use case 2:
{
// Retrieve the actual asset.
Effect fx = _lineEffect.Get<Effect>();
// ...
// Same as case 1
}
}
///
/// Holding on to an actual asset.
///
// Shows the usage of the Content Manager when you hold a reference to the actual Asset instead of the handle.
// This should usually not be done. Eventhough it is technically fine, it has the implication, that if the
// content manager decides to reload the asset the changes will not be reflected in the held reference
// (which they would if you used GetAsset before every usage.)
// Also the content manager can't unload the asset because the reference counter wouldn't be zero.
// Though there currently is no way for the content manager to unload an asset unless it is specifically told to do so.
/*
* Field to hold the asset.
*/
Effect _lineEffect;
void Start()
{
// Load the Asset.
AssetHandle handle = Content.LoadAsset("Shaders\\LineEffect.hlsl");
// Get the actual asset.
_lineEffect = Content.GetAsset<Effect>(handle); // Alternative: _lineEffect = handle.Get<Effect>();
// Increment the reference counter so that the asset doesn't get unloaded.
_lineEffect.AddRef();
}
void Destroy()
{
// Make sure to release the asset once you don't need it anymore. Otherwise it will leak.
_lineEffect.Release();
}
void Update()
{
_lineEffect.Variables["ViewProjection"].SetData(viewProjection * transform);
_lineEffect.Variables["Color"].SetData(color);
_lineEffect.ApplyChanges();
_lineEffect.Bind();
}
@@ -0,0 +1,44 @@
///
/// This shows the usage of a smart/typed Asset Handle.
///
// In the basic usage-patterns the user has two options:
// Either he/she holds a handle and has to query for the actual asset every time
// or he/she hold the actual asset and loses reloading capabilities.
// The idea of the auto asset is to provide a way that can provide both.
AssetHandle<Effect> _lineEffect;
void Start()
{
/*
* Loading an asset returns a handle to that asset.
* The asset handle is implicitly converted to a smart AssetHandle.
*/
_lineEffect = Content.LoadAsset("Shaders\\LineEffect.hlsl");
}
void Update()
{
// Use case 1 (Convenience)
{
_lineEffect.Variables["ViewProjection"].SetData(viewProjection * transform);
_lineEffect.Variables["Color"].SetData(color);
_lineEffect.ApplyChanges();
_lineEffect.Bind();
}
// Use case 2 (Technically more efficient)
{
Effect fx = _lineEffect.Get();
fx.Variables["ViewProjection"].SetData(viewProjection * transform);
fx.Variables["Color"].SetData(color);
fx.ApplyChanges();
fx.Bind();
}
}
//
// Auto Assets use comp time to expose all the fields, Methods and properties of the actual asset.
// They will automatically check if the asset for the handle changed and update accordingly.
// This means it is more convenient to use than both basic patterns and has all benefits of both.
//
+3
View File
@@ -0,0 +1,3 @@
# Design Code Examples
This directory (and its subdirectories) contains non functional code snippets showing the intended usage of certain systems of the engine.
+91 -21
View File
@@ -30,10 +30,10 @@
} }
}, },
MeshComponent = { MeshComponent = {
Mesh = "Models\\sphere.glb" Mesh = "Models/sphere.glb"
}, },
MeshRendererComponent = { MeshRendererComponent = {
Material = "Textures\\TestMaterial.mat" Material = "Textures/TestMaterial.mat"
} }
}, },
{ {
@@ -48,20 +48,20 @@
Z = -4 Z = -4
}, },
Rotation = { Rotation = {
X = 0.614328, X = 0.614328027,
Y = 0.239776, Y = 0.239776045,
Z = 0.031567, Z = 0.0315669999,
W = 0.751074 W = 0.751073837
}, },
Scale = { Scale = {
X = 0.999999, X = 0.999998868,
Y = 1, Y = 1,
Z = 1.000001 Z = 1.00000095
}, },
EditorEulerRotation = { EditorEulerRotation = {
X = 1.308998, X = 1.30899811,
Y = 0.349066, Y = 0.349065989,
Z = 0.349066 Z = 0.349065989
} }
}, },
LightComponent = { LightComponent = {
@@ -69,8 +69,8 @@
Illuminance = 10, Illuminance = 10,
Color = { Color = {
R = 1, R = 1,
G = 0.991772, G = 0.991771996,
B = 0.740862 B = 0.74086225
} }
} }
}, },
@@ -103,10 +103,10 @@
} }
}, },
MeshComponent = { MeshComponent = {
Mesh = "Models\\plane.glb" Mesh = "Models/plane.glb"
}, },
MeshRendererComponent = { MeshRendererComponent = {
Material = "Textures\\TestMaterial.mat" Material = "Textures/TestMaterial.mat"
} }
}, },
{ {
@@ -121,10 +121,10 @@
Z = -5 Z = -5
}, },
Rotation = { Rotation = {
X = 0.21644, X = 0.216440007,
Y = 0, Y = 0,
Z = 0, Z = 0,
W = 0.976296 W = 0.976296008
}, },
Scale = { Scale = {
X = 1, X = 1,
@@ -132,7 +132,7 @@
Z = 1 Z = 1
}, },
EditorEulerRotation = { EditorEulerRotation = {
X = 0.436332, X = 0.436332017,
Y = 0, Y = 0,
Z = 0 Z = 0
} }
@@ -140,15 +140,85 @@
CameraComponent = { CameraComponent = {
Primary = true, Primary = true,
ProjectionType = .InfinitePerspective, ProjectionType = .InfinitePerspective,
PerspectiveFovY = 1.047198, PerspectiveFovY = 1.30899692,
PerspectiveNearPlane = 0.1, PerspectiveNearPlane = 0.100000001,
PerspectiveFarPlane = 10000, PerspectiveFarPlane = 10000,
OrthographicHeight = 10, OrthographicHeight = 10,
OrthographicNearPlane = 0, OrthographicNearPlane = 0,
OrthographicFarPlane = 10, OrthographicFarPlane = 10,
AspectRatio = 2.156692, AspectRatio = 2.19888878,
FixedAspectRatio = false FixedAspectRatio = false
} }
},
{
Id = 420718992779301759,
NameComponent = {
Name = "Entity"
},
TransformComponent = {
Position = {
X = 3,
Y = 0.501076996,
Z = -0.669378757
},
Rotation = {
X = 0.18301262,
Y = -0.683013558,
Z = 0.683012128,
W = 0.183013007
},
Scale = {
X = 1,
Y = 1,
Z = 1
},
EditorEulerRotation = {
X = 1.57079446,
Y = -2.61799312,
Z = 0
}
},
MeshComponent = {
Mesh = "Models/plane.glb"
},
MeshRendererComponent = {
Material = ""
}
},
{
Id = 8080288871271083510,
NameComponent = {
Name = "Rocket"
},
TransformComponent = {
Position = {
X = 2.37353587,
Y = 0.629615188,
Z = -1.34831977
},
Rotation = {
X = 0.270598024,
Y = -0.65328145,
Z = 0.65328151,
W = 0.270598054
},
Scale = {
X = 0.999999881,
Y = 0.999999821,
Z = 0.999999702
},
EditorEulerRotation = {
X = 1.57079637,
Y = -2.3561945,
Z = 8.94069743e-08
}
},
MeshComponent = {
Mesh = "Models/plane.glb"
},
MeshRendererComponent = {
Material = "Textures/RocketMaterial.mat"
}
} }
] ]
} }
+34 -36
View File
@@ -6,15 +6,14 @@
NameComponent = { NameComponent = {
Name = "Quad" Name = "Quad"
}, },
SpriterRendererComponent = { SpriteRendererComponent = {
Color = { Color = {
R = 0, R = 0,
G = 0.551178, G = 0.55117774,
B = 0.328787, B = 0.32878688,
A = 1 A = 1
}, },
IsCircle = false, Sprite = "Textures/TestMat/rustediron2_albedo.png",
Sprite = "\\Textures\\TestMat\\rustediron2_albedo.png",
UvTransform = { UvTransform = {
X = 0, X = 0,
Y = 0, Y = 0,
@@ -69,15 +68,14 @@
NameComponent = { NameComponent = {
Name = "Floor" Name = "Floor"
}, },
SpriterRendererComponent = { SpriteRendererComponent = {
Color = { Color = {
R = 1, R = 1,
G = 0.941886, G = 0.941886365,
B = 0.401485, B = 0.401484847,
A = 1 A = 1
}, },
IsCircle = false, Sprite = "",
Sprite = null,
UvTransform = { UvTransform = {
X = 0, X = 0,
Y = 0, Y = 0,
@@ -158,13 +156,13 @@
CameraComponent = { CameraComponent = {
Primary = true, Primary = true,
ProjectionType = .InfinitePerspective, ProjectionType = .InfinitePerspective,
PerspectiveFovY = 1.308997, PerspectiveFovY = 1.30899692,
PerspectiveNearPlane = 0.1, PerspectiveNearPlane = 0.100000001,
PerspectiveFarPlane = 10000, PerspectiveFarPlane = 10000,
OrthographicHeight = 10, OrthographicHeight = 10,
OrthographicNearPlane = 0, OrthographicNearPlane = 0,
OrthographicFarPlane = 10, OrthographicFarPlane = 10,
AspectRatio = 2.969697, AspectRatio = 3.22169805,
FixedAspectRatio = false FixedAspectRatio = false
} }
}, },
@@ -173,15 +171,15 @@
NameComponent = { NameComponent = {
Name = "Circle" Name = "Circle"
}, },
SpriterRendererComponent = { CircleRendererComponent = {
Color = { Color = {
R = 1, R = 1,
G = 1, G = 0,
B = 1, B = 0,
A = 1 A = 1
}, },
IsCircle = true, InnerRadius = 0.300000012,
Sprite = "\\Textures\\rocket.png", Sprite = "",
UvTransform = { UvTransform = {
X = 0, X = 0,
Y = 0, Y = 0,
@@ -191,8 +189,8 @@
}, },
TransformComponent = { TransformComponent = {
Position = { Position = {
X = -0.121203, X = -0.12120308,
Y = 0.66016, Y = 0.660160363,
Z = 0 Z = 0
}, },
Rotation = { Rotation = {
@@ -235,34 +233,34 @@
}, },
TransformComponent = { TransformComponent = {
Position = { Position = {
X = 3.883276, X = 3.88327599,
Y = 0, Y = 0,
Z = -0.808795 Z = -0.808795214
}, },
Rotation = { Rotation = {
X = 0.497987, X = 0.497987002,
Y = -0.103421, Y = -0.103420995,
Z = 0.15538, Z = 0.155380026,
W = 0.846859 W = 0.846859217
}, },
Scale = { Scale = {
X = 0.999998, X = 0.999997795,
Y = 1, Y = 1,
Z = 1 Z = 1
}, },
EditorEulerRotation = { EditorEulerRotation = {
X = 1.090894, X = 1.0908947,
Y = -0.340794, Y = -0.340793997,
Z = 0.160858 Z = 0.160857916
} }
}, },
LightComponent = { LightComponent = {
LightType = .Directional, LightType = .Directional,
Illuminance = 12.9, Illuminance = 12.8999996,
Color = { Color = {
R = 0.985467, R = 0.985467017,
G = 1, G = 1,
B = 0.569979 B = 0.569978654
} }
} }
}, },
@@ -275,7 +273,7 @@
Position = { Position = {
X = 0, X = 0,
Y = 0, Y = 0,
Z = -1.523338 Z = -1.47202551
}, },
Rotation = { Rotation = {
X = 0, X = 0,
@@ -295,10 +293,10 @@
} }
}, },
MeshComponent = { MeshComponent = {
Mesh = "\\Models\\sphere.glb" Mesh = "Models/sphere.glb"
}, },
MeshRendererComponent = { MeshRendererComponent = {
Material = "\\Textures\\TestMaterial.mat" Material = "Textures/TestMaterial.mat"
} }
} }
] ]
@@ -0,0 +1,61 @@
#define EDITOR
cbuffer Constants : register(b0)
{
float4x4 ViewProjection;
};
struct VS_Input
{
float4 Position : POSITION;
float4 Color : COLOR;
#ifdef EDITOR
uint EntityId : ENTITYID;
#endif
};
typedef struct PS_Input
{
float4 Position : SV_Position;
float4 Color : COLOR;
#ifdef EDITOR
nointerpolation uint EntityId : ENTITYID;
#endif
} VS_Output;
VS_Output VS(VS_Input input)
{
VS_Output output;
output.Position = mul(ViewProjection, input.Position);
output.Color = input.Color;
#ifdef EDITOR
output.EntityId = input.EntityId;
#endif
return output;
}
struct PS_Output
{
float4 Color : SV_TARGET0;
#ifdef EDITOR
uint EntityId : SV_TARGET1;
#endif
};
PS_Output PS(PS_Input input)
{
PS_Output output;
output.Color = input.Color;
#ifdef EDITOR
output.EntityId = input.EntityId;
#endif
return output;
}
#pragma Effect[VS=VS; PS=PS]
@@ -0,0 +1,4 @@
{
AssetLoader = "EffectAssetLoader",
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */
}
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,31 @@
{
Effect = "Shaders/myEffect.hlsl",
Textures = [
"AlbedoTexture": "Textures/rocket.png",
"NormalTexture": "Textures/TestMat/rustediron2_normal.png",
"MetallicTexture": "Textures/TestMat/rustediron2_metallic.png",
"RoughnessTexture": "Textures/TestMat/rustediron2_roughness.png"
],
Variables = [
"AlbedoColor": .ColorRGBA{
Value = {
R = 1,
G = 1,
B = 1,
A = 1
}
},
"NormalScaling": .Float2{
Value = {
X = 1,
Y = 1
}
},
"MetallicFactor": .Float{
Value = 0
},
"RoughnessFactor": .Float{
Value = 1
}
]
}
@@ -0,0 +1,4 @@
{
AssetLoader = "MaterialAssetLoader",
Config = (GlitchyEditor.Assets.ModelAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.ModelAssetLoaderConfig. Add [BonTarget] or force it */
}
@@ -7,16 +7,20 @@
MinFilter = .Anisotropic, MinFilter = .Anisotropic,
MagFilter = .Anisotropic, MagFilter = .Anisotropic,
MipFilter = .Anisotropic, MipFilter = .Anisotropic,
FilterMode = .Default,
ComparisonFunction = .Never, ComparisonFunction = .Never,
AddressModeU = .Wrap, AddressModeU = .Wrap,
AddressModeV = .Border, AddressModeV = .Wrap,
AddressModeW = .Clamp, AddressModeW = .Wrap,
MipMaxLOD = 160, MipLODBias = 0,
MipMinLOD = 0,
MipMaxLOD = 3,
MaxAnisotropy = 16, MaxAnisotropy = 16,
BorderColor = { BorderColor = {
R = 0.756863, R = 1,
G = 0.2, G = 1,
A = 0.956863 B = 1,
A = 1
} }
} }
} }
@@ -1,16 +1,20 @@
{ {
AssetLoader = "EditorTextureAssetLoader", AssetLoader = "EditorTextureAssetLoader",
Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){ Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){
_generateMipMaps = false,
_isSrgb = false,
_samplerStateDescription = { _samplerStateDescription = {
MinFilter = .Linear, MinFilter = .Linear,
MagFilter = .Linear, MagFilter = .Linear,
MipFilter = .Linear, MipFilter = .Linear,
FilterMode = .Default,
ComparisonFunction = .Never, ComparisonFunction = .Never,
AddressModeU = .Clamp, AddressModeU = .Wrap,
AddressModeV = .Clamp, AddressModeV = .Wrap,
AddressModeW = .Clamp, AddressModeW = .Clamp,
MipMinLOD = -340282346638528859811704183484516925440, MipLODBias = 0,
MipMaxLOD = 340282346638528859811704183484516925440, MipMinLOD = -3.40282347e+38,
MipMaxLOD = 3.40282347e+38,
MaxAnisotropy = 1, MaxAnisotropy = 1,
BorderColor = { BorderColor = {
R = 1, R = 1,
@@ -1,16 +1,20 @@
{ {
AssetLoader = "EditorTextureAssetLoader", AssetLoader = "EditorTextureAssetLoader",
Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){ Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){
_generateMipMaps = false,
_isSrgb = false,
_samplerStateDescription = { _samplerStateDescription = {
MinFilter = .Linear, MinFilter = .Linear,
MagFilter = .Linear, MagFilter = .Linear,
MipFilter = .Linear, MipFilter = .Linear,
FilterMode = .Default,
ComparisonFunction = .Never, ComparisonFunction = .Never,
AddressModeU = .Clamp, AddressModeU = .Wrap,
AddressModeV = .Clamp, AddressModeV = .Wrap,
AddressModeW = .Clamp, AddressModeW = .Clamp,
MipMinLOD = -340282346638528859811704183484516925440, MipLODBias = 2.5999999,
MipMaxLOD = 340282346638528859811704183484516925440, MipMinLOD = -Infinity,
MipMaxLOD = Infinity,
MaxAnisotropy = 1, MaxAnisotropy = 1,
BorderColor = { BorderColor = {
R = 1, R = 1,
@@ -1,17 +1,20 @@
{ {
AssetLoader = "EditorTextureAssetLoader", AssetLoader = "EditorTextureAssetLoader",
Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){ Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){
_generateMipMaps = false,
_isSrgb = true, _isSrgb = true,
_samplerStateDescription = { _samplerStateDescription = {
MinFilter = .Linear, MinFilter = .Linear,
MagFilter = .Linear, MagFilter = .Linear,
MipFilter = .Linear, MipFilter = .Linear,
FilterMode = .Default,
ComparisonFunction = .Never, ComparisonFunction = .Never,
AddressModeU = .Clamp, AddressModeU = .Wrap,
AddressModeV = .Clamp, AddressModeV = .Wrap,
AddressModeW = .Clamp, AddressModeW = .Clamp,
MipMinLOD = -340282346638528859811704183484516925440, MipLODBias = 0,
MipMaxLOD = 340282346638528859811704183484516925440, MipMinLOD = -3.40282347e+38,
MipMaxLOD = 3.40282347e+38,
MaxAnisotropy = 1, MaxAnisotropy = 1,
BorderColor = { BorderColor = {
R = 1, R = 1,
@@ -1,7 +1,10 @@
{ {
Effect = "content/Shaders/myEffect.hlsl", Effect = "Shaders\\myEffect.hlsl",
Textures = [ Textures = [
"AlbedoTexture": "Textures/TestMat/rustediron2_albedo.png" "AlbedoTexture": "Textures\\TestMat\\rustediron2_albedo.png",
"NormalTexture": "Textures\\TestMat\\rustediron2_normal.png",
"MetallicTexture": "Textures\\TestMat\\rustediron2_metallic.png",
"RoughnessTexture": "Textures\\TestMat\\rustediron2_roughness.png"
], ],
Variables = [ Variables = [
"AlbedoColor": .ColorRGBA{ "AlbedoColor": .ColorRGBA{
@@ -4,13 +4,14 @@
_isSrgb = true, _isSrgb = true,
_samplerStateDescription = { _samplerStateDescription = {
MinFilter = .Linear, MinFilter = .Linear,
MagFilter = .Linear,
MipFilter = .Linear, MipFilter = .Linear,
ComparisonFunction = .Never, ComparisonFunction = .Never,
AddressModeU = .Clamp, AddressModeU = .Clamp,
AddressModeV = .Clamp, AddressModeV = .Clamp,
AddressModeW = .Clamp, AddressModeW = .Clamp,
MipMinLOD = -340282346638528859811704183484516925440, MipMinLOD = -3.40282347e+38,
MipMaxLOD = 340282346638528859811704183484516925440, MipMaxLOD = 3.40282347e+38,
MaxAnisotropy = 1, MaxAnisotropy = 1,
BorderColor = { BorderColor = {
R = 1, R = 1,
+2 -2
View File
@@ -31,7 +31,7 @@ class AssetFile
private bool _isDirectory; private bool _isDirectory;
private Object _loadedAsset; private Asset _loadedAsset;
public bool IsDirectory => _isDirectory; public bool IsDirectory => _isDirectory;
@@ -42,7 +42,7 @@ class AssetFile
public AssetConfig AssetConfig => _assetConfig; public AssetConfig AssetConfig => _assetConfig;
public Object LoadedAsset => _loadedAsset; public Asset LoadedAsset => _loadedAsset;
[AllowAppend] [AllowAppend]
public this(EditorContentManager contentManager, StringView identifier, StringView path, bool isDirectory) public this(EditorContentManager contentManager, StringView identifier, StringView path, bool isDirectory)
+86 -3
View File
@@ -34,6 +34,24 @@ public class SubAsset
public Texture2D PreviewImage ~ _?.ReleaseRef(); public Texture2D PreviewImage ~ _?.ReleaseRef();
} }
public static class AssetIdentifier
{
public const char8 DirectorySeparatorChar = '/';
public static void Fixup(String assetIdentifier)
{
const String DotSeperator = $".{DirectorySeparatorChar}";
const String SeperatorDot = $"{DirectorySeparatorChar}.";
assetIdentifier.Replace('\\', DirectorySeparatorChar);
assetIdentifier.Replace(DotSeperator, "");
assetIdentifier.Replace(SeperatorDot, "");
if (assetIdentifier.StartsWith(DirectorySeparatorChar))
assetIdentifier.Remove(0, 1);
}
}
class AssetHierarchy class AssetHierarchy
{ {
FileSystemWatcher fsw ~ { FileSystemWatcher fsw ~ {
@@ -108,7 +126,8 @@ class AssetHierarchy
fsw.OnRenamed.Add(new (oldName, newName) => { fsw.OnRenamed.Add(new (oldName, newName) => {
Log.EngineLogger.Trace($"File renamed (From \"{oldName}\" to \"{newName}\")"); Log.EngineLogger.Trace($"File renamed (From \"{oldName}\" to \"{newName}\")");
_fileSystemDirty = true; //_fileSystemDirty = true;
FileRenamed(oldName, newName);
/*String contentFilePath = scope String(); /*String contentFilePath = scope String();
Path.InternalCombine(contentFilePath, ContentDirectory, oldName); Path.InternalCombine(contentFilePath, ContentDirectory, oldName);
@@ -121,7 +140,7 @@ class AssetHierarchy
fsw.StartRaisingEvents(); fsw.StartRaisingEvents();
} }
/// Gets the tree node for the given filePath or null, if the file/directory doesn't exist. /// Gets the tree node for the given filePath or .Err, if the file/directory doesn't exist.
/// @param filePath the path for which to return the tree node. /// @param filePath the path for which to return the tree node.
/// @remarks Do not hold a reference to the TreeNode because it can become invalid when the file hierarchy changes. /// @remarks Do not hold a reference to the TreeNode because it can become invalid when the file hierarchy changes.
public Result<TreeNode<AssetNode>> GetNodeFromPath(StringView filePath) public Result<TreeNode<AssetNode>> GetNodeFromPath(StringView filePath)
@@ -161,6 +180,7 @@ class AssetHierarchy
_assetHierarchy = new TreeNode<AssetNode>(new AssetNode()); _assetHierarchy = new TreeNode<AssetNode>(new AssetNode());
_assetHierarchy->Path = new String(ContentDirectory); _assetHierarchy->Path = new String(ContentDirectory);
_assetHierarchy->Name = new String("Content"); _assetHierarchy->Name = new String("Content");
_assetHierarchy->IsDirectory = true;
Log.EngineLogger.Trace($"Created directory node for: \"{_assetHierarchy->Path}\""); Log.EngineLogger.Trace($"Created directory node for: \"{_assetHierarchy->Path}\"");
@@ -171,6 +191,7 @@ class AssetHierarchy
{ {
String identifier = scope .(node.Path.Length); String identifier = scope .(node.Path.Length);
Path.GetRelativePath(node.Path, _contentDirectory, identifier); Path.GetRelativePath(node.Path, _contentDirectory, identifier);
AssetIdentifier.Fixup(identifier);
node.AssetFile = new AssetFile(_contentManager, identifier, node.Path, node.IsDirectory); node.AssetFile = new AssetFile(_contentManager, identifier, node.Path, node.IsDirectory);
} }
@@ -333,11 +354,73 @@ class AssetHierarchy
return; return;
OnFileContentChanged(node.Value); OnFileContentChanged(node.Value);
}
// TODO: Handle file changes (reload asset, etc...) private void FileRenamed(StringView oldFilePath, StringView newFilePath)
{
var oldFilePath;
// Ignore Config files.
if (oldFilePath.EndsWith(AssetFile.ConfigFileExtension))
return;
String oldFileNameWithContentRoot = scope .();
Path.InternalCombine(oldFileNameWithContentRoot, _contentDirectory, oldFilePath);
String newFileNameWithContentRoot = scope .();
Path.InternalCombine(newFileNameWithContentRoot, _contentDirectory, newFilePath);
// Rename config file
{
String oldConfigFileName = scope $"{oldFileNameWithContentRoot}{AssetFile.ConfigFileExtension}";
String newConfigFileName = scope $"{newFileNameWithContentRoot}{AssetFile.ConfigFileExtension}";
if (File.Exists(oldConfigFileName) && !File.Exists(newConfigFileName))
{
if (File.Move(oldConfigFileName, newConfigFileName) case .Err(let value))
{
Log.EngineLogger.Error($"Failed to move file {oldConfigFileName} to {newConfigFileName}. Code: {value}");
}
}
}
var nodeResult = GetNodeFromPath(oldFileNameWithContentRoot);
TreeNode<AssetNode> node = null;
if (!(nodeResult case .Ok(out node)))
{
// This happens, when we create new files.
Log.EngineLogger.Trace($"Could not find node for file \"{oldFileNameWithContentRoot}\"");
return;
}
_pathToAssetNode.Remove(oldFileNameWithContentRoot);
node->Path.Set(newFileNameWithContentRoot);
_pathToAssetNode.Add(node->Path, node);
node->Name.Clear();
Path.GetFileName(newFileNameWithContentRoot, node->Name);
String oldIdentifier = scope .(node->AssetFile.[Friend]_identifier);
node->AssetFile.[Friend]_path.Set(node->Path);
node->AssetFile.[Friend]_identifier.Set(newFilePath);
AssetIdentifier.Fixup(node->AssetFile.[Friend]_identifier);
node->AssetFile.[Friend]_assetConfigPath..Set(node->Path).Append(AssetFile.ConfigFileExtension);
OnFileRenamed(node.Value, oldIdentifier);
} }
public delegate void FileContentChangedFunc(AssetNode node); public delegate void FileContentChangedFunc(AssetNode node);
public Event<FileContentChangedFunc> OnFileContentChanged ~ _.Dispose(); public Event<FileContentChangedFunc> OnFileContentChanged ~ _.Dispose();
public delegate void FileRenamedFunc(AssetNode node, StringView oldName);
public Event<FileRenamedFunc> OnFileRenamed ~ _.Dispose();
} }
+4 -43
View File
@@ -48,54 +48,15 @@ class EffectAssetLoader : IAssetLoader //, IReloadingAssetLoader
Effect effect = new Effect(file, assetIdentifier, contentManager); Effect effect = new Effect(file, assetIdentifier, contentManager);
return effect; return effect;
/*StreamReader reader = scope .(file);
String text = scope .();
reader.ReadToEnd(text);
MaterialFile materialFile = scope .();
var result = Bon.Deserialize<MaterialFile>(ref materialFile, text);
if (result case .Err)
{
Log.EngineLogger.Error("Failed to load material.");
return null;
// TODO: return error material
} }
Effect fx = new Effect(materialFile.Effect); public Asset GetPlaceholderAsset(Type assetType)
Material material = new Material(fx);
for (let (slotName, textureIdentifier) in materialFile.Textures)
{ {
Texture texture = contentManager.LoadAsset(textureIdentifier) as Texture; return default;
if (texture == null)
{
Log.EngineLogger.Error("Failed to load texture.");
// TODO: LoadAsset should return an error texture.
} }
material.SetTexture(slotName, texture); public Asset GetErrorAsset(Type assetType)
}
fx.ReleaseRef();
/*for (let (slotName, textureIdentifier) in materialFile.Variables)
{ {
if (texture == null) return default;
{
Log.EngineLogger.Error("Failed to load texture.");
// TODO: LoadAsset should return an error texture.
}
material.SetVariable(slotName, );
}*/
return material; //ModelLoader.LoadMesh(file, subAsset.Value, 0);*/
} }
} }
+29 -68
View File
@@ -29,7 +29,7 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
mixin DropAssetTarget<T>() where T : Asset mixin DropAssetTarget<T>() where T : Asset
{ {
Asset asset = null; AssetHandle handle = .Invalid;
if (ImGui.BeginDragDropTarget()) if (ImGui.BeginDragDropTarget())
{ {
@@ -39,13 +39,13 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
{ {
StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize); StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize);
asset = Content.LoadAsset<Asset>(fullpath); handle = Content.LoadAsset(fullpath);
} }
ImGui.EndDragDropTarget(); ImGui.EndDragDropTarget();
} }
asset handle
} }
public this(AssetFile asset) : base(asset) public this(AssetFile asset) : base(asset)
@@ -84,11 +84,10 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
{ {
StringView path = .((char8*)payload.Data, (int)payload.DataSize); StringView path = .((char8*)payload.Data, (int)payload.DataSize);
using (Texture2D newTexture = Content.LoadAsset<Texture2D>(path))//new Texture2D(path, true)) AssetHandle<Texture2D> newTexture = Content.LoadAsset(path);
{
newTexture.SamplerState = SamplerStateManager.AnisotropicWrap; //newTexture.Get().SamplerState = SamplerStateManager.AnisotropicWrap;
material.SetTexture(texture.key, newTexture); material.SetTexture(texture.key, newTexture.Cast<Texture>());
}
} }
ImGui.EndDragDropTarget(); ImGui.EndDragDropTarget();
@@ -98,7 +97,6 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
private void ShowVariables(Material material, Effect effect) private void ShowVariables(Material material, Effect effect)
{ {
for (let (name, arguments) in effect.[Friend]_variableDescriptions) for (let (name, arguments) in effect.[Friend]_variableDescriptions)
{ {
let variable = effect.Variables[name]; let variable = effect.Variables[name];
@@ -341,28 +339,23 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
Log.EngineLogger.Error("Failed to load material."); Log.EngineLogger.Error("Failed to load material.");
Debug.SafeBreak(); Debug.SafeBreak();
return null; return null;
// TODO: return error material
} }
Effect fx = new Effect(materialFile.Effect); Effect fx = Content.GetAsset<Effect>(contentManager.LoadAsset(materialFile.Effect, true), contentManager);
Material material = new Material(fx); Material material = new Material(fx);
for (let (slotName, textureIdentifier) in materialFile.Textures) for (let (slotName, textureIdentifier) in materialFile.Textures)
{ {
using (Texture texture = contentManager.LoadAsset(textureIdentifier) as Texture) AssetHandle<Texture> texture = contentManager.LoadAsset(textureIdentifier);
{
if (texture == null) if (texture.IsInvalid)
{ {
Log.EngineLogger.Error($"Failed to load texture \"{textureIdentifier}\"."); Log.EngineLogger.Error($"Failed to load texture \"{textureIdentifier}\".");
// TODO: LoadAsset should return an error texture.
} }
material.SetTexture(slotName, texture); material.SetTexture(slotName, texture);
} }
}
fx.ReleaseRef();
for (let (slotName, variableValue) in materialFile.Variables) for (let (slotName, variableValue) in materialFile.Variables)
{ {
@@ -382,7 +375,7 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
material.SetVariable(slotName, value); material.SetVariable(slotName, value);
case .None: case .None:
default: default:
Log.EngineLogger.Error("Errorre"); Log.EngineLogger.Error($"Unknown variable type of variable {slotName}: {variableValue}");
} }
@@ -407,12 +400,11 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
materialFile.Textures = new .(); materialFile.Textures = new .();
materialFile.Variables = new .(); materialFile.Variables = new .();
//material.SetTexture(); for (let (slotName, texture) in material.[Friend]_textures)
for (let (slotName, textureViewBinding) in material.[Friend]_textures)
{ {
//materialFile.Textures.Add(slotName, textureViewBinding.) Texture textureAsset = texture.Get();
materialFile.Textures.Add(new String(slotName), new String(textureAsset?.Identifier ?? ""));
} }
Effect effect = material.Effect; Effect effect = material.Effect;
@@ -423,7 +415,6 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
for (let (name, arguments) in effect.[Friend]_variableDescriptions) for (let (name, arguments) in effect.[Friend]_variableDescriptions)
{ {
VariableValue variableValue = .None; VariableValue variableValue = .None;
//Object variantValue = null;
let variable = effect.Variables[name]; let variable = effect.Variables[name];
@@ -449,7 +440,6 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
value = ColorRGBA.LinearToSRGB((ColorRGBA)value); value = ColorRGBA.LinearToSRGB((ColorRGBA)value);
variableValue = .ColorRGBA(value); variableValue = .ColorRGBA(value);
//variantValue = new box value;
} }
} }
else if (variable.Type == .Float && variable.Rows == 1) else if (variable.Type == .Float && variable.Rows == 1)
@@ -469,51 +459,9 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
material.GetVariable<Vector4>(variable.Name, let value); material.GetVariable<Vector4>(variable.Name, let value);
variableValue = .Float4(value); variableValue = .Float4(value);
} }
/*bool hasMin = TryGetValue(arguments, "Min", var min);
bool hasMax = TryGetValue(arguments, "Max", var max);
for (int r < variable.Rows)
{
switch (variable.Columns)
{
case 1:
material.GetVariable<float>(variable.Name, var value);
float[1] minV = hasMin ? min.Get<float[1]>() : .(float.MinValue);
float[1] maxV = hasMax ? max.Get<float[1]>() : .(float.MaxValue);
if (ImGui.EditVector<1>(displayName, ref *(float[1]*)&value, .(), 0.1f, 100.0f, minV, maxV))
material.SetVariable(variable.Name, value);
case 2:
material.GetVariable<Vector2>(variable.Name, var value);
Vector2 minV = hasMin ? min.Get<Vector2>() : .(float.MinValue);
Vector2 maxV = hasMax ? max.Get<Vector2>() : .(float.MaxValue);
if (ImGui.EditVector2(displayName, ref value, .Zero, 0.1f, 100.0f, minV, maxV))
material.SetVariable(variable.Name, value);
case 3:
material.GetVariable<Vector3>(variable.Name, var value);
Vector3 minV = hasMin ? min.Get<Vector3>() : .(float.MinValue);
Vector3 maxV = hasMax ? max.Get<Vector3>() : .(float.MaxValue);
if (ImGui.EditVector3(displayName, ref value, .Zero, 0.1f, 100.0f, minV, maxV))
material.SetVariable(variable.Name, value);
case 4:
material.GetVariable<Vector4>(variable.Name, var value);
Vector4 minV = hasMin ? min.Get<Vector4>() : .(float.MinValue);
Vector4 maxV = hasMax ? max.Get<Vector4>() : .(float.MaxValue);
if (ImGui.EditVector4(displayName, ref value, .Zero, 0.1f, 100.0f, minV, maxV))
material.SetVariable(variable.Name, value);
}
}*/
} }
materialFile.Variables.Add(name, variableValue); materialFile.Variables.Add(new String(name), variableValue);
} }
String text = scope .(); String text = scope .();
@@ -527,4 +475,17 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
return .Ok; return .Ok;
} }
Material _placeholder;
Material _error;
public Asset GetPlaceholderAsset(Type assetType)
{
return default;
}
public Asset GetErrorAsset(Type assetType)
{
return default;
}
} }
+12 -2
View File
@@ -44,8 +44,18 @@ class ModelAssetLoader : IAssetLoader //, IReloadingAssetLoader
public Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager) public Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager)
{ {
Log.EngineLogger.Assert(subAsset != null); //Log.EngineLogger.Assert(subAsset != null);
return ModelLoader.LoadMesh(file, subAsset.Value, 0); return ModelLoader.LoadMesh(file, subAsset ?? assetIdentifier, 0);
}
public Asset GetPlaceholderAsset(Type assetType)
{
return default;
}
public Asset GetErrorAsset(Type assetType)
{
return default;
} }
} }
+87 -68
View File
@@ -164,7 +164,7 @@ class EditorTextureAssetLoaderConfig : AssetLoaderConfig
} }
} }
class EditorTextureAssetLoader : IAssetLoader, IReloadingAssetLoader class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader
{ {
private static readonly List<StringView> _fileExtensions = new .(){".png", ".dds"} ~ delete _; // ".jpg", ".bmp" private static readonly List<StringView> _fileExtensions = new .(){".png", ".dds"} ~ delete _; // ".jpg", ".bmp"
@@ -243,65 +243,19 @@ class EditorTextureAssetLoader : IAssetLoader, IReloadingAssetLoader
case .PNG: case .PNG:
texture = LoadPng(data, config); texture = LoadPng(data, config);
case .Unknown: case .Unknown:
Runtime.FatalError("Unknown image format."); Log.EngineLogger.Error("Unknown texture format.");
texture = null;
} }
Log.EngineLogger.AssertDebug(texture != null); if (texture != null)
{
SetSampler(texture, config); SetSampler(texture, config);
texture.[Friend]Complete = true;
}
return texture; return texture;
} }
public void ReloadAsset(AssetFile assetFile, Stream data)
{
Texture reloadingTexture = assetFile.LoadedAsset as Texture;
if (reloadingTexture == null)
{
Log.EngineLogger.Error($"{nameof(Self)}: Requested reload of \"{assetFile.FilePath}\" but it's not a Texture!");
return;
}
EditorTextureAssetLoaderConfig config = assetFile.AssetConfig.Config as EditorTextureAssetLoaderConfig;
if (config == null)
{
Log.EngineLogger.Error($"{nameof(Self)}: Config of asset \"{assetFile.FilePath}\" doesn't have the correct type!");
return;
}
switch(GetTextureType(data))
{
case .DDS:
ReloadDds(reloadingTexture as Texture2D, data, config);
case .PNG:
ReloadPng(reloadingTexture as Texture2D, data, config);
case .Unknown:
Runtime.FatalError("Unknown image format.");
}
SetSampler(reloadingTexture, config);
}
private static void SetSampler(Texture texture, EditorTextureAssetLoaderConfig config)
{
using (SamplerState samplerState = SamplerStateManager.GetSampler(config.SamplerStateDescription))
{
texture.SamplerState = samplerState;
}
}
private static void ReloadPng(Texture2D reloadingTexture, Stream data, EditorTextureAssetLoaderConfig config)
{
Debug.Profiler.ProfileResourceFunction!();
using (Texture2D newTexture = LoadPng(data, config))
{
reloadingTexture.[Friend]SneakySwappyTexture(newTexture);
}
}
private static Texture2D LoadPng(Stream data, EditorTextureAssetLoaderConfig config) private static Texture2D LoadPng(Stream data, EditorTextureAssetLoaderConfig config)
{ {
Debug.Profiler.ProfileResourceFunction!(); Debug.Profiler.ProfileResourceFunction!();
@@ -313,40 +267,105 @@ class EditorTextureAssetLoader : IAssetLoader, IReloadingAssetLoader
if (result case .Err(let err)) if (result case .Err(let err))
{ {
Log.EngineLogger.Error($"Failed to read data from stream. Texture: Error: {err}"); Log.EngineLogger.Error($"Failed to read data from stream. Texture: Error: {err}");
return null;
}
uint8* rawData = null;
defer
{
if (rawData != null)
LodePng.LodePng.Free(rawData);
} }
uint8* rawData = ?;
uint32 width = 0, height = 0; uint32 width = 0, height = 0;
{
Debug.Profiler.ProfileResourceScope!("LodePng.LodePng.Decode32");
uint32 errorCode = LodePng.LodePng.Decode32(&rawData, &width, &height, pngData.Ptr, (.)pngData.Count); uint32 errorCode = LodePng.LodePng.Decode32(&rawData, &width, &height, pngData.Ptr, (.)pngData.Count);
if (errorCode != 0)
{
Log.EngineLogger.Error($"Failed to decode PNG file {errorCode}.");
return null;
}
}
Log.EngineLogger.Assert(errorCode == 0, "Failed to load png File");
// TODO: load as SRGB because PNGs are usually not stored as linear
//Texture2DDesc desc = .(width, height, srgb? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm, 1, 1, .Immutable);
Texture2DDesc desc = .(width, height, config.IsSRGB ? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm, 1, 1, .Immutable); Texture2DDesc desc = .(width, height, config.IsSRGB ? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm, 1, 1, .Immutable);
Texture2D texture = new Texture2D(desc); Texture2D texture = new Texture2D(desc);
texture.SetData<Color>((.)rawData); texture.SetData<Color>((.)rawData);
// TODO: Generate mip maps // TODO: Generate mip maps
LodePng.LodePng.Free(rawData);
return texture; return texture;
} }
private static void ReloadDds(Texture2D reloadingTexture, Stream data, EditorTextureAssetLoaderConfig config)
{
using (Texture2D newTexture = new [Friend]Texture2D(data))
{
reloadingTexture.[Friend]SneakySwappyTexture(newTexture);
}
}
private static Texture LoadDds(Stream data, EditorTextureAssetLoaderConfig config) private static Texture LoadDds(Stream data, EditorTextureAssetLoaderConfig config)
{ {
// TODO: Move the loading of Dds files here.
Texture2D texture = new [Friend]Texture2D(data); Texture2D texture = new [Friend]Texture2D(data);
return texture; return texture;
} }
private static void SetSampler(Texture texture, EditorTextureAssetLoaderConfig config)
{
using (SamplerState samplerState = SamplerStateManager.GetSampler(config.SamplerStateDescription))
{
texture.SamplerState = samplerState;
}
}
private static Texture2D _placeholder2D;
private static Texture2D _error2D;
public Asset GetPlaceholderAsset(Type assetType)
{
switch (assetType)
{
case typeof(Texture2D):
fallthrough;
default:
if (_placeholder2D == null)
{
Texture2DDesc desc = .(1, 1, .R8G8B8A8_UNorm, 1, 1, .Immutable, .None);
_placeholder2D = new Texture2D(desc);
_placeholder2D.SamplerState = SamplerStateManager.PointWrap;
Color color = Color.Cyan;
_placeholder2D.SetData<Color>(&color);
Content.ManageAsset(_placeholder2D);
_placeholder2D.ReleaseRef();
_placeholder2D.[Friend]Complete = false;
}
return _placeholder2D;
}
}
public Asset GetErrorAsset(Type assetType)
{
switch (assetType)
{
case typeof(Texture2D):
fallthrough;
default:
if (_error2D == null)
{
Texture2DDesc desc = .(2, 2, .R8G8B8A8_UNorm, 1, 1, .Immutable, .None);
_error2D = new Texture2D(desc);
_error2D.SamplerState = SamplerStateManager.PointWrap;
Color[4] color = .(Color.HotPink, Color.Black, Color.Black, Color.HotPink);
_error2D.SetData<Color>(&color);
Content.ManageAsset(_error2D);
_error2D.ReleaseRef();
_placeholder2D.[Friend]Complete = true;
}
return _error2D;
}
}
} }
@@ -60,7 +60,8 @@ namespace GlitchyEditor.EditWindows
ShowComponentEditor<TransformComponent>("Transform", entity, => ShowTransformComponentEditor); ShowComponentEditor<TransformComponent>("Transform", entity, => ShowTransformComponentEditor);
ShowComponentEditor<CameraComponent>("Camera", entity, => ShowCameraComponentEditor, => ShowComponentContextMenu<CameraComponent>); ShowComponentEditor<CameraComponent>("Camera", entity, => ShowCameraComponentEditor, => ShowComponentContextMenu<CameraComponent>);
ShowComponentEditor<SpriterRendererComponent>("Sprite Renderer", entity, => ShowSpriteRendererComponentEditor, => ShowComponentContextMenu<SpriterRendererComponent>); ShowComponentEditor<SpriteRendererComponent>("Sprite Renderer", entity, => ShowSpriteRendererComponentEditor, => ShowComponentContextMenu<SpriteRendererComponent>);
ShowComponentEditor<CircleRendererComponent>("Circle Renderer", entity, => ShowCircleRendererComponentEditor, => ShowComponentContextMenu<CircleRendererComponent>);
ShowComponentEditor<MeshRendererComponent>("Mesh Renderer", entity, => ShowMeshRendererComponentEditor, => ShowComponentContextMenu<MeshRendererComponent>); ShowComponentEditor<MeshRendererComponent>("Mesh Renderer", entity, => ShowMeshRendererComponentEditor, => ShowComponentContextMenu<MeshRendererComponent>);
ShowComponentEditor<LightComponent>("Light", entity, => ShowLightComponentEditor, => ShowComponentContextMenu<LightComponent>); ShowComponentEditor<LightComponent>("Light", entity, => ShowLightComponentEditor, => ShowComponentContextMenu<LightComponent>);
ShowComponentEditor<MeshComponent>("Mesh", entity, => ShowMeshComponentEditor, => ShowComponentContextMenu<MeshComponent>); ShowComponentEditor<MeshComponent>("Mesh", entity, => ShowMeshComponentEditor, => ShowComponentContextMenu<MeshComponent>);
@@ -117,18 +118,18 @@ namespace GlitchyEditor.EditWindows
private static void ShowNameComponentEditor(Entity entity) private static void ShowNameComponentEditor(Entity entity)
{ {
if (!entity.HasComponent<DebugNameComponent>()) if (!entity.HasComponent<NameComponent>())
return; return;
char8[256] nameBuffer = default; char8[256] nameBuffer = default;
DebugNameComponent* component = entity.GetComponent<DebugNameComponent>(); NameComponent* component = entity.GetComponent<NameComponent>();
String name = null; StringView name = null;
if(component != null) if(component != null)
{ {
name = component.DebugName; name = component.Name;
} }
else else
{ {
@@ -142,11 +143,10 @@ namespace GlitchyEditor.EditWindows
{ {
if(component == null) if(component == null)
{ {
component = entity.AddComponent<DebugNameComponent>(); component = entity.AddComponent<NameComponent>();
} }
component.DebugName.Clear(); component.Name = StringView(&nameBuffer);
component.DebugName.Append(&nameBuffer);
} }
} }
@@ -250,7 +250,7 @@ namespace GlitchyEditor.EditWindows
} }
} }
private static void ShowSpriteRendererComponentEditor(Entity entity, SpriterRendererComponent* spriteRendererComponent) private static void ShowSpriteRendererComponentEditor(Entity entity, SpriteRendererComponent* spriteRendererComponent)
{ {
ColorRGBA spriteColor = ColorRGBA.LinearToSRGB(spriteRendererComponent.Color); ColorRGBA spriteColor = ColorRGBA.LinearToSRGB(spriteRendererComponent.Color);
if (ImGui.ColorEdit4("Color", ref spriteColor)) if (ImGui.ColorEdit4("Color", ref spriteColor))
@@ -268,10 +268,7 @@ namespace GlitchyEditor.EditWindows
StringView path = .((char8*)payload.Data, (int)payload.DataSize); StringView path = .((char8*)payload.Data, (int)payload.DataSize);
using (Texture2D texture = Content.LoadAsset<Texture2D>(path)) spriteRendererComponent.Sprite = Content.LoadAsset(path);
{
spriteRendererComponent.Sprite = texture;
}
} }
ImGui.EndDragDropTarget(); ImGui.EndDragDropTarget();
@@ -279,8 +276,36 @@ namespace GlitchyEditor.EditWindows
ImGui.EditVector<4>("UV Transform", ref *(float[4]*)&spriteRendererComponent.UvTransform); ImGui.EditVector<4>("UV Transform", ref *(float[4]*)&spriteRendererComponent.UvTransform);
}
ImGui.Checkbox("Is Circle", &spriteRendererComponent.IsCircle); private static void ShowCircleRendererComponentEditor(Entity entity, CircleRendererComponent* circleRendererComponent)
{
ColorRGBA spriteColor = ColorRGBA.LinearToSRGB(circleRendererComponent.Color);
if (ImGui.ColorEdit4("Color", ref spriteColor))
circleRendererComponent.Color = ColorRGBA.SRgbToLinear(spriteColor);
ImGui.Button("Texture");
if (ImGui.BeginDragDropTarget())
{
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM");
if (payload != null)
{
Log.EngineLogger.Warning("");
StringView path = .((char8*)payload.Data, (int)payload.DataSize);
circleRendererComponent.Sprite = Content.LoadAsset(path);
}
ImGui.EndDragDropTarget();
}
ImGui.EditVector<4>("UV Transform", ref *(float[4]*)&circleRendererComponent.UvTransform);
ImGui.DragFloat("Inner Radius", &circleRendererComponent.InnerRadius, 0.1f, 0.0f, 1.0f);
} }
private static void ShowMeshRendererComponentEditor(Entity entity, MeshRendererComponent* meshRendererComponent) private static void ShowMeshRendererComponentEditor(Entity entity, MeshRendererComponent* meshRendererComponent)
@@ -301,10 +326,7 @@ namespace GlitchyEditor.EditWindows
{ {
StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize); StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize);
using (Material loadedMaterial = Content.LoadAsset<Material>(fullpath)) meshRendererComponent.Material = Content.LoadAsset(fullpath);
{
meshRendererComponent.Material = loadedMaterial;
}
} }
ImGui.EndDragDropTarget(); ImGui.EndDragDropTarget();
@@ -488,10 +510,7 @@ namespace GlitchyEditor.EditWindows
StringView filePath = fullpath.Substring(0, idx); StringView filePath = fullpath.Substring(0, idx);
StringView meshName = fullpath.Substring(idx + 1);*/ StringView meshName = fullpath.Substring(idx + 1);*/
using (GeometryBinding geometry = Content.LoadAsset<GeometryBinding>(fullpath)) meshComponent.Mesh = Content.LoadAsset(fullpath);
{
meshComponent.Mesh = geometry;
}
// TODO: support multiple primitives (treat every primitive as a single mesh? or: mesh can have multiple primitives) // TODO: support multiple primitives (treat every primitive as a single mesh? or: mesh can have multiple primitives)
/*using (GeometryBinding binding = ModelLoader.LoadMesh(filePath, meshName, 0)) /*using (GeometryBinding binding = ModelLoader.LoadMesh(filePath, meshName, 0))
@@ -557,7 +576,8 @@ namespace GlitchyEditor.EditWindows
ImGui.Separator(); ImGui.Separator();
ShowComponentButton<CameraComponent>("Camera"); ShowComponentButton<CameraComponent>("Camera");
ShowComponentButton<SpriterRendererComponent>("Sprite Renderer"); ShowComponentButton<SpriteRendererComponent>("Sprite Renderer");
ShowComponentButton<CircleRendererComponent>("Circle Renderer");
ShowComponentButton<LightComponent>("Light"); ShowComponentButton<LightComponent>("Light");
ShowComponentButton<Rigidbody2DComponent>("Rigidbody 2D"); ShowComponentButton<Rigidbody2DComponent>("Rigidbody 2D");
ShowComponentButton<BoxCollider2DComponent>("Box collider 2D"); ShowComponentButton<BoxCollider2DComponent>("Box collider 2D");
@@ -14,8 +14,7 @@ namespace GlitchyEditor.EditWindows
class ContentBrowserWindow : EditorWindow class ContentBrowserWindow : EditorWindow
{ {
// TODO: Get from project public const String s_WindowTitle = "Content Browser";
//const String ContentDirectory = "./content";
private append String _currentDirectory = .(); private append String _currentDirectory = .();
@@ -43,7 +42,7 @@ namespace GlitchyEditor.EditWindows
_currentDirectory.Set(_manager.ContentDirectory); _currentDirectory.Set(_manager.ContentDirectory);
} }
if(!ImGui.Begin("Content Browser", &_open, .None)) if(!ImGui.Begin(s_WindowTitle, &_open, .None))
{ {
ImGui.End(); ImGui.End();
return; return;
@@ -58,12 +57,20 @@ namespace GlitchyEditor.EditWindows
ImGui.Columns(2); ImGui.Columns(2);
ImGui.BeginChild("Sidebar");
DrawDirectorySideBar(); DrawDirectorySideBar();
ImGui.EndChild();
ImGui.NextColumn(); ImGui.NextColumn();
ImGui.BeginChild("Files");
DrawCurrentDirectory(); DrawCurrentDirectory();
ImGui.EndChild();
ImGui.Columns(1); ImGui.Columns(1);
ImGui.End(); ImGui.End();
@@ -149,6 +156,24 @@ namespace GlitchyEditor.EditWindows
return; return;
} }
if (currentDirectoryNode->Parent != null)
{
ImGui.PushID("Back");
DrawBackButton(currentDirectoryNode->Parent);
// X-Coordinate of the right side of the current entry.
float currentButtonRight = ImGui.GetItemRectMax().x;
// Expected right-Coordinate if next entry was on the same line.
float expectedButtonRight = currentButtonRight + style.ItemSpacing.x + DirectoryItemSize.X;
// If the next button won't fit on the same line we start a new line.
if (expectedButtonRight < window_visible_x2)
ImGui.SameLine();
ImGui.PopID();
}
for (var entry in currentDirectoryNode->Children) for (var entry in currentDirectoryNode->Children)
{ {
ImGui.PushID(entry->Name); ImGui.PushID(entry->Name);
@@ -168,6 +193,45 @@ namespace GlitchyEditor.EditWindows
} }
} }
/// Renders the button for the given directory item.
private void DrawBackButton(TreeNode<AssetNode> entry)
{
ImGui.BeginChild("item", (.)DirectoryItemSize);
if (entry->Path == _selectedFile)
{
var color = ImGui.GetStyleColorVec4(.ButtonHovered);
ImGui.PushStyleColor(.Button, *color);
}
else
{
ImGui.PushStyleColor(.Button, ImGui.Vec4(0, 0, 0, 0));
}
SubTexture2D image = s_FolderTexture;
ImGui.ImageButton(image, (.)(DirectoryItemSize - padding));
ImGui.PopStyleColor();
if (ImGui.IsItemHovered() && ImGui.IsMouseClicked(.Left))
{
if (_selectedFile != entry->Path)
{
_selectedFile.Set(entry->Path);
}
}
if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(.Left))
{
EntryDoubleClicked(entry);
}
ImGui.TextUnformatted("..");
ImGui.EndChild();
}
/// Renders the button for the given directory item. /// Renders the button for the given directory item.
private void DrawDirectoryItem(TreeNode<AssetNode> entry) private void DrawDirectoryItem(TreeNode<AssetNode> entry)
{ {
@@ -9,7 +9,7 @@ using GlitchyEngine.Events;
namespace GlitchyEditor.EditWindows namespace GlitchyEditor.EditWindows
{ {
class SceneViewportWindow : EditorWindow class EditorViewportWindow : EditorWindow
{ {
public const String s_WindowTitle = "Scene"; public const String s_WindowTitle = "Scene";
@@ -27,6 +27,8 @@ namespace GlitchyEditor.EditWindows
private float _angleSnap = 45.0f; private float _angleSnap = 45.0f;
private bool _doSnap = false; private bool _doSnap = false;
private bool _visible;
public uint32 SelectedEntityId {get; private set; } public uint32 SelectedEntityId {get; private set; }
public bool SelectionChanged { get; private set; } public bool SelectionChanged { get; private set; }
@@ -52,6 +54,8 @@ namespace GlitchyEditor.EditWindows
/// Gets or sets whether the editor functionality (gizmo, picking etc.) is enabled. /// Gets or sets whether the editor functionality (gizmo, picking etc.) is enabled.
public bool EditorMode { get; set; } = true public bool EditorMode { get; set; } = true
public bool Visible => _visible;
public this(Editor editor) public this(Editor editor)
{ {
_editor = editor; _editor = editor;
@@ -65,9 +69,13 @@ namespace GlitchyEditor.EditWindows
if(!ImGui.Begin(s_WindowTitle, &_open, .NoScrollbar | .MenuBar)) if(!ImGui.Begin(s_WindowTitle, &_open, .NoScrollbar | .MenuBar))
{ {
ImGui.End(); ImGui.End();
_visible = false;
return; return;
} }
_visible = true;
let viewportSize = ImGui.GetContentRegionAvail(); let viewportSize = ImGui.GetContentRegionAvail();
if(ImGui.IsWindowHovered() && Input.IsMouseButtonPressing(.RightButton)) if(ImGui.IsWindowHovered() && Input.IsMouseButtonPressing(.RightButton))
@@ -78,8 +86,6 @@ namespace GlitchyEditor.EditWindows
_hasFocus = ImGui.IsWindowFocused(); _hasFocus = ImGui.IsWindowFocused();
if (EditorMode)
{
ShowMenuBar(); ShowMenuBar();
if (_editor.CurrentCamera.[Friend]BindMouse && _hasFocus) if (_editor.CurrentCamera.[Friend]BindMouse && _hasFocus)
@@ -102,7 +108,6 @@ namespace GlitchyEditor.EditWindows
if (Input.IsKeyPressing(.L)) if (Input.IsKeyPressing(.L))
_gizmoMode = .LOCAL; _gizmoMode = .LOCAL;
} }
}
if(_renderTarget != null) if(_renderTarget != null)
{ {
@@ -111,14 +116,11 @@ namespace GlitchyEditor.EditWindows
//ImGui.Image(_editor.CurrentScene.[Friend]_compositeTarget.GetViewBinding(0), viewportSize); //ImGui.Image(_editor.CurrentScene.[Friend]_compositeTarget.GetViewBinding(0), viewportSize);
} }
if (EditorMode)
{
HandleDropTarget(); HandleDropTarget();
bool gizmoUsed = DrawImGuizmo(viewportSize); bool gizmoUsed = DrawImGuizmo(viewportSize);
MousePicking(viewportSize, gizmoUsed); MousePicking(viewportSize, gizmoUsed);
}
ImGui.End(); ImGui.End();
@@ -157,24 +159,26 @@ namespace GlitchyEditor.EditWindows
let regionMin = winPos + (Vector2)ImGui.GetWindowContentRegionMin(); let regionMin = winPos + (Vector2)ImGui.GetWindowContentRegionMin();
let regionMax = winPos + (Vector2)ImGui.GetWindowContentRegionMax(); let regionMax = winPos + (Vector2)ImGui.GetWindowContentRegionMax();
ImGui.DrawRect((.)regionMin, (.)regionMax, .(0, 255, 0));
Vector2 newMousePos = mousePos; Vector2 newMousePos = mousePos;
if (mousePos.X < regionMin.X) if (mousePos.X < regionMin.X + 1)
{ {
newMousePos.X = regionMax.X - 1; newMousePos.X = regionMax.X - 2;
} }
else if (mousePos.X > regionMax.X - 1) else if (mousePos.X > regionMax.X - 1)
{ {
newMousePos.X = regionMin.X + 1; newMousePos.X = regionMin.X + 2;
} }
if (mousePos.Y < regionMin.Y) if (mousePos.Y < regionMin.Y + 1)
{ {
newMousePos.Y = regionMax.Y - 1; newMousePos.Y = regionMax.Y - 100;
} }
else if (mousePos.Y > regionMax.Y - 1) else if (mousePos.Y > regionMax.Y - 1)
{ {
newMousePos.Y = regionMin.Y + 1; newMousePos.Y = regionMin.Y + 10;
} }
if (newMousePos != mousePos) if (newMousePos != mousePos)
@@ -193,8 +197,8 @@ namespace GlitchyEditor.EditWindows
{ {
Vector2 relativeMouse = (Vector2)ImGui.GetMousePos() - (Vector2)ImGui.GetItemRectMin(); Vector2 relativeMouse = (Vector2)ImGui.GetMousePos() - (Vector2)ImGui.GetItemRectMin();
int rtWidth = _editor.CurrentScene.[Friend]_compositeTarget.Width; int rtWidth = _editor.EditorSceneRenderer.CompositeTarget.Width;
int rtHeight = _editor.CurrentScene.[Friend]_compositeTarget.Height; int rtHeight = _editor.EditorSceneRenderer.CompositeTarget.Height;
if (Input.IsMouseButtonPressing(.LeftButton) && if (Input.IsMouseButtonPressing(.LeftButton) &&
ImGui.IsWindowHovered() && !gizmoUsed && !_editor.CurrentCamera.InUse && ImGui.IsWindowHovered() && !gizmoUsed && !_editor.CurrentCamera.InUse &&
@@ -204,7 +208,7 @@ namespace GlitchyEditor.EditWindows
{ {
uint32 id = uint32.MaxValue; uint32 id = uint32.MaxValue;
_editor.CurrentScene.[Friend]_compositeTarget.GetData<uint32>(&id, 1, (.)relativeMouse.X, (.)relativeMouse.Y, 1, 1); _editor.EditorSceneRenderer.CompositeTarget.GetData<uint32>(&id, 1, (.)relativeMouse.X, (.)relativeMouse.Y, 1, 1);
SelectionChanged = true; SelectionChanged = true;
SelectedEntityId = id; SelectedEntityId = id;
@@ -301,11 +301,11 @@ namespace GlitchyEditor.EditWindows
{ {
String name = null; String name = null;
var nameComponent = tree.Value.GetComponent<DebugNameComponent>(); var nameComponent = tree.Value.GetComponent<NameComponent>();
if(nameComponent != null) if(nameComponent != null)
{ {
name = nameComponent.DebugName; name = scope:: .(nameComponent.Name);
} }
else else
{ {
@@ -501,13 +501,13 @@ namespace GlitchyEditor.EditWindows
{ {
Entity entity = .(entityId, _scene); Entity entity = .(entityId, _scene);
String name = null; StringView name = null;
var nameComponent = entity.GetComponent<DebugNameComponent>(); var nameComponent = entity.GetComponent<NameComponent>();
if(nameComponent != null) if(nameComponent != null)
{ {
name = nameComponent.DebugName; name = nameComponent.Name;
} }
else else
{ {
@@ -0,0 +1,106 @@
using System;
using ImGui;
using GlitchyEngine.Renderer;
using GlitchyEngine.Math;
using GlitchyEngine;
using GlitchyEngine.World;
using ImGuizmo;
using GlitchyEngine.Events;
namespace GlitchyEditor.EditWindows
{
class GameViewportWindow : EditorWindow
{
public const String s_WindowTitle = "Game";
private ImGui.Vec2 _oldViewportSize = .(100, 100);
private bool _viewPortChanged;
private bool _visible;
private RenderTargetGroup _renderTarget ~ _?.ReleaseRef();
public Vector2 ViewportSize => (Vector2)_oldViewportSize;
// Occurs when the viewport is resized.
public Event<EventHandler<Vector2>> ViewportSizeChanged ~ _.Dispose();
// Occurs when an entity was clicked.
public Event<EventHandler<uint32>> EntityClicked ~ _.Dispose();
/// The render target that is shown in the viewport window.
public RenderTargetGroup RenderTarget
{
get => _renderTarget;
set
{
if(_renderTarget == value)
return;
SetReference!(_renderTarget, value);
}
}
public bool Visible => _visible;
public this(Editor editor)
{
_editor = editor;
}
protected override void InternalShow()
{
ImGui.PushStyleVar(.WindowPadding, ImGui.Vec2(1, 1));
defer ImGui.PopStyleVar();
if(!ImGui.Begin(s_WindowTitle, &_open, .NoScrollbar | .MenuBar))
{
ImGui.End();
_visible = false;
return;
}
_visible = true;
let viewportSize = ImGui.GetContentRegionAvail();
if(ImGui.IsWindowHovered() && Input.IsMouseButtonPressing(.RightButton))
{
let currentWindow = ImGui.GetCurrentWindow();
ImGui.FocusWindow(currentWindow);
}
_hasFocus = ImGui.IsWindowFocused();
if(_renderTarget != null)
{
ImGui.Image(_renderTarget.GetViewBinding(0), viewportSize);
}
ImGui.End();
if(_oldViewportSize != viewportSize)
{
ViewportSizeChanged.Invoke(this, (Vector2)viewportSize);
_viewPortChanged = true;
_oldViewportSize = viewportSize;
}
}
/// Provides the ImGui Drop target and handles dropped payload.
private void HandleDropTarget()
{
if (ImGui.BeginDragDropTarget())
{
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM");
if (payload != null)
{
StringView path = .((char8*)payload.Data, (int)payload.DataSize);
_editor.RequestOpenScene(this, path);
}
ImGui.EndDragDropTarget();
}
}
}
}
@@ -10,6 +10,8 @@ namespace GlitchyEditor.EditWindows;
class PropertiesWindow : EditorWindow class PropertiesWindow : EditorWindow
{ {
public const String s_WindowTitle = "Properties";
private AssetPropertiesEditor _currentPropertiesEditor ~ delete _; private AssetPropertiesEditor _currentPropertiesEditor ~ delete _;
private bool _lockCurrentAsset; private bool _lockCurrentAsset;
@@ -18,7 +20,7 @@ class PropertiesWindow : EditorWindow
private append String _selectedFileName = .(); private append String _selectedFileName = .();
private Asset _currentAsset ~ _?.ReleaseRef(); private AssetHandle _currentAssetHandle;
public this(Editor editor) public this(Editor editor)
{ {
@@ -28,7 +30,7 @@ class PropertiesWindow : EditorWindow
protected override void InternalShow() protected override void InternalShow()
{ {
defer { ImGui.End(); } defer { ImGui.End(); }
if(!ImGui.Begin("Properties", &_open, .None)) if(!ImGui.Begin(s_WindowTitle, &_open, .None))
return; return;
// TODO: make a little button in title bar? // TODO: make a little button in title bar?
@@ -74,11 +76,12 @@ class PropertiesWindow : EditorWindow
if (assetFile == null) if (assetFile == null)
return; return;
Asset asset = _editor.ContentManager.GetAsset(null, _currentAssetHandle);
// We need the actual asset for preview and sometimes for editing // We need the actual asset for preview and sometimes for editing
if (_currentAsset?.Identifier != assetFile.Identifier) if (asset?.Identifier != assetFile.Identifier)
{ {
_currentAsset?.ReleaseRef(); _currentAssetHandle = _editor.ContentManager.LoadAsset(assetFile.Identifier);
_currentAsset = _editor.ContentManager.LoadAsset(assetFile.Identifier);
} }
// TODO: allow changing AssetLoader // TODO: allow changing AssetLoader
@@ -106,7 +109,8 @@ class PropertiesWindow : EditorWindow
if (ImGui.Button("Save Asset")) if (ImGui.Button("Save Asset"))
{ {
_editor.ContentManager.SaveAsset(_currentAsset); Asset asset = _editor.ContentManager.GetAsset(null, _currentAssetHandle);
_editor.ContentManager.SaveAsset(asset);
} }
if (!assetFile.AssetConfig.Config.Changed) if (!assetFile.AssetConfig.Config.Changed)
+13 -3
View File
@@ -17,7 +17,8 @@ namespace GlitchyEditor
private EntityHierarchyWindow _entityHierarchyWindow ~ delete _; private EntityHierarchyWindow _entityHierarchyWindow ~ delete _;
private ComponentEditWindow _componentEditWindow ~ delete _; private ComponentEditWindow _componentEditWindow ~ delete _;
private SceneViewportWindow _sceneViewportWindow~ delete _; private EditorViewportWindow _sceneViewportWindow ~ delete _;
private GameViewportWindow _gameViewportWindow ~ delete _;
private ContentBrowserWindow _contentBrowserWindow ~ delete _; private ContentBrowserWindow _contentBrowserWindow ~ delete _;
private PropertiesWindow _propertiesWindow ~ delete _; private PropertiesWindow _propertiesWindow ~ delete _;
@@ -26,6 +27,9 @@ namespace GlitchyEditor
get => _scene; get => _scene;
set set
{ {
if (_scene == value)
return;
_scene = value; _scene = value;
_entityHierarchyWindow.SetContext(_scene); _entityHierarchyWindow.SetContext(_scene);
} }
@@ -35,7 +39,8 @@ namespace GlitchyEditor
public EntityHierarchyWindow EntityHierarchyWindow => _entityHierarchyWindow; public EntityHierarchyWindow EntityHierarchyWindow => _entityHierarchyWindow;
public ComponentEditWindow ComponentEditWindow => _componentEditWindow; public ComponentEditWindow ComponentEditWindow => _componentEditWindow;
public SceneViewportWindow SceneViewportWindow => _sceneViewportWindow; public EditorViewportWindow SceneViewportWindow => _sceneViewportWindow;
public GameViewportWindow GameViewportWindow => _gameViewportWindow;
public ContentBrowserWindow ContentBrowserWindow => _contentBrowserWindow; public ContentBrowserWindow ContentBrowserWindow => _contentBrowserWindow;
public PropertiesWindow PropertiesWindow => _propertiesWindow; public PropertiesWindow PropertiesWindow => _propertiesWindow;
@@ -43,6 +48,9 @@ namespace GlitchyEditor
public Event<EventHandler<StringView>> RequestOpenScene ~ _.Dispose(); public Event<EventHandler<StringView>> RequestOpenScene ~ _.Dispose();
public SceneRenderer GameSceneRenderer {get; set;}
public SceneRenderer EditorSceneRenderer {get; set;}
/// Creates a new editor for the given world /// Creates a new editor for the given world
public this(Scene scene, EditorContentManager contentManager) public this(Scene scene, EditorContentManager contentManager)
{ {
@@ -54,7 +62,8 @@ namespace GlitchyEditor
private void InitWindows() private void InitWindows()
{ {
_sceneViewportWindow = new SceneViewportWindow(this); _sceneViewportWindow = new EditorViewportWindow(this);
_gameViewportWindow = new GameViewportWindow(this);
_entityHierarchyWindow = new EntityHierarchyWindow(this, _scene); _entityHierarchyWindow = new EntityHierarchyWindow(this, _scene);
_componentEditWindow = new ComponentEditWindow(_entityHierarchyWindow); _componentEditWindow = new ComponentEditWindow(_entityHierarchyWindow);
_contentBrowserWindow = new ContentBrowserWindow((.)Application.Get().ContentManager); _contentBrowserWindow = new ContentBrowserWindow((.)Application.Get().ContentManager);
@@ -64,6 +73,7 @@ namespace GlitchyEditor
public void Update() public void Update()
{ {
_sceneViewportWindow.Show(); _sceneViewportWindow.Show();
_gameViewportWindow.Show();
_entityHierarchyWindow.Show(); _entityHierarchyWindow.Show();
_componentEditWindow.Show(); _componentEditWindow.Show();
_contentBrowserWindow.Show(); _contentBrowserWindow.Show();
+358 -168
View File
@@ -8,71 +8,11 @@ using GlitchyEngine.Content;
using GlitchyEditor.Assets; using GlitchyEditor.Assets;
using GlitchyEngine; using GlitchyEngine;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using internal GlitchyEngine.Content.Asset;
namespace GlitchyEditor; namespace GlitchyEditor;
/*class PreviewImageManager
{
/// Thread that loads/generates preview images in the background
//private Thread _loaderThread;
/// Set to true to notify the loader to stop
//private bool _stopLoader;
private append Dictionary<String, Texture2D> _previewImages = .() ~ {
for (var v in _)
{
delete v.key;
v.value.ReleaseRef();
}
delete _;
};
public Texture2D GetPreviewImage(String assetName)
{
if (_previewImages.TryGetValue(assetName, let image))
{
return image..AddRef();
}
if (assetName.EndsWith(".png", .OrdinalIgnoreCase))
{
/*using (Texture2D texture = new Texture2D(assetName, true))
{
Texture2DDesc desc = .(128, 128, .R8G8B8A8_UNorm_SRGB)
{
Usage = .Immutable
};
Texture2D myActualTexture = new Texture2D(desc);
// TODO: Scale down texture (on GPU?)
}*/
Texture2D texture = new Texture2D(assetName, true);
if (texture.Width <= 128 && texture.Height <= 128)
{
}
if ( texture.MipLevels > 1)
{
}
_previewImages.Add(new String(assetName), texture);
return texture..AddRef();
}
else
{
Runtime.NotImplemented();
}
}
}*/
class EditorContentManager : IContentManager class EditorContentManager : IContentManager
{ {
private append String _contentDirectory = .(); private append String _contentDirectory = .();
@@ -81,15 +21,20 @@ class EditorContentManager : IContentManager
//private append List<String> _identifiers = .() ~ _.ClearAndDeleteItems(); //private append List<String> _identifiers = .() ~ _.ClearAndDeleteItems();
private append Dictionary<StringView, Asset> _loadedAssets = .(); // Check if all resources are unloaded private append Dictionary<StringView, AssetHandle> _identiferToHandle = .(); // TODO: Check if all resources are unloaded
private append Dictionary<AssetHandle, Asset> _handleToAsset = .();
private append AssetHierarchy _assetHierarchy = .(this); private append AssetHierarchy _assetHierarchy = .(this);
public AssetHierarchy AssetHierarchy => _assetHierarchy; public AssetHierarchy AssetHierarchy => _assetHierarchy;
private append List<AssetHandle> _reloadQueue = .();
public this() public this()
{ {
_assetHierarchy.OnFileContentChanged.Add(new => OnFileContentChanged); _assetHierarchy.OnFileContentChanged.Add(new => OnFileContentChanged);
_assetHierarchy.OnFileRenamed.Add(new => OnFileRenamed);
} }
public ~this() public ~this()
@@ -99,46 +44,24 @@ class EditorContentManager : IContentManager
private void OnFileContentChanged(AssetNode assetNode) private void OnFileContentChanged(AssetNode assetNode)
{ {
// TODO: Subassets break reloading because we can't find them when we only receive the file that changed...
// Asset isn't loaded so we don't need to reload it. // Asset isn't loaded so we don't need to reload it.
if (assetNode.AssetFile.LoadedAsset == null) if (assetNode.AssetFile.LoadedAsset == null)
return; return;
String neededAssetLoaderName = assetNode.AssetFile.AssetConfig?.AssetLoader; _reloadQueue.Add(assetNode.AssetFile.LoadedAsset.Handle);
}
if (String.IsNullOrWhiteSpace(neededAssetLoaderName)) public void OnFileRenamed(AssetNode assetNode, StringView oldIdentifier)
{
// Asset isn't loaded so we don't need to reload it.
if (assetNode.AssetFile.LoadedAsset == null)
return; return;
IAssetLoader assetLoader = null; Asset asset = assetNode.AssetFile.LoadedAsset;
String loaderNameBuffer = scope String(64); _identiferToHandle.Remove(oldIdentifier);
asset.Identifier = assetNode.AssetFile.Identifier;
for (IAssetLoader loader in _assetLoaders) _identiferToHandle.Add(asset.Identifier, asset.Handle);
{
loader.GetType().GetName(loaderNameBuffer..Clear());
if (loaderNameBuffer == neededAssetLoaderName)
{
assetLoader = loader;
break;
}
}
if (assetLoader == null)
{
Log.EngineLogger.Error($"Could not find asset loader \"{neededAssetLoaderName}\"");
return;
}
if (var assetReloader = assetLoader as IReloadingAssetLoader)
{
Stream stream = GetStream(assetNode.Path);
assetReloader.ReloadAsset(assetNode.AssetFile, stream);
delete stream;
}
} }
public void SetContentDirectory(StringView contentDirectory) public void SetContentDirectory(StringView contentDirectory)
@@ -152,9 +75,54 @@ class EditorContentManager : IContentManager
public void Update() public void Update()
{ {
SwapInLoadedAssets();
if (!_reloadQueue.IsEmpty)
{
for (AssetHandle handle in _reloadQueue)
{
ReloadAsset(handle);
}
_reloadQueue.Clear();
}
_assetHierarchy.Update(); _assetHierarchy.Update();
} }
/// Replaces placeholders with the loaded assets
private void SwapInLoadedAssets()
{
// Don't take the lock if we have nothing to do.
if (_finishedEntries.Count == 0)
return;
using (_finishedEntriesLock.Enter())
{
while (_finishedEntries.Count > 0)
{
let (placeholder, asset) = _finishedEntries[0];
delete placeholder.LoadingTask;
if (asset == null)
placeholder.PlaceholderType = .Error;
else
{
// TODO: I'm not sure whether AssetFiles are guaranteed to persist.
// Get the reference here because placeholder wont survive SwapAsset.
AssetFile file = placeholder.AssetFile;
file.[Friend]_loadedAsset = asset;
SwapAsset(placeholder, asset);
// SwapAsset increases RefCount, but this scope also holds a reference.
asset.ReleaseRef();
}
_finishedEntries.RemoveAtFast(0);
}
}
}
public IAssetLoader GetDefaultAssetLoader(StringView fileExtension) public IAssetLoader GetDefaultAssetLoader(StringView fileExtension)
{ {
if (_defaultAssetLoaders.TryGetValue(fileExtension, let value)) if (_defaultAssetLoaders.TryGetValue(fileExtension, let value))
@@ -174,9 +142,11 @@ class EditorContentManager : IContentManager
public void RegisterAssetLoader<T>() where T : new, class, IAssetLoader public void RegisterAssetLoader<T>() where T : new, class, IAssetLoader
{ {
//Log.EngineLogger.AssertDebug(!_assetLoaders.Any((l) => l.GetType() == typeof(T)), "Asset loader already registered."); // Log.EngineLogger.AssertDebug(!_assetLoaders.Any((l) => l.GetType() == typeof(T)), "Asset loader already registered.");
_assetLoaders.Add(new T()); T assetLoader = new T();
_assetLoaders.Add(assetLoader);
for (StringView ext in T.FileExtensions) for (StringView ext in T.FileExtensions)
_supportedExtensions.Add(new String(ext)); _supportedExtensions.Add(new String(ext));
@@ -237,99 +207,230 @@ class EditorContentManager : IContentManager
public bool IsLoaded(StringView identifier) public bool IsLoaded(StringView identifier)
{ {
return _loadedAssets.ContainsKey(identifier); return _identiferToHandle.ContainsKey(identifier);
} }
public Asset LoadAsset(StringView identifier) public Asset GetAsset(Type assetType, AssetHandle handle)
{ {
if (_loadedAssets.TryGetValue(identifier, let asset)) Asset asset = null;
_handleToAsset.TryGetValue(handle, out asset);
if (var placeholder = asset as PlaceholderAsset)
{ {
return asset..AddRef(); if (placeholder.PlaceholderType == .Loading)
return placeholder.AssetLoader.GetPlaceholderAsset(assetType);
else if (placeholder.PlaceholderType == .Error)
return placeholder.AssetLoader.GetErrorAsset(assetType);
} }
// Find subasset name if (assetType == null)
int poundIndex = identifier.IndexOf('#'); {
return asset;
}
else if (asset?.GetType().IsSubtypeOf(assetType) ?? false)
{
return asset;
}
else
{
// TODO: get default asset
StringView resourceName = poundIndex == -1 ? identifier : identifier.Substring(0, poundIndex); return null;
StringView? subassetName = identifier.Substring(poundIndex + 1); }
}
String filePath = scope String(resourceName.Length + _contentDirectory.Length + 2); private void ReloadAsset(AssetHandle handle)
Path.Combine(filePath, _contentDirectory, resourceName); {
Debug.Profiler.ProfileResourceFunction!();
Path.Fixup(filePath); Asset oldAsset = null;
//filePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); if (!_handleToAsset.TryGetValue(handle, out oldAsset))
{
Log.EngineLogger.Error("Can't reload! No asset exists for handle.");
return;
}
Log.EngineLogger.AssertDebug(oldAsset != null);
StringView oldIdentifier = oldAsset.Identifier;
GetResourceAndSubassetName(oldIdentifier, let resourceName, let subassetName);
String filePath = scope .();
GetResourceFilePath(resourceName, filePath);
Result<TreeNode<AssetNode>> resultNode = AssetHierarchy.GetNodeFromPath(filePath); Result<TreeNode<AssetNode>> resultNode = AssetHierarchy.GetNodeFromPath(filePath);
if (resultNode case .Err) if (resultNode case .Err)
{ {
Log.EngineLogger.Error($"Could not find asset \"{filePath}\"."); Log.EngineLogger.Error($"Could not find asset \"{filePath}\".");
return null; return;
} }
AssetFile file = resultNode->Value.AssetFile; AssetFile file = resultNode->Value.AssetFile;
IAssetLoader assetLoader = null; IAssetLoader assetLoader = GetAssetLoader(file);
String loaderTypeName = scope .(128); Stream stream = GetStream(filePath);
for (IAssetLoader loader in _assetLoaders) // TODO: Add async loading!
Asset loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this);
delete stream;
if (loadedAsset == null)
return;
file.[Friend]_loadedAsset = loadedAsset;
SwapAsset(oldAsset, loadedAsset);
// SwapAsset increases RefCount, but this scope also holds a reference.
loadedAsset.ReleaseRef();
}
/// Returns the resource name and, if it exists, the subasset name.
private void GetResourceAndSubassetName(StringView identifier, out StringView resourceName, out StringView? subassetName)
{ {
loader.GetType().GetName(loaderTypeName..Clear()); int poundIndex = identifier.IndexOf('#');
if (loaderTypeName == file.AssetConfig.AssetLoader) resourceName = (poundIndex != -1) ? identifier.Substring(0, poundIndex) : identifier;
subassetName = (poundIndex != -1) ? identifier.Substring(poundIndex + 1) : null;
}
private void GetResourceFilePath(StringView resourceName, String filePath)
{ {
assetLoader = loader; Path.Combine(filePath, _contentDirectory, resourceName);
break;
Path.Fixup(filePath);
}
private enum PlaceholderType
{
Loading,
Error
}
private class PlaceholderAsset : Asset
{
public AssetFile AssetFile {get; private set;}
public Task LoadingTask {get;set;}
public IAssetLoader AssetLoader {get; private set;}
public PlaceholderType PlaceholderType {get; set;}
public this(AssetFile assetFile, IAssetLoader assetLoader, PlaceholderType placeholderType)
{
AssetFile = assetFile;
AssetLoader = assetLoader;
PlaceholderType = placeholderType;
} }
} }
private append Monitor _finishedEntriesLock = .();
private append List<(PlaceholderAsset placeholder, Asset newAsset)> _finishedEntries = .();
private class MissingAsset : Asset {}
public AssetHandle LoadAsset(StringView identifier, bool blocking = false)
{
Debug.Profiler.ProfileResourceFunction!();
// Todo: How strict should we be on paths?
String fixedIdentifier = scope String(identifier);
AssetIdentifier.Fixup(fixedIdentifier);
if (_identiferToHandle.TryGetValue(fixedIdentifier, let asset))
return asset;
GetResourceAndSubassetName(fixedIdentifier, let resourceName, let subassetName);
String filePath = scope .();
GetResourceFilePath(resourceName, filePath);
Result<TreeNode<AssetNode>> resultNode = AssetHierarchy.GetNodeFromPath(filePath);
if (resultNode case .Err)
{
Log.EngineLogger.Error($"Could not find asset \"{filePath}\".");
return .Invalid;
}
AssetFile file = resultNode->Value.AssetFile;
IAssetLoader assetLoader = GetAssetLoader(file);
// TODO: what are we supposed to do if we don't find a loader? Sure not crash...
Log.EngineLogger.AssertDebug(assetLoader != null); Log.EngineLogger.AssertDebug(assetLoader != null);
Asset loadedAsset;
// TODO: Support lazy loading for all asset types
if (!(assetLoader is EditorTextureAssetLoader) || blocking)
{
Stream stream = GetStream(filePath);
loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this);
delete stream;
if (loadedAsset == null)
return .Invalid;
}
else
{
PlaceholderAsset placeholder = new PlaceholderAsset(file, assetLoader, .Loading);
String filePath2 = new String(filePath);
String newResourceName = new String(resourceName);
String newSesourceName = subassetName == null ? null : new String(subassetName.Value);
placeholder.LoadingTask = new Task(new () => {
AsyncLoadAsset(placeholder, filePath2, assetLoader, file,
newResourceName, newSesourceName);
});
ThreadPool.QueueUserWorkItem(placeholder.LoadingTask);
loadedAsset = placeholder;
}
loadedAsset.Identifier = fixedIdentifier;
AssetHandle handle = ManageAsset(loadedAsset);
// ManageAsset increases RefCount, but this scope also holds a reference.
loadedAsset.ReleaseRef();
// Add to Identifier -> Handle map
_identiferToHandle.Add(loadedAsset.Identifier, handle);
file.[Friend]_loadedAsset = loadedAsset;
return handle;
}
private void AsyncLoadAsset(PlaceholderAsset placeholder, String filePath, IAssetLoader assetLoader, AssetFile file, String resourceName, String subassetName)
{
Debug.Profiler.ProfileResourceFunction!();
Stream stream = GetStream(filePath); Stream stream = GetStream(filePath);
Asset loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this); Asset loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this);
delete stream; delete stream;
delete filePath;
delete resourceName;
delete subassetName;
if (loadedAsset == null) using (_finishedEntriesLock.Enter())
return null; {
_finishedEntries.Add((placeholder, loadedAsset));
//String identifierString = new .(identifier); }
//_identifiers.Add(identifierString);
//_loadedAssets[identifierString] = loadedAsset;
loadedAsset.Identifier = identifier;
ManageAsset(loadedAsset);
file.[Friend]_loadedAsset = loadedAsset;
return loadedAsset;
} }
/// Saves the asset. /// Gets the asset loader that has to be used for the given file.
public Result<void> SaveAsset(Asset asset) IAssetLoader GetAssetLoader(AssetFile file)
{ {
// Find subasset name
int poundIndex = asset.Identifier.IndexOf('#');
StringView resourceName = poundIndex == -1 ? asset.Identifier : asset.Identifier.Substring(0, poundIndex);
StringView? subassetName = asset.Identifier.Substring(poundIndex + 1);
String filePath = scope String(resourceName.Length + _contentDirectory.Length + 2);
Path.Combine(filePath, _contentDirectory, resourceName);
Path.Fixup(filePath);
//filePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
TreeNode<AssetNode> assetNode = Try!(AssetHierarchy.GetNodeFromPath(filePath));
AssetFile file = assetNode->AssetFile;
IAssetLoader assetLoader = null; IAssetLoader assetLoader = null;
String loaderTypeName = scope .(128); String loaderTypeName = scope .(128);
@@ -345,24 +446,67 @@ class EditorContentManager : IContentManager
} }
} }
return assetLoader;
}
public enum SaveAssetError
{
case Unknown;
case Unsavable;
case PathNotFound;
}
/// Saves the asset.
public Result<void, SaveAssetError> SaveAsset(Asset asset)
{
if (asset == null)
return .Err(.Unknown);
if (asset.Identifier.IsWhiteSpace)
return .Err(.Unsavable);
GetResourceAndSubassetName(asset.Identifier, let resourceName, let subassetName);
if (subassetName != null)
{
// TODO: should this ever be allowed? Couldn't we just save the entire asset?
// Would this ever be necessary?
Runtime.NotImplemented("Saving subassets is not currently allowed");
}
String filePath = scope String();
GetResourceFilePath(resourceName, filePath);
Result<TreeNode<AssetNode>> assetNode = AssetHierarchy.GetNodeFromPath(filePath);
if (assetNode case .Err)
return .Err(.PathNotFound);
AssetFile file = assetNode.Get()->AssetFile;
IAssetLoader assetLoader = GetAssetLoader(file);
IAssetSaver assetSaver = assetLoader as IAssetSaver; IAssetSaver assetSaver = assetLoader as IAssetSaver;
if (assetSaver == null) if (assetSaver == null)
{ {
Log.EngineLogger.Error("The asset loader can't save!"); Log.EngineLogger.Error("The asset loader can't save!");
return .Err; return .Err(.Unsavable);
} }
Stream stream = OpenStream(filePath, false, true); Stream stream = OpenStream(filePath, false);
assetSaver.EditorSaveAsset(stream, asset, file.AssetConfig.Config, resourceName, subassetName, this); assetSaver.EditorSaveAsset(stream, asset, file.AssetConfig.Config, resourceName, subassetName, this);
// Trim off the end of the file.
stream.SetLength(stream.Position);
delete stream; delete stream;
return .Ok; return .Ok;
} }
private Stream OpenStream(StringView assetIdentifier, bool openOnly, bool truncate = false) private Stream OpenStream(StringView assetIdentifier, bool openOnly)
{ {
var assetIdentifier; var assetIdentifier;
@@ -378,9 +522,6 @@ class EditorContentManager : IContentManager
FileMode fileMode = openOnly ? FileMode.Open : FileMode.OpenOrCreate; FileMode fileMode = openOnly ? FileMode.Open : FileMode.OpenOrCreate;
if (truncate)
fileMode |= .Truncate;
var result = fs.Open(assetIdentifier, fileMode, openOnly ? .Read : .ReadWrite, .ReadWrite); var result = fs.Open(assetIdentifier, fileMode, openOnly ? .Read : .ReadWrite, .ReadWrite);
if (result case .Err) if (result case .Err)
@@ -413,37 +554,86 @@ class EditorContentManager : IContentManager
return fs;*/ return fs;*/
} }
public void ManageAsset(Asset asset) public AssetHandle ManageAsset(Asset asset)
{ {
_loadedAssets.Add(asset.Identifier, asset); Log.EngineLogger.AssertDebug(asset.Handle == .Invalid, "Asset is already managed.");
asset.[Friend]_contentManager = this; Log.EngineLogger.AssertDebug(asset.ContentManager == null, "Asset is already managed.");
AssetHandle handle = .();
// Generate until we find a unique key (shouldn't happen too often)
while (handle.IsInvalid || _handleToAsset.ContainsKey(handle))
{
handle = .();
Log.EngineLogger.Trace("Handle was invalid or already taken.");
// TODO: perhaps test how often this happens.
// If this happens too often we could use a different random generator
} }
public void UnmanageAsset(Asset asset) //_handles.Add(asset.Identifier, handle);
_handleToAsset.Add(handle, asset);
asset.[Friend]_contentManager = this;
asset.[Friend]_handle = handle;
asset.AddRef();
return handle;
}
private void SwapAsset(Asset oldAsset, Asset newAsset)
{ {
_loadedAssets.Remove(asset.Identifier); newAsset.Identifier = oldAsset.Identifier;
newAsset._contentManager = this;
newAsset._handle = oldAsset.Handle;
_handleToAsset[oldAsset.Handle] = newAsset;
if (_identiferToHandle.ContainsKey(oldAsset.Identifier))
{
_identiferToHandle.Remove(oldAsset.Identifier);
_identiferToHandle.Add(newAsset.Identifier, newAsset.Handle);
}
newAsset.AddRef();
oldAsset.ReleaseRef();
}
public void UnmanageAsset(AssetHandle handle)
{
Log.EngineLogger.AssertDebug(_handleToAsset.ContainsKey(handle), "Handle doesn't correspond to an asset.");
Asset asset = _handleToAsset[handle];
if (_identiferToHandle.ContainsKey(asset.Identifier))
_identiferToHandle.Remove(asset.Identifier);
_handleToAsset.Remove(handle);
asset.[Friend]_contentManager = null; asset.[Friend]_contentManager = null;
asset.ReleaseRef();
} }
/// This will unregister all assets from this content manager. /// This will unregister all assets from this content manager.
/// Note: This will not release any assets. /// Note: This will not release any assets.
private void UnmanageAllAssets() private void UnmanageAllAssets()
{ {
for (let (_, asset) in _loadedAssets) for (let (handle, _) in _handleToAsset)
{ {
UnmanageAsset(asset); UnmanageAsset(handle);
} }
} }
public void UpdateAssetIdentifier(Asset asset, StringView oldIdentifier, StringView newIdentifier) public void AssetMoved(Asset asset, StringView oldIdentifier, StringView newIdentifier)
{ {
Runtime.NotImplemented();
if (oldIdentifier == newIdentifier) if (oldIdentifier == newIdentifier)
return; return;
Log.EngineLogger.Assert(_loadedAssets.ContainsKey(newIdentifier), "An asset with the same identifier is already managed by this content manager."); Log.EngineLogger.Assert(_identiferToHandle.ContainsKey(newIdentifier), "An asset with the same identifier is already managed by this content manager.");
// Since all we do in order to track assets is add them to a dictionary we can simply unmanage and manage it again. // Since all we do in order to track assets is add them to a dictionary we can simply unmanage and manage it again.
UnmanageAsset(asset); //UnmanageAsset(asset);
ManageAsset(asset); //ManageAsset(asset);
} }
} }
+9 -4
View File
@@ -2,12 +2,13 @@ using System;
using GlitchyEngine.Renderer; using GlitchyEngine.Renderer;
using GlitchyEngine.Math; using GlitchyEngine.Math;
using GlitchyEngine; using GlitchyEngine;
using GlitchyEngine.Content;
namespace GlitchyEditor namespace GlitchyEditor
{ {
class EditorIcons : RefCounted class EditorIcons : RefCounted
{ {
Texture2D _texture ~ _.ReleaseRef(); AssetHandle<Texture2D> _texture;
public SubTexture2D DirectionalLight ~ _.ReleaseRef(); public SubTexture2D DirectionalLight ~ _.ReleaseRef();
public SubTexture2D Camera ~ _.ReleaseRef(); public SubTexture2D Camera ~ _.ReleaseRef();
@@ -15,16 +16,18 @@ namespace GlitchyEditor
public SubTexture2D File ~ _.ReleaseRef(); public SubTexture2D File ~ _.ReleaseRef();
public SubTexture2D Play ~ _.ReleaseRef(); public SubTexture2D Play ~ _.ReleaseRef();
public SubTexture2D Stop ~ _.ReleaseRef(); public SubTexture2D Stop ~ _.ReleaseRef();
public SubTexture2D Simulate ~ _.ReleaseRef();
public SubTexture2D Pause ~ _.ReleaseRef();
public SamplerState SamplerState public SamplerState SamplerState
{ {
get => _texture.SamplerState; get => _texture.Get().SamplerState;
set => _texture.SamplerState = value; set => _texture.Get().SamplerState = value;
} }
public this(String texturePath, Vector2 iconSize) public this(String texturePath, Vector2 iconSize)
{ {
_texture = Content.LoadAsset<Texture2D>(texturePath);//new Texture2D(texturePath); _texture = Content.LoadAsset(texturePath, null, true);
Vector2 pen = .(); Vector2 pen = .();
@@ -34,6 +37,8 @@ namespace GlitchyEditor
File = GetNextGridTexture(ref pen, iconSize); File = GetNextGridTexture(ref pen, iconSize);
Play = GetNextGridTexture(ref pen, iconSize); Play = GetNextGridTexture(ref pen, iconSize);
Stop = GetNextGridTexture(ref pen, iconSize); Stop = GetNextGridTexture(ref pen, iconSize);
Simulate = GetNextGridTexture(ref pen, iconSize);
Pause = GetNextGridTexture(ref pen, iconSize);
} }
private SubTexture2D GetNextGridTexture(ref Vector2 pen, Vector2 iconSize) private SubTexture2D GetNextGridTexture(ref Vector2 pen, Vector2 iconSize)
+320 -256
View File
@@ -18,67 +18,84 @@ namespace GlitchyEditor
{ {
class EditorLayer : Layer class EditorLayer : Layer
{ {
enum SceneState
{
Edit,
Play,
Simulate
}
RasterizerState _rasterizerState ~ _?.ReleaseRef(); RasterizerState _rasterizerState ~ _?.ReleaseRef();
RasterizerState _rasterizerStateClockWise ~ _?.ReleaseRef(); RasterizerState _rasterizerStateClockWise ~ _?.ReleaseRef();
// TODO: we shouldn't hold a reference to the context
GraphicsContext _context ~ _.ReleaseRef(); GraphicsContext _context ~ _.ReleaseRef();
BlendState _alphaBlendState ~ _.ReleaseRef(); BlendState _alphaBlendState ~ _.ReleaseRef();
BlendState _opaqueBlendState ~ _.ReleaseRef(); BlendState _opaqueBlendState ~ _.ReleaseRef();
DepthStencilState _depthStencilState ~ _.ReleaseRef(); DepthStencilState _depthStencilState ~ _.ReleaseRef();
Scene _scene ~ delete _; /// Reference to the scene that is currently being played and worked on.
String _sceneFilePath = new String() ~ delete _; Scene _activeScene ~ _?.ReleaseRef();
public String SceneFilePath /**
* Referece to the editor scene.
* We hold a reference to the editor scene because we need it in order
* to restore the original state once we stop the game/simulation.
* Before starting the simulation the editor scene will be copied and
* the reference in _activeScene will be replaced with the new scene.
*/
Scene _editorScene ~ _?.ReleaseRef();
SceneRenderer _gameSceneRenderer ~ delete _;
SceneRenderer _editorSceneRenderer ~ delete _;
/// Path of the current scene.
append String _sceneFilePath = .();
Editor _editor ~ delete _;
RenderTargetGroup _cameraTarget ~ _.ReleaseRef();
RenderTargetGroup _editorViewportTarget ~ _.ReleaseRef();
RenderTargetGroup _gameViewportTarget ~ _.ReleaseRef();
SettingsWindow _settingsWindow = new .() ~ delete _;
EditorCamera _camera ~ _.Dispose();
EditorIcons _editorIcons ~ _.ReleaseRef();
EditorContentManager _contentManager;
SceneState _sceneState = .Edit;
bool _isPaused = false;
/// Gets or sets the path of the current scene.
public StringView SceneFilePath
{ {
get => _sceneFilePath; get => _sceneFilePath;
set set
{ {
_sceneFilePath.Clear(); _sceneFilePath.Clear();
if (value != null) if (!value.IsWhiteSpace)
_sceneFilePath.Append(value); _sceneFilePath.Append(value);
} }
} }
Editor _editor ~ delete _; public this(EditorContentManager contentManager) : base("Editor")
RenderTargetGroup _cameraTarget ~ _.ReleaseRef();
RenderTargetGroup _viewportTarget ~ _.ReleaseRef();
SettingsWindow _settingsWindow = new .() ~ delete _;
EditorCamera _camera ~ _.Dispose();
/*Texture2D _editorIcons ~ _.ReleaseRef();
SubTexture2D _iconDirectionalLight ~ _.ReleaseRef();
SubTexture2D _iconCamera ~ _.ReleaseRef();*/
EditorIcons _editorIcons ~ _.ReleaseRef();
EditorContentManager _contentManager;
enum SceneState
{
Edit,
Play,
// Pause,
// Simulate
}
SceneState _sceneState = .Edit;
public this(EditorContentManager contentManager) : base("Example")
{ {
Application.Get().Window.IsVSync = false; Application.Get().Window.IsVSync = false;
//InitContentManager();
_contentManager = contentManager; _contentManager = contentManager;
InitGraphics(); InitGraphics();
_scene = new Scene(); _editorScene = new Scene();
SetReference!(_activeScene, _editorScene);
_gameSceneRenderer = new SceneRenderer();
_editorSceneRenderer = new SceneRenderer();
_camera = EditorCamera(Vector3(3.5f, 1.25f, 2.75f), Quaternion.FromEulerAngles(MathHelper.ToRadians(40), MathHelper.ToRadians(25), 0), MathHelper.ToRadians(75), 0.1f, 1); _camera = EditorCamera(Vector3(3.5f, 1.25f, 2.75f), Quaternion.FromEulerAngles(MathHelper.ToRadians(40), MathHelper.ToRadians(25), 0), MathHelper.ToRadians(75), 0.1f, 1);
_camera.RenderTarget = _cameraTarget; _camera.RenderTarget = _cameraTarget;
@@ -88,31 +105,6 @@ namespace GlitchyEditor
NewScene(); NewScene();
} }
/*private void InitContentManager()
{
_contentManager = new EditorContentManager();
_contentManager.RegisterAssetLoader<EditorTextureAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<EditorTextureAssetLoader>(".png", ".dds");
_contentManager.SetAssetPropertiesEditor<EditorTextureAssetLoader>(=> TextureAssetPropertiesEditor.Factory);
_contentManager.RegisterAssetLoader<ModelAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<ModelAssetLoader>(".glb", ".gltf");
_contentManager.SetAssetPropertiesEditor<ModelAssetLoader>(=> ModelAssetPropertiesEditor.Factory);
_contentManager.RegisterAssetLoader<MaterialAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<MaterialAssetLoader>(".mat");
_contentManager.SetAssetPropertiesEditor<MaterialAssetLoader>(=> MaterialAssetPropertiesEditor.Factory);
_contentManager.RegisterAssetLoader<EffectAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<EffectAssetLoader>(".hlsl");
_contentManager.SetAssetPropertiesEditor<EffectAssetLoader>(=> EffectAssetPropertiesEditor.Factory);
_contentManager.SetContentDirectory("./content");
// Todo: Sketchy...
Application.Get().[Friend]_contentManager = _contentManager;
}*/
private void InitGraphics() private void InitGraphics()
{ {
_context = Application.Get().Window.Context..AddRef(); _context = Application.Get().Window.Context..AddRef();
@@ -141,7 +133,15 @@ namespace GlitchyEditor
DepthTargetDescription = .(.D24_UNorm_S8_UInt) DepthTargetDescription = .(.D24_UNorm_S8_UInt)
}); });
_viewportTarget = new RenderTargetGroup(.() _editorViewportTarget = new RenderTargetGroup(.()
{
Width = 100,
Height = 100,
ColorTargetDescriptions = TargetDescription[](
.(.R8G8B8A8_UNorm))
});
_gameViewportTarget = new RenderTargetGroup(.()
{ {
Width = 100, Width = 100,
Height = 100, Height = 100,
@@ -154,18 +154,16 @@ namespace GlitchyEditor
ContentBrowserWindow.s_FolderTexture = _editorIcons.Folder; ContentBrowserWindow.s_FolderTexture = _editorIcons.Folder;
ContentBrowserWindow.s_FileTexture = _editorIcons.File; ContentBrowserWindow.s_FileTexture = _editorIcons.File;
/*_editorIcons = new Texture2D("Textures/EditorIcons.dds");
_editorIcons.SamplerState = SamplerStateManager.AnisotropicClamp;
_iconDirectionalLight = .CreateFromGrid(_editorIcons, .(0, 0), .(64, 64));
_iconCamera = .CreateFromGrid(_editorIcons, .(1, 0), .(64, 64));*/
} }
private void InitEditor() private void InitEditor()
{ {
_editor = new Editor(_scene, _contentManager); _editor = new Editor(_editorScene, _contentManager);
_editor.SceneViewportWindow.ViewportSizeChanged.Add(new (s, e) => ViewportSizeChanged(s, e)); _editor.SceneViewportWindow.ViewportSizeChanged.Add(new (s, e) => EditorViewportSizeChanged(s, e));
_editor.GameViewportWindow.ViewportSizeChanged.Add(new (s, e) => GameViewportSizeChanged(s, e));
_editor.CurrentCamera = &_camera; _editor.CurrentCamera = &_camera;
_editor.GameSceneRenderer = _gameSceneRenderer;
_editor.EditorSceneRenderer = _editorSceneRenderer;
_editor.RequestOpenScene.Add(new (s, fileName) => { _editor.RequestOpenScene.Add(new (s, fileName) => {
LoadSceneFile(fileName); LoadSceneFile(fileName);
@@ -174,20 +172,54 @@ namespace GlitchyEditor
public override void Update(GameTime gameTime) public override void Update(GameTime gameTime)
{ {
_camera.Update(gameTime); Debug.Profiler.ProfileFunction!();
_editor.CurrentScene = _activeScene;
Scene.UpdateMode updateMode;
switch (_sceneState)
{
case .Edit:
updateMode = .Editor;
case .Play:
updateMode = .Runtime;
case .Simulate:
updateMode = .Physics;
}
if (_sceneState != .Edit && _isPaused)
updateMode = .None;
_activeScene.Update(gameTime, updateMode);
// Clear the swapchain-buffer // Clear the swapchain-buffer
RenderCommand.Clear(null, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0); RenderCommand.Clear(null, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0);
RenderCommand.Clear(_viewportTarget, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0); RenderCommand.SetBlendState(_alphaBlendState);
RenderCommand.SetDepthStencilState(_depthStencilState);
if (_editor.SceneViewportWindow.Visible)
{
_camera.Update(gameTime);
_editorSceneRenderer.Scene = _activeScene;
RenderCommand.Clear(_editorViewportTarget, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0);
_editorSceneRenderer.RenderEditor(gameTime, _camera, _editorViewportTarget, scope => DebugDraw3D, scope => DebugDraw2D);
}
RenderCommand.SetBlendState(_alphaBlendState); RenderCommand.SetBlendState(_alphaBlendState);
RenderCommand.SetDepthStencilState(_depthStencilState); RenderCommand.SetDepthStencilState(_depthStencilState);
if (_sceneState == .Edit) if (_editor.GameViewportWindow.Visible)
_scene.UpdateEditor(gameTime, _camera, _viewportTarget, scope => DebugDraw3D, scope => DebugDraw2D); {
else if (_sceneState == .Play) _gameSceneRenderer.Scene = _activeScene;
_scene.UpdateRuntime(gameTime, _viewportTarget);
RenderCommand.Clear(_gameViewportTarget, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0);
_gameSceneRenderer.RenderRuntime(gameTime, _gameViewportTarget);
}
RenderCommand.UnbindRenderTargets(); RenderCommand.UnbindRenderTargets();
RenderCommand.SetRenderTarget(null, 0, true); RenderCommand.SetRenderTarget(null, 0, true);
@@ -226,11 +258,11 @@ namespace GlitchyEditor
return Math.Clamp(1.5f - Vector3.Distance(_editor.CurrentCamera.Position, pos) / 50, 0, 1); return Math.Clamp(1.5f - Vector3.Distance(_editor.CurrentCamera.Position, pos) / 50, 0, 1);
} }
for (var (entity, transform, camera) in _scene.[Friend]_ecsWorld.Enumerate<TransformComponent, CameraComponent>()) for (var (entity, transform, camera) in _activeScene.GetEntities<TransformComponent, CameraComponent>())
{ {
if (_editor.EntityHierarchyWindow.SelectedEntities.Contains(.(entity, _scene))) if (_editor.EntityHierarchyWindow.SelectedEntities.Contains(.(entity, _activeScene)))
{ {
DebugRenderer.DrawViewFrustum(transform.WorldTransform, camera.Camera.Projection, _camera.Projection * _camera.View, .White); DebugRenderer.DrawViewFrustum(transform.WorldTransform, camera.Camera.Projection, .White);
} }
Matrix world = Billboard(transform.WorldTransform); Matrix world = Billboard(transform.WorldTransform);
@@ -240,9 +272,9 @@ namespace GlitchyEditor
//Renderer2D.DrawQuad(world, _iconCamera, .White, .(0, 0, 1, 1), entity.Index); //Renderer2D.DrawQuad(world, _iconCamera, .White, .(0, 0, 1, 1), entity.Index);
} }
for (var (entity, transform, light) in _scene.[Friend]_ecsWorld.Enumerate<TransformComponent, LightComponent>()) for (var (entity, transform, light) in _activeScene.GetEntities<TransformComponent, LightComponent>())
{ {
if (_editor.EntityHierarchyWindow.SelectedEntities.Contains(.(entity, _scene))) if (_editor.EntityHierarchyWindow.SelectedEntities.Contains(.(entity, _activeScene)))
{ {
Renderer.DrawRay(.Zero, .(0, 0, 20), ColorRGBA(light.SceneLight.Color, 1.0f), transform.WorldTransform); Renderer.DrawRay(.Zero, .(0, 0, 20), ColorRGBA(light.SceneLight.Color, 1.0f), transform.WorldTransform);
@@ -258,7 +290,16 @@ namespace GlitchyEditor
float alpha = CalculateAlpha(transform.WorldTransform.Translation); float alpha = CalculateAlpha(transform.WorldTransform.Translation);
Renderer2D.DrawQuad(world, _editorIcons.DirectionalLight, ColorRGBA(light.SceneLight.Color.R * alpha, light.SceneLight.Color.G * alpha, light.SceneLight.Color.B * alpha, alpha), .(0, 0, 1, 1), entity.Index); Renderer2D.DrawQuad(world, _editorIcons.DirectionalLight, ColorRGBA(light.SceneLight.Color.R * alpha, light.SceneLight.Color.G * alpha, light.SceneLight.Color.B * alpha, alpha), .(0, 0, 1, 1), entity.Index);
//Renderer2D.DrawQuad(world, _iconDirectionalLight, ColorRGBA(light.SceneLight.Color, alpha), .(0, 0, 1, 1), entity.Index); }
for (var (entity, transform, collider) in _activeScene.GetEntities<TransformComponent, BoxCollider2DComponent>())
{
Renderer2D.DrawRect(transform.WorldTransform * Matrix.Translation(collider.Offset.X, collider.Offset.Y, 0) * Matrix.Scaling(collider.Size.X * 2, collider.Size.Y * 2, 0));
}
for (var (entity, transform, collider) in _activeScene.GetEntities<TransformComponent, CircleCollider2DComponent>())
{
Renderer2D.DrawCircle(transform.WorldTransform * Matrix.Translation(collider.Offset.X, collider.Offset.Y, 0) * Matrix.Scaling(collider.Radius * 2), (Texture2D)null, ColorRGBA(0f, 1f, 0f), 0.01f);
} }
} }
@@ -287,7 +328,8 @@ namespace GlitchyEditor
DrawMainMenuBar(); DrawMainMenuBar();
_editor.SceneViewportWindow.RenderTarget = _viewportTarget; _editor.SceneViewportWindow.RenderTarget = _editorViewportTarget;
_editor.GameViewportWindow.RenderTarget = _gameViewportTarget;
_editor.Update(); _editor.Update();
@@ -317,22 +359,87 @@ namespace GlitchyEditor
ImGui.Begin("##toolbar", null, .NoDecoration | .NoScrollbar | .NoScrollWithMouse); ImGui.Begin("##toolbar", null, .NoDecoration | .NoScrollbar | .NoScrollWithMouse);
SubTexture2D icon = _sceneState == .Edit ? _editorIcons.Play : _editorIcons.Stop; float padding = 2.0f;
float size = ImGui.GetWindowHeight() - 4.0f; float size = ImGui.GetWindowHeight() - 2 * padding;
float centerX = ImGui.GetContentRegionMax().x / 2;
ImGui.SameLine((ImGui.GetContentRegionMax().x / 2 - size / 2));
if (ImGui.ImageButton(icon, .(size, size), .Zero, .Ones, 0))
{
if (_sceneState == .Edit) if (_sceneState == .Edit)
EditorButtons:
{ {
// Display the buttons for edit state
float totalWidth = size * 3 + padding * 4;
ImGui.SameLine();
ImGui.SetCursorPosX(centerX - totalWidth / 2);
if (ImGui.ImageButton(_editorIcons.Play, .(size, size), .Zero, .Ones, 0))
OnScenePlay(); OnScenePlay();
}
else if (_sceneState == .Play) ImGui.AttachTooltip("Play the game.");
ImGui.SameLine();
ImGui.PushID(1);
if (ImGui.ImageButton(_editorIcons.Simulate, .(size, size), .Zero, .Ones, 0))
OnSceneSimulate();
ImGui.PopID();
ImGui.AttachTooltip("Enter simulation mode.\nThis only runs the physics engine.");
if (_isPaused)
{ {
OnSceneStop(); ImGui.PushStyleColor(.Button, *ImGui.GetStyleColorVec4(.ButtonActive));
defer:EditorButtons { ImGui.PopStyleColor(); }
} }
ImGui.PushID(2);
//ImGui.SameLine(penX += size + 2 * padding);
ImGui.SameLine();
if (ImGui.ImageButton(_editorIcons.Pause, .(size, size), .Zero, .Ones, 0))
_isPaused = !_isPaused;
ImGui.PopID();
ImGui.AttachTooltip("If enabled the game or simulation will be started in paused state.");
}
else
{
// Display the buttons for play/simulation state
ImGui.SameLine();
ImGui.SetCursorPosX(centerX - size - padding);
SubTexture2D pauseButtonIcon = _isPaused ? _editorIcons.Play : _editorIcons.Pause;
if (ImGui.ImageButton(pauseButtonIcon, .(size, size), .Zero, .Ones, 0))
{
if (_isPaused)
OnSceneResume();
else
OnScenePause();
}
ImGui.AttachTooltip(_isPaused ? "Resume" : "Pause");
ImGui.SameLine();
ImGui.PushID(1);
if (ImGui.ImageButton(_editorIcons.Stop, .(size, size), .Zero, .Ones, 0))
OnSceneStop();
ImGui.PopID();
ImGui.AttachTooltip("Stop");
} }
ImGui.End(); ImGui.End();
@@ -343,194 +450,129 @@ namespace GlitchyEditor
private void OnScenePlay() private void OnScenePlay()
{ {
_sceneState = .Play;
_editor.SceneViewportWindow.EditorMode = false; _editor.SceneViewportWindow.EditorMode = false;
_sceneState = .Play;
_scene.OnRuntimeStart(); using (Scene runtimeScene = new Scene())
{
_editorScene.CopyTo(runtimeScene);
runtimeScene.OnRuntimeStart();
SetReference!(_activeScene, runtimeScene);
}
_editor.CurrentScene = _activeScene;
}
private void OnSceneSimulate()
{
_editor.SceneViewportWindow.EditorMode = false;
_sceneState = .Simulate;
using (Scene simulationScene = new Scene())
{
_editorScene.CopyTo(simulationScene);
simulationScene.OnSimulationStart();
SetReference!(_activeScene, simulationScene);
}
_editor.CurrentScene = _activeScene;
}
private void OnScenePause()
{
_isPaused = true;
}
private void OnSceneResume()
{
_isPaused = false;
} }
private void OnSceneStop() private void OnSceneStop()
{ {
_scene.OnRuntimeStop(); if (_sceneState == .Play)
_activeScene.OnRuntimeStop();
else
_activeScene.OnSimulationStop();
SetReference!(_activeScene, _editorScene);
_editor.SceneViewportWindow.EditorMode = true; _editor.SceneViewportWindow.EditorMode = true;
_sceneState = .Edit; _sceneState = .Edit;
}
// Just for testing _editor.CurrentScene = _activeScene;
private void TestEntitiesWithModels()
{
/*{
var lightNtt = _scene.CreateEntity("My Sexy Sun 2");
let transform = lightNtt.GetComponent<TransformComponent>();
transform.Position = .(0, 0, 0);
transform.RotationEuler = .(MathHelper.ToRadians(70), MathHelper.ToRadians(-30), 0);
let light = lightNtt.AddComponent<LightComponent>(); _isPaused = false;
light.SceneLight.Illuminance = 10.0f;
light.SceneLight.Color = .(1.0f, 0.95f, 0.8f);
}
{ /*
var lightNtt = _scene.CreateEntity("My Sexy Sun 3"); * Update the viewport size because if the game windows size changed in
let transform = lightNtt.GetComponent<TransformComponent>(); * "Game"-mode the updated aspect-rations will reset once we go back
transform.Position = .(0, 0, 0); * into "Editor"-mode (because Game-Mode works on a copy of the scene).
transform.RotationEuler = .(MathHelper.ToRadians(20), MathHelper.ToRadians(-55), 0); */
GameViewportSizeChanged(null, _editor.GameViewportWindow.ViewportSize);
let light = lightNtt.AddComponent<LightComponent>();
light.SceneLight.Illuminance = 10.0f;
light.SceneLight.Color = .(1.0f, 0.95f, 0.8f);
}
{
var cameraNtt = _scene.CreateEntity("My Camera");
let transform = cameraNtt.GetComponent<TransformComponent>();
transform.Position = .(5, 5, -5);
transform.RotationEuler = .(MathHelper.ToRadians(45), MathHelper.ToRadians(-45), 0);
let camera = cameraNtt.AddComponent<CameraComponent>();
camera.Primary = true;
camera.Camera.SetPerspective(MathHelper.ToRadians(45), 0.1f, 10.0f);
camera.RenderTarget = _cameraTarget;
}
var fxLib = Application.Get().EffectLibrary;
using (Effect myEffect = fxLib.Load("content/Shaders/myEffect.hlsl"))
/*using (Texture2D albedo = new Texture2D("Textures/White.png", true))
using (Texture2D normal = new Texture2D("Textures/White.png"))
using (Texture2D rough = new Texture2D("Textures/White.png"))
using (Texture2D metal = new Texture2D("Textures/White.png"))*/
using (Texture2D albedo = new Texture2D("Textures/TestMat/rustediron2_albedo.png", true))
using (Texture2D normal = new Texture2D("Textures/TestMat/rustediron2_normal.png"))
using (Texture2D rough = new Texture2D("Textures/TestMat/rustediron2_roughness.png"))
using (Texture2D metal = new Texture2D("Textures/TestMat/rustediron2_metallic.png"))
{
albedo.SamplerState = SamplerStateManager.AnisotropicWrap;
normal.SamplerState = SamplerStateManager.AnisotropicWrap;
rough.SamplerState = SamplerStateManager.AnisotropicWrap;
metal.SamplerState = SamplerStateManager.AnisotropicWrap;
List<AnimationClip> clips = scope .();
using (Material mat = new .(myEffect))
{
mat.SetTexture("AlbedoTexture", albedo);
mat.SetTexture("NormalTexture", normal);
mat.SetTexture("MetallicTexture", metal);
mat.SetTexture("RoughnessTexture", rough);
mat.SetVariable("AlbedoColor", ColorRGBA.White);
mat.SetVariable("NormalScaling", Vector2.One);
mat.SetVariable("MetallicFactor", 1.0f);
mat.SetVariable("RoughnessFactor", 1.0f);
// TODO: completely wrong!
EcsEntity e = ModelLoader.LoadModel("content/Models/sphere.glb", mat, _scene.[Friend]_ecsWorld, clips, "Sphere 1");
Entity entity = .(e, _scene);
var transform = entity.GetComponent<TransformComponent>();
transform.Position = .(5, 0, 5);
}
/*using (Material mat = new .(myEffect))
{
mat.SetTexture("AlbedoTexture", albedo);
mat.SetTexture("NormalTexture", normal);
mat.SetTexture("MetallicTexture", metal);
mat.SetTexture("RoughnessTexture", rough);
//mat.SetVariable("BaseColor", Vector4(1, 1, 0, 1));
//mat.SetVariable("LightDir", Vector3(0, 1, 0).Normalized());
ModelLoader.LoadModel("content/Models/sphere.glb", myEffect, mat, _scene.[Friend]_ecsWorld, clips);
}*/
ClearAndReleaseItems!(clips);
}
using (Effect myEffect = fxLib.Get("myEffect"))
using (Texture2D white = new Texture2D("Textures/White.png"))
using (Texture2D normal = new Texture2D("Textures/DefaultNormal.png"))
{
white.SamplerState = SamplerStateManager.PointClamp;
normal.SamplerState = SamplerStateManager.PointClamp;
List<AnimationClip> clips = scope .();
for (int x < 10)
for (int y < 10)
{
using (Material mat = new .(myEffect))
{
mat.SetTexture("AlbedoTexture", white);
mat.SetTexture("NormalTexture", normal);
mat.SetTexture("MetallicTexture", white);
mat.SetTexture("RoughnessTexture", white);
mat.SetVariable("AlbedoColor", Vector4(1, 0, 0, 1));
mat.SetVariable("NormalScaling", Vector2(1.0f));
mat.SetVariable("RoughnessFactor", (x + 1) / 10.0f);
mat.SetVariable("MetallicFactor", y / 9.0f);
EcsEntity e = ModelLoader.LoadModel("content/Models/sphere.glb", mat, _scene.[Friend]_ecsWorld, clips, scope $"Sphere {x} {y}");
Entity entity = .(e, _scene);
var transform = entity.GetComponent<TransformComponent>();
transform.Position = .(x * 1.5f, y * 1.5f, 0);
}
}
ClearAndReleaseItems!(clips);
}*/
} }
/// Creates a new scene. /// Creates a new scene.
private void NewScene() private void NewScene()
{ {
OnSceneStop();
SceneFilePath = null; SceneFilePath = null;
delete _scene; Scene newScene = new Scene();
_scene = new Scene();
_editor.CurrentScene = _scene;
var vpSize = _editor.SceneViewportWindow.ViewportSize;
_scene.OnViewportResize((.)vpSize.X, (.)vpSize.Y);
_camera.Position = .(-1.5f, 1.5f, -2.5f); _camera.Position = .(-1.5f, 1.5f, -2.5f);
_camera.RotationEuler = .(MathHelper.ToRadians(25), MathHelper.ToRadians(35), 0); _camera.RotationEuler = .(MathHelper.ToRadians(25), MathHelper.ToRadians(35), 0);
// Create default camera // Create a default camera
/*{
let camEntity = _scene.CreateEntity("Camera");
let transform = camEntity.Transform;
transform.Position =
}*/
/*// Create the default light source
{ {
let lightNtt = _scene.CreateEntity("Light"); let cameraEntity = newScene.CreateEntity("Camera");
let transform = lightNtt.Transform; let transform = cameraEntity.Transform;
transform.Position = .(0, 0, 0); transform.Position = Vector3(0, 2, -5);
transform.RotationEuler = .(MathHelper.ToRadians(45), MathHelper.ToRadians(-100), 0); transform.RotationEuler = Vector3(0, MathHelper.ToRadians(25), 0);
let light = lightNtt.AddComponent<LightComponent>(); let camera = cameraEntity.AddComponent<CameraComponent>();
camera.Primary = true;
camera.Camera.ProjectionType = .InfinitePerspective;
camera.Camera.PerspectiveFovY = MathHelper.ToRadians(75);
camera.Camera.PerspectiveNearPlane = 0.1f;
}
// Create a default light source
{
let lightEntity = newScene.CreateEntity("Light");
let transform = lightEntity.Transform;
transform.Position = .(-3, 4, -1.5f);
transform.RotationEuler = .(MathHelper.ToRadians(20), MathHelper.ToRadians(75), MathHelper.ToRadians(20));
let light = lightEntity.AddComponent<LightComponent>();
light.SceneLight.Illuminance = 10.0f; light.SceneLight.Illuminance = 10.0f;
light.SceneLight.Color = .(1.0f, 0.95f, 0.8f); light.SceneLight.Color = .(1.0f, 0.95f, 0.8f);
} }
TestEntitiesWithModels();*/ _editorScene.ReleaseRef();
_editorScene = newScene;
_editor.CurrentScene = _editorScene;
var vpSize = _editor.SceneViewportWindow.ViewportSize;
_editorScene.OnViewportResize((.)vpSize.X, (.)vpSize.Y);
SetReference!(_activeScene, _editorScene);
} }
/// Saves the scene in the file that is was loaded from or saved to last. If there is no such path (i.e. it is a new scene) the save file dialog will open. /// Saves the scene in the file that is was loaded from or saved to last. If there is no such path (i.e. it is a new scene) the save file dialog will open.
private void SaveScene() private void SaveScene()
{ {
if (String.IsNullOrWhiteSpace(SceneFilePath)) if (SceneFilePath.IsWhiteSpace)
{ {
SaveSceneAs(); SaveSceneAs();
return; return;
} }
SceneSerializer serializer = scope .(_scene); SceneSerializer serializer = scope .(_editorScene);
serializer.Serialize(SceneFilePath); serializer.Serialize(SceneFilePath);
} }
@@ -565,16 +607,20 @@ namespace GlitchyEditor
/// Loads the given scene file. /// Loads the given scene file.
private void LoadSceneFile(StringView filename) private void LoadSceneFile(StringView filename)
{ {
OnSceneStop();
SceneFilePath = scope String(filename); SceneFilePath = scope String(filename);
delete _scene; _editorScene.ReleaseRef();
_scene = new Scene(); _editorScene = new Scene();
_editor.CurrentScene = _scene; _editor.CurrentScene = _editorScene;
var vpSize = _editor.SceneViewportWindow.ViewportSize; var vpSize = _editor.SceneViewportWindow.ViewportSize;
_scene.OnViewportResize((.)vpSize.X, (.)vpSize.Y); _editorScene.OnViewportResize((.)vpSize.X, (.)vpSize.Y);
SceneSerializer serializer = scope .(_scene); SceneSerializer serializer = scope .(_editorScene);
serializer.Deserialize(SceneFilePath); serializer.Deserialize(SceneFilePath);
SetReference!(_activeScene, _editorScene);
} }
private void DrawMainMenuBar() private void DrawMainMenuBar()
@@ -610,25 +656,27 @@ namespace GlitchyEditor
if(ImGui.BeginMenu("View", true)) if(ImGui.BeginMenu("View", true))
{ {
if(ImGui.MenuItem(EntityHierarchyWindow.s_WindowTitle))
{
_editor.EntityHierarchyWindow.Open = true;
}
if(ImGui.MenuItem(ComponentEditWindow.s_WindowTitle)) if(ImGui.MenuItem(ComponentEditWindow.s_WindowTitle))
{
_editor.ComponentEditWindow.Open = true; _editor.ComponentEditWindow.Open = true;
}
if(ImGui.MenuItem(SceneViewportWindow.s_WindowTitle)) if(ImGui.MenuItem(ContentBrowserWindow.s_WindowTitle))
{ _editor.ComponentEditWindow.Open = true;
if(ImGui.MenuItem(EditorViewportWindow.s_WindowTitle))
_editor.SceneViewportWindow.Open = true; _editor.SceneViewportWindow.Open = true;
}
if(ImGui.MenuItem(EntityHierarchyWindow.s_WindowTitle))
_editor.EntityHierarchyWindow.Open = true;
if(ImGui.MenuItem(GameViewportWindow.s_WindowTitle))
_editor.GameViewportWindow.Open = true;
if(ImGui.MenuItem(PropertiesWindow.s_WindowTitle))
_editor.PropertiesWindow.Open = true;
ImGui.EndMenu(); ImGui.EndMenu();
} }
ImGui.EndMainMenuBar(); ImGui.EndMainMenuBar();
} }
@@ -637,7 +685,7 @@ namespace GlitchyEditor
return false; return false;
} }
private void ViewportSizeChanged(Object sender, Vector2 viewportSize) private void EditorViewportSizeChanged(Object sender, Vector2 viewportSize)
{ {
uint32 sizeX = (uint32)viewportSize.X; uint32 sizeX = (uint32)viewportSize.X;
uint32 sizeY = (uint32)viewportSize.Y; uint32 sizeY = (uint32)viewportSize.Y;
@@ -645,11 +693,27 @@ namespace GlitchyEditor
if(sizeX == 0 || sizeY == 0) if(sizeX == 0 || sizeY == 0)
return; return;
_viewportTarget.Resize(sizeX, sizeY); _editorViewportTarget.Resize(sizeX, sizeY);
_cameraTarget.Resize(sizeX, sizeY); _cameraTarget.Resize(sizeX, sizeY);
_scene.OnViewportResize(sizeX, sizeY);
_camera.OnViewportResize(sizeX, sizeY); _camera.OnViewportResize(sizeX, sizeY);
_editorSceneRenderer.OnViewportResize(sizeX, sizeY);
}
private void GameViewportSizeChanged(Object sender, Vector2 viewportSize)
{
uint32 sizeX = (uint32)viewportSize.X;
uint32 sizeY = (uint32)viewportSize.Y;
if(sizeX == 0 || sizeY == 0)
return;
_gameViewportTarget.Resize(sizeX, sizeY);
_activeScene.OnViewportResize(sizeX, sizeY);
_gameSceneRenderer.OnViewportResize(sizeX, sizeY);
} }
private bool OnKeyPressed(KeyPressedEvent e) private bool OnKeyPressed(KeyPressedEvent e)
+2
View File
@@ -34,6 +34,8 @@ namespace GlitchyEngine
public bool IsMinimized => _isMinimized; public bool IsMinimized => _isMinimized;
public GameTime GameTime => _gameTime;
[Inline] [Inline]
public static Application Get() => s_Instance; public static Application Get() => s_Instance;
@@ -11,6 +11,7 @@ namespace GlitchyEngine.Collections
{ {
public T Value; public T Value;
public Self Parent;
public List<Self> Children = new .() ~ DeleteContainerAndItems!(_); public List<Self> Children = new .() ~ DeleteContainerAndItems!(_);
public this() {} public this() {}
@@ -29,6 +30,7 @@ namespace GlitchyEngine.Collections
} }
Self newChild = new .(value); Self newChild = new .(value);
newChild.Parent = this;
Children.Add(newChild); Children.Add(newChild);
+19 -10
View File
@@ -8,8 +8,10 @@ using System.IO;
namespace GlitchyEngine.Content; namespace GlitchyEngine.Content;
[BonTarget] [BonTarget]
class Asset : RefCounter abstract class Asset : RefCounter
{ {
internal AssetHandle _handle = .Invalid;
private append String _identifier; private append String _identifier;
internal IContentManager _contentManager; internal IContentManager _contentManager;
@@ -20,19 +22,18 @@ class Asset : RefCounter
public StringView Identifier public StringView Identifier
{ {
get => _identifier; get => _identifier;
set internal set => _identifier.Set(value);
{ }
_contentManager?.UpdateAssetIdentifier(this, _identifier, value);
_identifier.Set(value); /// If true the asset is completely loaded. If false it is only partially loaded (if at all).
// TODO: do we need to tell the content manager, that the name changed? public bool Complete { get; internal set; }
}
}
// TODO: do we need unmanaged assets? Probably not... // TODO: do we need unmanaged assets? Probably not...
/// Gets the content manager that manages this asset; or null if this asset isn't managed. /// Gets the content manager that manages this asset; or null if this asset isn't managed.
public IContentManager ContentManager => _contentManager; public IContentManager ContentManager => _contentManager;
public AssetHandle Handle => _handle;
static this static this
{ {
gBonEnv.typeHandlers.Add(typeof(Asset), gBonEnv.typeHandlers.Add(typeof(Asset),
@@ -43,7 +44,7 @@ class Asset : RefCounter
{ {
// TODO: crash when _contentManager is deleted first... // TODO: crash when _contentManager is deleted first...
// TODO: unregister from content manager // TODO: unregister from content manager
_contentManager?.UnmanageAsset(this); //_contentManager?.UnmanageAsset(this);
} }
static void AssetSerialize(BonWriter writer, ValueView value, BonEnvironment environment, SerializeValueState state) static void AssetSerialize(BonWriter writer, ValueView value, BonEnvironment environment, SerializeValueState state)
@@ -62,7 +63,15 @@ class Asset : RefCounter
Deserialize.String!(reader, ref identifier, environment); Deserialize.String!(reader, ref identifier, environment);
Asset asset = Application.Get().ContentManager.LoadAsset(identifier); AssetHandle handle = Content.LoadAsset(identifier);
if (handle == .Invalid)
{
value.Assign<Asset>(null);
return .Ok;
}
Asset asset = Content.GetAsset<Asset>(handle);
if (asset != null) if (asset != null)
{ {
+336
View File
@@ -0,0 +1,336 @@
using Bon;
using Bon.Integrated;
using System;
using xxHash;
using System.Collections;
using System.Reflection;
using System.Diagnostics;
using GlitchyEngine.Core;
namespace GlitchyEngine.Content;
struct AssetHandle : IHashable
{
private UUID _uuid;
/// Defines an asset that is invalid.
public const AssetHandle Invalid = .(UUID(0));
public bool IsValid => this != .Invalid;
public bool IsInvalid => this == .Invalid;
/// Create a new random AssetHandle
public this()
{
_uuid = UUID();
}
private this(UUID uuid)
{
_uuid = uuid;
}
static this
{
gBonEnv.typeHandlers.Add(typeof(AssetHandle),
((.)new => AssetSerialize, new => AssetDeserialize));
}
[Inline]
public T Get<T>(IContentManager contentManager = null) where T : Asset
{
return Content.GetAsset<T>(this, contentManager);
}
public int GetHashCode() => _uuid.GetHashCode();
static void AssetSerialize(BonWriter writer, ValueView value, BonEnvironment environment, SerializeValueState state)
{
Log.EngineLogger.Assert(value.type == typeof(AssetHandle));
AssetHandle handle = value.Get<AssetHandle>();
if (handle.IsInvalid)
writer.String("");
else
{
let identifier = handle.Get<Asset>().Identifier;
writer.String(identifier);
}
}
static Result<void> AssetDeserialize(BonReader reader, ValueView value, BonEnvironment environment, DeserializeValueState state)
{
Log.EngineLogger.Assert(value.type == typeof(AssetHandle));
String identifier = scope .();
Deserialize.String!(reader, ref identifier, environment);
AssetHandle handle = Content.LoadAsset(identifier);
value.Assign<AssetHandle>(handle);
return .Ok;
}
}
struct AssetHandle<T> where T : Asset
{
private AssetHandle _handle;
private IContentManager _contentManager;
/*
* We don't increment/decrement the reference counter since we guarantee that we query for the asset every frame.
*/
private T _asset;
//private uint8 _currentFrame;
//private uint64 _actualCurrentFrame = 0;
public const Self Invalid = .();
public bool IsValid => this != .Invalid;
public bool IsInvalid => this == .Invalid;
public this(AssetHandle handle, IContentManager contentManager = null)
{
_handle = handle;
_asset = handle.Get<T>(contentManager);
_contentManager = _asset?.ContentManager;
if (_contentManager == null)
_contentManager = contentManager;
//_currentFrame = (uint8)Application.Get().GameTime.FrameCount;
}
// Creates a new invalid asset handle
private this()
{
_handle = .Invalid;
//_currentFrame = 0;
_contentManager = null;
_asset = null;
}
static this
{
gBonEnv.typeHandlers.Add(typeof(AssetHandle<T>),
((.)new => AssetSerialize, new => AssetDeserialize));
}
public static implicit operator Self(AssetHandle handle)
{
return Self(handle);
}
public static implicit operator AssetHandle(Self handle)
{
return handle._handle;
}
public static implicit operator T(ref Self handle)
{
return handle.Get();
}
public T Get(IContentManager contentManager = null)
{
return Content.GetAsset<T>(_handle, contentManager == null ? _contentManager : contentManager);
}
[Comptime, OnCompile(.TypeInit)]
static void Init()
{
for (var field in typeof(T).GetFields(BindingFlags.FlattenHierarchy))
{
if (field.IsStatic || !field.IsPublic)
continue;
String modifier = field.IsPublic ? "public" : "private";
String code = scope $"""
{modifier} {field.FieldType} {field.Name}
{{
get mut
{{
return Get().{field.Name};
}}
set mut
{{
Get().{field.Name} = value;
}}
}}
""";
Compiler.EmitTypeBody(typeof(Self), code);
}
Dictionary<StringView, (MethodInfo? Getter, MethodInfo? Setter)> properties = scope .();
for (var method in typeof(T).GetMethods(BindingFlags.FlattenHierarchy | BindingFlags.Public))
{
if (method.IsStatic || !method.IsPublic || method.IsConstructor || method.IsDestructor)
continue;
// Filter out destructors
if (method.Name == "~this")
continue;
if (method.Name.StartsWith("get__"))
{
StringView name = method.Name;
name.RemoveFromStart(5);
if (!properties.TryGetValue(name, var propertyInfo))
{
propertyInfo = default;
}
propertyInfo.Getter = method;
properties[name] = propertyInfo;
continue;
}
if (method.Name.StartsWith("set__"))
{
StringView name = method.Name;
name.RemoveFromStart(5);
if (!properties.TryGetValue(name, var propertyInfo))
{
propertyInfo = default;
}
propertyInfo.Setter = method;
properties[name] = propertyInfo;
continue;
}
String modifier = method.IsPublic ? "public" : "private";
String parameters = scope String();
String arguments = scope String();
for (int param < method.ParamCount)
{
Type paramType = method.GetParamType(param);
StringView paramName = method.GetParamName(param);
/*String buffer = scope .();
method.GetParamsDecl(buffer);*/
if (param != 0)
{
parameters.Append(", ");
arguments.Append(", ");
}
// TODO: Default value
parameters.AppendF($"{paramType} {paramName}");
/*if (!buffer.IsEmpty)
{
parameters.AppendF($"/*{buffer}*/");
}*/
arguments.AppendF($" {paramName}");
}
String code = scope $"""
{modifier} {method.ReturnType} {method.Name}({parameters}) mut
{{
return Get().{method.Name}({arguments});
}}
""";
Compiler.EmitTypeBody(typeof(Self), code);
}
for (var (propertyName, property) in properties)
{
Type propertyType = property.Getter?.ReturnType ?? property.Setter?.GetParamType(0);
String getter = scope .();
String setter = scope .();
if (property.Getter != null)
{
getter.AppendF($"""
get mut
{{
return Get().{propertyName};
}}
""");
}
if (property.Setter != null)
{
getter.AppendF($"""
set mut
{{
Get().{propertyName} = value;
}}
""");
}
String code = scope $"""
public {propertyType} {propertyName}
{{
{getter}{setter}
}}
""";
Compiler.EmitTypeBody(typeof(Self), code);
}
}
public AssetHandle<NewT> Cast<NewT>()
where NewT : Asset
where T : NewT
{
return AssetHandle<NewT>(this._handle, this._contentManager);
}
// TODO: Cast up?
public AssetHandle<NewT> Cast<NewT>() where NewT : T
{
return AssetHandle<NewT>(this._handle, this._contentManager);
}
static void AssetSerialize(BonWriter writer, ValueView value, BonEnvironment environment, SerializeValueState state)
{
Log.EngineLogger.Assert(value.type == typeof(AssetHandle<T>));
AssetHandle handle = value.Get<AssetHandle<T>>();
if (handle.IsInvalid)
writer.String("");
else
{
let identifier = handle.Get<Asset>().Identifier;
writer.String(identifier);
}
}
static Result<void> AssetDeserialize(BonReader reader, ValueView value, BonEnvironment environment, DeserializeValueState state)
{
Log.EngineLogger.Assert(value.type == typeof(AssetHandle<T>));
String identifier = scope .();
Deserialize.String!(reader, ref identifier, environment);
AssetHandle<T> handle = Content.LoadAsset(identifier);
value.Assign<AssetHandle<T>>(handle);
return .Ok;
}
}
+58 -16
View File
@@ -57,40 +57,82 @@ namespace GlitchyEngine.Content
/// @param contentManager The content manager used to load the asset. /// @param contentManager The content manager used to load the asset.
/// @returns The loaded asset. /// @returns The loaded asset.
Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager); Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager);
/// Returns the placeholder asset.
Asset GetPlaceholderAsset(Type assetType);
/// Returns the error asset.
Asset GetErrorAsset(Type assetType);
} }
static static class Content
{ {
/// Loads the specified asset with the given contentManager or the current applications content manager. /// Loads the specified asset with the given contentManager or the current applications content manager.
public static T LoadAsset<T>(StringView assetIdentifier, IContentManager contentManager = null) where T : Asset public static AssetHandle LoadAsset(StringView assetIdentifier, IContentManager contentManager = null, bool blocking = false)
{ {
var contentManager; var contentManager;
if (contentManager == null) if (contentManager == null)
contentManager = Application.Get().ContentManager; contentManager = Application.Get().ContentManager;
Asset asset = contentManager.LoadAsset(assetIdentifier); AssetHandle handle = contentManager.LoadAsset(assetIdentifier, blocking);
Log.EngineLogger.AssertDebug(asset is T); return handle;
}
/// Loads the specified asset with the given contentManager or the current applications content manager.
public static T GetAsset<T>(AssetHandle handle, IContentManager contentManager = null) where T : Asset
{
var contentManager;
if (contentManager == null)
contentManager = Application.Get().ContentManager;
Asset asset = contentManager.GetAsset(typeof(T), handle);
return (T)asset; return (T)asset;
} }
public static AssetHandle ManageAsset(Asset asset, IContentManager contentManager = null)
{
var contentManager;
if (contentManager == null)
contentManager = Application.Get().ContentManager;
return contentManager.ManageAsset(asset);
}
/*public static AssetHandle<T> ManageAsset<T>(T asset, IContentManager contentManager = null) where T : Asset
{
var contentManager;
if (contentManager == null)
contentManager = Application.Get().ContentManager;
contentManager.ManageAsset(asset);
}*/
} }
interface IContentManager interface IContentManager
{ {
/// Loads the given asset. /// Loads the Asset with the given handle and returns the handle.
Asset LoadAsset(StringView assetIdentifier); AssetHandle LoadAsset(StringView assetIdentifier, bool blocking = false);
/// Returns the asset for the given handle or null, if it isn't loaded.
Asset GetAsset(AssetHandle handle)
{
return GetAsset(null, handle);
}
/// Returns the asset for the given handle or the default asset of the given type.
Asset GetAsset(Type assetType, AssetHandle handle);
/// The content manager will manage the asset (e.g. provide it when LoadAsset is called with the assets identifier) /// The content manager will manage the asset (e.g. provide it when LoadAsset is called with the assets identifier)
void ManageAsset(Asset asset); AssetHandle ManageAsset(Asset asset);
/// The content manager will no longer manage the asset. /// The content manager will no longer manage the asset.
void UnmanageAsset(Asset asset); void UnmanageAsset(AssetHandle asset);
// TODO: Maybe calling UnmanageAsset -> ManageAsset is enough....
/// Provides a method for the asset to tell its content manager that the identifer changed.
void UpdateAssetIdentifier(Asset asset, StringView oldIdentifier, StringView newIdentifier);
/// Returns a data stream for the given asset. /// Returns a data stream for the given asset.
Stream GetStream(StringView assetIdentifier); Stream GetStream(StringView assetIdentifier);
@@ -109,22 +151,22 @@ namespace GlitchyEngine.Content
Runtime.NotImplemented(); Runtime.NotImplemented();
} }
public Asset LoadAsset(StringView assetIdentifier) public AssetHandle LoadAsset(StringView assetIdentifier, bool blocking = false)
{ {
Runtime.NotImplemented(); Runtime.NotImplemented();
} }
public void ManageAsset(Asset asset) public Asset GetAsset(Type assetType, AssetHandle handle)
{ {
Runtime.NotImplemented(); Runtime.NotImplemented();
} }
public void UnmanageAsset(Asset asset) public AssetHandle ManageAsset(Asset asset)
{ {
Runtime.NotImplemented(); Runtime.NotImplemented();
} }
public void UpdateAssetIdentifier(Asset asset, StringView oldIdentifier, StringView newIdentifier) public void UnmanageAsset(AssetHandle asset)
{ {
Runtime.NotImplemented(); Runtime.NotImplemented();
} }
-150
View File
@@ -229,156 +229,6 @@ namespace GlitchyEngine.Content
return .Success; return .Success;
} }
public static EcsEntity LoadModel(String filename, Material material, EcsWorld world,
List<AnimationClip> outClips, StringView entityName = StringView())
{
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");
(EcsEntity entity, ?) = CreateEntity(world, entityName, .InvalidEntity);
for(var node in data.Scenes[0].Nodes)
{
NodesToEntities(data, node, entity, world, material, outClips);
}
CGLTF.Free(data);
return entity;
}
private static (EcsEntity Entity, TransformComponent* Transform) CreateEntity(EcsWorld world, StringView? name, EcsEntity parent)
{
EcsEntity entity = world.NewEntity();
var nameComponent = world.AssignComponent<DebugNameComponent>(entity);
if (name != null && name.Value.Ptr != null)
{
nameComponent.SetName(name.Value);
}
else
{
nameComponent.SetName("Unnamed Node");
}
/////TODO: !!!!!!!!REPORT!!!!!!!!!!!!!
// This works
TransformComponent cmp = .();
var childTransform = world.AssignComponent<TransformComponent>(entity, cmp);
// This trashes the stack
//var childTransform = world.AssignComponent<TransformComponent>(entity);
childTransform.Parent = parent;
return (entity, childTransform);
}
private static void NodesToEntities(CGLTF.Data* data, CGLTF.Node* node, EcsEntity parentEntity, EcsWorld world, Material material, List<AnimationClip> clips)
{
(EcsEntity entity, TransformComponent* childTransform) = CreateEntity(world, node.Name == null ? null : StringView(node.Name), parentEntity);
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 == .InvalidEntity)
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]))
{
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);
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, material, clips);
}
}
public static GeometryBinding PrimitiveToGeoBinding(CGLTF.Primitive primitive) public static GeometryBinding PrimitiveToGeoBinding(CGLTF.Primitive primitive)
{ {
GeometryBinding binding = new GeometryBinding(); GeometryBinding binding = new GeometryBinding();
@@ -20,12 +20,11 @@ namespace GlitchyEngine.Generators
outFileName.Append(name); outFileName.Append(name);
outText.AppendF( outText.AppendF(
$""" $"""
namespace {Namespace} namespace {Namespace};
{{
struct {name} struct {name}
{{ {{
}} }}
}}
"""); """);
} }
} }
-1
View File
@@ -4,5 +4,4 @@ namespace GlitchyEngine.Math
{ {
typealias Matrix3x3 = DirectX.Math.Matrix3x3; typealias Matrix3x3 = DirectX.Math.Matrix3x3;
typealias Matrix4x3 = DirectX.Math.Matrix4x3; typealias Matrix4x3 = DirectX.Math.Matrix4x3;
typealias Matrix = DirectX.Math.Matrix;
} }
+877
View File
@@ -0,0 +1,877 @@
using System;
namespace GlitchyEngine.Math;
/**
* Represents a 4 by 4 column-major matrix.
*/
[Union]
public struct Matrix
{
public struct Values
{
public float _11, _21, _31, _41,
_12, _22, _32, _42,
_13, _23, _33, _43,
_14, _24, _34, _44;
}
public const Matrix Zero = .();
public const Matrix Identity = .(.UnitX, .UnitY, .UnitZ, .UnitW);
public using Values V;
public float[4][4] Values;
public Vector4[4] Columns;
/// Creates a new zero-matrix.
public this() => this = default;
/**
* Initializes a new Matrix.
* @param value The value that will be assigned to all components.
*/
/// Creates a new matrix.
public this(float value)
{
this = ?;
_11 = _12 = _13 = _14 =
_21 = _22 = _23 = _24 =
_31 = _32 = _33 = _34 =
_41 = _42 = _43 = _44 = value;
}
/// Creates a new matrix and initializes it with the given entries.
public this(float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33)
{
Values[0][0] = m00; Values[0][1] = m10; Values[0][2] = m20; Values[0][3] = m30;
Values[1][0] = m01; Values[1][1] = m11; Values[1][2] = m21; Values[1][3] = m31;
Values[2][0] = m02; Values[2][1] = m12; Values[2][2] = m22; Values[2][3] = m32;
Values[3][0] = m03; Values[3][1] = m13; Values[3][2] = m23; Values[3][3] = m33;
}
/// Creates a new matrix and initializes it with the given column-vectors.
public this(Vector4 c0, Vector4 c1, Vector4 c2, Vector4 c3)
{
Columns[0] = c0;
Columns[1] = c1;
Columns[2] = c2;
Columns[3] = c3;
}
public ref Vector3 Right
{
[Inline]
get
{
#unwarn
return ref *(Vector3*)&Columns[0];
}
}
public ref Vector3 Up
{
[Inline]
get
{
#unwarn
return ref *(Vector3*)&Columns[1];
}
}
public ref Vector3 Forward
{
[Inline]
get
{
#unwarn
return ref *(Vector3*)&Columns[2];
}
}
public ref Vector3 Translation
{
[Inline]
get
{
#unwarn
return ref *(Vector3*)&Columns[3];
}
}
public Vector3 Scale
{
[Inline]
get => .(_11, _22, _33);
[Inline]
set mut
{
_11 = value.X;
_22 = value.Y;
_33 = value.Z;
}
}
public ref float this[int row, int column]
{
get
{
#unwarn
return ref *(float*)&Values[column][row];
}
[Checked]
get
{
if(column < 0 || column > 3 || row < 0 || row > 3)
Internal.ThrowIndexOutOfRange();
#unwarn
return ref *(float*)&Values[column][row];
}
}
public ref Vector4 this[int column]
{
get
{
#unwarn
return ref *(Vector4*)&Columns[column];
}
[Checked]
get
{
if(column < 0 || column > 3)
Internal.ThrowIndexOutOfRange();
#unwarn
return ref *(Vector4*)&Columns[column];
}
}
//
// Assignment Operators
//
// Addition
public void operator +=(Matrix value) mut
{
Columns[0] += value.Columns[0];
Columns[1] += value.Columns[1];
Columns[2] += value.Columns[2];
Columns[3] += value.Columns[3];
}
// Matrix + Scalar : Matrix + Scalar * Identity
public void operator +=(float scalar) mut
{
_11 += scalar;
_22 += scalar;
_33 += scalar;
_44 += scalar;
}
// Subtraction
public void operator -=(Matrix value) mut
{
Columns[0] -= value.Columns[0];
Columns[1] -= value.Columns[1];
Columns[2] -= value.Columns[2];
Columns[3] -= value.Columns[3];
}
// Matrix - Scalar : Matrix - Scalar * Identity
public void operator -=(float scalar) mut
{
_11 -= scalar;
_22 -= scalar;
_33 -= scalar;
_44 -= scalar;
}
// Multiplication
public void operator *=(float scalar) mut
{
Columns[0] *= scalar;
Columns[1] *= scalar;
Columns[2] *= scalar;
Columns[3] *= scalar;
}
public void operator *=(Matrix value) mut
{
this = this * value;
}
// Divide
public void operator /=(float scalar) mut
{
float inv = 1.0f / scalar;
Columns[0] *= inv;
Columns[1] *= inv;
Columns[2] *= inv;
Columns[3] *= inv;
}
//
// Operators
//
// Addition
public static Matrix operator +(Matrix left, Matrix right)
{
return .(left.Columns[0] + right.Columns[0],
left.Columns[1] + right.Columns[1],
left.Columns[2] + right.Columns[2],
left.Columns[3] + right.Columns[3]);
}
public static Matrix operator +(Matrix left, float right)
{
Matrix result = left;
result._11 += right;
result._22 += right;
result._33 += right;
result._44 += right;
return result;
}
public static Matrix operator +(float left, Matrix right)
{
Matrix result = right;
result._11 += left;
result._22 += left;
result._33 += left;
result._44 += left;
return result;
}
// Subtraction
public static Matrix operator -(Matrix left, Matrix right)
{
return .(left.Columns[0] - right.Columns[0],
left.Columns[1] - right.Columns[1],
left.Columns[2] - right.Columns[2],
left.Columns[3] - right.Columns[3]);
}
public static Matrix operator -(Matrix value, float scalar)
{
Matrix result = value;
result._11 -= scalar;
result._22 -= scalar;
result._33 -= scalar;
result._44 -= scalar;
return result;
}
public static Matrix operator -(float scalar, Matrix value)
{
Matrix result = value;
result._11 -= scalar;
result._22 -= scalar;
result._33 -= scalar;
result._44 -= scalar;
return result;
}
public static Matrix operator -(Matrix value)
{
return .(-value.Columns[0],
-value.Columns[1],
-value.Columns[2],
-value.Columns[3]);
}
// Multiplication
public static Matrix operator *(Matrix left, Matrix right)
{
#unwarn
var l = &left.V;
#unwarn
var r = &right.V;
Matrix result = ?;
result._11 = (l._11 * r._11) + (l._12 * r._21) + (l._13 * r._31) + (l._14 * r._41);
result._12 = (l._11 * r._12) + (l._12 * r._22) + (l._13 * r._32) + (l._14 * r._42);
result._13 = (l._11 * r._13) + (l._12 * r._23) + (l._13 * r._33) + (l._14 * r._43);
result._14 = (l._11 * r._14) + (l._12 * r._24) + (l._13 * r._34) + (l._14 * r._44);
result._21 = (l._21 * r._11) + (l._22 * r._21) + (l._23 * r._31) + (l._24 * r._41);
result._22 = (l._21 * r._12) + (l._22 * r._22) + (l._23 * r._32) + (l._24 * r._42);
result._23 = (l._21 * r._13) + (l._22 * r._23) + (l._23 * r._33) + (l._24 * r._43);
result._24 = (l._21 * r._14) + (l._22 * r._24) + (l._23 * r._34) + (l._24 * r._44);
result._31 = (l._31 * r._11) + (l._32 * r._21) + (l._33 * r._31) + (l._34 * r._41);
result._32 = (l._31 * r._12) + (l._32 * r._22) + (l._33 * r._32) + (l._34 * r._42);
result._33 = (l._31 * r._13) + (l._32 * r._23) + (l._33 * r._33) + (l._34 * r._43);
result._34 = (l._31 * r._14) + (l._32 * r._24) + (l._33 * r._34) + (l._34 * r._44);
result._41 = (l._41 * r._11) + (l._42 * r._21) + (l._43 * r._31) + (l._44 * r._41);
result._42 = (l._41 * r._12) + (l._42 * r._22) + (l._43 * r._32) + (l._44 * r._42);
result._43 = (l._41 * r._13) + (l._42 * r._23) + (l._43 * r._33) + (l._44 * r._43);
result._44 = (l._41 * r._14) + (l._42 * r._24) + (l._43 * r._34) + (l._44 * r._44);
return result;
}
public static Matrix operator *(Matrix value, float scalar)
{
return .(value.Columns[0] * scalar,
value.Columns[1] * scalar,
value.Columns[2] * scalar,
value.Columns[3] * scalar);
}
public static Matrix operator *(float scalar, Matrix value)
{
return .(value.Columns[0] * scalar,
value.Columns[1] * scalar,
value.Columns[2] * scalar,
value.Columns[3] * scalar);
}
/**
* Multiplies a matrix and a column-vector resulting in a column vector.
*/
public static Vector4 operator *(Matrix matrix, Vector4 columnVector)
{
#unwarn
var m = &matrix.V;
Vector4 result = ?;
result.X = (m._11 * columnVector.X) + (m._12 * columnVector.Y) + (m._13 * columnVector.Z) + (m._14 * columnVector.W);
result.Y = (m._21 * columnVector.X) + (m._22 * columnVector.Y) + (m._23 * columnVector.Z) + (m._24 * columnVector.W);
result.Z = (m._31 * columnVector.X) + (m._32 * columnVector.Y) + (m._33 * columnVector.Z) + (m._34 * columnVector.W);
result.W = (m._41 * columnVector.X) + (m._42 * columnVector.Y) + (m._43 * columnVector.Z) + (m._44 * columnVector.W);
return result;
}
/**
* Multiplies a row-vector and a matrix resulting in a row vector.
*/
public static Vector4 operator *(Vector4 rowVector, Matrix matrix)
{
#unwarn
var m = &matrix.V;
Vector4 result = ?;
result.X = (rowVector.X * m._11) + (rowVector.Y * m._21) + (rowVector.Z * m._31) + (rowVector.W * m._41);
result.Y = (rowVector.X * m._12) + (rowVector.Y * m._22) + (rowVector.Z * m._32) + (rowVector.W * m._42);
result.Z = (rowVector.X * m._13) + (rowVector.Y * m._23) + (rowVector.Z * m._33) + (rowVector.W * m._43);
result.W = (rowVector.X * m._14) + (rowVector.Y * m._24) + (rowVector.Z * m._34) + (rowVector.W * m._44);
return result;
}
// Divison
public static Matrix operator /(Matrix m, float s)
{
float f = 1 / s;
Matrix M = m;
return .(M.Columns[0] * f, M.Columns[1] * f, M.Columns[2] * f, M.Columns[3] * f);
}
public static Matrix Scaling(float scale)
{
return .(scale, 0, 0, 0,
0, scale, 0, 0,
0, 0, scale, 0,
0, 0, 0, 1);
}
public static Matrix Scaling(float scaleX, float scaleY, float scaleZ)
{
return .(scaleX, 0, 0, 0,
0, scaleY, 0, 0,
0, 0, scaleZ, 0,
0, 0, 0, 1);
}
public static Matrix Scaling(Vector3 scale)
{
return .(scale.X, 0, 0, 0,
0, scale.Y, 0, 0,
0, 0, scale.Z, 0,
0, 0, 0, 1);
}
public static Matrix Translation(float x, float y, float z)
{
return .(1, 0, 0, x,
0, 1, 0, y,
0, 0, 1, z,
0, 0, 0, 1);
}
public static Matrix Translation(Vector3 translation)
{
return .(1, 0, 0, translation.X,
0, 1, 0, translation.Y,
0, 0, 1, translation.Z,
0, 0, 0, 1);
}
public static Matrix RotationX(float rot)
{
float sin = Math.Sin(rot);
float cos = Math.Cos(rot);
return .(1, 0, 0, 0,
0, cos, -sin, 0,
0, sin, cos, 0,
0, 0, 0, 1);
}
public static Matrix RotationY(float rot)
{
float sin = Math.Sin(rot);
float cos = Math.Cos(rot);
return .(cos, 0, sin, 0,
0, 1, 0, 0,
-sin, 0, cos, 0,
0, 0, 0, 1);
}
public static Matrix RotationZ(float rot)
{
float sin = Math.Sin(rot);
float cos = Math.Cos(rot);
return .(cos, -sin, 0, 0,
sin, cos, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
/**
* Calculates a view Matrix that is located at specified postion and looks at the given target.
* @param position The cameras position.
* @param target The point the camera looks at.
* @param up A vector defining the up direction of the camera.
* @returns a view matrix.
*/
public static Matrix LookAt(Vector3 position, Vector3 target, Vector3 up)
{
Vector3 forward = target - position;
forward.Normalize();
Vector3 right = Vector3.Cross(up, forward);
right.Normalize();
Vector3 newUp = Vector3.Cross(forward, right);
newUp.Normalize();
Matrix result = .Identity;
result.Forward = forward;
result.Up = up;
result.Right = right;
result.Translation.X = -Vector3.Dot(position, right);
result.Translation.Y = -Vector3.Dot(position, up);
result.Translation.Z = -Vector3.Dot(position, forward);
return result;
}
/// Returns the transpose of this matrix
[DisableChecks]
public Matrix Transpose()
{
return .(_11, _21, _31, _41,
_12, _22, _32, _42,
_13, _23, _33, _43,
_14, _24, _34, _44);
}
/**
Calculates the inverse of the matrix.
*/
[DisableChecks]
public float Determinant() mut
{
// From: Lengyel, Eric. Foundations of Game Engine Development, Volume 1: Mathematics (S.61). Kindle-Version.
Vector3 a = *(Vector3*)&Columns[0];
Vector3 b = *(Vector3*)&Columns[1];
Vector3 c = *(Vector3*)&Columns[2];
Vector3 d = *(Vector3*)&Columns[3];
float x = this[3, 0];
float y = this[3, 1];
float z = this[3, 2];
float w = this[3, 3];
Vector3 s = Vector3.Cross(a, b);
Vector3 t = Vector3.Cross(c, d);
Vector3 u = y * a - x * b;
Vector3 v = w * c - z * d;
return Vector3.Dot(s, v) + Vector3.Dot(t, u);
}
/**
Calculates the inverse of the matrix.
*/
public Matrix Invert()
{
// From: Lengyel, Eric. Foundations of Game Engine Development, Volume 1: Mathematics (S.61). Kindle-Version.
#unwarn
Vector3 a = *(Vector3*)&Columns[0];
#unwarn
Vector3 b = *(Vector3*)&Columns[1];
#unwarn
Vector3 c = *(Vector3*)&Columns[2];
#unwarn
Vector3 d = *(Vector3*)&Columns[3];
float x = this[3, 0];
float y = this[3, 1];
float z = this[3, 2];
float w = this[3, 3];
Vector3 s = Vector3.Cross(a, b);
Vector3 t = Vector3.Cross(c, d);
Vector3 u = y * a - x * b;
Vector3 v = w * c - z * d;
float invDet = 1.0f / (Vector3.Dot(s, v) + Vector3.Dot(t, u));
s *= invDet;
t *= invDet;
u *= invDet;
v *= invDet;
Vector3 r0 = Vector3.Cross(b, v) + t * y;
Vector3 r1 = Vector3.Cross(v, a) - t * x;
Vector3 r2 = Vector3.Cross(d, u) + s * w;
Vector3 r3 = Vector3.Cross(u, c) - s * z;
return .(r0.X, r0.Y, r0.Z, -Vector3.Dot(b, t),
r1.X, r1.Y, r1.Z, Vector3.Dot(a, t),
r2.X, r2.Y, r2.Z, -Vector3.Dot(d, s),
r3.X, r3.Y, r3.Z, Vector3.Dot(c, s));
}
/// Calculates the inverse of the matrix.
public static Matrix Invert(in Matrix matrix)
{
// From: Lengyel, Eric. Foundations of Game Engine Development, Volume 1: Mathematics (S.61). Kindle-Version.
#unwarn
Vector3 a = *(Vector3*)&matrix.Columns[0];
#unwarn
Vector3 b = *(Vector3*)&matrix.Columns[1];
#unwarn
Vector3 c = *(Vector3*)&matrix.Columns[2];
#unwarn
Vector3 d = *(Vector3*)&matrix.Columns[3];
float x = matrix[3, 0];
float y = matrix[3, 1];
float z = matrix[3, 2];
float w = matrix[3, 3];
Vector3 s = Vector3.Cross(a, b);
Vector3 t = Vector3.Cross(c, d);
Vector3 u = y * a - x * b;
Vector3 v = w * c - z * d;
float invDet = 1.0f / (Vector3.Dot(s, v) + Vector3.Dot(t, u));
s *= invDet;
t *= invDet;
u *= invDet;
v *= invDet;
Vector3 r0 = Vector3.Cross(b, v) + t * y;
Vector3 r1 = Vector3.Cross(v, a) - t * x;
Vector3 r2 = Vector3.Cross(d, u) + s * w;
Vector3 r3 = Vector3.Cross(u, c) - s * z;
return .(r0.X, r0.Y, r0.Z, -Vector3.Dot(b, t),
r1.X, r1.Y, r1.Z, Vector3.Dot(a, t),
r2.X, r2.Y, r2.Z, -Vector3.Dot(d, s),
r3.X, r3.Y, r3.Z, Vector3.Dot(c, s));
}
/**
* Creates a perspective projection matrix.
* @param fovY The vertical field of view.
* @param aspectRation The aspect ratio of the viewport.
* @param nearPlane The distance to the near plane.
* @param farPlane The distance to the far plane.
*/
public static Matrix PerspectiveProjection(float fovY, float aspectRatio, float nearPlane, float farPlane)
{
// Lengyel, Eric. Foundations of Game Engine Development, Volume 2: Rendering (Seite82). . Kindle-Version.
float g = 1.0f / Math.Tan(fovY * 0.5f);
float k = farPlane / (farPlane - nearPlane);
return .(g / aspectRatio, 0, 0, 0,
0, g, 0, 0,
0, 0, k, -nearPlane * k,
0, 0, 1, 0);
}
/**
* Creates a perspective projection matrix with reversed near- and far plane.
* (i.e. Points on near plane have z-value of 1 and points of far plane have z-value of 0)
* @param fovY The vertical field of view.
* @param aspectRation The aspect ratio of the viewport.
* @param nearPlane The distance to the near plane.
* @param farPlane The distance to the far plane.
*/
public static Matrix ReversedPerspectiveProjection(float fovY, float aspectRatio, float nearPlane, float farPlane)
{
// Lengyel, Eric. Foundations of Game Engine Development, Volume 2: Rendering (Seite86). . Kindle-Version.
float g = 1.0f / Math.Tan(fovY * 0.5f);
float k = nearPlane / (nearPlane - farPlane);
return .(g / aspectRatio, 0, 0, 0,
0, g, 0, 0,
0, 0, k, -farPlane * k,
0, 0, 1, 0);
}
/**
* Creates a perspective projection matrix with a far plane at infinity.
* @param fovY The vertical field of view.
* @param aspectRation The aspect ratio of the viewport.
* @param nearPlane The distance to the near plane.
* @param ε An offset to account for floating point round-off errors at infinity.
* Note: Use a tiny value significant compared to the floating-point value of one.
*/
public static Matrix InfinitePerspectiveProjection(float fovY, float aspectRatio, float nearPlane, float ε = 1e-6f)
{
// Lengyel, Eric. Foundations of Game Engine Development, Volume 2: Rendering (Seite83). . Kindle-Version.
float g = 1.0f / Math.Tan(fovY * 0.5f);
float f = 1 - ε;
return .(g / aspectRatio, 0, 0, 0,
0, g, 0, 0,
0, 0, f, -nearPlane * f,
0, 0, 1, 0);
}
/**
* Creates a perspective projection matrix with a far plane at infinity with reversed near- and far plane.
* (i.e. Points on near plane have z-value of 1 and points of far plane have z-value of 0)
* @param fovY The vertical field of view.
* @param aspectRation The aspect ratio of the viewport.
* @param nearPlane The distance to the near plane.
* @param ε An offset to account for floating point round-off errors at infinity.
* Note: Use a tiny value significant compared to the floating-point value of one.
*/
public static Matrix ReversedInfinitePerspectiveProjection(float fovY, float aspectRatio, float nearPlane, float ε = 1e-6f)
{
// Lengyel, Eric. Foundations of Game Engine Development, Volume 2: Rendering (Seite88). . Kindle-Version.
float g = 1.0f / Math.Tan(fovY * 0.5f);
return .(g / aspectRatio, 0, 0, 0,
0, g, 0, 0,
0, 0, ε, nearPlane * (1 - ε),
0, 0, 1, 0);
}
/**
* Creates an orthographic projection matrix with the camera centered at the near-plane.
* @param width The width of the view volume.
* @param height The height of the view volume.
* @param depth The depth of the view volume.
*/
public static Matrix OrthographicProjection(float width, float height, float depth)
{
// Lengyel, Eric. Foundations of Game Engine Development, Volume 2: Rendering (Seite91). . Kindle-Version.
return .(2.0f / width, 0, 0, 0,
0, 2.0f/height, 0, 0,
0, 0, 1.0f / depth, 0,
0, 0, 0, 1);
}
/**
* Creates an orthographic projection matrix.
* @param left The left side of the view volume.
* @param right The right side of the view volume.
* @param top The top side of the view volume.
* @param bottom The bottom side of the view volume.
* @param near The near plane of the view volume.
* @param far The far plane of the view volume.
*/
public static Matrix OrthographicProjectionOffCenter(float left, float right, float top, float bottom, float near, float far)
{
// Lengyel, Eric. Foundations of Game Engine Development, Volume 2: Rendering (Seite91). . Kindle-Version.
float w_inv = 1.0f / (right - left);
float h_inv = 1.0f / (top - bottom);
float d_inv = 1.0f / (far - near);
return .(2.0f * w_inv, 0.0f, 0.0f, -(right + left) * w_inv,
0.0f, 2.0f * h_inv, 0.0f, -(bottom + top) * h_inv,
0.0f, 0.0f, d_inv, -near * d_inv,
0.0f, 0.0f, 0.0f, 1.0f);
}
public static bool operator ==(Matrix left, Matrix right)
{
return Matrix.Equals(left, right);
}
public static bool operator !=(Matrix left, Matrix right)
{
return !Matrix.Equals(left, right);
}
public static bool Equals(Matrix left, Matrix right)
{
return left.Values == right.Values;
}
public static explicit operator Matrix3x3(Matrix value)
{
return .(value.Right, value.Up, value.Forward);
}
public static void Exponent(ref Matrix matrix, int exponent, out Matrix result)
{
if(exponent == 0)
result = .Identity;
else if(exponent == 1)
result = matrix;
else if(exponent > 1)
{
result = .Identity;
Matrix b = matrix;
var exponent;
for(;exponent > 0;)
{
if(exponent & 1 > 0)
result *= b;
exponent >>= 1;
if(exponent > 0)
b *= b;
}
}
else // Exponent < 0
{
Matrix m = matrix.Invert();
Exponent(ref m, -exponent, out result);
}
}
/**
* Orthogonalizes the matrix.
*/
public void Orthogonalize() mut
{
Columns[1] -= .Project(Columns[1], Columns[0]);
Columns[2] -= .Project(Columns[2], Columns[0]) + .Project(Columns[2], Columns[1]);
Columns[3] -= .Project(Columns[3], Columns[0]) + .Project(Columns[3], Columns[1]) + .Project(Columns[3], Columns[2]);
}
/**
* Orthonormalizes the matrix.
*/
public void Orthonormalize() mut
{
Columns[0].Normalize();
Columns[1] = .Normalize(.Reject(Columns[1], Columns[0]));
Columns[2] = .Normalize(.Reject(.Reject(Columns[2], Columns[0]), Columns[1]));
Columns[3] = .Normalize(.Reject(.Reject(.Reject(Columns[2], Columns[0]), Columns[1]), Columns[2]));
}
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._11 = 1 - ySq - zSq;
result._21 = xy + zw;
result._31 = xz - yw;
result._41 = 0;
result._12 = xy - zw;
result._22 = 1 - xSq - zSq;
result._32 = yz + xw;
result._42 = 0;
result._13 = xz + yw;
result._23 = yz - xw;
result._33 = 1 - xSq - ySq;
result._43 = 0;
result._14 = 0;
result._24 = 0;
result._34 = 0;
result._44 = 1;
return result;
}
public static void Decompose(Self matrix, out Vector3 position, out Quaternion rotation, out Vector3 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 = (*(Vector3*)&matrix.Columns[0]).Magnitude();
scale.Y = (*(Vector3*)&matrix.Columns[1]).Magnitude();
scale.Z = (*(Vector3*)&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);
}
}
-89
View File
@@ -1,89 +0,0 @@
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();
}*/
}
}
+1 -1
View File
@@ -144,7 +144,7 @@ namespace GlitchyEngine.Math
{ {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
var m = matrix.V; var m = matrix;
Quaternion result = ?; Quaternion result = ?;
@@ -85,6 +85,7 @@ namespace GlitchyEngine.Renderer
SetBlendState(_nonblendingState); SetBlendState(_nonblendingState);
_clearUintFx.Variables["ClearValue"].SetData(value); _clearUintFx.Variables["ClearValue"].SetData(value);
_clearUintFx.ApplyChanges();
_clearUintFx.Bind(); _clearUintFx.Bind();
FullscreenQuad.Draw(); FullscreenQuad.Draw();
@@ -101,7 +101,7 @@ namespace GlitchyEngine.Renderer
LoadDdsResourcePlatform(stream, ref nativeTexture); LoadDdsResourcePlatform(stream, ref nativeTexture);
let resType = nativeTexture.GetResourceType(); let resType = nativeTexture.GetResourceType();
Log.EngineLogger.Assert(resType == .Texture2D, scope $"The texture \"{_path}\" is not a 2D texture (it is {resType})."); Log.EngineLogger.Assert(resType == .Texture2D, scope $"The texture is not a 2D texture (it is {resType}).");
nativeTexture.GetDescription(out nativeDesc); nativeTexture.GetDescription(out nativeDesc);
} }
@@ -261,13 +261,6 @@ namespace GlitchyEngine.Renderer
{ {
return .(_nativeResourceView, _samplerState?.nativeSamplerState); return .(_nativeResourceView, _samplerState?.nativeSamplerState);
} }
protected override void PlatformSneakySwappyTexture(Texture2D otherTexture)
{
Swap!(nativeDesc, otherTexture.nativeDesc);
Swap!(nativeTexture, otherTexture.nativeTexture);
Swap!(_nativeResourceView, otherTexture._nativeResourceView);
}
} }
extension TextureCube extension TextureCube
+13 -13
View File
@@ -59,7 +59,7 @@ namespace GlitchyEngine.Renderer
* @param observerVP The view projection of the rendering camera. * @param observerVP The view projection of the rendering camera.
* @param color The color of the frustum. * @param color The color of the frustum.
*/ */
public static void DrawViewFrustum(Matrix worldTransform, Matrix projection, Matrix observerVP, ColorRGBA color = .Red) public static void DrawViewFrustum(Matrix worldTransform, Matrix projection, ColorRGBA color = .Red)
{ {
/*if (_frustumGeometry == null) /*if (_frustumGeometry == null)
{ {
@@ -111,15 +111,15 @@ namespace GlitchyEngine.Renderer
Vector4[8] corners; Vector4[8] corners;
// Perspective // Perspective
if(projection.V._43 != 0.0f) if(projection._43 != 0.0f)
{ {
// near plane for perspective projection, far plane if reversed // near plane for perspective projection, far plane if reversed
float d1 = -projection.V._34 / projection.V._33; float d1 = -projection._34 / projection._33;
float d2 = projection.V._34 / (1.0f - projection.V._33); float d2 = projection._34 / (1.0f - projection._33);
float gOverS = projection.V._11; float gOverS = projection._11;
float g = projection.V._22; float g = projection._22;
//var corners = //(Vector4*)&_vbFrustum.Data; //var corners = //(Vector4*)&_vbFrustum.Data;
@@ -158,14 +158,14 @@ namespace GlitchyEngine.Renderer
} }
else else
{ {
float l = -(projection.V._14 + 1.0f) / projection.V._11; float l = -(projection._14 + 1.0f) / projection._11;
float r = (1.0f - projection.V._14) / projection.V._11; float r = (1.0f - projection._14) / projection._11;
float t = -(projection.V._24 + 1.0f) / projection.V._22; float t = -(projection._24 + 1.0f) / projection._22;
float b = (1.0f - projection.V._24) / projection.V._22; float b = (1.0f - projection._24) / projection._22;
float n = -projection.V._34 / projection.V._33; float n = -projection._34 / projection._33;
float f = (1 - projection.V._34) / projection.V._33; float f = (1 - projection._34) / projection._33;
//var corners = (Vector4*)&_vbFrustum.Data; //var corners = (Vector4*)&_vbFrustum.Data;
corners[0] = .(r, t, n, 1.0f); corners[0] = .(r, t, n, 1.0f);
@@ -184,7 +184,7 @@ namespace GlitchyEngine.Renderer
uint16 index0 = indices[i]; uint16 index0 = indices[i];
uint16 index1 = indices[i + 1]; uint16 index1 = indices[i + 1];
Renderer.DrawLine(corners[index0], corners[index1], color, worldTransform, observerVP); Renderer2D.DrawLine(worldTransform * corners[index0], worldTransform * corners[index1], color);
} }
} }
+18 -2
View File
@@ -134,7 +134,20 @@ public class Effect : Asset
BufferVariableCollection _variables ~ delete _; BufferVariableCollection _variables ~ delete _;
typealias TextureEntry = (TextureViewBinding BoundTexture, ShaderTextureCollection.ResourceEntry* VsSlot, ShaderTextureCollection.ResourceEntry* PsSlot); public struct TextureEntry
{
public TextureViewBinding BoundTexture;
public ShaderTextureCollection.ResourceEntry* VsSlot;
public ShaderTextureCollection.ResourceEntry* PsSlot;
public this(TextureViewBinding boundTexture, ShaderTextureCollection.ResourceEntry* vsSlot, ShaderTextureCollection.ResourceEntry* psSlot)
{
BoundTexture = boundTexture;
VsSlot = vsSlot;
PsSlot = psSlot;
}
}
Dictionary<String, TextureEntry> _textures ~ delete _; Dictionary<String, TextureEntry> _textures ~ delete _;
public Dictionary<String, TextureEntry> Textures => _textures; public Dictionary<String, TextureEntry> Textures => _textures;
@@ -199,6 +212,9 @@ public class Effect : Asset
{ {
Debug.Profiler.ProfileRendererFunction!(); Debug.Profiler.ProfileRendererFunction!();
if (texture == null)
return;
[Inline]InternalSetTexture(name, texture.GetViewBinding()); [Inline]InternalSetTexture(name, texture.GetViewBinding());
} }
@@ -725,7 +741,7 @@ public class Effect : Asset
// Get existing entry or create new // Get existing entry or create new
if(!_textures.TryGetValue(shaderEntry.Name, out entry)) if(!_textures.TryGetValue(shaderEntry.Name, out entry))
{ {
entry = (shaderEntry.BoundTexture, null, null); entry = .(shaderEntry.BoundTexture, null, null);
entry.BoundTexture.AddRef(); entry.BoundTexture.AddRef();
} }
+10 -16
View File
@@ -14,7 +14,7 @@ public class Material : Asset
private uint8[] _rawVariables ~ delete _; private uint8[] _rawVariables ~ delete _;
private Dictionary<String, TextureViewBinding> _textures = new .(); private Dictionary<String, AssetHandle<Texture>> _textures = new .() ~ delete _;
private Dictionary<String, (uint32 Offset, BufferVariable Variable)> _variables = new .() ~ delete _; private Dictionary<String, (uint32 Offset, BufferVariable Variable)> _variables = new .() ~ delete _;
@@ -26,26 +26,20 @@ public class Material : Asset
// TODO: get variables from effect // TODO: get variables from effect
// Get texture slots from effect
for(let (name, entry) in _effect.Textures) for(let (name, entry) in _effect.Textures)
{ {
var texture = entry.BoundTexture; // TODO: We need to be able to define default textures in the shader.
texture.AddRef(); // At least things like "Black", "White", "Normal"
// At best whole paths. Shouldn't be that hard to do...
/*var texture = entry.BoundTexture;*/
_textures.Add(name, texture); _textures.Add(name, .Invalid);
} }
InitRawData(); InitRawData();
} }
public ~this()
{
for(let (name, texture) in _textures)
{
texture.Release();
}
delete _textures;
}
/** @brief Initializes the raw data array for the variables. /** @brief Initializes the raw data array for the variables.
*/ */
@@ -88,12 +82,12 @@ public class Material : Asset
* @param name The name of the texture to set. * @param name The name of the texture to set.
* @param texture The texture to bind to the effect. * @param texture The texture to bind to the effect.
*/ */
public void SetTexture(String name, Texture texture) public void SetTexture(String name, AssetHandle<Texture> texture)
{ {
if(_textures.TryGetValue(name, var entry)) if(_textures.TryGetValue(name, var entry))
{ {
entry.Release(); //entry?.ReleaseRef();
_textures[name] = texture.GetViewBinding(); _textures[name] = texture;
//texture?.AddRef(); //texture?.AddRef();
} }
else else
+3 -19
View File
@@ -1,27 +1,11 @@
using System; using System;
using GlitchyEngine.World; using GlitchyEngine.World;
using GlitchyEngine.Content;
namespace GlitchyEngine.Renderer namespace GlitchyEngine.Renderer
{ {
public struct MeshComponent : IDisposableComponent public struct MeshComponent
{ {
private GeometryBinding _mesh; public AssetHandle<GeometryBinding> Mesh = .Invalid;
public GeometryBinding Mesh
{
[Inline]
get => _mesh;
set mut
{
if(_mesh == value)
return;
SetReference!(_mesh, value);
}
}
public void Dispose() mut
{
ReleaseRefAndNullify!(_mesh);
}
} }
} }
+33 -73
View File
@@ -2,6 +2,7 @@ using GlitchyEngine.Math;
using GlitchyEngine.World; using GlitchyEngine.World;
using System.Collections; using System.Collections;
using System; using System;
using GlitchyEngine.Content;
namespace GlitchyEngine.Renderer namespace GlitchyEngine.Renderer
{ {
@@ -79,13 +80,13 @@ namespace GlitchyEngine.Renderer
static SceneConstants _sceneConstants; static SceneConstants _sceneConstants;
static Effect LineEffect; //static AssetHandle<Effect> LineEffect;
static VertexBuffer LineVertices; static VertexBuffer LineVertices;
static GeometryBinding LineGeometry; static GeometryBinding LineGeometry;
static GBuffer _gBuffer; static GBuffer _gBuffer;
static Effect TestFullscreenEffect; static AssetHandle<Effect> TestFullscreenEffect;
static Effect s_tonemappingEffect; static AssetHandle<Effect> s_tonemappingEffect;
static BlendState _gBufferBlend; static BlendState _gBufferBlend;
static BlendState _lightBlend; static BlendState _lightBlend;
@@ -132,7 +133,7 @@ namespace GlitchyEngine.Renderer
{ {
Debug.Profiler.ProfileFunction!(); Debug.Profiler.ProfileFunction!();
LineEffect = Content.LoadAsset<Effect>("Shaders\\lineShader.hlsl"); //LineEffect = Content.LoadAsset("Shaders\\lineShader.hlsl");
LineGeometry = new GeometryBinding(); LineGeometry = new GeometryBinding();
LineGeometry.SetPrimitiveTopology(.LineList); LineGeometry.SetPrimitiveTopology(.LineList);
@@ -158,13 +159,12 @@ namespace GlitchyEngine.Renderer
{ {
LineVertices.ReleaseRef(); LineVertices.ReleaseRef();
LineGeometry.ReleaseRef(); LineGeometry.ReleaseRef();
LineEffect.ReleaseRef();
} }
static void InitDeferredRenderer() static void InitDeferredRenderer()
{ {
TestFullscreenEffect = Content.LoadAsset<Effect>("Shaders\\simpleLight.hlsl"); TestFullscreenEffect = Content.LoadAsset("Shaders\\simpleLight.hlsl");
s_tonemappingEffect = Content.LoadAsset<Effect>("Shaders\\SimpleTonemapping.hlsl"); s_tonemappingEffect = Content.LoadAsset("Shaders\\SimpleTonemapping.hlsl");
_gBuffer = new GBuffer(); _gBuffer = new GBuffer();
BlendStateDescription gBufferBlendDesc = .Default; BlendStateDescription gBufferBlendDesc = .Default;
@@ -203,9 +203,6 @@ namespace GlitchyEngine.Renderer
_lightBlend.ReleaseRef(); _lightBlend.ReleaseRef();
_gBufferBlend.ReleaseRef(); _gBufferBlend.ReleaseRef();
delete _gBuffer; delete _gBuffer;
s_tonemappingEffect.ReleaseRef();
TestFullscreenEffect.ReleaseRef();
} }
// [Obsolete("", false)] // [Obsolete("", false)]
@@ -346,22 +343,23 @@ namespace GlitchyEngine.Renderer
Vector3 lightDir = -light.Transform.Forward; Vector3 lightDir = -light.Transform.Forward;
TestFullscreenEffect.SetTexture("GBuffer_Albedo", _gBuffer.Target, 0); Effect fsEffect = TestFullscreenEffect.Get();
TestFullscreenEffect.SetTexture("GBuffer_Normal", _gBuffer.Target, 1); fsEffect.SetTexture("GBuffer_Albedo", _gBuffer.Target, 0);
TestFullscreenEffect.SetTexture("GBuffer_Tangent", _gBuffer.Target, 2); fsEffect.SetTexture("GBuffer_Normal", _gBuffer.Target, 1);
TestFullscreenEffect.SetTexture("GBuffer_Position", _gBuffer.Target, 3); fsEffect.SetTexture("GBuffer_Tangent", _gBuffer.Target, 2);
TestFullscreenEffect.SetTexture("GBuffer_Material", _gBuffer.Target, 4); fsEffect.SetTexture("GBuffer_Position", _gBuffer.Target, 3);
fsEffect.SetTexture("GBuffer_Material", _gBuffer.Target, 4);
TestFullscreenEffect.Variables["LightColor"].SetData(light.Light.Color); fsEffect.Variables["LightColor"].SetData(light.Light.Color);
TestFullscreenEffect.Variables["Illuminance"].SetData(light.Light.Illuminance); fsEffect.Variables["Illuminance"].SetData(light.Light.Illuminance);
TestFullscreenEffect.Variables["LightDir"].SetData(lightDir); fsEffect.Variables["LightDir"].SetData(lightDir);
TestFullscreenEffect.Variables["CameraPos"].SetData(_sceneConstants.CameraPosition); fsEffect.Variables["CameraPos"].SetData(_sceneConstants.CameraPosition);
TestFullscreenEffect.Variables["Scaling"].SetData(scaling); fsEffect.Variables["Scaling"].SetData(scaling);
TestFullscreenEffect.ApplyChanges(); fsEffect.ApplyChanges();
TestFullscreenEffect.Bind(); fsEffect.Bind();
//RenderCommand.BindEffect(TestFullscreenEffect); //RenderCommand.BindEffect(TestFullscreenEffect);
@@ -379,10 +377,11 @@ namespace GlitchyEngine.Renderer
RenderCommand.BindRenderTargets(); RenderCommand.BindRenderTargets();
RenderCommand.SetRenderTargetGroup(_sceneConstants.CompositionTarget, true); RenderCommand.SetRenderTargetGroup(_sceneConstants.CompositionTarget, true);
Effect toneMappingFx = s_tonemappingEffect.Get();
// TODO: Postprocessing effects // TODO: Postprocessing effects
s_tonemappingEffect.SetTexture("CameraTarget", _sceneConstants.CameraTarget, 0); toneMappingFx.SetTexture("CameraTarget", _sceneConstants.CameraTarget, 0);
s_tonemappingEffect.ApplyChanges(); toneMappingFx.ApplyChanges();
s_tonemappingEffect.Bind(); toneMappingFx.Bind();
RenderCommand.BindRenderTargets(); RenderCommand.BindRenderTargets();
//RenderCommand.BindEffect(s_tonemappingEffect); //RenderCommand.BindEffect(s_tonemappingEffect);
@@ -470,6 +469,9 @@ namespace GlitchyEngine.Renderer
{ {
Debug.Profiler.ProfileRendererFunction!(); Debug.Profiler.ProfileRendererFunction!();
if (geometry == null || material == null)
return;
_queue.Add(SubmittedMesh(geometry, material, transform, entity.[Friend]Index)); _queue.Add(SubmittedMesh(geometry, material, transform, entity.[Friend]Index));
} }
@@ -485,9 +487,10 @@ namespace GlitchyEngine.Renderer
* @param end The end point of the line. * @param end The end point of the line.
* @param color The color of the line. * @param color The color of the line.
*/ */
public static void DrawLine(Vector3 start, Vector3 end, Color color) public static void DrawLine(Vector3 start, Vector3 end, ColorRGBA color)
{ {
DrawLine(Vector4(start, 1.0f), Vector4(end, 1.0f), (ColorRGBA)color, .Identity); Renderer2D.DrawLine(start, end, color);
//DrawLine(Vector4(start, 1.0f), Vector4(end, 1.0f), (ColorRGBA)color, .Identity);
} }
/** @brief Draws a line. /** @brief Draws a line.
@@ -498,7 +501,7 @@ namespace GlitchyEngine.Renderer
*/ */
public static void DrawLine(Vector3 start, Vector3 end, ColorRGBA color, Matrix transform) public static void DrawLine(Vector3 start, Vector3 end, ColorRGBA color, Matrix transform)
{ {
DrawLine(Vector4(start, 1.0f), Vector4(end, 1.0f), color, transform); Renderer2D.DrawLine(transform * Vector4(start, 1.0f), transform * Vector4(end, 1.0f), color);
} }
/** @brief Draws a ray. /** @brief Draws a ray.
@@ -508,7 +511,7 @@ namespace GlitchyEngine.Renderer
*/ */
public static void DrawRay(Vector3 start, Vector3 direction, ColorRGBA color) public static void DrawRay(Vector3 start, Vector3 direction, ColorRGBA color)
{ {
DrawLine(Vector4(start, 1.0f), Vector4(direction, 0.0f), color, .Identity); Renderer2D.DrawRay(start, direction, color);
} }
/** @brief Draws a ray. /** @brief Draws a ray.
@@ -519,50 +522,7 @@ namespace GlitchyEngine.Renderer
*/ */
public static void DrawRay(Vector3 start, Vector3 direction, ColorRGBA color, Matrix transform) public static void DrawRay(Vector3 start, Vector3 direction, ColorRGBA color, Matrix transform)
{ {
DrawLine(Vector4(start, 1.0f), Vector4(direction, 0.0f), color, transform); Renderer2D.DrawLine(transform * Vector4(start, 1.0f), transform * Vector4(direction, 0.0f), color);
}
/** @brief Draws a line.
* @param start The start point of the line.
* @param end The end point of the line.
* @param color The color of the line.
* @param transform A transform matrix transforming the line.
*/
public static void DrawLine(Vector4 start, Vector4 end, ColorRGBA color, Matrix transform)
{
Debug.Profiler.ProfileRendererFunction!();
LineVertices.SetData(Vector4[2](start, end), 0, .WriteDiscard);
LineEffect.Variables["ViewProjection"].SetData(_sceneConstants.ViewProjection * transform);
LineEffect.Variables["Color"].SetData(color);
LineEffect.ApplyChanges();
LineEffect.Bind();
LineGeometry.Bind();
RenderCommand.DrawIndexed(LineGeometry);
}
/** @brief Draws a line.
* @param start The start point of the line.
* @param end The end point of the line.
* @param color The color of the line.
* @param transform A transform matrix transforming the line.
* @param viewProjection The observing cameras viewprojection
*/
public static void DrawLine(Vector4 start, Vector4 end, ColorRGBA color, Matrix transform, Matrix viewProjection)
{
Debug.Profiler.ProfileRendererFunction!();
LineVertices.SetData(Vector4[2](start, end), 0, .WriteDiscard);
LineEffect.Variables["ViewProjection"].SetData(viewProjection * transform);
LineEffect.Variables["Color"].SetData(color);
LineEffect.ApplyChanges();
LineEffect.Bind();
LineGeometry.Bind();
RenderCommand.DrawIndexed(LineGeometry);
} }
} }
} }
+306 -56
View File
@@ -44,7 +44,23 @@ namespace GlitchyEngine.Renderer
} }
[CRepr] [CRepr]
struct BatchVertex struct LineBatchVertex
{
public Vector4 Position;
public ColorRGBA Color;
public uint32 EntityId;
public this(Vector4 position, ColorRGBA color, uint32 entityId)
{
Position = position;
Color = color;
EntityId = entityId;
}
}
[CRepr]
struct QuadBatchVertex
{ {
public Matrix Transform; public Matrix Transform;
public ColorRGBA Color; public ColorRGBA Color;
@@ -62,7 +78,7 @@ namespace GlitchyEngine.Renderer
} }
[CRepr] [CRepr]
struct CircleBatchVertex : BatchVertex struct CircleBatchVertex : QuadBatchVertex
{ {
public float InnerRadius; public float InnerRadius;
@@ -94,6 +110,8 @@ namespace GlitchyEngine.Renderer
FrontToBack FrontToBack
} }
struct QueueLine: this(Vector4 Start, Vector4 End, ColorRGBA Color, float Depth, uint32 entityId = uint32.MaxValue) { }
struct QueueQuad: this(Matrix Transform, ColorRGBA Color, Texture Texture, float Depth, Vector4 uvTransform, uint32 entityId = uint32.MaxValue) { } struct QueueQuad: this(Matrix Transform, ColorRGBA Color, Texture Texture, float Depth, Vector4 uvTransform, uint32 entityId = uint32.MaxValue) { }
struct QueueCircle : QueueQuad struct QueueCircle : QueueQuad
@@ -112,8 +130,9 @@ namespace GlitchyEngine.Renderer
private static bool s_sceneRunning; private static bool s_sceneRunning;
#endif #endif
private static Effect s_batchEffect; private static Effect s_quadBatchEffect;
private static Effect s_circleBatchEffect; private static Effect s_circleBatchEffect;
private static Effect s_lineBatchEffect;
private static GeometryBinding s_quadGeometry; private static GeometryBinding s_quadGeometry;
@@ -121,23 +140,30 @@ namespace GlitchyEngine.Renderer
private static GeometryBinding s_quadBatchBinding; private static GeometryBinding s_quadBatchBinding;
private static GeometryBinding s_circleBatchBinding; private static GeometryBinding s_circleBatchBinding;
private static GeometryBinding s_lineBatchBinding;
private static VertexBuffer s_quadInstanceBuffer; private static VertexBuffer s_quadInstanceBuffer;
private static VertexBuffer s_circleInstanceBuffer; private static VertexBuffer s_circleInstanceBuffer;
private static VertexBuffer s_lineInstanceBuffer;
private static uint32 s_maxInstancesPerBatch = 8192; private static uint32 s_maxInstancesPerBatch = 8192;
private static BatchVertex[] s_rawQuadInstances; private static QuadBatchVertex[] s_rawQuadInstances;
private static CircleBatchVertex[] s_rawCircleInstances; private static CircleBatchVertex[] s_rawCircleInstances;
private static uint32 s_setInstances = 0; private static LineBatchVertex[] s_rawLineVertices;
private static uint32 s_setQuadInstances = 0;
private static uint32 s_setCircleInstances = 0;
private static uint32 s_setLineInstances = 0;
private static List<QueueQuad> s_QuadinstanceQueue; private static List<QueueQuad> s_QuadinstanceQueue;
private static List<QueueCircle> s_circleInstanceQueue; private static List<QueueCircle> s_circleInstanceQueue;
private static List<QueueLine> s_lineInstanceQueue;
private static DrawOrder s_drawOrder; private static DrawOrder s_drawOrder;
/// The effect that is currently used to draw the sprites. /// The effect that is currently used to draw the sprites.
private static Effect s_currentEffect; private static Effect s_currentQuadEffect;
private static Effect s_currentCircleEffect; private static Effect s_currentCircleEffect;
private static Effect s_currentLineEffect;
public static uint32 MaxInstancesPerBatch public static uint32 MaxInstancesPerBatch
{ {
@@ -157,8 +183,9 @@ namespace GlitchyEngine.Renderer
{ {
Debug.Profiler.ProfileFunction!(); Debug.Profiler.ProfileFunction!();
s_batchEffect = new Effect("content\\Shaders\\spritebatch.hlsl"); s_quadBatchEffect = new Effect("content\\Shaders\\spritebatch.hlsl");
s_circleBatchEffect = new Effect("content\\Shaders\\circlebatch.hlsl"); s_circleBatchEffect = new Effect("content\\Shaders\\circlebatch.hlsl");
s_lineBatchEffect = new Effect("content\\Shaders\\linebatch.hlsl");
} }
private static void InitGeometry() private static void InitGeometry()
@@ -252,6 +279,23 @@ namespace GlitchyEngine.Renderer
s_circleBatchBinding.SetIndexBuffer(s_quadGeometry.GetIndexBuffer(), 0); s_circleBatchBinding.SetIndexBuffer(s_quadGeometry.GetIndexBuffer(), 0);
} }
// Line
{
VertexElement[] vertexElements = new .(
VertexElement(.R32G32B32A32_Float, "POSITION", false, 0, 0, 0, .PerVertexData, 0),
VertexElement(.R32G32B32A32_Float, "COLOR", false, 0, 0, (.)-1, .PerVertexData, 0),
VertexElement(.R32_UInt, "ENTITYID", false, 0, 0, (.)-1, .PerVertexData, 0)
);
s_lineBatchBinding = new GeometryBinding();
s_lineBatchBinding.SetPrimitiveTopology(.LineList);
using (var lineBatchLayout = new VertexLayout(vertexElements, true))
{
s_lineBatchBinding.SetVertexLayout(lineBatchLayout);
}
}
ApplyInstanceCount(); ApplyInstanceCount();
} }
@@ -262,7 +306,7 @@ namespace GlitchyEngine.Renderer
// Quads // Quads
{ {
VertexBuffer quadInstanceBuffer = new VertexBuffer(typeof(BatchVertex), s_maxInstancesPerBatch, .Dynamic, .Write); VertexBuffer quadInstanceBuffer = new VertexBuffer(typeof(QuadBatchVertex), s_maxInstancesPerBatch, .Dynamic, .Write);
quadInstanceBuffer.SetData(0); quadInstanceBuffer.SetData(0);
s_quadInstanceBuffer?.ReleaseRef(); s_quadInstanceBuffer?.ReleaseRef();
@@ -271,7 +315,7 @@ namespace GlitchyEngine.Renderer
delete s_rawQuadInstances; delete s_rawQuadInstances;
delete s_QuadinstanceQueue; delete s_QuadinstanceQueue;
s_rawQuadInstances = new BatchVertex[s_maxInstancesPerBatch]; s_rawQuadInstances = new QuadBatchVertex[s_maxInstancesPerBatch];
s_QuadinstanceQueue = new List<QueueQuad>(s_maxInstancesPerBatch); s_QuadinstanceQueue = new List<QueueQuad>(s_maxInstancesPerBatch);
} }
@@ -290,6 +334,21 @@ namespace GlitchyEngine.Renderer
s_rawCircleInstances = new CircleBatchVertex[s_maxInstancesPerBatch]; s_rawCircleInstances = new CircleBatchVertex[s_maxInstancesPerBatch];
s_circleInstanceQueue = new List<QueueCircle>(s_maxInstancesPerBatch); s_circleInstanceQueue = new List<QueueCircle>(s_maxInstancesPerBatch);
} }
// Lines
{
VertexBuffer lineInstanceBuffer = new VertexBuffer(typeof(LineBatchVertex), s_maxInstancesPerBatch, .Dynamic, .Write);
lineInstanceBuffer.SetData(0);
s_lineInstanceBuffer?.ReleaseRef();
s_lineInstanceBuffer = lineInstanceBuffer;
s_lineBatchBinding.SetVertexBufferSlot(s_lineInstanceBuffer, 0);
delete s_rawLineVertices;
delete s_lineInstanceQueue;
s_rawLineVertices = new LineBatchVertex[s_maxInstancesPerBatch];
s_lineInstanceQueue = new List<QueueLine>(s_maxInstancesPerBatch);
}
} }
private static void InitWhitetexture() private static void InitWhitetexture()
@@ -366,8 +425,9 @@ namespace GlitchyEngine.Renderer
FontRenderer.Deinit(); FontRenderer.Deinit();
s_batchEffect.ReleaseRef(); s_quadBatchEffect.ReleaseRef();
s_circleBatchEffect.ReleaseRef(); s_circleBatchEffect.ReleaseRef();
s_lineBatchEffect.ReleaseRef();
s_quadGeometry.ReleaseRef(); s_quadGeometry.ReleaseRef();
@@ -375,16 +435,21 @@ namespace GlitchyEngine.Renderer
s_quadBatchBinding.ReleaseRef(); s_quadBatchBinding.ReleaseRef();
s_circleBatchBinding.ReleaseRef(); s_circleBatchBinding.ReleaseRef();
s_lineBatchBinding.ReleaseRef();
s_quadInstanceBuffer.ReleaseRef(); s_quadInstanceBuffer.ReleaseRef();
s_circleInstanceBuffer.ReleaseRef(); s_circleInstanceBuffer.ReleaseRef();
s_lineInstanceBuffer.ReleaseRef();
delete s_rawQuadInstances; delete s_rawQuadInstances;
delete s_rawCircleInstances; delete s_rawCircleInstances;
delete s_rawLineVertices;
delete s_QuadinstanceQueue; delete s_QuadinstanceQueue;
delete s_circleInstanceQueue; delete s_circleInstanceQueue;
delete s_lineInstanceQueue;
s_currentEffect?.ReleaseRef(); s_currentQuadEffect?.ReleaseRef();
s_currentCircleEffect?.ReleaseRef(); s_currentCircleEffect?.ReleaseRef();
s_currentLineEffect?.ReleaseRef();
s_opaqueBlendState.ReleaseRef(); s_opaqueBlendState.ReleaseRef();
s_transparentBlendState.ReleaseRef(); s_transparentBlendState.ReleaseRef();
@@ -405,14 +470,14 @@ namespace GlitchyEngine.Renderer
//s_textureColorEffect.Bind(Renderer._context); //s_textureColorEffect.Bind(Renderer._context);
s_currentEffect?.ReleaseRef(); s_currentQuadEffect?.ReleaseRef();
if(effect != null) if(effect != null)
{ {
s_currentEffect = effect..AddRef(); s_currentQuadEffect = effect..AddRef();
} }
else else
{ {
s_currentEffect = s_batchEffect..AddRef(); s_currentQuadEffect = s_quadBatchEffect..AddRef();
} }
s_currentCircleEffect?.ReleaseRef(); s_currentCircleEffect?.ReleaseRef();
@@ -425,7 +490,7 @@ namespace GlitchyEngine.Renderer
s_currentCircleEffect = s_circleBatchEffect..AddRef(); s_currentCircleEffect = s_circleBatchEffect..AddRef();
} }
s_currentEffect.Variables["ViewProjection"].SetData(camera.ViewProjection); s_currentQuadEffect.Variables["ViewProjection"].SetData(camera.ViewProjection);
s_currentCircleEffect.Variables["ViewProjection"].SetData(camera.ViewProjection); s_currentCircleEffect.Variables["ViewProjection"].SetData(camera.ViewProjection);
s_drawOrder = drawOrder; s_drawOrder = drawOrder;
@@ -445,14 +510,14 @@ namespace GlitchyEngine.Renderer
//s_textureColorEffect.Bind(Renderer._context); //s_textureColorEffect.Bind(Renderer._context);
s_currentEffect?.ReleaseRef(); s_currentQuadEffect?.ReleaseRef();
if(effect != null) if(effect != null)
{ {
s_currentEffect = effect..AddRef(); s_currentQuadEffect = effect..AddRef();
} }
else else
{ {
s_currentEffect = s_batchEffect..AddRef(); s_currentQuadEffect = s_quadBatchEffect..AddRef();
} }
s_currentCircleEffect?.ReleaseRef(); s_currentCircleEffect?.ReleaseRef();
@@ -465,10 +530,21 @@ namespace GlitchyEngine.Renderer
s_currentCircleEffect = s_circleBatchEffect..AddRef(); s_currentCircleEffect = s_circleBatchEffect..AddRef();
} }
s_currentLineEffect?.ReleaseRef();
/*if(circleEffect != null)
{
s_currentLineEffect = effect..AddRef();
}
else
{*/
s_currentLineEffect = s_lineBatchEffect..AddRef();
//}
Matrix viewProjection = camera.Projection * Matrix.Invert(transform); Matrix viewProjection = camera.Projection * Matrix.Invert(transform);
s_currentEffect.Variables["ViewProjection"].SetData(viewProjection); s_currentQuadEffect.Variables["ViewProjection"].SetData(viewProjection);
s_currentCircleEffect.Variables["ViewProjection"].SetData(viewProjection); s_currentCircleEffect.Variables["ViewProjection"].SetData(viewProjection);
s_currentLineEffect.Variables["ViewProjection"].SetData(viewProjection);
s_drawOrder = drawOrder; s_drawOrder = drawOrder;
@@ -487,14 +563,14 @@ namespace GlitchyEngine.Renderer
//s_textureColorEffect.Bind(Renderer._context); //s_textureColorEffect.Bind(Renderer._context);
s_currentEffect?.ReleaseRef(); s_currentQuadEffect?.ReleaseRef();
if(effect != null) if(effect != null)
{ {
s_currentEffect = effect..AddRef(); s_currentQuadEffect = effect..AddRef();
} }
else else
{ {
s_currentEffect = s_batchEffect..AddRef(); s_currentQuadEffect = s_quadBatchEffect..AddRef();
} }
s_currentCircleEffect?.ReleaseRef(); s_currentCircleEffect?.ReleaseRef();
@@ -507,10 +583,21 @@ namespace GlitchyEngine.Renderer
s_currentCircleEffect = s_circleBatchEffect..AddRef(); s_currentCircleEffect = s_circleBatchEffect..AddRef();
} }
s_currentLineEffect?.ReleaseRef();
/*if(circleEffect != null)
{
s_currentLineEffect = effect..AddRef();
}
else
{*/
s_currentLineEffect = s_lineBatchEffect..AddRef();
//}
Matrix viewProjection = camera.Projection * camera.View; Matrix viewProjection = camera.Projection * camera.View;
s_currentEffect.Variables["ViewProjection"].SetData(viewProjection); s_currentQuadEffect.Variables["ViewProjection"].SetData(viewProjection);
s_currentCircleEffect.Variables["ViewProjection"].SetData(viewProjection); s_currentCircleEffect.Variables["ViewProjection"].SetData(viewProjection);
s_currentLineEffect.Variables["ViewProjection"].SetData(viewProjection);
s_drawOrder = drawOrder; s_drawOrder = drawOrder;
@@ -559,22 +646,30 @@ namespace GlitchyEngine.Renderer
s_statistics.CircleCount++; s_statistics.CircleCount++;
} }
private static void FlushInstances() /// Adds a line instance to the instance queue.
[Inline]
private static void QueueLineInstance(Vector4 start, Vector4 end, ColorRGBA color, uint32 id = uint32.MaxValue)
{
s_lineInstanceQueue.Add(QueueLine(start, end, color, id));
s_statistics.LineCount++;
}
private static void FlushQuadInstances()
{ {
Debug.Profiler.ProfileRendererFunction!(); Debug.Profiler.ProfileRendererFunction!();
if(s_setInstances == 0) if(s_setQuadInstances == 0)
return; return;
s_quadInstanceBuffer.SetData<BatchVertex>(s_rawQuadInstances.Ptr, s_setInstances, 0, .WriteDiscard); s_quadInstanceBuffer.SetData<QuadBatchVertex>(s_rawQuadInstances.Ptr, s_setQuadInstances, 0, .WriteDiscard);
s_currentEffect.ApplyChanges(); s_currentQuadEffect.ApplyChanges();
s_currentEffect.Bind(); s_currentQuadEffect.Bind();
s_quadBatchBinding.InstanceCount = s_setInstances; s_quadBatchBinding.InstanceCount = s_setQuadInstances;
s_quadBatchBinding.Bind(); s_quadBatchBinding.Bind();
RenderCommand.DrawIndexedInstanced(s_quadBatchBinding); RenderCommand.DrawIndexedInstanced(s_quadBatchBinding);
s_setInstances = 0; s_setQuadInstances = 0;
s_statistics.QuadDrawCalls++; s_statistics.QuadDrawCalls++;
} }
@@ -583,22 +678,42 @@ namespace GlitchyEngine.Renderer
{ {
Debug.Profiler.ProfileRendererFunction!(); Debug.Profiler.ProfileRendererFunction!();
if(s_setInstances == 0) if(s_setCircleInstances == 0)
return; return;
s_circleInstanceBuffer.SetData<CircleBatchVertex>(s_rawCircleInstances.Ptr, s_setInstances, 0, .WriteDiscard); s_circleInstanceBuffer.SetData<CircleBatchVertex>(s_rawCircleInstances.Ptr, s_setCircleInstances, 0, .WriteDiscard);
s_currentCircleEffect.ApplyChanges(); s_currentCircleEffect.ApplyChanges();
s_currentCircleEffect.Bind(); s_currentCircleEffect.Bind();
s_circleBatchBinding.InstanceCount = s_setInstances; s_circleBatchBinding.InstanceCount = s_setCircleInstances;
s_circleBatchBinding.Bind(); s_circleBatchBinding.Bind();
RenderCommand.DrawIndexedInstanced(s_circleBatchBinding); RenderCommand.DrawIndexedInstanced(s_circleBatchBinding);
s_setInstances = 0; s_setCircleInstances = 0;
s_statistics.CircleDrawCalls++; s_statistics.CircleDrawCalls++;
} }
private static void FlushLineInstances()
{
Debug.Profiler.ProfileRendererFunction!();
if(s_setLineInstances == 0)
return;
s_lineInstanceBuffer.SetData<LineBatchVertex>(s_rawLineVertices.Ptr, s_setLineInstances, 0, .WriteDiscard);
s_currentLineEffect.ApplyChanges();
s_currentLineEffect.Bind();
s_lineBatchBinding.VertexCount = (.)s_setLineInstances;
s_lineBatchBinding.Bind();
RenderCommand.DrawIndexed(s_lineBatchBinding);
s_setLineInstances = 0;
s_statistics.LineDrawCalls++;
}
// Quad comparison // Quad comparison
private static int TextureComparison(QueueQuad lhs, QueueQuad rhs) private static int TextureComparison(QueueQuad lhs, QueueQuad rhs)
{ {
@@ -627,6 +742,20 @@ namespace GlitchyEngine.Renderer
return lhs.Depth <=> rhs.Depth; return lhs.Depth <=> rhs.Depth;
} }
// Line comparison
/*private static int TextureComparison(QueueLine lhs, QueueLine rhs)
{
return (int)Internal.UnsafeCastToPtr(lhs.Texture) - (int)Internal.UnsafeCastToPtr(rhs.Texture);
}*/
private static int BackToFrontComparison(QueueLine lhs, QueueLine rhs)
{
return rhs.Depth <=> lhs.Depth;
}
private static int FrontToBackComparison(QueueLine lhs, QueueLine rhs)
{
return lhs.Depth <=> rhs.Depth;
}
private static void SortInstances() private static void SortInstances()
{ {
Debug.Profiler.ProfileRendererFunction!(); Debug.Profiler.ProfileRendererFunction!();
@@ -636,12 +765,16 @@ namespace GlitchyEngine.Renderer
case .SortByTexture: case .SortByTexture:
s_QuadinstanceQueue.Sort(scope => TextureComparison); s_QuadinstanceQueue.Sort(scope => TextureComparison);
s_circleInstanceQueue.Sort(scope => TextureComparison); s_circleInstanceQueue.Sort(scope => TextureComparison);
/*Lines cant be sorted by texture*/
s_lineInstanceQueue.Sort(scope => BackToFrontComparison);
case .BackToFront: case .BackToFront:
s_QuadinstanceQueue.Sort(scope => BackToFrontComparison); s_QuadinstanceQueue.Sort(scope => BackToFrontComparison);
s_circleInstanceQueue.Sort(scope => BackToFrontComparison); s_circleInstanceQueue.Sort(scope => BackToFrontComparison);
s_lineInstanceQueue.Sort(scope => BackToFrontComparison);
case .FrontToBack: case .FrontToBack:
s_QuadinstanceQueue.Sort(scope => FrontToBackComparison); s_QuadinstanceQueue.Sort(scope => FrontToBackComparison);
s_circleInstanceQueue.Sort(scope => FrontToBackComparison); s_circleInstanceQueue.Sort(scope => FrontToBackComparison);
s_lineInstanceQueue.Sort(scope => FrontToBackComparison);
case .Immediate: case .Immediate:
default: default:
Log.EngineLogger.Error("Unknown instance draw order."); Log.EngineLogger.Error("Unknown instance draw order.");
@@ -652,13 +785,14 @@ namespace GlitchyEngine.Renderer
{ {
Debug.Profiler.ProfileRendererFunction!(); Debug.Profiler.ProfileRendererFunction!();
if(s_QuadinstanceQueue.IsEmpty && s_circleInstanceQueue.IsEmpty) if(s_QuadinstanceQueue.IsEmpty && s_circleInstanceQueue.IsEmpty && s_lineInstanceQueue.IsEmpty)
return; return;
SortInstances(); SortInstances();
DrawDeferredQuads(); DrawDeferredQuads();
DrawDeferredCircles(); DrawDeferredCircles();
DrawDeferredLines();
} }
private static void DrawDeferredQuads() private static void DrawDeferredQuads()
@@ -672,9 +806,9 @@ namespace GlitchyEngine.Renderer
RenderCommand.SetBlendState(s_transparentBlendState); RenderCommand.SetBlendState(s_transparentBlendState);
Texture texture = s_QuadinstanceQueue[0].Texture; Texture texture = s_QuadinstanceQueue[0].Texture;
s_currentEffect.SetTexture("Texture", texture); s_currentQuadEffect.SetTexture("Texture", texture);
s_setInstances = 0; s_setQuadInstances = 0;
for(int i < s_QuadinstanceQueue.Count) for(int i < s_QuadinstanceQueue.Count)
{ {
@@ -683,21 +817,21 @@ namespace GlitchyEngine.Renderer
// flush every time the texture changes // flush every time the texture changes
if(quad.Texture != texture) if(quad.Texture != texture)
{ {
FlushInstances(); FlushQuadInstances();
texture = quad.Texture; texture = quad.Texture;
s_currentEffect.SetTexture("Texture", texture); s_currentQuadEffect.SetTexture("Texture", texture);
} }
s_rawQuadInstances[s_setInstances++] = .(quad.Transform, quad.Color, quad.uvTransform, quad.entityId); s_rawQuadInstances[s_setQuadInstances++] = .(quad.Transform, quad.Color, quad.uvTransform, quad.entityId);
if(s_setInstances == s_rawQuadInstances.Count) if(s_setQuadInstances == s_rawQuadInstances.Count)
{ {
FlushInstances(); FlushQuadInstances();
} }
} }
FlushInstances(); FlushQuadInstances();
s_QuadinstanceQueue.Clear(); s_QuadinstanceQueue.Clear();
} }
@@ -715,7 +849,7 @@ namespace GlitchyEngine.Renderer
Texture texture = s_circleInstanceQueue[0].Texture; Texture texture = s_circleInstanceQueue[0].Texture;
s_currentCircleEffect.SetTexture("Texture", texture); s_currentCircleEffect.SetTexture("Texture", texture);
s_setInstances = 0; s_setCircleInstances = 0;
for(int i < s_circleInstanceQueue.Count) for(int i < s_circleInstanceQueue.Count)
{ {
@@ -730,9 +864,9 @@ namespace GlitchyEngine.Renderer
s_currentCircleEffect.SetTexture("Texture", texture); s_currentCircleEffect.SetTexture("Texture", texture);
} }
s_rawCircleInstances[s_setInstances++] = .(circle.Transform, circle.Color, circle.uvTransform, circle.InnerRadius, circle.entityId); s_rawCircleInstances[s_setCircleInstances++] = .(circle.Transform, circle.Color, circle.uvTransform, circle.InnerRadius, circle.entityId);
if(s_setInstances == s_rawCircleInstances.Count) if(s_setCircleInstances == s_rawCircleInstances.Count)
{ {
FlushCircleInstances(); FlushCircleInstances();
} }
@@ -743,6 +877,36 @@ namespace GlitchyEngine.Renderer
s_circleInstanceQueue.Clear(); s_circleInstanceQueue.Clear();
} }
private static void DrawDeferredLines()
{
Debug.Profiler.ProfileRendererFunction!();
if(s_lineInstanceQueue.IsEmpty)
return;
// TODO: per object blendstate
RenderCommand.SetBlendState(s_transparentBlendState);
s_setLineInstances = 0;
for(int i < s_lineInstanceQueue.Count)
{
let line = ref s_lineInstanceQueue[i];
s_rawLineVertices[s_setLineInstances++] = .(line.Start, line.Color, line.entityId);
s_rawLineVertices[s_setLineInstances++] = .(line.End, line.Color, line.entityId);
if(s_setLineInstances == s_rawLineVertices.Count)
{
FlushLineInstances();
}
}
FlushLineInstances();
s_lineInstanceQueue.Clear();
}
/// A specialized function that calculates the 2D transform matrix /// A specialized function that calculates the 2D transform matrix
private static Matrix Calculate2DTransform(Vector3 translation, Vector2 scale, float rotation) private static Matrix Calculate2DTransform(Vector3 translation, Vector2 scale, float rotation)
{ {
@@ -763,6 +927,85 @@ namespace GlitchyEngine.Renderer
// Primitives // Primitives
/** @brief Draws a line.
* @param start The start point of the line.
* @param end The end point of the line.
* @param color The color of the line.
* @param entityId The optional ID of the entity that belongs to this line (for picking).
*/
public static void DrawLine(Vector3 start, Vector3 end, ColorRGBA color = .White, uint32 entityId = uint32.MaxValue)
{
DrawLine(Vector4(start, 1.0f), Vector4(end, 1.0f), color, entityId);
}
/** @brief Draws a ray.
* @param start The start point of the ray.
* @param direction The direction of the ray.
* @param color The color of the ray.
* @param entityId The optional ID of the entity that belongs to this ray (for picking).
*/
public static void DrawRay(Vector3 start, Vector3 direction, ColorRGBA color = .White, uint32 entityId = uint32.MaxValue)
{
DrawLine(Vector4(start, 1.0f), Vector4(direction, 0.0f), color, entityId);
}
/** @brief Draws a rectangle.
* @param position The center of the rectangle.
* @param size The size of the rectangle.
* @param color The color of the rectangle.
* @param entityId The optional ID of the entity that belongs to this rectangle (for picking).
*/
public static void DrawRect(Vector2 position, Vector2 size, ColorRGBA color = .White, uint32 entityId = uint32.MaxValue)
{
Vector2 halfSize = size / 2;
Vector4 p0 = Vector4(position + Vector2(-halfSize.X, -halfSize.Y), 0.0f, 1.0f);
Vector4 p1 = Vector4(position + Vector2(halfSize.X, -halfSize.Y), 0.0f, 1.0f);
Vector4 p2 = Vector4(position + Vector2(halfSize.X, halfSize.Y), 0.0f, 1.0f);
Vector4 p3 = Vector4(position + Vector2(-halfSize.X, halfSize.Y), 0.0f, 1.0f);
DrawLine(p0, p1, color, entityId);
DrawLine(p1, p2, color, entityId);
DrawLine(p2, p3, color, entityId);
DrawLine(p3, p0, color, entityId);
}
/** @brief Draws a rectangle.
* @param transform The transform of the rectangle.
* @param color The color of the rectangle.
* @param entityId The optional ID of the entity that belongs to this rectangle (for picking).
*/
public static void DrawRect(Matrix transform, ColorRGBA color = .White, uint32 entityId = uint32.MaxValue)
{
Vector2 halfSize = Vector2.One / 2.0f;
Vector4 p0 = transform * Vector4(-halfSize.X, -halfSize.Y, 0.0f, 1.0f);
Vector4 p1 = transform * Vector4(halfSize.X, -halfSize.Y, 0.0f, 1.0f);
Vector4 p2 = transform * Vector4(halfSize.X, halfSize.Y, 0.0f, 1.0f);
Vector4 p3 = transform * Vector4(-halfSize.X, halfSize.Y, 0.0f, 1.0f);
DrawLine(p0, p1, color, entityId);
DrawLine(p1, p2, color, entityId);
DrawLine(p2, p3, color, entityId);
DrawLine(p3, p0, color, entityId);
}
public static void DrawLine(Vector4 start, Vector4 end, ColorRGBA color = .White, uint32 entityId = uint32.MaxValue)
{
Debug.Profiler.ProfileRendererFunction!();
#if DEBUG
Log.EngineLogger.AssertDebug(s_sceneRunning, "Missing call of BeginScene.");
#endif
QueueLineInstance(start, end, color, entityId);
if(s_drawOrder == .Immediate)
{
DrawDeferred();
}
}
// Colored Quad // Colored Quad
public static void DrawQuad(Vector2 position, Vector2 size, float rotation, ColorRGBA color) public static void DrawQuad(Vector2 position, Vector2 size, float rotation, ColorRGBA color)
@@ -873,12 +1116,14 @@ namespace GlitchyEngine.Renderer
} }
} }
public static void DrawSprite(Matrix transform, SpriterRendererComponent* spriteRenderer, uint32 entityId) public static void DrawSprite(Matrix transform, SpriteRendererComponent* spriteRenderer, uint32 entityId)
{ {
if (spriteRenderer.IsCircle) DrawQuad(transform, spriteRenderer.Sprite.Get() ?? s_whiteTexture, spriteRenderer.Color, spriteRenderer.UvTransform, entityId);
DrawCircle(transform, spriteRenderer.Sprite ?? s_whiteTexture, spriteRenderer.Color, 1.0f, spriteRenderer.UvTransform, entityId); }
else
DrawQuad(transform, spriteRenderer.Sprite ?? s_whiteTexture, spriteRenderer.Color, spriteRenderer.UvTransform, entityId); public static void DrawCircle(Matrix transform, CircleRendererComponent* spriteRenderer, uint32 entityId)
{
DrawCircle(transform, spriteRenderer.Sprite.Get() ?? s_whiteTexture, spriteRenderer.Color, spriteRenderer.InnerRadius, spriteRenderer.UvTransform, entityId);
} }
// Textured quad pivot // Textured quad pivot
@@ -937,21 +1182,26 @@ namespace GlitchyEngine.Renderer
{ {
public uint32 QuadDrawCalls = 0; public uint32 QuadDrawCalls = 0;
public uint32 CircleDrawCalls = 0; public uint32 CircleDrawCalls = 0;
public uint32 LineDrawCalls = 0;
public uint32 QuadCount = 0; public uint32 QuadCount = 0;
public uint32 CircleCount = 0; public uint32 CircleCount = 0;
public uint32 LineCount = 0;
public uint32 TotalDrawCalls => QuadDrawCalls + CircleDrawCalls; public uint32 TotalDrawCalls => QuadDrawCalls + CircleDrawCalls + LineDrawCalls;
public uint32 TotalInstanceCount => QuadCount + CircleCount; public uint32 TotalInstanceCount => QuadCount + CircleCount + LineCount;
public uint32 TotalVertexCount => TotalInstanceCount * 4; public uint32 TotalVertexCount => (QuadCount + CircleCount) * 4 + LineCount * 2;
public uint32 TotalTriangleCount => TotalInstanceCount * 2; public uint32 TotalTriangleCount => (QuadCount + CircleCount) * 2;
public uint32 TotalIndexCount => TotalInstanceCount * 6; public uint32 TotalIndexCount => (QuadCount + CircleCount) * 6;
public void Reset() mut public void Reset() mut
{ {
QuadDrawCalls = 0; QuadDrawCalls = 0;
CircleDrawCalls = 0; CircleDrawCalls = 0;
LineDrawCalls = 0;
QuadCount = 0; QuadCount = 0;
CircleCount = 0; CircleCount = 0;
LineCount = 0;
} }
} }
@@ -333,8 +333,8 @@ namespace GlitchyEngine.Renderer.Text
Renderer2D.Flush(); Renderer2D.Flush();
// TODO: this is very not good! // TODO: this is very not good!
var lastEffect = Renderer2D.[Friend]s_currentEffect; var lastEffect = Renderer2D.[Friend]s_currentQuadEffect;
Renderer2D.[Friend]s_currentEffect = _msdfEffect..AddRef(); Renderer2D.[Friend]s_currentQuadEffect = _msdfEffect..AddRef();
// TODO: oh no.... // TODO: oh no....
// Copy viewProjection from current effect // Copy viewProjection from current effect
Matrix viewProjection = lastEffect.Variables["ViewProjection"].[Friend]GetData<Matrix>(); Matrix viewProjection = lastEffect.Variables["ViewProjection"].[Friend]GetData<Matrix>();
@@ -410,7 +410,7 @@ namespace GlitchyEngine.Renderer.Text
// TODO: not good! // TODO: not good!
// Change back effect // Change back effect
_msdfEffect.ReleaseRef(); _msdfEffect.ReleaseRef();
Renderer2D.[Friend]s_currentEffect = lastEffect; Renderer2D.[Friend]s_currentQuadEffect = lastEffect;
// release all atlas textures // release all atlas textures
for(int i < atlasses.Count) for(int i < atlasses.Count)
@@ -439,8 +439,8 @@ namespace GlitchyEngine.Renderer.Text
Renderer2D.Flush(); Renderer2D.Flush();
// TODO: this is very not good! // TODO: this is very not good!
var lastEffect = Renderer2D.[Friend]s_currentEffect; var lastEffect = Renderer2D.[Friend]s_currentQuadEffect;
Renderer2D.[Friend]s_currentEffect = _msdfEffect..AddRef(); Renderer2D.[Friend]s_currentQuadEffect = _msdfEffect..AddRef();
// TODO: oh no.... // TODO: oh no....
// Copy viewProjection from current effect // Copy viewProjection from current effect
Matrix viewProjection = lastEffect.Variables["ViewProjection"].[Friend]GetData<Matrix>(); Matrix viewProjection = lastEffect.Variables["ViewProjection"].[Friend]GetData<Matrix>();
@@ -664,7 +664,7 @@ namespace GlitchyEngine.Renderer.Text
// TODO: not good! // TODO: not good!
// Change back effect // Change back effect
_msdfEffect.ReleaseRef(); _msdfEffect.ReleaseRef();
Renderer2D.[Friend]s_currentEffect = lastEffect; Renderer2D.[Friend]s_currentQuadEffect = lastEffect;
// release all atlas textures // release all atlas textures
for(int i < atlasses.Count) for(int i < atlasses.Count)
-74
View File
@@ -63,92 +63,18 @@ namespace GlitchyEngine.Renderer
public class Texture2D : Texture public class Texture2D : Texture
{ {
protected String _path ~ delete _;
//public override extern uint32 Width {get;} //public override extern uint32 Width {get;}
//public override extern uint32 Height {get;} //public override extern uint32 Height {get;}
public override uint32 Depth => 1; public override uint32 Depth => 1;
//public override extern uint32 ArraySize {get;} //public override extern uint32 ArraySize {get;}
//public override extern uint32 MipLevels {get;} //public override extern uint32 MipLevels {get;}
private this(StringView path, bool pngSrgb = false)
{
_path = new String(path);
LoadTexture(pngSrgb);
}
// TODO: remove // TODO: remove
private this(Stream data) private this(Stream data)
{ {
LoadDds(data); LoadDds(data);
} }
const String PngMagicWord = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
const String DdsMagicWord = "DDS ";
private void LoadTexture(bool pngSrgb)
{
Debug.Profiler.ProfileResourceFunction!();
Stream data = Application.Get().ContentManager.GetStream(_path);
defer delete data;
var readResult = data.Read<char8[8]>();
data.Position = 0;
char8[8] magicWord;
if (readResult case .Ok(out magicWord))
{
StringView strView = .(&magicWord, magicWord.Count);
if (strView.StartsWith(PngMagicWord))
{
LoadPng(data, pngSrgb);
}
else if (strView.StartsWith(DdsMagicWord))
{
LoadDds(data);
}
else
{
Runtime.FatalError("Unknown image format.");
}
}
}
protected void LoadPng(Stream stream, bool srgb)
{
Debug.Profiler.ProfileResourceFunction!();
uint8[] pngData = new:ScopedAlloc! uint8[stream.Length];
var result = stream.TryRead(pngData);
if (result case .Err(let err))
{
Log.EngineLogger.Error($"Failed to read data from stream. Texture: \"{_path}\", Error: {err}");
}
uint8* rawData = ?;
uint32 width = 0, height = 0;
uint32 errorCode = LodePng.LodePng.Decode32(&rawData, &width, &height, pngData.Ptr, (.)pngData.Count);
Debug.Assert(errorCode == 0, "Failed to load png File");
// TODO: load as SRGB because PNGs are usually not stored as linear
//Texture2DDesc desc = .(width, height, .R8G8B8A8_UNorm_SRGB, 1, 1, .Immutable);
Texture2DDesc desc = .(width, height, srgb? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm, 1, 1, .Immutable);
PrepareTexturePlatform(desc, false);
SetData<Color>((.)rawData);
LodePng.LodePng.Free(rawData);
}
protected void LoadDds(Stream stream) protected void LoadDds(Stream stream)
{ {
LoadDdsPlatform(stream); LoadDdsPlatform(stream);
@@ -3,6 +3,7 @@ using GlitchyEngine.Math;
using GlitchyEngine.Renderer; using GlitchyEngine.Renderer;
using GlitchyEngine.Core; using GlitchyEngine.Core;
using Box2D; using Box2D;
using GlitchyEngine.Content;
namespace GlitchyEngine.World namespace GlitchyEngine.World
{ {
@@ -21,7 +22,7 @@ namespace GlitchyEngine.World
{ {
public readonly UUID ID; public readonly UUID ID;
/// Create aa new IDComponent with a random UUID. /// Create a new IDComponent with a random UUID.
public this() public this()
{ {
ID = UUID(); ID = UUID();
@@ -44,27 +45,13 @@ namespace GlitchyEngine.World
} }
[Component("Sprite Renderer")] [Component("Sprite Renderer")]
struct SpriterRendererComponent : IDisposableComponent struct SpriteRendererComponent
{ {
private Texture2D _sprite = null; public AssetHandle<Texture2D> Sprite = .Invalid;
public Texture2D Sprite
{
get => _sprite;
set mut
{
if (_sprite == value)
return;
SetReference!(_sprite, value);
}
}
public ColorRGBA Color = .White; public ColorRGBA Color = .White;
public Vector4 UvTransform = .(0, 0, 1, 1); public Vector4 UvTransform = .(0, 0, 1, 1);
public bool IsCircle = false;
public this() public this()
{ {
} }
@@ -73,10 +60,25 @@ namespace GlitchyEngine.World
{ {
Color = color; Color = color;
} }
}
public void Dispose() [Component("Circle Renderer")]
struct CircleRendererComponent
{ {
_sprite?.ReleaseRef(); public AssetHandle<Texture2D> Sprite = .Invalid;
public ColorRGBA Color = .White;
public Vector4 UvTransform = .(0, 0, 1, 1);
public float InnerRadius = 0.0f;
public this()
{
}
public this(ColorRGBA color)
{
Color = color;
} }
} }
@@ -0,0 +1,12 @@
using GlitchyEngine.Renderer;
using System;
using GlitchyEngine.Content;
namespace GlitchyEngine.World
{
/// A component that allows to render a mesh.
public struct MeshRendererComponent
{
public AssetHandle<Material> Material = .Invalid;
}
}
@@ -0,0 +1,25 @@
using System;
namespace GlitchyEngine.World
{
struct NameComponent : IDisposableComponent
{
private String _name;
public StringView Name
{
get => _name;
set mut
{
if (_name == null)
_name = new String(value);
else
_name.Set(value);
}
}
public void Dispose() mut
{
DeleteAndNullify!(_name);
}
}
}
@@ -20,6 +20,7 @@ namespace GlitchyEngine.World
/// The frame when the transform was recalculated /// The frame when the transform was recalculated
public uint Frame; public uint Frame;
// TODO: probably use UUID
public EcsEntity Parent public EcsEntity Parent
{ {
get => _parent; get => _parent;
@@ -1,26 +0,0 @@
using System;
namespace GlitchyEngine.World
{
struct DebugNameComponent : IDisposableComponent
{
private String _debugName;
public String DebugName
{
get => _debugName;
set mut => SetName(value);
}
public void SetName(StringView name) mut
{
delete _debugName;
_debugName = new String(name);
}
public void Dispose() mut
{
DeleteAndNullify!(_debugName);
}
}
}
+6
View File
@@ -66,6 +66,12 @@ namespace GlitchyEngine.World
public UUID UUID => GetComponent<IDComponent>().ID; public UUID UUID => GetComponent<IDComponent>().ID;
public StringView Name
{
get => GetComponent<NameComponent>().Name;
set => GetComponent<NameComponent>().Name = value;
}
public TransformComponent* Transform => GetComponent<TransformComponent>(); public TransformComponent* Transform => GetComponent<TransformComponent>();
public T* AddComponent<T>(T value = T()) where T: struct, new public T* AddComponent<T>(T value = T()) where T: struct, new
@@ -1,31 +0,0 @@
using GlitchyEngine.Renderer;
using System;
namespace GlitchyEngine.World
{
/// A component that allows to render a mesh.
public struct MeshRendererComponent : IDisposableComponent
{
private Material _material;
public Material Material
{
[Inline]
get => _material;
set mut
{
if(_material == value)
return;
_material?.ReleaseRef();
_material = value;
_material?.AddRef();
}
}
public void Dispose() mut
{
_material?.ReleaseRef();
}
}
}
@@ -1,10 +0,0 @@
namespace GlitchyEngine.World
{
/**
* A Component that allows to specify a parent entity.
*/
public struct ParentComponent
{
public EcsEntity Entity;
}
}
+112 -332
View File
@@ -11,7 +11,7 @@ namespace GlitchyEngine.World
using internal ScriptableEntity; using internal ScriptableEntity;
using internal GlitchyEngine.World; using internal GlitchyEngine.World;
class Scene class Scene : RefCounter
{ {
internal EcsWorld _ecsWorld = new .() ~ delete _; internal EcsWorld _ecsWorld = new .() ~ delete _;
@@ -19,12 +19,7 @@ namespace GlitchyEngine.World
private Dictionary<Type, function void(Entity entity, Type componentType, void* component)> _onComponentAddedHandlers = new .() ~ delete _; private Dictionary<Type, function void(Entity entity, Type componentType, void* component)> _onComponentAddedHandlers = new .() ~ delete _;
private RenderTargetGroup _compositeTarget ~ _.ReleaseRef(); private uint32 _viewportWidth, _viewportHeight;
// Temporary target for camera. Needs to change as soon as we support multiple cameras
private RenderTargetGroup _cameraTarget ~ _.ReleaseRef();
private Effect _gammaCorrectEffect ~ _.ReleaseRef();
// Maps ids to the entities they represent. // Maps ids to the entities they represent.
private Dictionary<UUID, EcsEntity> _idToEntity = new .() ~ delete _; private Dictionary<UUID, EcsEntity> _idToEntity = new .() ~ delete _;
@@ -45,44 +40,72 @@ namespace GlitchyEngine.World
public this() public this()
{ {
/*Entity entity = CreateEntity("Green Quad");
entity.AddComponent<SpriterRendererComponent>(.(ColorRGBA.SRgbToLinear(.(0.2f, 0.9f, 0.15f))));
Entity entity2 = CreateEntity("Red Square");
var v = entity2.AddComponent<SpriterRendererComponent>(.(ColorRGBA.SRgbToLinear(.(0.95f, 0.1f, 0.3f))));
v.Sprite = new Texture2D("Textures/rocket.dds");
v.Sprite.SamplerState = SamplerStateManager.PointClamp;*/
_onComponentAddedHandlers.Add(typeof(CameraComponent), (e, t, c) => { _onComponentAddedHandlers.Add(typeof(CameraComponent), (e, t, c) => {
CameraComponent* cameraComponent = (.)c; CameraComponent* cameraComponent = (.)c;
cameraComponent.Camera.SetViewportSize(e.Scene.ViewportWidth, e.Scene.ViewportHeight); cameraComponent.Camera.SetViewportSize(e.Scene._viewportWidth, e.Scene._viewportHeight);
}); });
RenderTargetGroupDescription desc = .(100, 100,
TargetDescription[](
RenderTargetFormat.R16G16B16A16_Float,
.(RenderTargetFormat.R32_UInt) {ClearColor = .UInt(uint32.MaxValue)}),
RenderTargetFormat.D24_UNorm_S8_UInt);
_compositeTarget = new RenderTargetGroup(desc);
_cameraTarget = new RenderTargetGroup(.(){
Width = 100,
Height = 100,
ColorTargetDescriptions = TargetDescription[](
.(.R16G16B16A16_Float),
.(.R32_UInt)
),
DepthTargetDescription = .(.D24_UNorm_S8_UInt)
});
_gammaCorrectEffect = Content.LoadAsset<Effect>("Shaders/GammaCorrect.hlsl");//Application.Get().EffectLibrary.Load("content/Shaders/GammaCorrect.hlsl");
} }
public ~this() public ~this()
{ {
} }
public void CopyTo(Scene target)
{
// Copy entities
for (let sourceHandle in _ecsWorld.Enumerate())
{
Entity sourceEntity = .(sourceHandle, this);
target.CreateEntity(sourceEntity.Name, sourceEntity.UUID);
}
// TODO: perhaps use reflection and comptime
// Copy components
//CopyComponents<TransformComponent>(this, target); /* Parent will be copied below*/
CopyComponents<MeshRendererComponent>(this, target);
CopyComponents<MeshComponent>(this, target);
CopyComponents<EditorComponent>(this, target);
CopyComponents<SpriteRendererComponent>(this, target);
CopyComponents<CircleRendererComponent>(this, target);
CopyComponents<CameraComponent>(this, target);
CopyComponents<NativeScriptComponent>(this, target);
CopyComponents<LightComponent>(this, target);
CopyComponents<Rigidbody2DComponent>(this, target);
CopyComponents<BoxCollider2DComponent>(this, target);
CopyComponents<CircleCollider2DComponent>(this, target);
// Copy transforms
for (let (sourceHandle, sourceTransform) in _ecsWorld.Enumerate<TransformComponent>())
{
Entity sourceEntity = Entity(sourceHandle, this);
Entity sourceParent = Entity(sourceTransform.Parent, this);
Entity targetEntity = target.GetEntityByID(sourceEntity.UUID);
*targetEntity.Transform = *sourceTransform;
if (sourceParent.IsValid)
{
Entity targetParent = target.GetEntityByID(sourceParent.UUID);
targetEntity.Parent = targetParent;
}
}
target.OnViewportResize(_viewportWidth, _viewportHeight);
}
private static void CopyComponents<TComponent>(Scene source, Scene target) where TComponent : struct, new
{
for (let (sourceHandle, sourceComponent) in source._ecsWorld.Enumerate<TComponent>())
{
Entity sourceEntity = .(sourceHandle, source);
Entity targetEntity = target.GetEntityByID(sourceEntity.UUID);
targetEntity.AddComponent<TComponent>(*sourceComponent);
}
}
b2Vec2 _gravity2D = .(0.0f, -9.8f); b2Vec2 _gravity2D = .(0.0f, -9.8f);
static b2BodyType GetBox2DBodyType(Rigidbody2DComponent.BodyType bodyType) static b2BodyType GetBox2DBodyType(Rigidbody2DComponent.BodyType bodyType)
@@ -102,6 +125,16 @@ namespace GlitchyEngine.World
} }
public void OnRuntimeStart() public void OnRuntimeStart()
{
OnSimulationStart();
}
public void OnRuntimeStop()
{
OnSimulationStop();
}
public void OnSimulationStart()
{ {
_physicsWorld2D = Box2D.World.Create(ref _gravity2D); _physicsWorld2D = Box2D.World.Create(ref _gravity2D);
@@ -160,20 +193,32 @@ namespace GlitchyEngine.World
} }
} }
public void OnRuntimeStop() public void OnSimulationStop()
{ {
Box2D.World.Delete(_physicsWorld2D); Box2D.World.Delete(_physicsWorld2D);
_physicsWorld2D = null; _physicsWorld2D = null;
} }
public void UpdateRuntime(GameTime gameTime, RenderTargetGroup finalTarget) public enum UpdateMode
{
/// No special update configuration (this does NOT mean nothing will be updated!)
None = 0x00,
/// Update editor-specific stuff
Editor = 0x01,
/// Update the physics related stuff
Physics = 0x02,
/// Update the runtume related stuff (e.g. execute scripts). Also run physics!
Runtime = 0x04 | Physics,
}
public void Update(GameTime gameTime, UpdateMode mode)
{ {
Debug.Profiler.ProfileRendererFunction!(); Debug.Profiler.ProfileRendererFunction!();
finalTarget.AddRef();
TransformSystem.Update(_ecsWorld); TransformSystem.Update(_ecsWorld);
if (mode.HasFlag(.Runtime))
{
// Run scripts // Run scripts
for (var (entity, script) in _ecsWorld.Enumerate<NativeScriptComponent>()) for (var (entity, script) in _ecsWorld.Enumerate<NativeScriptComponent>())
{ {
@@ -186,7 +231,10 @@ namespace GlitchyEngine.World
script.Instance.[Friend]OnUpdate(gameTime); script.Instance.[Friend]OnUpdate(gameTime);
} }
}
if (mode.HasFlag(.Physics))
{
// Update 2D physics // Update 2D physics
{ {
const int32 velocityIterations = 6; const int32 velocityIterations = 6;
@@ -211,300 +259,17 @@ namespace GlitchyEngine.World
transform.RotationEuler = .(transform.RotationEuler.XY, angle); transform.RotationEuler = .(transform.RotationEuler.XY, angle);
} }
} }
// Find camera
Camera* primaryCamera = null;
Matrix primaryCameraTransform = default;
RenderTargetGroup renderTarget = null;
for (var (entity, transform, camera) in _ecsWorld.Enumerate<TransformComponent, CameraComponent>())
{
if (camera.Primary)// && camera.RenderTarget != null)
{
primaryCamera = &camera.Camera;
primaryCameraTransform = transform.WorldTransform;
// TODO: bind render targets to cameras
// renderTarget = camera.RenderTarget..AddRef();
} }
} }
renderTarget = _cameraTarget..AddRef();
// 3D render
Renderer.BeginScene(*primaryCamera, primaryCameraTransform, renderTarget, _compositeTarget);
for (var (entity, transform, mesh, meshRenderer) in _ecsWorld.Enumerate<TransformComponent, MeshComponent, MeshRendererComponent>())
{
Renderer.Submit(mesh.Mesh, meshRenderer.Material, entity, transform.WorldTransform);
}
for (var (entity, transform, light) in _ecsWorld.Enumerate<TransformComponent, LightComponent>())
{
Renderer.Submit(light.SceneLight, transform.WorldTransform);
}
Renderer.EndScene();
renderTarget.ReleaseRef();
// TODO: alphablending (handle in Renderer2D)
// TODO: 2D-Postprocessing requires rendering into separate target instead of directly into compositeTarget
RenderCommand.SetRenderTargetGroup(_compositeTarget);
RenderCommand.BindRenderTargets();
// Sprite renderer
Renderer2D.BeginScene(*primaryCamera, primaryCameraTransform, .BackToFront);
for (var (entity, transform, sprite) in _ecsWorld.Enumerate<TransformComponent, SpriterRendererComponent>())
{
Renderer2D.DrawSprite(transform.WorldTransform, sprite, entity.Index);
}
Renderer2D.EndScene();
// Gamma correct composit target and draw it into viewport
{
RenderCommand.UnbindRenderTargets();
RenderCommand.SetRenderTargetGroup(finalTarget, false);
RenderCommand.BindRenderTargets();
_gammaCorrectEffect.SetTexture("Texture", _compositeTarget, 0);
// TODO: iiihhh
_gammaCorrectEffect.ApplyChanges();
_gammaCorrectEffect.Bind();
FullscreenQuad.Draw();
}
finalTarget.ReleaseRef();
/*Debug.Profiler.ProfileRendererFunction!();
finalTarget.AddRef();
TransformSystem.Update(_ecsWorld);
// Run scripts
for (var (entity, script) in _ecsWorld.Enumerate<NativeScriptComponent>())
{
if (script.Instance == null)
{
script.Instance = script.InstantiateFunction();
script.Instance._entity = Entity(entity, this);
script.Instance.[Friend]OnCreate();
}
script.Instance.[Friend]OnUpdate(gameTime);
}
// Find camera
Camera* primaryCamera = null;
Matrix primaryCameraTransform = default;
RenderTargetGroup renderTarget = null;
for (var (entity, transform, camera) in _ecsWorld.Enumerate<TransformComponent, CameraComponent>())
{
if (camera.Primary)// && camera.RenderTarget != null)
{
primaryCamera = &camera.Camera;
primaryCameraTransform = transform.WorldTransform;
//renderTarget = camera.RenderTarget..AddRef();
}
}
if (primaryCamera != null)
{
// 3D render
Renderer.BeginScene(*primaryCamera, primaryCameraTransform, _compositeTarget, finalTarget);
for (var (entity, transform, mesh, meshRenderer) in _ecsWorld.Enumerate<TransformComponent, MeshComponent, MeshRendererComponent>())
{
Renderer.Submit(mesh.Mesh, meshRenderer.Material, entity, transform.WorldTransform);
}
for (var (entity, transform, light) in _ecsWorld.Enumerate<TransformComponent, LightComponent>())
{
Renderer.Submit(light.SceneLight, transform.WorldTransform);
}
Renderer.EndScene();
// TODO: alphablending (handle in Renderer2D)
// TODO: 2D-Postprocessing requires rendering into separate target instead of directly into compositeTarget
RenderCommand.SetRenderTargetGroup(_compositeTarget);
RenderCommand.BindRenderTargets();
// Sprite renderer
Renderer2D.BeginScene(*primaryCamera, primaryCameraTransform, .BackToFront);
for (var (entity, transform, sprite) in _ecsWorld.Enumerate<TransformComponent, SpriterRendererComponent>())
{
Renderer2D.DrawSprite(transform.WorldTransform, sprite, entity.Index);
}
Renderer2D.EndScene();
// Gamma correct composit target and draw it into viewport
{
RenderCommand.UnbindRenderTargets();
RenderCommand.SetRenderTargetGroup(finalTarget, false);
RenderCommand.BindRenderTargets();
_gammaCorrectEffect.SetTexture("Texture", _compositeTarget, 0);
// TODO: iiihhh
_gammaCorrectEffect.Bind(Application.Get().Window.Context);
FullscreenQuad.Draw();
}
}
finalTarget.ReleaseRef();*/
/*
Debug.Profiler.ProfileRendererFunction!();
TransformSystem.Update(_ecsWorld);
for (var (entity, script) in _ecsWorld.Enumerate<NativeScriptComponent>())
{
if (script.Instance == null)
{
script.Instance = script.InstantiateFunction();
script.Instance._entity = Entity(entity, this);
script.Instance.[Friend]OnCreate();
}
script.Instance.[Friend]OnUpdate(gameTime);
}
Camera* primaryCamera = null;
Matrix primaryCameraTransform = default;
RenderTargetGroup renderTarget = null;
for (var (entity, transform, camera) in _ecsWorld.Enumerate<TransformComponent, CameraComponent>())
{
if (camera.Primary && camera.RenderTarget != null)
{
primaryCamera = &camera.Camera;
primaryCameraTransform = transform.WorldTransform;
renderTarget = camera.RenderTarget..AddRef();
}
}
if (primaryCamera != null)
{
// 3D render
Renderer.BeginScene(*primaryCamera, primaryCameraTransform, renderTarget, finalTarget);
for (var (entity, transform, mesh, meshRenderer) in _ecsWorld.Enumerate<TransformComponent, MeshComponent, MeshRendererComponent>())
{
Renderer.Submit(mesh.Mesh, meshRenderer.Material, transform.WorldTransform);
}
for (var (entity, transform, light) in _ecsWorld.Enumerate<TransformComponent, LightComponent>())
{
Renderer.Submit(light.SceneLight, transform.WorldTransform);
}
Renderer.EndScene();
// Sprite renderer
Renderer2D.BeginScene(*primaryCamera, primaryCameraTransform);
for (var (entity, transform, sprite) in _ecsWorld.Enumerate<TransformComponent, SpriterRendererComponent>())
{
Renderer2D.DrawQuad(transform.WorldTransform, sprite.Sprite, sprite.Color);
}
Renderer2D.EndScene();
renderTarget?.ReleaseRef();
}
*/
}
public void UpdateEditor(GameTime gameTime, EditorCamera camera, RenderTargetGroup viewportTarget, delegate void() DebugDraw3D, delegate void() DrawDebug2D)
{
Debug.Profiler.ProfileRendererFunction!();
viewportTarget.AddRef();
TransformSystem.Update(_ecsWorld);
// 3D render
Renderer.BeginScene(camera, _compositeTarget);
for (var (entity, transform, mesh, meshRenderer) in _ecsWorld.Enumerate<TransformComponent, MeshComponent, MeshRendererComponent>())
{
if (mesh.Mesh == null || meshRenderer.Material == null)
continue;
Renderer.Submit(mesh.Mesh, meshRenderer.Material, entity, transform.WorldTransform);
}
for (var (entity, transform, light) in _ecsWorld.Enumerate<TransformComponent, LightComponent>())
{
Renderer.Submit(light.SceneLight, transform.WorldTransform);
}
DebugDraw3D();
Renderer.EndScene();
// TODO: alphablending (handle in Renderer2D)
// TODO: 2D-Postprocessing requires rendering into separate target instead of directly into compositeTarget
RenderCommand.SetRenderTargetGroup(_compositeTarget);
RenderCommand.BindRenderTargets();
// Sprite renderer
Renderer2D.BeginScene(camera, .BackToFront);
for (var (entity, transform, sprite) in _ecsWorld.Enumerate<TransformComponent, SpriterRendererComponent>())
{
Renderer2D.DrawSprite(transform.WorldTransform, sprite, entity.Index);
}
Renderer2D.EndScene();
Renderer2D.BeginScene(camera, .BackToFront);
DrawDebug2D();
Renderer2D.EndScene();
// Gamma correct composit target and draw it into viewport
{
RenderCommand.UnbindRenderTargets();
RenderCommand.SetRenderTargetGroup(viewportTarget, false);
RenderCommand.BindRenderTargets();
_gammaCorrectEffect.SetTexture("Texture", _compositeTarget, 0);
// TODO: iiihhh
_gammaCorrectEffect.ApplyChanges();
_gammaCorrectEffect.Bind();
FullscreenQuad.Draw();
}
viewportTarget.ReleaseRef();
}
/// Creates a new Entity with the given name. /// Creates a new Entity with the given name.
public Entity CreateEntity(String name = "", UUID id = default) public Entity CreateEntity(StringView name = "", UUID id = default)
{ {
Entity entity = Entity(_ecsWorld.NewEntity(), this); Entity entity = Entity(_ecsWorld.NewEntity(), this);
entity.AddComponent<TransformComponent>(); entity.AddComponent<TransformComponent>();
let nameComponent = entity.AddComponent<DebugNameComponent>(); let nameComponent = entity.AddComponent<NameComponent>();
nameComponent.SetName(name.IsEmpty ? "Entity" : name); nameComponent.Name = (name.IsEmpty ? "Entity" : name);
// If no id is given generate a random one. // If no id is given generate a random one.
IDComponent idComponent = (id == default) ? IDComponent() : IDComponent(id); IDComponent idComponent = (id == default) ? IDComponent() : IDComponent(id);
@@ -545,13 +310,11 @@ namespace GlitchyEngine.World
return .Err; return .Err;
} }
private uint32 ViewportWidth, ViewportHeight;
/// Sets the size of the viewport into which the scene will be rendered. /// Sets the size of the viewport into which the scene will be rendered.
public void OnViewportResize(uint32 width, uint32 height) public void OnViewportResize(uint32 width, uint32 height)
{ {
ViewportWidth = width; _viewportWidth = width;
ViewportHeight = height; _viewportHeight = height;
for (var (entity, cameraComponent) in _ecsWorld.Enumerate<CameraComponent>()) for (var (entity, cameraComponent) in _ecsWorld.Enumerate<CameraComponent>())
{ {
@@ -560,9 +323,6 @@ namespace GlitchyEngine.World
cameraComponent.Camera.SetViewportSize(width, height); cameraComponent.Camera.SetViewportSize(width, height);
} }
} }
_compositeTarget.Resize(ViewportWidth, ViewportHeight);
_cameraTarget.Resize(ViewportWidth, ViewportHeight);
} }
private void OnComponentAdded(Entity entity, Type componentType, void* component) private void OnComponentAdded(Entity entity, Type componentType, void* component)
@@ -572,5 +332,25 @@ namespace GlitchyEngine.World
handler(entity, componentType, component); handler(entity, componentType, component);
} }
} }
public WorldEnumerator<TComponent> GetEntities<TComponent>() where TComponent : struct
{
return _ecsWorld.Enumerate<TComponent>();
}
public WorldEnumerator<TComponent1, TComponent2> GetEntities<TComponent1, TComponent2>()
where TComponent1 : struct
where TComponent2 : struct
{
return _ecsWorld.Enumerate<TComponent1, TComponent2>();
}
public WorldEnumerator<TComponent1, TComponent2, TComponent3> GetEntities<TComponent1, TComponent2, TComponent3>()
where TComponent1 : struct
where TComponent2 : struct
where TComponent3 : struct
{
return _ecsWorld.Enumerate<TComponent1, TComponent2, TComponent3>();
}
} }
} }
+212
View File
@@ -0,0 +1,212 @@
using GlitchyEngine.Renderer;
using GlitchyEngine.Content;
using GlitchyEngine.Math;
namespace GlitchyEngine.World;
using internal GlitchyEngine.World;
class SceneRenderer
{
public Scene Scene { get; set; }
private RenderTargetGroup _compositeTarget ~ _.ReleaseRef();
// Temporary target for camera. Needs to change as soon as we support multiple cameras
private RenderTargetGroup _cameraTarget ~ _.ReleaseRef();
private uint32 _viewportWidth;
private uint32 _viewportHeight;
private AssetHandle _gammaCorrectEffect;
public RenderTargetGroup CompositeTarget => _compositeTarget;
public this()
{
RenderTargetGroupDescription desc = .(100, 100,
TargetDescription[](
RenderTargetFormat.R16G16B16A16_Float,
.(RenderTargetFormat.R32_UInt) {ClearColor = .UInt(uint32.MaxValue)}),
RenderTargetFormat.D24_UNorm_S8_UInt);
_compositeTarget = new RenderTargetGroup(desc);
_cameraTarget = new RenderTargetGroup(.(){
Width = 100,
Height = 100,
ColorTargetDescriptions = TargetDescription[](
.(.R16G16B16A16_Float),
.(.R32_UInt)
),
DepthTargetDescription = .(.D24_UNorm_S8_UInt)
});
_gammaCorrectEffect = Content.LoadAsset("Shaders/GammaCorrect.hlsl");//Application.Get().EffectLibrary.Load("content/Shaders/GammaCorrect.hlsl");
}
/// Sets the size of the viewport into which the scene will be rendered.
public void OnViewportResize(uint32 width, uint32 height)
{
_viewportWidth = width;
_viewportHeight = height;
_compositeTarget.Resize(_viewportWidth, _viewportHeight);
_cameraTarget.Resize(_viewportWidth, _viewportHeight);
}
public void RenderRuntime(GameTime gameTime, RenderTargetGroup finalTarget)
{
Debug.Profiler.ProfileRendererFunction!();
// Find camera
Camera* primaryCamera = null;
Matrix primaryCameraTransform = default;
RenderTargetGroup renderTarget = null;
for (var (entity, transform, camera) in Scene._ecsWorld.Enumerate<TransformComponent, CameraComponent>())
{
if (camera.Primary)// && camera.RenderTarget != null)
{
primaryCamera = &camera.Camera;
primaryCameraTransform = transform.WorldTransform;
// TODO: bind render targets to cameras
// renderTarget = camera.RenderTarget..AddRef();
}
}
if (primaryCamera == null)
return;
finalTarget.AddRef();
renderTarget = _cameraTarget..AddRef();
// 3D render
Renderer.BeginScene(*primaryCamera, primaryCameraTransform, renderTarget, _compositeTarget);
for (var (entity, transform, mesh, meshRenderer) in Scene._ecsWorld.Enumerate<TransformComponent, MeshComponent, MeshRendererComponent>())
{
Renderer.Submit(mesh.Mesh, meshRenderer.Material, entity, transform.WorldTransform);
}
for (var (entity, transform, light) in Scene._ecsWorld.Enumerate<TransformComponent, LightComponent>())
{
Renderer.Submit(light.SceneLight, transform.WorldTransform);
}
Renderer.EndScene();
renderTarget.ReleaseRef();
// TODO: alphablending (handle in Renderer2D)
// TODO: 2D-Postprocessing requires rendering into separate target instead of directly into compositeTarget
RenderCommand.SetRenderTargetGroup(_compositeTarget);
RenderCommand.BindRenderTargets();
// Sprite renderer
Renderer2D.BeginScene(*primaryCamera, primaryCameraTransform, .BackToFront);
for (var (entity, transform, sprite) in Scene._ecsWorld.Enumerate<TransformComponent, SpriteRendererComponent>())
{
Renderer2D.DrawSprite(transform.WorldTransform, sprite, entity.Index);
}
for (var (entity, transform, circle) in Scene._ecsWorld.Enumerate<TransformComponent, CircleRendererComponent>())
{
Renderer2D.DrawCircle(transform.WorldTransform, circle, entity.Index);
}
Renderer2D.EndScene();
// Gamma correct composit target and draw it into viewport
{
RenderCommand.UnbindRenderTargets();
RenderCommand.SetRenderTargetGroup(finalTarget, false);
RenderCommand.BindRenderTargets();
Effect gammaEffect = Content.GetAsset<Effect>(_gammaCorrectEffect);
gammaEffect.SetTexture("Texture", _compositeTarget, 0);
// TODO: iiihhh
gammaEffect.ApplyChanges();
gammaEffect.Bind();
FullscreenQuad.Draw();
}
finalTarget.ReleaseRef();
}
public void RenderEditor(GameTime gameTime, EditorCamera camera, RenderTargetGroup viewportTarget, delegate void() DebugDraw3D, delegate void() DrawDebug2D)
{
Debug.Profiler.ProfileRendererFunction!();
viewportTarget.AddRef();
// 3D render
Renderer.BeginScene(camera, _compositeTarget);
for (var (entity, transform, mesh, meshRenderer) in Scene._ecsWorld.Enumerate<TransformComponent, MeshComponent, MeshRendererComponent>())
{
if (mesh.Mesh == .Invalid || meshRenderer.Material == .Invalid)
continue;
Renderer.Submit(mesh.Mesh, meshRenderer.Material, entity, transform.WorldTransform);
}
for (var (entity, transform, light) in Scene._ecsWorld.Enumerate<TransformComponent, LightComponent>())
{
Renderer.Submit(light.SceneLight, transform.WorldTransform);
}
DebugDraw3D();
Renderer.EndScene();
// TODO: alphablending (handle in Renderer2D)
// TODO: 2D-Postprocessing requires rendering into separate target instead of directly into compositeTarget
RenderCommand.SetRenderTargetGroup(_compositeTarget);
RenderCommand.BindRenderTargets();
// Sprite renderer
Renderer2D.BeginScene(camera, .BackToFront);
for (var (entity, transform, sprite) in Scene._ecsWorld.Enumerate<TransformComponent, SpriteRendererComponent>())
{
Renderer2D.DrawSprite(transform.WorldTransform, sprite, entity.Index);
}
for (var (entity, transform, circle) in Scene._ecsWorld.Enumerate<TransformComponent, CircleRendererComponent>())
{
Renderer2D.DrawCircle(transform.WorldTransform, circle, entity.Index);
}
Renderer2D.EndScene();
Renderer2D.BeginScene(camera, .BackToFront);
DrawDebug2D();
Renderer2D.EndScene();
// Gamma correct composit target and draw it into viewport
{
RenderCommand.UnbindRenderTargets();
RenderCommand.SetRenderTargetGroup(viewportTarget, false);
RenderCommand.BindRenderTargets();
Effect gammaEffect = Content.GetAsset<Effect>(_gammaCorrectEffect);
gammaEffect.SetTexture("Texture", _compositeTarget, 0);
// TODO: iiihhh
gammaEffect.ApplyChanges();
gammaEffect.Bind();
FullscreenQuad.Draw();
}
viewportTarget.ReleaseRef();
}
}
+51 -31
View File
@@ -9,12 +9,12 @@ using System.Collections;
using GlitchyEngine.Renderer; using GlitchyEngine.Renderer;
using GlitchyEngine.Content; using GlitchyEngine.Content;
namespace GlitchyEngine.World namespace GlitchyEngine.World;
{
using internal GlitchyEngine.World;
class SceneSerializer using internal GlitchyEngine.World;
{
class SceneSerializer
{
private Scene _scene; private Scene _scene;
// Maps from ParentID to ChildEntity // Maps from ParentID to ChildEntity
@@ -79,16 +79,22 @@ namespace GlitchyEngine.World
SerializeComponent<EditorComponent>(writer, entity, "EditorComponent", scope (component) => {}); SerializeComponent<EditorComponent>(writer, entity, "EditorComponent", scope (component) => {});
SerializeComponent<DebugNameComponent>(writer, entity, "NameComponent", scope (component) => SerializeComponent<NameComponent>(writer, entity, "NameComponent", scope (component) =>
{ {
Serialize.Value(writer, "Name", component.DebugName); Serialize.Value(writer, "Name", component.Name);
}); });
SerializeComponent<SpriterRendererComponent>(writer, entity, "SpriterRendererComponent", scope (component) => SerializeComponent<SpriteRendererComponent>(writer, entity, "SpriteRendererComponent", scope (component) =>
{ {
Serialize.Value(writer, "Color", component.Color); Serialize.Value(writer, "Color", component.Color);
Serialize.Value(writer, "IsCircle", component.IsCircle); Serialize.Value(writer, "Sprite", component.Sprite);
Serialize.Value(writer, "Sprite", component.Sprite?.Identifier); Serialize.Value(writer, "UvTransform", component.UvTransform);
});
SerializeComponent<CircleRendererComponent>(writer, entity, "CircleRendererComponent", scope (component) =>
{
Serialize.Value(writer, "Color", component.Color);
Serialize.Value(writer, "InnerRadius", component.InnerRadius);
Serialize.Value(writer, "Sprite", component.Sprite);
Serialize.Value(writer, "UvTransform", component.UvTransform); Serialize.Value(writer, "UvTransform", component.UvTransform);
}); });
@@ -172,12 +178,12 @@ namespace GlitchyEngine.World
SerializeComponent<MeshComponent>(writer, entity, "MeshComponent", scope (component) => SerializeComponent<MeshComponent>(writer, entity, "MeshComponent", scope (component) =>
{ {
Serialize.Value(writer, "Mesh", component.Mesh.Identifier); Serialize.Value(writer, "Mesh", component.Mesh);
}); });
SerializeComponent<MeshRendererComponent>(writer, entity, "MeshRendererComponent", scope (component) => SerializeComponent<MeshRendererComponent>(writer, entity, "MeshRendererComponent", scope (component) =>
{ {
Serialize.Value(writer, "Material", component.Material.Identifier); Serialize.Value(writer, "Material", component.Material);
}); });
} }
@@ -270,7 +276,7 @@ namespace GlitchyEngine.World
private Result<void> DeserializeEntity(BonReader reader) private Result<void> DeserializeEntity(BonReader reader)
{ {
mixin DeserializeAsset<T>(StringView identifier) where T : Asset /*mixin DeserializeAsset<T>(StringView identifier) where T : Asset
{ {
Asset asset = null; Asset asset = null;
@@ -283,6 +289,21 @@ namespace GlitchyEngine.World
} }
(T)asset (T)asset
}*/
mixin DeserializeAssetHandle<T>(StringView identifier) where T : Asset
{
Asset asset = null;
Try!(Deserialize.Value(reader, identifier, out asset));
if (asset != null && !(asset is T))
{
Log.EngineLogger.Error($"Asset {asset.Identifier} is not a {nameof(T)}.");
return .Err;
}
asset?.Handle ?? .Invalid
} }
Try!(reader.ObjectBlock()); Try!(reader.ObjectBlock());
@@ -302,32 +323,38 @@ namespace GlitchyEngine.World
case "EditorComponent": case "EditorComponent":
Try!(DeserializeComponent<EditorComponent>(reader, entity, scope (component) => { return .Ok; })); Try!(DeserializeComponent<EditorComponent>(reader, entity, scope (component) => { return .Ok; }));
case "NameComponent": case "NameComponent":
Try!(DeserializeComponent<DebugNameComponent>(reader, entity, scope (component) => Try!(DeserializeComponent<NameComponent>(reader, entity, scope (component) =>
{ {
String name; String name;
Deserialize.Value(reader, "Name", out name); Deserialize.Value(reader, "Name", out name);
component.SetName(name); component.Name = name;
delete name; delete name;
return .Ok; return .Ok;
})); }));
case "SpriterRendererComponent": case "SpriteRendererComponent":
Try!(DeserializeComponent<SpriterRendererComponent>(reader, entity, scope (component) => Try!(DeserializeComponent<SpriteRendererComponent>(reader, entity, scope (component) =>
{ {
Try!(Deserialize.Value(reader, "Color", out component.Color)); Try!(Deserialize.Value(reader, "Color", out component.Color));
reader.EntryEnd(); reader.EntryEnd();
Try!(Deserialize.Value(reader, "IsCircle", out component.IsCircle)); Try!(Deserialize.Value(reader, "Sprite", out component.Sprite));
reader.EntryEnd(); reader.EntryEnd();
Try!(Deserialize.Value(reader, "UvTransform", out component.UvTransform));
using (Texture2D sprite = DeserializeAsset!<Texture2D>("Sprite")) return .Ok;
}));
case "CircleRendererComponent":
Try!(DeserializeComponent<CircleRendererComponent>(reader, entity, scope (component) =>
{ {
component.Sprite = (Texture2D)sprite; Try!(Deserialize.Value(reader, "Color", out component.Color));
} reader.EntryEnd();
Try!(Deserialize.Value(reader, "InnerRadius", out component.InnerRadius));
reader.EntryEnd();
Try!(Deserialize.Value(reader, "Sprite", out component.Sprite));
reader.EntryEnd(); reader.EntryEnd();
Try!(Deserialize.Value(reader, "UvTransform", out component.UvTransform)); Try!(Deserialize.Value(reader, "UvTransform", out component.UvTransform));
return .Ok; return .Ok;
@@ -474,20 +501,14 @@ namespace GlitchyEngine.World
case "MeshComponent": case "MeshComponent":
Try!(DeserializeComponent<MeshComponent>(reader, entity, scope (component) => Try!(DeserializeComponent<MeshComponent>(reader, entity, scope (component) =>
{ {
using (GeometryBinding mesh = DeserializeAsset!<GeometryBinding>("Mesh")) Try!(Deserialize.Value(reader, "Mesh", out component.Mesh));
{
component.Mesh = mesh;
}
return .Ok; return .Ok;
})); }));
case "MeshRendererComponent": case "MeshRendererComponent":
Try!(DeserializeComponent<MeshRendererComponent>(reader, entity, scope (component) => Try!(DeserializeComponent<MeshRendererComponent>(reader, entity, scope (component) =>
{ {
using (Material material = DeserializeAsset!<Material>("Material")) Try!(Deserialize.Value(reader, "Material", out component.Material));
{
component.Material = material;
}
return .Ok; return .Ok;
})); }));
@@ -595,5 +616,4 @@ namespace GlitchyEngine.World
{ {
Runtime.NotImplemented(); Runtime.NotImplemented();
} }
}
} }
+13 -10
View File
@@ -1,4 +1,4 @@
using GlitchyEngine.Renderer; /*using GlitchyEngine.Renderer;
using GlitchyEngine.Events; using GlitchyEngine.Events;
using GlitchyEngine.ImGui; using GlitchyEngine.ImGui;
using GlitchyEngine.Math; using GlitchyEngine.Math;
@@ -63,8 +63,8 @@ namespace Sandbox
Material _checkerMaterial ~ _?.ReleaseRef(); Material _checkerMaterial ~ _?.ReleaseRef();
Material _logoMaterial ~ _?.ReleaseRef(); Material _logoMaterial ~ _?.ReleaseRef();
Texture2D _texture ~ _?.ReleaseRef(); AssetHandle _texture;
Texture2D _ge_logo ~ _?.ReleaseRef(); AssetHandle _ge_logo;
BlendState _alphaBlendState ~ _?.ReleaseRef(); BlendState _alphaBlendState ~ _?.ReleaseRef();
BlendState _opaqueBlendState ~ _?.ReleaseRef(); BlendState _opaqueBlendState ~ _?.ReleaseRef();
@@ -91,7 +91,7 @@ namespace Sandbox
//effectLibrary.LoadNoRefInc("content\\Shaders\\testShader.hlsl"); //effectLibrary.LoadNoRefInc("content\\Shaders\\testShader.hlsl");
Effect textureEffect = Content.LoadAsset<Effect>("Shaders\\textureShader.hlsl"); Effect textureEffect = Content.GetAsset<Effect>(Content.LoadAsset("Shaders\\textureShader.hlsl"));
_depthTarget = new DepthStencilTarget(_context.SwapChain.Width, _context.SwapChain.Height); _depthTarget = new DepthStencilTarget(_context.SwapChain.Width, _context.SwapChain.Height);
@@ -99,7 +99,7 @@ namespace Sandbox
VertexLayout vertexLayout = new VertexLayout(VertexColorTexture.VertexElements, false); VertexLayout vertexLayout = new VertexLayout(VertexColorTexture.VertexElements, false);
textureEffect.ReleaseRef(); //textureEffect.ReleaseRef();
// Create hexagon // Create hexagon
{ {
@@ -174,8 +174,11 @@ namespace Sandbox
rsDesc.FrontCounterClockwise = false; rsDesc.FrontCounterClockwise = false;
_rasterizerStateClockWise = new RasterizerState(rsDesc); _rasterizerStateClockWise = new RasterizerState(rsDesc);
_texture = Content.LoadAsset<Texture2D>("content/Textures/Checkerboard.dds");//new Texture2D("content/Textures/Checkerboard.dds"); _texture = Content.LoadAsset("content/Textures/Checkerboard.dds");//new Texture2D("content/Textures/Checkerboard.dds");
_ge_logo = Content.LoadAsset<Texture2D>("content/Textures/GE_Logo.dds");//new Texture2D("content/Textures/GE_Logo.dds"); _ge_logo = Content.LoadAsset("content/Textures/GE_Logo.dds");//new Texture2D("content/Textures/GE_Logo.dds");
Texture2D texture = Content.GetAsset<Texture2D>(_texture);
Texture2D ge_logo = Content.GetAsset<Texture2D>(_ge_logo);
let sampler = SamplerStateManager.GetSampler( let sampler = SamplerStateManager.GetSampler(
SamplerStateDescription() SamplerStateDescription()
@@ -183,8 +186,8 @@ namespace Sandbox
MagFilter = .Point MagFilter = .Point
}); });
_texture.SamplerState = sampler; texture.SamplerState = sampler;
_ge_logo.SamplerState = sampler; ge_logo.SamplerState = sampler;
sampler.ReleaseRef(); sampler.ReleaseRef();
@@ -518,4 +521,4 @@ namespace Sandbox
} }
} }
} }*/
+3 -2
View File
@@ -1,4 +1,4 @@
using System; /*using System;
using GlitchyEngine; using GlitchyEngine;
using GlitchyEngine.Events; using GlitchyEngine.Events;
using System.Diagnostics; using System.Diagnostics;
@@ -12,6 +12,7 @@ using GlitchyEngine.Renderer.Text;
using System.IO; using System.IO;
using msdfgen; using msdfgen;
using System.Collections; using System.Collections;
using GlitchyEngine.Content;
namespace Sandbox namespace Sandbox
{ {
@@ -282,4 +283,4 @@ namespace Sandbox
return false; return false;
} }
} }
} }*/
+1 -1
View File
@@ -21,7 +21,7 @@ namespace Sandbox
#if GAMMA_TEST #if GAMMA_TEST
PushLayer(new GammaTestLayer()); PushLayer(new GammaTestLayer());
#elif SANDBOX_2D #elif SANDBOX_2D
PushLayer(new ExampleLayer2D()); //PushLayer(new ExampleLayer2D());
#else #else
PushLayer(new ExampleLayer()); PushLayer(new ExampleLayer());
#endif #endif