186 Commits
Author SHA1 Message Date
Simon Lübeß aa70d99e31 Fin 2022-02-14 20:36:48 +01:00
Simon Lübeß c8cc2f442a Added ColorHSV 2022-02-14 20:36:07 +01:00
Simon Lübeß 574f54e32b Added Window.SetIcon 2022-02-14 20:35:35 +01:00
Simon Lübeß 58cf92fb57 Renderer2D limit instances per batch 2022-02-14 20:19:19 +01:00
Simon Lübeß f49acf34a1 Added particle system 2022-02-13 23:56:58 +01:00
Simon Lübeß 7f11b982ee Added text and restart 2022-02-13 22:51:55 +01:00
Simon Lübeß d9e34b27a3 Rocket Game 2022-02-13 15:22:45 +01:00
Simon Lübeß e61c53400f Added DeltaTime 2022-02-13 15:22:18 +01:00
Simon Lübeß a2db0774f8 Cleanup 2022-02-13 02:14:56 +01:00
Simon Lübeß 5b1f694bc9 SwapChain.AspectRatio fix 2022-01-22 10:44:10 +01:00
Simon Lübeß 69be98bb38 FontRenderer: Allow preparing empty strings 2022-01-22 10:42:27 +01:00
Simon Lübeß 7eb4001a1b PrepareFont calculates total pen advance 2022-01-21 21:19:44 +01:00
Simon Lübeß bca4552341 Added AspectRation property to SwapChain 2022-01-21 21:19:02 +01:00
Simon Lübeß d96ec6f21b Renderer2D: DrawQuad with Matrix + BeginScene now takes any camera 2022-01-21 21:17:43 +01:00
Simon Lübeß 44fe2c3281 Start of gamma correction 2022-01-15 17:50:52 +01:00
Simon Lübeß 6a55108a37 Fixed RenderTargets leaking a texure when it's a Swapchain target 2022-01-15 17:49:36 +01:00
Simon Lübeß a7b2515cf4 Added PNG-Loader to Texture2D 2022-01-15 14:34:46 +01:00
Simon Lübeß ee9dfd4593 Don't use Harfbuzz for simple text rendering + Ignored warnings 2022-01-15 13:29:45 +01:00
Simon Lübeß 3c1897b926 Even more Profiling 2022-01-14 22:01:41 +01:00
Simon Lübeß 8a84843f54 Updated Freetype-beef 2022-01-14 21:50:06 +01:00
Simon Lübeß 06469f3350 Instrumentation 2022-01-14 21:49:15 +01:00
Simon Lübeß 78faf4cb81 Simple bidirectional text 2022-01-08 02:17:35 +01:00
Simon Lübeß e292029015 Start of font preparation 2022-01-07 18:42:43 +01:00
Simon Lübeß 5373d92297 Updated Freetype 2022-01-07 03:28:39 +01:00
Simon Lübeß deedb62a03 Merge remote-tracking branch 'origin/main' into main 2022-01-05 15:42:57 +01:00
Simon Lübeß 6199496987 Fixed release build 2022-01-05 15:36:34 +01:00
Simon Lübeß 826ac68873 Basic Harfbuzz support and use MSDGen with Skia 2022-01-05 15:31:09 +01:00
Simon Lübeß b2ce560bfc Added DepthStencilState 2022-01-01 19:34:59 +01:00
Simon Lübeß 664ba552ea Added struct and disposable component Generators 2021-12-30 16:52:32 +01:00
Simon Lübeß b555cc37d1 Simplified dispose function of DisposableComponents 2021-12-30 16:52:13 +01:00
Simon Lübeß b3ab30e72a DrawQuad with transform matrix 2021-12-30 16:18:43 +01:00
Simon Lübeß 1cc37ee168 Run default constructor when assigning component 2021-12-30 16:18:22 +01:00
Simon Lübeß f4d64b15fa Merge remote-tracking branch 'origin/main' into main 2021-12-15 21:25:17 +01:00
Simon Lübeß e55b73db0f Renamed Int-Vector and added swizzle 2021-12-08 20:58:26 +01:00
Simon Lübeß f28984bec5 Enabled vector swizzle 2021-12-04 15:52:48 +01:00
Simon Lübeß 0fd6ac1dee Fixed preprocessor macros 2021-10-30 14:21:26 +02:00
Simon Lübeß 419509ab7f Improved Define names 2021-10-30 14:17:17 +02:00
Simon Lübeß 2bc87b437a Removed delete:append in GameTime 2021-10-30 11:25:31 +02:00
Simon Lübeß b3f9376dc8 Added Min/Max to Vectors 2021-10-30 11:24:58 +02:00
Simon Lübeß ffafa380ea Added Backbuffer to Swapchain + RenderCommand API changes 2021-10-15 22:40:42 +02:00
Simon Lübeß 90e654b750 Renderer2D cleanup 2021-10-15 22:39:24 +02:00
Simon Lübeß adaf62de62 Fixed circle instancing buffer + clip pixels 2021-10-15 12:57:32 +02:00
Simon Lübeß dcb8424c2d Added circle rendering to Renderer2D 2021-10-14 23:01:08 +02:00
Simon Lübeß 84df8c111b Fixed various memory leaks + ModelLoader cleanup 2021-10-14 20:47:25 +02:00
Simon Lübeß cd82a99450 Added helpers for deleting/clearing Lists of RefCounters 2021-10-14 20:42:24 +02:00
Simon Lübeß 9216462a51 ComponentPools no longer falsely trigger leak detection
Fixes #3
2021-10-14 20:40:18 +02:00
Simon Lübeß 3eb7068bca Dispose Components when EcsWorld gets deleted 2021-10-14 18:56:29 +02:00
Simon Lübeß 54870330b6 Fixed memory leak 2021-10-14 15:54:13 +02:00
Simon Lübeß ac74aeb116 Updated Submodule msdfgen 2021-10-13 23:12:32 +02:00
Simon Lübeß b5bfcf5741 Fixed memory leak 2021-10-13 23:04:43 +02:00
Simon Lübeß 0cbbf94fe6 Use static D3D11 device and context 2021-10-13 22:56:22 +02:00
Simon Lübeß 84daee95e4 Replaced RefCounted with RefCounter 2021-10-13 09:52:12 +02:00
Simon Lübeß 2cd9ab3e69 Fixed memory leaks 2021-10-11 12:41:56 +02:00
Simon Lübeß 5a82b89622 Fixed memory leak in Renderer2D 2021-10-09 16:47:20 +02:00
Simon Lübeß b020855ec5 Cleaned ExampleLayer2D 2021-09-29 23:58:01 +02:00
Simon Lübeß 4dc6c916ae Reimplemented Renderer2D
- Renderer2D now static
- Renderer now calls Init and Deinit of Renderer2D
- Renderer2D  now calls Init and Deinit of FontRenderer
- Fixed Deinit order of Layers and renderer (e.g. Layers now deinit first)
- Sandbox2D now uses SandboxApp (only loads different layer)
2021-09-29 23:27:41 +02:00
Simon Lübeß 9a9d75afbc Movement speed now depends on zoom 2021-09-29 23:18:32 +02:00
Simon Lübeß fab0f99951 Added guizmo
Translation is broken for some reason
2021-09-28 21:56:51 +02:00
Simon Lübeß 122f7e9904 Replaced imgui with engine specific repository 2021-09-28 21:27:08 +02:00
Simon Lübeß eb2bbf4b82 Keep references to Images used by ImGui 2021-09-21 16:49:10 +02:00
Simon Lübeß 611782653c Adapted Editor to new RenderTarget api 2021-09-21 16:36:22 +02:00
Simon Lübeß 229d2b578e Reworked RenderTarget2D and corresponding API 2021-09-21 16:30:34 +02:00
Simon Lübeß 8e39d7988b Added RefCounted helpers 2021-09-21 16:29:22 +02:00
Simon Lübeß 4debaee7d4 SetViewport now part of RenderCommand 2021-09-21 13:15:43 +02:00
Simon Lübeß 4af61a372f Fire WindowResizeEvent when window minimizes
Also no longer updates layer-stack when the application is minimized
2021-09-21 13:14:58 +02:00
Simon Lübeß 3f1f5736b5 Improved MSDF generation and rendering 2021-09-20 15:42:10 +02:00
Simon Lübeß 2afff4ec20 Added MSDF to Font and FontRenderer 2021-09-19 21:19:00 +02:00
Simon Lübeß 764b9f27b6 Start of TextureViewer 2021-09-19 21:16:57 +02:00
Simon Lübeß 2d5c408b54 Flush Renderer2D when instance buffer is full 2021-09-19 17:36:58 +02:00
Simon Lübeß 22a4bc355e Fixed typo 2021-09-19 17:35:53 +02:00
Simon Lübeß 90284739c0 Added support for int shader variables 2021-09-19 14:22:37 +02:00
Simon Lübeß 8471b7d7b9 Added SamplerState for white texture 2021-09-19 13:33:48 +02:00
Simon Lübeß 38fd5fa2c8 Switched to static c-library, updated submodules 2021-09-18 22:44:11 +02:00
Simon Lübeß 0c4fd27226 Removed warnings when converting Vectors from GlitchyEngine to DirectX 2021-09-18 18:59:01 +02:00
Simon Lübeß a80a63fbc1 Start of msdf text 2021-09-18 18:39:34 +02:00
Simon Lübeß 9cd95ed0e4 Renderer2D can now use custom effects 2021-09-18 18:38:45 +02:00
Simon Lübeß b5efaf2b5b Added submodule msdfgen 2021-09-18 18:37:12 +02:00
Simon Lübeß da6d242353 Adapted to internal change in Texture 2021-09-17 13:09:15 +02:00
Simon Lübeß 0b1ec9a837 Improved Editor
- Added window docking
- Added MenuBar
- Added scene viewport
- Added closing and reopening for editor windows
2021-09-17 13:05:15 +02:00
Simon Lübeß 45d298b755 Added overload for Image to ImGui extension 2021-09-17 13:00:17 +02:00
Simon Lübeß 266d7a4651 Added RenderTarget2D 2021-09-17 12:58:33 +02:00
Simon Lübeß d051b392d0 Fixed class name in EditorApp 2021-09-16 18:16:40 +02:00
Simon Lübeß e2fc373a06 Update EntityHierarchyWindow.bf 2021-09-16 18:14:37 +02:00
Simon Lübeß 48de55f987 Updated imgui 2021-09-16 18:13:10 +02:00
Simon Lübeß 0079b80b1c Created editor project and moved Editor there 2021-09-16 13:19:25 +02:00
Simon Lübeß f6b073557a Updated .gitignore 2021-09-16 13:18:49 +02:00
Simon Lübeß b8626f3c27 Made buffer fixed array 2021-09-15 18:43:45 +02:00
Simon Lübeß 0b8be33d68 Workaround for localTransform calculation changing transform values 2021-09-15 17:53:27 +02:00
Simon Lübeß e14ebec387 Added Component editor for Transform- and DebugNameComponent 2021-09-15 17:52:35 +02:00
Simon Lübeß b4bb5c4a39 Added Editor and EntityHierarchyWindow 2021-09-15 13:27:03 +02:00
Simon Lübeß c04cd8a956 Added generic TreeNode class 2021-09-15 13:25:57 +02:00
Simon Lübeß 908167c83e Added DebugNameComponent 2021-09-14 23:54:50 +02:00
Simon Lübeß ab17598972 [ECS] WorldEnumerator now skips deleted entities 2021-09-14 23:29:33 +02:00
Simon Lübeß db05a5a6a9 [ECS] Fixed_disposingPools gets trashed when _componentPools reallocates 2021-09-14 23:28:18 +02:00
Simon Lübeß cb2c83678e Assigning a component again now returns the component instead of null 2021-09-14 23:00:42 +02:00
Simon Lübeß b0b5f76423 Animations now component based 2021-09-13 20:52:41 +02:00
Simon Lübeß 44374a84a7 Added DrawCoordinateCross function 2021-09-11 17:50:30 +02:00
Simon Lübeß 39f5893c7e Updated AnimationClip, Model Loader now loads Nodes into ECS
- SandboxApp: Started basic skinned mesh render system
2021-09-11 17:00:11 +02:00
Simon Lübeß 89bb475994 Added SkinnedMeshRendererComponent 2021-09-11 16:58:00 +02:00
Simon Lübeß c4ed04ba96 Replaced Error with debug assert 2021-09-11 16:57:33 +02:00
Simon Lübeß 886ce2fe3d Added basic AnimationComponent 2021-09-11 16:57:16 +02:00
Simon Lübeß 8442f7741d Fixed setting matrix array in material 2021-09-11 16:31:58 +02:00
Simon Lübeß 29749f64e9 Added WorldEnumerator with 4 generic parameters 2021-09-11 16:31:16 +02:00
Simon Lübeß 5c95668a1d Start of DebugRenderer 2021-09-11 12:58:25 +02:00
Simon Lübeß cb51880925 Reference fixes 2021-09-11 12:57:44 +02:00
Simon Lübeß da24643c64 Added SetVariable for Matrix-arrays 2021-09-11 12:50:27 +02:00
Simon Lübeß f761f87e1d Added Setter for LocalTransform 2021-09-11 12:49:17 +02:00
Simon Lübeß fc2f1cd7e6 Skeleton and AnimationClip now refcounted 2021-09-11 12:48:49 +02:00
Simon Lübeß 155495eeef Added basic Matrix decomposition 2021-09-11 12:46:30 +02:00
Simon Lübeß 1491ea8e75 PI now optimal, added Zero-Check 2021-09-11 12:45:59 +02:00
Simon Lübeß ff4cd48f1e Changed Rotation from Euler to Quaternion (TransformComponent) 2021-09-03 16:44:50 +02:00
Simon Lübeß d555a80d8d Updated Quaternion and added Test 2021-09-02 23:36:17 +02:00
Simon Lübeß 2d8efa0154 Added MathHelper
- common Values of Pi
- Degree <-> Radians conversions
2021-09-02 23:34:42 +02:00
Simon Lübeß 78aeacc8a1 Added project for UnitTesting 2021-09-02 23:33:41 +02:00
Simon Lübeß 5545d84284 Changed pointer type to prevent compiler error 2021-09-02 13:09:32 +02:00
Simon Lübeß 30280c5f93 Re-Removed old code in TransformSystem 2021-09-02 13:02:43 +02:00
Simon Lübeß e42b587d88 Added MeshRendererComponent
updated SandoxApp to utilize it
2021-09-02 12:34:28 +02:00
Simon Lübeß c270fdcb03 Fixed parentTransform not causing update of child transform
(also improved performance)
2021-09-02 12:30:18 +02:00
Simon Lübeß 0534824c07 Refactored SetVariable and added some more types 2021-09-02 12:28:36 +02:00
Simon Lübeß 5ce7ebe674 Added generic World Enumerators
also moved WorldEnumerators out of EcsWorld
2021-09-02 12:20:33 +02:00
Simon Lübeß c9e9a11e4e No longer showing ImGui demo-window 2021-09-02 12:18:46 +02:00
Simon Lübeß d1175519eb Disabled assert that forces Debugger to be present 2021-09-02 12:17:44 +02:00
Simon Lübeß 566b4ea59e Switched Allocator from Debug to CRT 2021-09-02 12:17:12 +02:00
Simon Lübeß 0e7217778f Implemented disposing for component 2021-08-29 20:23:44 +02:00
Simon Lübeß b376525d30 Added Entity hierarchy and hierarchical transforms 2021-08-28 22:28:40 +02:00
Simon Lübeß ce03f7c600 Start of AnimationSystem 2021-08-27 22:37:59 +02:00
Simon Lübeß eafdc2f17f Transposed matrix resulting from RotationQuaternion 2021-08-27 20:54:34 +02:00
Simon Lübeß 22252f9161 Application now has EffectLibrary field 2021-08-25 22:46:50 +02:00
Simon Lübeß 7dda8bdd25 Added properties to PerspectiveCameraController 2021-08-25 22:42:48 +02:00
Simon Lübeß 41754f5c67 Added IndexOf and IndexOfBinary to Array 2021-08-25 22:39:34 +02:00
Simon Lübeß 099763e1e3 Added Vertexcount to GeometryBinding and made Indexbuffer optional 2021-08-25 22:39:11 +02:00
Simon Lübeß a8bef51834 Deduplicated Vertexbuffer constructor 2021-08-25 22:36:05 +02:00
Simon Lübeß c880c4ef73 Removed unneeded debug code 2021-08-25 22:28:33 +02:00
Simon Lübeß 8ab740fa2e Added basic support for arrays in BufferVariables 2021-08-25 22:27:04 +02:00
Simon Lübeß 93513f3ea8 Vector Lerp 2021-08-25 22:22:59 +02:00
Simon Lübeß 9dbf9fd501 Added Quaternions 2021-08-25 22:19:02 +02:00
Simon Lübeß e239de52d6 Added Line and Ray drawing 2021-08-20 19:44:26 +02:00
Simon Lübeß 9e06d4d51f Refactored SetVertexShader and SetPixelShader 2021-08-15 14:19:24 +02:00
Simon Lübeß d200c2c849 Refactored FontRenderer, fixed Sandbox2D 2021-08-15 13:20:38 +02:00
Simon Lübeß f5f3bad8db Fixed Renderer2D
- Fixed textures not being bound
- Fixed destructor trying to delete scoped object
2021-08-11 17:20:48 +02:00
Simon Lübeß 344accea60 Implemented Materials
- Shaders get Textureslots via reflection
- added Textureslots to Effect
2021-08-09 18:03:47 +02:00
Simon Lübeß 2326e69c0c Added submodule gltf
This time for sure
2021-08-06 12:31:51 +02:00
Simon Lübeß 80e0bae347 Added GLTF submodule and start of ModelLoader 2021-08-06 12:22:46 +02:00
Simon Lübeß 5c0ee3bae0 VertexBuffer, VertexLayout and GeometryBinding improvements
- VertexBuffer: Added constructor that takes stride instead of type
- VertexLayout: Added Refcounting to VertexLayout and ownership parameters for VertexElements
- Fixed: GeometryBinding using stride and offset from buffer instead of buffer binding
2021-08-05 18:32:21 +02:00
Simon Lübeß e04d058381 Added PerspectiveCameraController 2021-08-04 19:23:29 +02:00
Simon Lübeß 0735aae74f Added OrthographicCameraController 2021-08-04 17:22:49 +02:00
Simon Lübeß 3ac99491cf Changed EventDispacher from class to struct 2021-08-04 17:22:14 +02:00
Simon Lübeß 303578a83f Fixed Width and Height property of ApplicationEvent 2021-08-04 17:21:03 +02:00
Simon Lübeß ce163db30b Unified texture loading, updated DirectX-beef 2021-07-22 18:57:35 +02:00
Simon Lübeß dbdb8a0d32 Made FontRenderer static, added scale and linegap to DrawText
it seems I forgot to push the freetype-submodule
2021-07-19 20:19:57 +02:00
Simon Lübeß 9b418521af Fixed WasKeyPressed and WasKeyReleased 2021-07-19 20:07:30 +02:00
Simon Lübeß b1ad7b5ba3 Added single stepping 2021-07-19 20:06:37 +02:00
Simon Lübeß 9158da60c2 Refcounted Font, added Fallback property 2021-06-23 19:39:32 +02:00
Simon Lübeß 7f8379ea6c Implemented FontRenderer 2021-06-23 19:17:17 +02:00
Simon Lübeß 8c957f9d9f Renderer2D: render Null-Texture and custom UV-Coordinates 2021-06-23 19:16:40 +02:00
Simon Lübeß 819476a3ec Added constructor and SetData to Texture2D
obviously this has to be done for the other texture types as well at some point
2021-06-23 19:11:37 +02:00
Simon Lübeß 1f1b32e101 Moved CPUAccessFlags and Usage to seperate file 2021-06-23 19:10:04 +02:00
Simon Lübeß f131b21273 Added default SamplerStates 2021-06-23 19:08:41 +02:00
Simon Lübeß 5ae0192bc6 Added DepthStencilTarget to Sandbox2DApp 2021-06-02 16:51:45 +02:00
Simon Lübeß dc088dcb0a Added pixelformat parameter to DepthStencilTarget-constructor 2021-06-02 16:51:25 +02:00
Simon Lübeß bf94f10a64 Added rudimentary DepthStencilTarget 2021-05-30 19:49:10 +02:00
Simon Lübeß 562f4855bd Added Renderer2D and 2D Example 2021-05-30 19:38:24 +02:00
Simon Lübeß 64579f9603 Constantbuffer now supports single bools 2021-05-30 19:38:08 +02:00
Simon Lübeß ae290936b9 Added basic support for instanced rendering 2021-05-30 19:37:23 +02:00
Simon Lübeß 0f2c481616 Fixed vector indexed access 2021-05-22 15:08:14 +02:00
Simon Lübeß b19dd60e52 Debuglogger no longer asserts a Debugger to be present in release builds 2021-05-20 12:42:17 +02:00
Simon Lübeß 2f281f13fd Updated submodule imgui-beef 2021-05-18 23:02:31 +02:00
Simon Lübeß 3eeca63d91 Fixed DirectXTK-Submodule path (for real this time) 2021-05-18 19:39:34 +02:00
Simon Lübeß 3f938d3488 Fixed DirectXTK-Submodule path 2021-05-18 19:37:21 +02:00
Simon Lübeß 7fb1f30303 Componentified Camera 2021-05-17 20:07:58 +02:00
Simon Lübeß 3e0c4b2eb7 Releasing RasterizerState insted of deleting 2021-04-09 19:38:20 +02:00
Simon Lübeß bfd81745e9 Disabled Vector swizzle
caused by beef bug
2021-04-09 19:37:54 +02:00
Simon Lübeß 6376bde112 Implemented basic ECS 2021-04-09 19:35:24 +02:00
Simon Lübeß 047c09017c Removed epsilon value from camera
(bug fixed in DirectX-beef)
2021-04-09 19:35:19 +02:00
Simon Lübeß 1f687f9b96 Added TextureCube 2021-04-09 19:35:13 +02:00
Simon Lübeß ad4933a1f9 Added larger epsilon to perspective camera 2021-04-09 19:35:08 +02:00
Simon Lübeß 838bb7ec2e Added explicit conversion from float to Vector 2021-04-09 19:34:44 +02:00
Simon Lübeß 92a3eaa426 Added protected function to create Texture2D from raw data 2021-04-09 19:32:24 +02:00
Simon Lübeß 4227882afa Added GE_D3D11 flag 2021-04-09 19:32:02 +02:00
Simon Lübeß 5a8499ac43 Moved ImplBind implementation from Texture2D to Texture 2021-04-09 19:31:49 +02:00
Simon Lübeß 72cb982b13 Added lodepng-beef 2021-04-09 19:31:18 +02:00
Simon Lübeß 92b22acb91 Added refcounting to RasterizerState 2021-04-09 19:28:46 +02:00
Simon Lübeß 2ad45edef5 Improved Windows keyboard input
- added handling of syskey-messages (alt-Key now generates Key events)
- right and left variants of keys now generate seperate events (e.g. left shift and right shift are seperate keys now instead of simply being "shift")
- added method to get the localized name of a Key
2021-03-14 17:59:13 +01:00
Simon Lübeß cfff2cb738 Added window (de-)activate events 2021-03-13 17:50:58 +01:00
Simon Lübeß 163dadbe37 Added EffectLibrary 2021-03-05 16:40:45 +01:00
Simon Lübeß 08daa2f59e Added Int32 vectors 2021-03-03 16:42:17 +01:00
197 changed files with 12988 additions and 6933 deletions
+1 -2
View File
@@ -4,5 +4,4 @@ build/
.vs/
recovery/
vendor/directx
Sandbox/imgui.ini
Sandbox/worlds
imgui.ini
+17 -5
View File
@@ -1,12 +1,24 @@
[submodule "GlitchyEngine/vendor/directx"]
path = GlitchyEngine/vendor/directx
url = https://github.com/aharabada/directx-beef.git
[submodule "GlitchyEngine/vendor/imgui-beef"]
path = GlitchyEngine/vendor/imgui-beef
url = https://github.com/aharabada/imgui-beef
[submodule ".\\GlitchyEngine\\vendor\\DirectXTK"]
path = .\\GlitchyEngine\\vendor\\DirectXTK
[submodule "GlitchyEngine/vendor/DirectXTK"]
path = GlitchyEngine/vendor/DirectXTK
url = https://github.com/aharabada/DirectXTK-beef.git
[submodule "vendor/lodepng-beef"]
path = vendor/lodepng-beef
url = https://github.com/aharabada/lodepng-beef.git
[submodule "GlitchyEngine\\vendor\\freetype"]
path = GlitchyEngine\\vendor\\freetype
url = https://github.com/aharabada/FreeType-beef.git
[submodule "GlitchyEngine/vendor/freetype"]
path = GlitchyEngine/vendor/freetype
url = https://github.com/aharabada/FreeType-beef.git
[submodule "GlitchyEngine/vendor/gltf"]
path = GlitchyEngine/vendor/gltf
url = https://github.com/aharabada/cgltf-beef.git
[submodule "GlitchyEngine/vendor/msdfgen"]
path = GlitchyEngine/vendor/msdfgen
url = https://github.com/aharabada/msdfgen-beef.git
[submodule "GlitchyEngine/vendor/imgui"]
path = GlitchyEngine/vendor/imgui
url = https://github.com/aharabada/ImGui_GlitchyEngine.git
+1 -1
View File
@@ -1,5 +1,5 @@
FileVersion = 1
Projects = {Sandbox = {Path = "Sandbox"}, GlitchyEngine = {Path = "GlitchyEngine"}, GlitchLog = {Path = "GlitchLog"}, DirectX = {Path = "GlitchyEngine/vendor/directx/DirectX"}, ImGui = {Path = "GlitchyEngine/vendor/imgui-beef/ImGui"}, ImGuiImplWin32 = {Path = "GlitchyEngine/vendor/imgui-beef/ImGuiImplWin32"}, ImGuiImplDX11 = {Path = "GlitchyEngine/vendor/imgui-beef/ImGuiImplDX11"}, DirectXTK = {Path = "GlitchyEngine/vendor/DirectXTK/DirectXTK-beef"}, LodePng = {Path = "vendor/lodepng-beef/lodepng-beef"}}
Projects = {Sandbox = {Path = "Sandbox"}, GlitchyEngine = {Path = "GlitchyEngine"}, GlitchLog = {Path = "GlitchLog"}, DirectX = {Path = "GlitchyEngine/vendor/directx/DirectX"}, DirectXTK = {Path = "GlitchyEngine/vendor/DirectXTK/DirectXTK-beef"}, 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"}}
[Workspace]
StartupProject = "Sandbox"
+6 -6
View File
@@ -50,10 +50,10 @@ namespace GlitchLog
public this()
{
Runtime.Assert(Debug.IsDebuggerPresent, "The DebugLogger requires a debugger to be present.");
//Debug.Assert(Debug.IsDebuggerPresent, "The DebugLogger requires a debugger to be present.");
}
#if GL_NOLOG || GL_NOTRACE
#if GL_NOLOG || GL_LOG_NOTRACE
[SkipCall]
#endif
[Inline]
@@ -62,7 +62,7 @@ namespace GlitchLog
InternalLog(.Trace, format, params args);
}
#if GL_NOLOG || GL_NOINFO
#if GL_NOLOG || GL_LOG_NOINFO
[SkipCall]
#endif
[Inline]
@@ -71,7 +71,7 @@ namespace GlitchLog
InternalLog(.Info, format, params args);
}
#if GL_NOLOG || GL_NOWARNING
#if GL_NOLOG || GL_LOG_NOWARNING
[SkipCall]
#endif
[Inline]
@@ -80,7 +80,7 @@ namespace GlitchLog
InternalLog(.Warning, format, params args);
}
#if GL_NOLOG || GL_NOERROR
#if GL_NOLOG || GL_LOG_NOERROR
[SkipCall]
#endif
[Inline]
@@ -89,7 +89,7 @@ namespace GlitchLog
InternalLog(.Error, format, params args);
}
#if GL_NOLOG || GL_NOCRITICAL
#if GL_NOLOG || GL_LOG_NOCRITICAL
[SkipCall]
#endif
[Inline]
+7
View File
@@ -0,0 +1,7 @@
FileVersion = 1
Dependencies = {corlib = "*", GlitchLog = "*", GlitchyEngine = "*"}
[Project]
Name = "GlitchyEditor"
TargetType = "BeefGUIApplication"
StartupObject = "GlitchyEngine.Program"
@@ -0,0 +1,31 @@
cbuffer Constants : register(b0)
{
float4x4 ViewProjection;
float4 Color;
};
struct VS_Input
{
float4 Position : POSITION;
};
struct PS_Input
{
float4 Position : SV_Position;
};
PS_Input VS(VS_Input input)
{
PS_Input output;
output.Position = mul(ViewProjection, input.Position);
return output;
}
float4 PS(PS_Input input) : SV_Target0
{
return Color;
}
#effect[VS=VS, PS=PS]
@@ -0,0 +1,37 @@
Texture2D Texture : register(t0);
SamplerState Sampler : register(s0);
struct VS_Input
{
float2 Position : POSITION;
float4x4 Tranform : TRANSFORM;
float4 Color : COLOR;
float4 UVTransform : TEXCOORD;
};
struct PS_Input
{
float4 Position : SV_Position;
float2 TexCoord : TEXCOORD0;
float4 Color : COLOR;
};
PS_Input VS(VS_Input input)
{
PS_Input output;
output.Position = mul(float4(input.Position, 0.0f, 1.0f), input.Tranform);
output.TexCoord = input.UVTransform.xy + input.UVTransform.zw * input.Position;
output.Color = input.Color;
return output;
}
float4 PS(PS_Input input) : SV_Target0
{
float4 color = input.Color * Texture.Sample(Sampler, input.TexCoord);
return color;
}
#effect[VS=VS, PS=PS]
@@ -0,0 +1,84 @@
cbuffer SceneConstants
{
float4x4 ViewProjection = float4x4(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
cbuffer ObjectConstants
{
float4x4 Transform;
}
cbuffer Constants
{
float4 BaseColor;
float3 LightDir;
}
cbuffer SkinningMatrices
{
matrix SkinningMatrices[255];
float3x3 InvTransSkinningMatrices[255];
}
struct VS_IN
{
float3 Position : POSITION;
float3 Normal : NORMAL;
uint4 JointIndices : JOINTS_0;
float4 JointWeights : WEIGHTS_0;
};
struct PS_IN
{
float4 Position : SV_POSITION;
float3 Normal : NORMAL;
};
PS_IN VS(VS_IN input)
{
PS_IN output;
// Unanimated position in modelspace
float4 positionRaw = float4(input.Position, 1);
// Calculate animated position in modelspace
float4 position =
mul(SkinningMatrices[input.JointIndices.x], positionRaw) * input.JointWeights.x +
mul(SkinningMatrices[input.JointIndices.y], positionRaw) * input.JointWeights.y +
mul(SkinningMatrices[input.JointIndices.z], positionRaw) * input.JointWeights.z +
mul(SkinningMatrices[input.JointIndices.w], positionRaw) * input.JointWeights.w;
float3 normal =
mul(InvTransSkinningMatrices[input.JointIndices.x], input.Normal) * input.JointWeights.x +
mul(InvTransSkinningMatrices[input.JointIndices.y], input.Normal) * input.JointWeights.y +
mul(InvTransSkinningMatrices[input.JointIndices.z], input.Normal) * input.JointWeights.z +
mul(InvTransSkinningMatrices[input.JointIndices.w], input.Normal) * input.JointWeights.w;
//position = saturate(position - 1000) + positionRaw;
//float4 position = mul(SkinningMatrices[input.JointIndices.x], positionRaw);
float4 worldPosition = mul(Transform, position);
output.Position = mul(ViewProjection, worldPosition);
output.Normal = mul((float3x3)Transform, normal);
return output;
}
float4 PS(PS_IN input) : SV_TARGET
{
input.Normal = normalize(input.Normal);
float shading = dot(LightDir, input.Normal);
//float shading = dot(LightDir, LightDir) + 1;
shading = clamp(shading, 0.0f, 1.0f);
return BaseColor * shading;
}
#effect[VS=VS,PS=PS]
@@ -0,0 +1,157 @@
using ImGui;
using GlitchyEngine.World;
using System;
using GlitchyEngine.Math;
namespace GlitchyEditor.EditWindows
{
class ComponentEditWindow
{
public const String s_WindowTitle = "Components";
private Editor _editor;
private bool _open = true;
public Editor Editor => _editor;
public bool Open
{
get => _open;
set => _open = value;
}
public this(Editor editor)
{
_editor = editor;
}
public void Show()
{
if(!_open)
return;
if(!ImGui.Begin(s_WindowTitle, &_open, .None))
{
ImGui.End();
return;
}
if(_editor.SelectedEntities.Count == 1)
{
Entity entity = _editor.SelectedEntities.Front;
ShowComponents(entity);
}
ImGui.End();
}
private void ShowComponents(Entity entity)
{
NameComponentEditor.Show(_editor.World, entity);
TransformComponentEditor.Show(_editor.World, entity);
}
}
static class NameComponentEditor
{
public static void Show(EcsWorld world, Entity entity)
{
char8[128] nameBuffer = default;
DebugNameComponent* component = world.GetComponent<DebugNameComponent>(entity);
String name = null;
if(component != null)
{
name = component.DebugName;
}
else
{
name = scope:: $"Entity {entity.[Friend]Index}";
}
// Copy name to buffer
Internal.MemCpy(&nameBuffer, name.Ptr, Math.Min(nameBuffer.Count, name.Length));
if(ImGui.InputText("Name", &nameBuffer, nameBuffer.Count))
{
if(component == null)
{
component = world.AssignComponent<DebugNameComponent>(entity);
}
component.DebugName.Clear();
component.DebugName.Append(&nameBuffer);
}
}
}
static class TransformComponentEditor
{
public static void Show(EcsWorld world, Entity entity)
{
TransformComponent* component = world.GetComponent<TransformComponent>(entity);
if(component == null)
return;
if(ImGui.TreeNodeEx("Transform", .DefaultOpen))
{
Vector3 position = component.Position;
bool positionChanged = false;
Vector3 rotationEuler = MathHelper.ToDegrees(component.RotationEuler);
bool rotationChanged = false;
Vector3 scale = component.Scale;
bool scaleChanged = false;
void ShowValue(String text, ref float value, ref bool valueChanged, String id)
{
ImGui.Text(text);
ImGui.SameLine();
ImGui.PushID(id);
valueChanged |= ImGui.DragFloat(String.Empty, &value, 0.1f);
ImGui.PopID();
}
void ShowTableRow(String name, ref Vector3 value, ref bool valueChanged)
{
ImGui.TableNextColumn();
ImGui.Text(name);
ImGui.TableNextColumn();
ShowValue("X: ", ref value.X, ref valueChanged, scope $"{name}X");
ImGui.TableNextColumn();
ShowValue("Y: ", ref value.Y, ref valueChanged, scope $"{name}Y");
ImGui.TableNextColumn();
ShowValue("Z: ", ref value.Z, ref valueChanged, scope $"{name}Z");
ImGui.TableNextRow();
}
ImGui.BeginTable("posRotScaleTable", 4);
ShowTableRow("Position", ref position, ref positionChanged);
ShowTableRow("Rotation", ref rotationEuler, ref rotationChanged);
ShowTableRow("Scale", ref scale, ref scaleChanged);
ImGui.EndTable();
if(positionChanged)
component.Position = position;
if(rotationChanged)
component.RotationEuler = MathHelper.ToRadians(rotationEuler);
if(scaleChanged)
component.Scale = scale;
ImGui.TreePop();
}
}
}
}
@@ -0,0 +1,340 @@
using GlitchyEngine.Collections;
using GlitchyEngine.World;
using ImGui;
using System;
using System.Collections;
using GlitchyEngine;
namespace GlitchyEditor.EditWindows
{
using internal GlitchyEditor;
/// A window for viewing and editing the scene hierarchy
class EntityHierarchyWindow
{
public const String s_WindowTitle = "Entity Hierarchy";
private Editor _editor;
/// Buffer for the entity search string.
private char8[64] _entitySearchChars;
private bool _open = true;
public Editor Editor => _editor;
public bool Open
{
get => _open;
set => _open = value;
}
public this(Editor editor)
{
_editor = editor;
}
public void Show()
{
if(!_open)
return;
if(!ImGui.Begin(s_WindowTitle, &_open, .MenuBar))
{
ImGui.End();
return;
}
ShowEntityHierarchyMenuBar();
ShowEntityHierarchy();
ImGui.End();
}
private void ShowEntityHierarchyMenuBar()
{
if(ImGui.BeginMenuBar())
{
if(ImGui.BeginMenu("Create"))
{
if(ImGui.MenuItem("Empty Entity"))
{
_editor.CreateEntityWithTransform();
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity with a transform component.");
if(ImGui.MenuItem("Empty Child", null, false, !_editor.SelectedEntities.IsEmpty))
{
var newEntity = _editor.CreateEntityWithTransform();
var parent = _editor.World.AssignComponent<ParentComponent>(newEntity);
// Last entity in list is the entity that has been selected last.
parent.Entity = _editor.SelectedEntities.Back;
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity that is a child of the currently selected entity.");
if(ImGui.MenuItem("Empty Parent", null, false, !_editor.SelectedEntities.IsEmpty && _editor.AllSelectionsOnSameLevel()))
{
var commonParent = _editor.World.GetComponent<ParentComponent>(_editor.SelectedEntities.Front);
var newEntity = _editor.CreateEntityWithTransform();
if(commonParent != null)
{
// parent of selected entities is parent of the new entity.
// (which is why this doesn't work if the entities don't have the same parent)
var newEntityParent = _editor.World.AssignComponent<ParentComponent>(newEntity);
newEntityParent.Entity = commonParent.Entity;
}
// new entity is parent of all selected entities.
for(var selectedEntity in _editor.SelectedEntities)
{
var selectedEntityParent = _editor.World.AssignComponent<ParentComponent>(selectedEntity);
selectedEntityParent.Entity = newEntity;
}
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity that is the parent of the currently selected entities.");
ImGui.EndMenu();
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity.");
if(ImGui.MenuItem("Delete", null, false, !_editor.SelectedEntities.IsEmpty) || Input.IsKeyPressed(.Delete))
{
_editor.DeleteSelectedEntities();
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Deletes the selected Entities and their children.");
ImGui.Text("Search:");
ImGui.InputText(String.Empty, &_entitySearchChars, (.)_entitySearchChars.Count);
ImGui.EndMenuBar();
}
}
private void ImGuiPrintEntityTree(TreeNode<Entity> tree)
{
String name = null;
var nameComponent = _editor.World.GetComponent<DebugNameComponent>(tree.Value);
if(nameComponent != null)
{
name = nameComponent.DebugName;
}
else
{
name = scope:: $"Entity {(tree.Value.[Friend]Index)}";
}
ImGui.TreeNodeFlags flags = .OpenOnArrow;
if(tree.Children.Count == 0)
flags |= .Leaf;
bool inSelectedList = _editor.SelectedEntities.Contains(tree.Value);
if(inSelectedList)
flags |= .Selected;
bool isOpen = ImGui.TreeNodeEx(name, flags);
if(ImGui.BeginDragDropSource())
{
ImGui.SetDragDropPayload("DND_Entity", &tree.Value, sizeof(Entity));
ImGui.Text(name);
ImGui.EndDragDropSource();
}
if(ImGui.BeginDragDropTarget())
{
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("DND_Entity");
if(payload != null)
{
Log.ClientLogger.AssertDebug(payload.DataSize == sizeof(Entity));
Entity movedEntity = *(Entity*)payload.Data;
bool dropLegal = true;
Entity walker = tree.Value;
// make sure the dropped entity is not a parent of the entity we dropped it on.
while(true)
{
var walkerParent = _editor.World.GetComponent<ParentComponent>(walker);
if(walkerParent == null)
{
dropLegal = true;
break;
}
else if(walkerParent.Entity == movedEntity)
{
dropLegal = false;
break;
}
walker = walkerParent.Entity;
}
if(dropLegal)
{
var movedEntityParent = _editor.World.AssignComponent<ParentComponent>(movedEntity);
movedEntityParent.Entity = tree.Value;
}
}
ImGui.EndDragDropTarget();
}
bool clicked = ImGui.IsItemClicked();
if(isOpen)
{
for(var child in tree.Children)
{
ImGuiPrintEntityTree(child);
}
ImGui.TreePop();
}
if(clicked)
{
if(inSelectedList)
{
_editor.SelectedEntities.Remove(tree.Value);
}
else
{
if(!ImGui.GetIO().KeyCtrl)
{
_editor.SelectedEntities.Clear();
}
_editor.SelectedEntities.Add(tree.Value);
}
inSelectedList = !inSelectedList;
}
}
private void ShowEntityHierarchy()
{
StringView searchString = StringView(&_entitySearchChars);
if(searchString.Length == 0)
{
// Show entity hierarchy as tree
TreeNode<Entity> root = scope .(.InvalidEntity);
TreeNode<Entity> AddEntity(Entity entity)
{
var parent = _editor.World.GetComponent<ParentComponent>(entity);
if(parent == null)
{
return root.AddChild(entity);
}
else
{
var parentNode = root.FindNode(parent.Entity);
if(parentNode == null)
parentNode = AddEntity(parent.Entity);
return parentNode.AddChild(entity);
}
}
for(var entity in _editor.World.Enumerate())
{
AddEntity(entity);
}
if(ImGui.TreeNodeEx("Scene", .DefaultOpen))
{
if(ImGui.BeginDragDropTarget())
{
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("DND_Entity");
if(payload != null)
{
Log.ClientLogger.AssertDebug(payload.DataSize == sizeof(Entity));
Entity movedEntity = *(Entity*)payload.Data;
_editor.World.RemoveComponent<ParentComponent>(movedEntity);
// Also mark transform as dirty
var transformComponent = _editor.World.GetComponent<TransformComponent>(movedEntity);
transformComponent?.IsDirty = true;
}
ImGui.EndDragDropTarget();
}
for(var child in root.Children)
{
ImGuiPrintEntityTree(child);
}
ImGui.TreePop();
}
}
// Otherwise
else
{
// Show search results as flat list
List<StringView> searchTokens = new:ScopedAlloc! .(searchString.Split(' ', .RemoveEmptyEntries));
worldEnumeration:
for(var entity in _editor.World.Enumerate())
{
String name = null;
var nameComponent = _editor.World.GetComponent<DebugNameComponent>(entity);
if(nameComponent != null)
{
name = nameComponent.DebugName;
}
else
{
name = scope:worldEnumeration $"Entity {entity.[Friend]Index}";
}
StringView nameView = StringView(name);
for(var token in searchTokens)
{
if(nameView.IndexOf(token, true) == -1)
{
continue worldEnumeration;
}
}
ImGuiPrintEntityTree(scope .(entity));
}
}
}
}
}
@@ -0,0 +1,130 @@
using System;
using ImGui;
using GlitchyEngine.Renderer;
using GlitchyEngine.Math;
using GlitchyEngine;
using GlitchyEngine.World;
using ImGuizmo;
namespace GlitchyEditor.EditWindows
{
class SceneViewportWindow
{
private Editor _editor;
public Camera _camera;
public const String s_WindowTitle = "Scene";
private bool _open = true;
private RenderTarget2D _renderTarget ~ _?.ReleaseRef();
private bool _hasFocus;
public Event<EventHandler<Vector2>> ViewportSizeChangedEvent ~ _.Dispose();
public bool Open
{
get => _open;
set => _open = value;
}
public bool HasFocus
{
get => _hasFocus;
}
public RenderTarget2D RenderTarget
{
get => _renderTarget;
set
{
if(_renderTarget == value)
return;
SetReference!(_renderTarget, value);
}
}
public this(Editor editor)
{
_editor = editor;
}
private ImGui.Vec2 oldViewportSize;
private bool viewPortChanged;
public void Show()
{
if(!_open)
return;
ImGui.PushStyleVar(.WindowPadding, ImGui.Vec2(1, 1));
defer ImGui.PopStyleVar();
if(!ImGui.Begin(s_WindowTitle, &_open, .NoScrollbar))
{
ImGui.End();
return;
}
if(ImGui.IsWindowHovered() && Input.IsMouseButtonPressing(.RightButton))
{
var currentWindow = ImGui.GetCurrentWindow();
ImGui.FocusWindow(currentWindow);
}
_hasFocus = ImGui.IsWindowFocused();
var viewportSize = ImGui.GetContentRegionAvail();
if(_renderTarget != null)
{
ImGui.Image(_renderTarget, viewportSize);
}
if(_editor.SelectedEntities.Count > 0)
{
ImGuizmo.SetDrawlist();
var entity = _editor.SelectedEntities.Back;
var transformCmp = _editor.World.GetComponent<TransformComponent>(entity);
var transform = transformCmp.LocalTransform;
var view = _camera.View;
var projection = _camera.Projection;
var v = ImGui.GetWindowPos();
var cntMin = ImGui.GetWindowContentRegionMin();
v.x += cntMin.x;
v.y += cntMin.y;
ImGuizmo.SetRect(v.x, v.y, viewportSize.x, viewportSize.y);
Color c = .(0,0,0,255);
//ImGuizmo.DrawCubes((.)&view, (.)&projection, (.)&transform, 1);
ImGuizmo.Manipulate((.)&view, (.)&projection, .TRANSLATE, .LOCAL, (.)&transform);
Matrix mat = .Identity;
ImGuizmo.DrawGrid((.)&view, (.)&projection, (.)&mat, 10);
transformCmp.LocalTransform = transform;
//ImGuizmo.ViewManipulate((.)&view, , .TRANSLATE, .LOCAL, (.)&transform);
}
ImGui.End();
if(oldViewportSize != viewportSize)
{
ViewportSizeChangedEvent.Invoke(this, *(Vector2*)&oldViewportSize);
viewPortChanged = true;
oldViewportSize = viewportSize;
}
}
}
}
+114
View File
@@ -0,0 +1,114 @@
using GlitchyEngine.World;
using ImGui;
using System;
using System.Collections;
using GlitchyEngine.Collections;
using GlitchyEditor.EditWindows;
namespace GlitchyEditor
{
class Editor
{
private EcsWorld _world;
private EntityHierarchyWindow _entityHierarchyWindow = new .(this) ~ delete _;
private ComponentEditWindow _componentEditWindow = new .(this) ~ delete _;
private SceneViewportWindow _sceneViewportWindow = new .(this) ~ delete _;
private List<Entity> _selectedEntities = new .() ~ delete _;
public EcsWorld World => _world;
public List<Entity> SelectedEntities => _selectedEntities;
public EntityHierarchyWindow EntityHierarchyWindow => _entityHierarchyWindow;
public ComponentEditWindow ComponentEditWindow => _componentEditWindow;
public SceneViewportWindow SceneViewportWindow => _sceneViewportWindow;
/// Creates a new editor for the given world
public this(EcsWorld world)
{
_world = world;
}
public void Update()
{
_entityHierarchyWindow.Show();
_componentEditWindow.Show();
_sceneViewportWindow.Show();
}
/// Creates a new entity with a transform component.
internal Entity CreateEntityWithTransform()
{
var entity = _world.NewEntity();
var transformComponent = ref *_world.AssignComponent<TransformComponent>(entity);
transformComponent = TransformComponent();
var nameComponent = ref *_world.AssignComponent<DebugNameComponent>(entity);
nameComponent.SetName("Entity");
return entity;
}
/// Returns whether or not all selected entities have the same parent.
internal bool AllSelectionsOnSameLevel()
{
Entity? parent = .InvalidEntity;
for(var selectedEntity in _selectedEntities)
{
var parentComponent = _world.GetComponent<ParentComponent>(selectedEntity);
if(parent == .InvalidEntity)
{
parent = parentComponent?.Entity;
}
else if(parentComponent?.Entity != parent)
{
return false;
}
}
return true;
}
/// Finds all children of the given entity and stores their IDs in the given list.
internal void FindChildren(Entity entity, List<Entity> entities)
{
for(var (child, childParent) in _world.Enumerate<ParentComponent>())
{
if(childParent.Entity == entity)
{
if(!entities.Contains(child))
entities.Add(child);
FindChildren(child, entities);
}
}
}
/// Deletes all selected entities and their children.
internal void DeleteSelectedEntities()
{
List<Entity> entities = scope .();
for(var entity in _selectedEntities)
{
entities.Add(entity);
FindChildren(entity, entities);
}
for(var entity in entities)
{
_world.RemoveEntity(entity);
}
_selectedEntities.Clear();
}
}
}
+19
View File
@@ -0,0 +1,19 @@
using System;
using GlitchyEngine;
namespace GlitchyEditor
{
class EditorApp : Application
{
public this()
{
PushLayer(new EditorLayer());
}
[Export, LinkName("CreateApplication")]
public static Application CreateApplication()
{
return new EditorApp();
}
}
}
+208
View File
@@ -0,0 +1,208 @@
using System;
using ImGui;
using GlitchyEngine;
using GlitchyEditor.EditWindows;
using GlitchyEngine.Events;
using GlitchyEngine.ImGui;
using GlitchyEngine.Math;
using GlitchyEngine.Renderer;
using GlitchyEngine.World;
namespace GlitchyEditor
{
class EditorLayer : Layer
{
RasterizerState _rasterizerState ~ _?.ReleaseRef();
RasterizerState _rasterizerStateClockWise ~ _?.ReleaseRef();
GraphicsContext _context ~ _?.ReleaseRef();
DepthStencilTarget _swapchainDepthBuffer ~ _?.ReleaseRef();
BlendState _alphaBlendState ~ _?.ReleaseRef();
BlendState _opaqueBlendState ~ _?.ReleaseRef();
EcsWorld _world = new EcsWorld() ~ delete _;
Editor _editor ~ delete _;
PerspectiveCameraController _cameraController ~ delete _;
RenderTarget2D _renderTarget2D ~ _?.ReleaseRef();
public this() : base("Example")
{
Application.Get().Window.IsVSync = false;
InitGraphics();
InitEcs();
InitEditor();
}
private void InitGraphics()
{
_context = Application.Get().Window.Context..AddRef();
_swapchainDepthBuffer = new DepthStencilTarget(_context.SwapChain.Width, _context.SwapChain.Height);
RasterizerStateDescription rsDesc = .(.Solid, .Back, true);
_rasterizerState = new RasterizerState(rsDesc);
rsDesc.FrontCounterClockwise = false;
_rasterizerStateClockWise = new RasterizerState(rsDesc);
BlendStateDescription blendDesc = .();
blendDesc.RenderTarget[0] = .(true, .SourceAlpha, .InvertedSourceAlpha, .Add, .SourceAlpha, .InvertedSourceAlpha, .Add, .All);
_alphaBlendState = new BlendState(blendDesc);
_opaqueBlendState = new BlendState(.Default);
_renderTarget2D = new RenderTarget2D(RenderTarget2DDescription(.R8G8B8A8_UNorm, 100, 100) {DepthStencilFormat = .D32_Float});
SamplerStateDescription desc = .();
SamplerState sampler = new SamplerState(desc);
_renderTarget2D.SamplerState = sampler;
sampler.ReleaseRef();
}
private void InitEcs()
{
_world.Register<DebugNameComponent>();
_world.Register<TransformComponent>();
_world.Register<ParentComponent>();
_world.Register<MeshComponent>();
_world.Register<MeshRendererComponent>();
_world.Register<SkinnedMeshRendererComponent>();
_world.Register<CameraComponent>();
_world.Register<AnimationComponent>();
}
private void InitEditor()
{
_editor = new Editor(_world);
_editor.SceneViewportWindow.ViewportSizeChangedEvent.Add(new (s, e) => ViewportSizeChanged(s, e));
_cameraController = new .(Application.Get().Window.Context.SwapChain.BackbufferViewport.Width /
Application.Get().Window.Context.SwapChain.BackbufferViewport.Height);
_cameraController.CameraPosition = .(0, 0, -5);
_cameraController.TranslationSpeed = 10;
_editor.[Friend]CreateEntityWithTransform();
_editor.SceneViewportWindow._camera = _cameraController.Camera;
}
public override void Update(GameTime gameTime)
{
if(_editor.SceneViewportWindow.HasFocus && Input.IsMouseButtonPressed(.RightButton))
_cameraController.Update(gameTime);
TransformSystem.Update(_world);
RenderCommand.Clear(_renderTarget2D, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0);
_context.SetRenderTarget(_renderTarget2D);
_context.BindRenderTargets();
RenderCommand.SetViewport(Viewport(0, 0, _renderTarget2D.Width, _renderTarget2D.Height));
RenderCommand.SetBlendState(_opaqueBlendState);
Renderer.BeginScene(_cameraController.Camera);
DebugRenderer.Render(_world);
Renderer.EndScene();
RenderCommand.Clear(null, .(0.2f, 0.2f, 0.2f));
RenderCommand.Clear(_swapchainDepthBuffer, .Depth, 1.0f, 0);
_context.SetRenderTarget(null);
_context.SetDepthStencilTarget(_swapchainDepthBuffer);
_context.BindRenderTargets();
RenderCommand.SetViewport(_context.SwapChain.BackbufferViewport);
}
public override void OnEvent(Event event)
{
_cameraController.OnEvent(event);
EventDispatcher dispatcher = EventDispatcher(event);
dispatcher.Dispatch<ImGuiRenderEvent>(scope (e) => OnImGuiRender(e));
dispatcher.Dispatch<WindowResizeEvent>(scope (e) => OnWindowResize(e));
}
ImGui.ID _mainDockspaceId;
private bool OnImGuiRender(ImGuiRenderEvent event)
{
ImGui.Viewport* viewport = ImGui.GetMainViewport();
ImGui.DockSpaceOverViewport(viewport);
DrawMainMenuBar();
_editor.SceneViewportWindow.RenderTarget = _renderTarget2D;
_editor.Update();
return false;
}
private void DrawMainMenuBar()
{
ImGui.BeginMainMenuBar();
if(ImGui.BeginMenu("File", false))
{
ImGui.EndMenu();
}
if(ImGui.BeginMenu("View", true))
{
if(ImGui.MenuItem(EntityHierarchyWindow.s_WindowTitle))
{
_editor.EntityHierarchyWindow.Open = true;
}
if(ImGui.MenuItem(ComponentEditWindow.s_WindowTitle))
{
_editor.ComponentEditWindow.Open = true;
}
if(ImGui.MenuItem(SceneViewportWindow.s_WindowTitle))
{
_editor.SceneViewportWindow.Open = true;
}
ImGui.EndMenu();
}
ImGui.EndMainMenuBar();
}
private bool OnWindowResize(WindowResizeEvent e)
{
_swapchainDepthBuffer.ReleaseRef();
_swapchainDepthBuffer = new DepthStencilTarget(_context.SwapChain.Width, _context.SwapChain.Height);
return false;
}
private void ViewportSizeChanged(Object sender, Vector2 viewportSize)
{
uint32 sizeX = (uint32)viewportSize.X;
uint32 sizeY = (uint32)viewportSize.Y;
if(sizeX == 0 || sizeY == 0)
return;
_renderTarget2D.Resize(sizeX, sizeY);
_cameraController.AspectRatio = (float)sizeX / (float)sizeY;
}
}
}
+7
View File
@@ -0,0 +1,7 @@
FileVersion = 1
Dependencies = {corlib = "*", GlitchyEngine = "*"}
[Project]
Name = "GlitchyEngine.Test"
TargetType = "BeefTest"
StartupObject = "GlitchyEngine.Test.Program"
+5
View File
@@ -0,0 +1,5 @@
FileVersion = 1
Projects = {"GlitchyEngine.Test" = {Path = "."}, GlitchyEngine = {Path = "../GlitchyEngine"}, GlitchLog = {Path = "../GlitchLog"}, DirectX = {Path = "../GlitchyEngine/vendor/directx/DirectX"}, ImGui = {Path = "../GlitchyEngine/vendor/imgui-beef/ImGui"}, ImGuiImplWin32 = {Path = "../GlitchyEngine/vendor/imgui-beef/ImGuiImplWin32"}, ImGuiImplDX11 = {Path = "../GlitchyEngine/vendor/imgui-beef/ImGuiImplDX11"}, DirectXTK = {Path = "../GlitchyEngine/vendor/DirectXTK/DirectXTK-beef"}, FreeType = {Path = "../GlitchyEngine/vendor/freetype"}, cgltf-beef = {Path = "../GlitchyEngine/vendor/gltf/cgltf-beef"}}
[Workspace]
StartupProject = "GlitchyEngine.Test"
@@ -0,0 +1,64 @@
using System;
using GlitchyEngine.Math;
namespace GlitchyEngine.Test.Math
{
static class MathHelperTest
{
[Test]
public static void TestDegToRad()
{
{
float deg = 0;
float rad = MathHelper.ToRadians(deg);
Test.Assert(rad == 0.0f);
}
{
float deg = 45;
float rad = MathHelper.ToRadians(deg);
Test.Assert(rad == MathHelper.PiOverFour);
}
{
float deg = 180;
float rad = MathHelper.ToRadians(deg);
Test.Assert(rad == MathHelper.Pi);
}
{
float deg = -360;
float rad = MathHelper.ToRadians(deg);
Test.Assert(rad == -MathHelper.TwoPi);
}
}
[Test]
public static void TestRadToDeg()
{
{
float rad = 0;
float deg = MathHelper.ToDegrees(rad);
Test.Assert(deg == 0.0f);
}
{
float rad = MathHelper.PiOverFour;
float deg = MathHelper.ToDegrees(rad);
Test.Assert(deg == 45);
}
{
float rad = MathHelper.Pi;
float deg = MathHelper.ToDegrees(rad);
Test.Assert(deg == 180);
}
{
float rad = -MathHelper.TwoPi;
float deg = MathHelper.ToDegrees(rad);
Test.Assert(deg == -360);
}
}
}
}
@@ -0,0 +1,199 @@
using System;
using GlitchyEngine.Math;
namespace GlitchyEngine.Test.Math
{
/**
* @Note Testing 180° Angles is quite stupid as 180° = -180° and thus the result is quite dependant of floating point precision...
*/
static class QuaternionTest
{
static bool QuatEquals(Quaternion q1, Quaternion q2, float delta = 0.0001f)
{
return Math.Abs(q1.X - q2.X) < delta && Math.Abs(q1.Y - q2.Y) < delta &&
Math.Abs(q1.Z - q2.Z) < delta && Math.Abs(q1.W - q2.W) < delta;
}
static bool Vector3Equals(Vector3 v1, Vector3 v2, float delta = 0.0001f)
{
return Math.Abs(v1.X - v2.X) < delta && Math.Abs(v1.Y - v2.Y) < delta &&
Math.Abs(v1.Z - v2.Z) < delta;
}
static bool FloatEquals(float l, float r, float delta = 0.0001f)
{
return Math.Abs(l - r) < delta;
}
[Test]
public static void TestAxisAngleToQuaternion()
{
// angle in degrees
void Test(Vector3 axis, float angle, Quaternion expectedQuat)
{
float angleRad = MathHelper.ToRadians(angle);
Quaternion result = .FromAxisAngle(axis, angleRad);
Quaternion resultNormalized = .Normalize(result);
Quaternion expectedNormalized = .Normalize(expectedQuat);
Test.Assert(QuatEquals(resultNormalized, expectedNormalized));
}
// Reference for conversions: https://www.andre-gaschler.com/rotationconverter/
Test(.(0, 0, 0), 55, Quaternion.Identity);
// x: 90°
Test(.(1, 0, 0), 90, Quaternion(1, 0, 0, 1));
// y: 90°
Test(.(0, 1, 0), 90, Quaternion(0, 1, 0, 1));
// z: 90°
Test(.(0, 0, 1), 90, Quaternion(0, 0, 1, 1));
Test(.(1, 1, 0), 90, Quaternion(0.5f, 0.5f, 0, 0.707107f));
Test(.(1, 1, 1), 90, Quaternion(0.4082483f, 0.4082483f, 0.4082483f, 0.7071068f));
Test(.(0.3f, 0.5f, 0.4f), -25, Quaternion(-0.0918276f, -0.1530459f, -0.1224367f, 0.976296f));
Test(.(0.3f, -0.5f, 0.4f), -25, Quaternion(-0.0918276f, 0.1530459f, -0.1224367f, 0.976296f));
}
[Test]
public static void TestQuaternionToAxisAngle()
{
// angle in degrees
void Test(Quaternion quat, Vector3 expectedAxis, float expectedAngle)
{
Quaternion quatNrm = .Normalize(quat);
(Vector3 resultAxis, float resultAngle) = quatNrm.ToAxisAngle();
resultAxis.Normalize();
resultAngle = MathHelper.ToDegrees(resultAngle);
Vector3 nrmExpectedAxis = .Normalize(expectedAxis);
Test.Assert(Vector3Equals(resultAxis, nrmExpectedAxis) && FloatEquals(expectedAngle, resultAngle));
}
// Reference for conversions: https://www.andre-gaschler.com/rotationconverter/
Test(Quaternion.Identity, .(0, 0, 0), 0);
// x: 90°
Test(Quaternion(1, 0, 0, 1), .(1, 0, 0), 90);
// y: 90°
Test(Quaternion(0, 1, 0, 1), .(0, 1, 0), 90);
// z: 90°
Test(Quaternion(0, 0, 1, 1), .(0, 0, 1), 90);
Test(Quaternion(0.5f, 0.5f, 0, 0.707107f), .(1, 1, 0), 90);
Test(Quaternion(0.4082483f, 0.4082483f, 0.4082483f, 0.7071068f), .(1, 1, 1), 90);
Test(Quaternion(-0.0918276f, -0.1530459f, -0.1224367f, 0.976296f), .(-0.4242643f, -0.7071067f, -0.5656853f), 24.9999988f);
Test(Quaternion(-0.0918276f, 0.1530459f, -0.1224367f, 0.976296f), .(-0.4242643f, 0.7071067f, -0.5656853f), 24.9999988f);
}
[Test]
public static void TestEulerToQuaternion()
{
void Test(Vector3 inEuler, Quaternion expectedQuat)
{
Quaternion result = .FromEulerAngles(MathHelper.ToRadians(inEuler.Y), MathHelper.ToRadians(inEuler.X), MathHelper.ToRadians(inEuler.Z));
Test.Assert(QuatEquals(result, expectedQuat));
}
Test(Vector3.Zero, Quaternion.Identity);
// X: 90°
Test(.(90, 0, 0), Quaternion(1, 0, 0, 1)..Normalize());
// Y: 90°
Test(.(0, 90, 0), Quaternion(0, 1, 0, 1)..Normalize());
// Z: 90°
Test(.(0, 0, 90), Quaternion(0, 0, 1, 1)..Normalize());
// X: 90° Y: 90°
Test(.(90, 90, 0), Quaternion(1, 1, -1, 1)..Normalize());
// 90° 45° 30°
Test(.(90, 45, 30), Quaternion(0.7010574f, 0.4304593f, -0.092296f, 0.5609855f)..Normalize());
// 105° 90° 0°
Test(.(105, 90, 0), Quaternion(0.560986f, 0.430459f, -0.560986f, 0.430459f)..Normalize());
// 180° 90° -75°
Test(.(180, 90, -75), Quaternion(0.5609855f, -0.4304593f, -0.5609855f, 0.4304593f)..Normalize());
}
[Test]
public static void TestQuaternionToEuler()
{
void Test(Quaternion inQuat, Vector3 expectedEuler)
{
Quaternion nrmInQuat = .Normalize(inQuat);
Vector3 result = Quaternion.ToEulerAngles(nrmInQuat);
Vector3 resultDeg = MathHelper.ToDegrees(result);
Test.Assert(Vector3Equals(resultDeg, expectedEuler));
}
Test(Quaternion.Identity, Vector3.Zero);
// X: 90°
Test(Quaternion(1, 0, 0, 1), .(90, 0, 0));
// Y: 90°
Test(Quaternion(0, 1, 0, 1), .(0, 90, 0));
// Z: 90°
Test(Quaternion(0, 0, 1, 1), .(0, 0, 90));
// X: 90° Y: 90°
Test(Quaternion(1, 1, -1, 1), .(90, 90, 0));
// 90° 45° 30°
Test(Quaternion(0.7010574f, 0.4304593f, -0.092296f, 0.5609855f), .(90, 45, 30));
// 105° 90° 0°
Test(Quaternion(0.560986f, 0.430459f, -0.560986f, 0.430459f), .(105, 90, 0));
// 180° 90° -75°
Test(Quaternion(0.5609855f, -0.4304593f, -0.5609855f, 0.4304593f), .(180, 90, -75));
}
[Test]
public static void TestEulerToQautToEuler()
{
void Test(float yaw, float pitch, float roll)
{
Quaternion quat = .FromEulerAngles(MathHelper.ToRadians(yaw), MathHelper.ToRadians(pitch), MathHelper.ToRadians(roll));
Vector3 result = Quaternion.ToEulerAngles(quat);
result = .(MathHelper.ToDegrees(result.X), MathHelper.ToDegrees(result.Y), MathHelper.ToDegrees(result.Z));
Test.Assert(Vector3Equals(.(pitch, yaw, roll), result));
}
Test(0, 90, 0);
Test(90, 0, 0);
Test(0, 0, 90);
Test(90, 90, 0);
Test(90, 45, 0);
Test(90, 45, 30);
Test(105, 90, 0);
Test(179, 90, -75);
}
}
}
+11 -15
View File
@@ -1,22 +1,9 @@
FileVersion = 1
Dependencies = {GlitchLog = "*", corlib = "*", DirectX = "*", ImGui = "*", ImGuiImplWin32 = "*", ImGuiImplDX11 = "*", DirectXTK = "*"}
Dependencies = {GlitchLog = "*", corlib = "*", DirectX = "*", DirectXTK = "*", FreeType = "*", cgltf-beef = "*", msdfgen-beef = "*", ImGui = "*", ImGuiImplDX11 = "*", ImGuiImplWin32 = "*", ImGuizmo = "*", Beefy2D = "*", LodePng = "*"}
[Project]
Name = "GlitchyEngine"
ProcessorMacros = ["GE_ERROR_SHADER_MATRIX_MISMATCH", "GE_ERROR_SHADER_VAR_TYPE_MISMATCH"]
[Configs.Debug.Win32]
PreprocessorMacros = ["DEBUG", "GE_WINDOWS"]
[Configs.Debug.Win64]
CLibType = "DynamicDebug"
PreprocessorMacros = ["DEBUG", "GE_WINDOWS", "GE_D3D11"]
[Configs.Release.Win32]
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
[Configs.Release.Win64]
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
ProcessorMacros = ["GE_GRAPHICS_DX11", "GE_SHADER_MATRIX_MISMATCH_IS_ERROR", "GE_SHADER_VAR_TYPE_MISMATCH_IS_ERROR", "GE_SHADER_UNUSED_VARIABLE_IS_WARNING", "GE_WINDOWS"]
[Configs.Paranoid.Win32]
PreprocessorMacros = ["DEBUG", "PARANOID", "GE_WINDOWS"]
@@ -24,8 +11,17 @@ PreprocessorMacros = ["DEBUG", "PARANOID", "GE_WINDOWS"]
[Configs.Paranoid.Win64]
PreprocessorMacros = ["DEBUG", "PARANOID", "GE_WINDOWS"]
[Configs.Release.Win32]
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
[Configs.Release.Win64]
PreprocessorMacros = ["RELEASE", "GE_PROFILE"]
[Configs.Test.Win32]
PreprocessorMacros = ["TEST", "GE_WINDOWS"]
[Configs.Test.Win64]
PreprocessorMacros = ["TEST", "GE_WINDOWS"]
[Configs.Debug.Win64]
PreprocessorMacros = ["DEBUG", "GE_PROFILE", "GE_PROFILE_RENDERER", "GE_PROFILE_RESOURCES"]
+88 -8
View File
@@ -2,6 +2,7 @@ using System;
using GlitchyEngine.Events;
using GlitchyEngine.ImGui;
using GlitchyEngine.Renderer;
using GlitchyEngine.Debug;
namespace GlitchyEngine
{
@@ -11,22 +12,31 @@ namespace GlitchyEngine
private Window _window ~ delete _;
private RendererAPI _rendererApi ~ delete _;
private bool _running = true;
private EffectLibrary _effectLibrary ~ delete _;
private LayerStack _layerStack = new LayerStack() ~ delete _;
private bool _running = true;
private bool _isMinimized = false;
private LayerStack _layerStack = new LayerStack();
private ImGuiLayer _imGuiLayer;
private GameTime _gameTime = new GameTime(true) ~ delete _;
private GameTime _gameTime = new GameTime(true);
public bool IsRunning => _running;
public Window Window => _window;
public EffectLibrary EffectLibrary => _effectLibrary;
public bool IsMinimized => _isMinimized;
[Inline]
public static Application Get() => s_Instance;
public this()
{
Profiler.ProfileFunction!();
Log.EngineLogger.Assert(s_Instance == null, "Tried to create a second application.");
s_Instance = this;
@@ -36,11 +46,13 @@ namespace GlitchyEngine
_rendererApi = new RendererAPI();
_rendererApi.Context = _window.Context;
SamplerStateManager.Init(_window.Context);
SamplerStateManager.Init();
RenderCommand.RendererAPI = _rendererApi;
Renderer.Init(_window.Context);
_effectLibrary = new EffectLibrary();
Renderer.Init(_window.Context, _effectLibrary);
_imGuiLayer = new ImGuiLayer();
PushOverlay(_imGuiLayer);
@@ -48,14 +60,26 @@ namespace GlitchyEngine
public ~this()
{
Profiler.ProfileFunction!();
delete _layerStack;
SamplerStateManager.Uninit();
Renderer.Deinit();
delete _gameTime;
}
public void OnEvent(Event e)
{
EventDispatcher dispatcher = scope .(e);
Debug.Profiler.ProfileFunction!();
if(!_running)
return;
EventDispatcher dispatcher = EventDispatcher(e);
dispatcher.Dispatch<WindowCloseEvent>(scope => OnWindowClose);
dispatcher.Dispatch<WindowResizeEvent>(scope => OnWindowResize);
for(Layer layer in _layerStack)
{
layer.OnEvent(e);
@@ -64,33 +88,89 @@ namespace GlitchyEngine
}
}
#if GE_APP_SINGLESTEP
bool allowFrame = false;
bool disableSingleFrame = false;
#else
const bool allowFrame = true;
#endif
public void Run()
{
Debug.Profiler.ProfileFunction!();
while(_running)
{
Debug.Profiler.ProfileScope!("Loop");
if(allowFrame)
{
_gameTime.NewFrame();
}
Input.NewFrame();
#if GE_APP_SINGLESTEP
allowFrame = disableSingleFrame || Input.IsKeyPressing(Key.F10);
if(Input.IsKeyPressing(Key.F11))
{
disableSingleFrame = !disableSingleFrame;
}
#endif
if(allowFrame && !_isMinimized)
{
Debug.Profiler.ProfileScope!("Update Layers");
for(Layer layer in _layerStack)
layer.Update(_gameTime);
}
_window.Update();
if(allowFrame)
{
_imGuiLayer.ImGuiRender();
_window.Context.SwapChain.Present();
}
}
}
public void PushLayer(Layer ownLayer) => _layerStack.PushLayer(ownLayer);
public void PushLayer(Layer ownLayer)
{
Profiler.ProfileFunction!();
public void PushOverlay(Layer ownOverlay) => _layerStack.PushOverlay(ownOverlay);
_layerStack.PushLayer(ownLayer);
ownLayer.OnAttach();
}
public void PushOverlay(Layer ownOverlay)
{
Profiler.ProfileFunction!();
_layerStack.PushOverlay(ownOverlay);
ownOverlay.OnAttach();
}
public bool OnWindowClose(WindowCloseEvent e)
{
_running = false;
return true;
}
public bool OnWindowResize(WindowResizeEvent e)
{
if(e.Width == 0 || e.Height == 0)
{
_isMinimized = true;
return false;
}
_isMinimized = false;
return false;
}
}
}
+48
View File
@@ -0,0 +1,48 @@
using System.Collections;
namespace GlitchyEngine.Collections
{
public class TreeNode<T>
{
public T Value;
public List<Self> Children = new .() ~ DeleteContainerAndItems!(_);
public this() {}
public this(T value)
{
Value = value;
}
public Self AddChild(T value)
{
for (var child in Children)
{
if(child.Value == value)
return child;
}
Self newChild = new .(value);
Children.Add(newChild);
return newChild;
}
public Self FindNode(T value)
{
if (Value == value)
return this;
for (var child in Children)
{
var childResult = child.FindNode(value);
if (childResult != null)
return childResult;
}
return null;
}
}
}
+635
View File
@@ -0,0 +1,635 @@
using System;
using System.Collections;
using cgltf;
using GlitchyEngine.Math;
using GlitchyEngine.Renderer;
using GlitchyEngine.Renderer.Animation;
using GlitchyEngine.World;
namespace GlitchyEngine.Content
{
public static class ModelLoader
{
static readonly Matrix RightToLeftHand = .Scaling(1, 1, -1);
public static void LoadModel(String filename, Effect validationEffect, Material material, EcsWorld world,
List<AnimationClip> outClips)
{
CGLTF.Options options = .();
CGLTF.Data* data;
CGLTF.Result result = CGLTF.ParseFile(options, filename, out data);
Log.EngineLogger.Assert(result == .Success, "Failed to load model.");
result = CGLTF.LoadBuffers(options, data, filename);
Log.EngineLogger.Assert(result == .Success, "Failed to load buffers");
for(var node in data.Scenes[0].Nodes)
{
NodesToEntities(data, node, null, world, validationEffect, material, outClips);
}
CGLTF.Free(data);
}
private static void NodesToEntities(CGLTF.Data* data, CGLTF.Node* node, Entity? parentEntity, EcsWorld world, Effect validationEffect, Material material, List<AnimationClip> clips)
{
Entity entity = world.NewEntity();
#if DEBUG
var nameComponent = world.AssignComponent<DebugNameComponent>(entity);
if (node.Name != null)
{
nameComponent.SetName(StringView(node.Name));
}
else
{
nameComponent.SetName("Unnamed Node");
}
#endif
if(parentEntity.HasValue)
{
var childParent = world.AssignComponent<ParentComponent>(entity);
childParent.Entity = parentEntity.Value;
}
var childTransform = world.AssignComponent<TransformComponent>(entity);
if(node.HasMatrix)
{
childTransform.LocalTransform = *(Matrix*)&node.Matrix;
}
else
{
if(node.HasTranslation)
childTransform.Position = *(Vector3*)&node.Translation;
else
childTransform.Position = .Zero;
if(node.HasRotation)
childTransform.Rotation = *(Quaternion*)&node.Rotation;
else
childTransform.Rotation = .Identity;
if(node.HasScale)
childTransform.Scale = *(Vector3*)&node.Scale;
else
childTransform.Scale = .(1, 1, 1);
}
// Invert the Z-Axis of the root Node to convert the coordinate system from right-handed to left-handed
if(parentEntity == null)
childTransform.Scale *= .(1, 1, -1);
Skeleton skeleton = null;
if(node.Skin != null)
{
skeleton = ExtractSkeleton(node.Skin);
LoadAnimationClips(data, node.Skin, skeleton, clips);
}
if(node.Mesh != null)
{
// If we have only one primitive, add it directly to the entity
if(node.Mesh.Primitives.Length == 1)
{
var mesh = world.AssignComponent<MeshComponent>(entity);
using (var geo = PrimitiveToGeoBinding(node.Mesh.Primitives[0], validationEffect))
{
mesh.Mesh = geo;
}
if(skeleton == null)
{
var meshRenderer = world.AssignComponent<MeshRendererComponent>(entity);
meshRenderer.Material = material;
}
else
{
var meshRenderer = world.AssignComponent<SkinnedMeshRendererComponent>(entity);
meshRenderer.Material = material;
meshRenderer.Skeleton = skeleton;
}
}
// otherwise one child-entity per primitive
else
{
for(var primitive in node.Mesh.Primitives)
{
Entity meshEntity = world.NewEntity();
var meshParent = world.AssignComponent<ParentComponent>(meshEntity);
meshParent.Entity = entity;
var mesh = world.AssignComponent<MeshComponent>(meshEntity);
mesh.Mesh = PrimitiveToGeoBinding(primitive, validationEffect);
if(skeleton == null)
{
var meshRenderer = world.AssignComponent<MeshRendererComponent>(meshEntity);
meshRenderer.Material = material;
}
else
{
var meshRenderer = world.AssignComponent<SkinnedMeshRendererComponent>(meshEntity);
meshRenderer.Material = material;
meshRenderer.Skeleton = skeleton;
}
}
}
}
skeleton?.ReleaseRef();
for(var child in node.Children)
{
NodesToEntities(data, child, entity, world, validationEffect, material, clips);
}
}
public static GeometryBinding PrimitiveToGeoBinding(CGLTF.Primitive primitive, Effect validationEffect)
{
GeometryBinding binding = new GeometryBinding();
// primitive topology
switch(primitive.Type)
{
case .Points:
binding.SetPrimitiveTopology(.PointList);
case .Lines:
binding.SetPrimitiveTopology(.LineList);
case .LineStrip:
binding.SetPrimitiveTopology(.LineStrip);
case .Triangles:
binding.SetPrimitiveTopology(.TriangleList);
case .TriangleStrip:
binding.SetPrimitiveTopology(.TriangleStrip);
default:
Log.EngineLogger.Assert(false, scope $"{primitive.Type} not supported.");
}
// indices
{
CGLTF.Accessor* indices = primitive.Indices;
if(indices != null)
{
bool is16bit = indices.ComponentType == .R_16u || indices.ComponentType == .R_16;
IndexBuffer ib = new IndexBuffer((.)indices.Count, .Immutable, .None, is16bit ? .Index16Bit : .Index32Bit);
uint8* bufferData = (uint8*)indices.BufferView.Buffer.Data;
bufferData += indices.BufferView.Offset;
// TODO: this is a mess
ib.SetData<uint8>(bufferData, (.)indices.BufferView.Size);
binding.SetIndexBuffer(ib);
ib.ReleaseRef();
}
}
bool hasNormals = false;
bool hasTangents = false;
// vertices
{
List<VertexElement> elements = scope .(primitive.Attributes.Length);
Dictionary<void*, VertexBuffer> buffers = scope .();
List<VertexBufferBinding> bindings = scope .();
for(var attribute in primitive.Attributes)
{
{
StringView attributeName = StringView(attribute.Name);
if(attributeName.Equals("NORMAL"))
{
hasNormals = true;
}
else if(attributeName.Equals("TANGENT"))
{
hasTangents = true;
}
}
// Get Input Element format
Format format = FormatFromVectorComponent(attribute.Data.Type, attribute.Data.ComponentType);
Log.EngineLogger.AssertDebug(format != .Unknown, "Vertex element format must not be \"Unknown.\"");
VertexBuffer vertexBuffer = null;
binding.VertexCount = (uint32)attribute.Data.Count;
// Get vertex buffer
{
CGLTF.BufferView* bufferView = attribute.Data.BufferView;
// if buffer doesn't exist -> create
if(!buffers.TryGetValue(bufferView, out vertexBuffer))
{
vertexBuffer = new VertexBuffer(1, (uint32)bufferView.Size, .Immutable)..ReleaseRefNoDelete();
uint8* bufferData = (uint8*)bufferView.Buffer.Data;
bufferData += bufferView.Offset;
vertexBuffer.SetData(bufferData, (.)bufferView.Size);
buffers.Add(attribute.Data.BufferView, vertexBuffer);
}
Log.EngineLogger.AssertDebug(vertexBuffer != null);
}
VertexBufferBinding bufferBinding = .(vertexBuffer, (.)attribute.Data.Stride, (.)attribute.Data.Offset);
// get slot of bufferBinding
int bindingSlot = bindings.IndexOf(bufferBinding);
// binding has no slot -> add to list
if(bindingSlot == -1)
{
bindingSlot = bindings.Count;
bindings.Add(bufferBinding);
binding.SetVertexBufferSlot(bufferBinding, (.)bindingSlot);
}
StringView strView = .(attribute.Name);
// Remove number from end of name
while((*(strView.EndPtr - 1)).IsDigit)
{
strView.Length--;
}
VertexElement element = .(format, new String(strView), true, (.)attribute.Index, (.)bindingSlot);
elements.Add(element);
}
// Generate normals if missing
if(!hasNormals)
{
CGLTF.Accessor* positions = null;
// Find position accessor
for(var attribute in primitive.Attributes)
{
if(StringView(attribute.Name).Equals("POSITION"))
{
positions = attribute.Data;
break;
}
}
Log.EngineLogger.AssertDebug(positions != null, "The model appears to have no position data?!");
Vector3[] normals = new Vector3[positions.Count];
if(primitive.Indices != null)
GenerateNormals(primitive.Indices, positions, normals);
else
GenerateNormals(positions, normals);
VertexBuffer vertexBuffer = new VertexBuffer((uint32)sizeof(Vector3), (uint32)normals.Count, .Immutable);
vertexBuffer.SetData<Vector3>(normals);
bindings.Add(vertexBuffer.Binding);
uint32 bindingSlot = (.)bindings.Count - 1;
binding.SetVertexBufferSlot(vertexBuffer.Binding, (.)bindingSlot);
VertexElement element = .(.R32G32B32_Float, "NORMAL", false, 0, bindingSlot);
elements.Add(element);
}
// TODO: validate vertex layout somewhere else
VertexElement[] vertexElements = new VertexElement[elements.Count];
for(int i < elements.Count)
{
vertexElements[i] = elements[i];
}
VertexLayout layout = new VertexLayout(vertexElements, true, validationEffect.VertexShader);
binding.SetVertexLayout(layout..ReleaseRefNoDelete());
}
// TODO: calculate tangents if missing (MikkTSpace algorithm)
// Note: Bitangent = cross(normal, tangent.xyz) * tangent.w
return binding;
}
/// Generates the normals for one triangle
static mixin GenerateTriangleNormals(int index0, int index1, int index2, CGLTF.Accessor* positions, Vector3[] normals)
{
Vector3 position0 = GetEntry<Vector3>(positions, (.)index0);
Vector3 position1 = GetEntry<Vector3>(positions, (.)index1);
Vector3 position2 = GetEntry<Vector3>(positions, (.)index2);
ref Vector3 normal0 = ref normals[(.)index0];
ref Vector3 normal1 = ref normals[(.)index1];
ref Vector3 normal2 = ref normals[(.)index2];
Vector3 e0 = position1 - position0;
Vector3 e1 = position2 - position0;
Vector3 normal = Vector3.Cross(e0, e1);
normal0 += normal;
normal1 += normal;
normal2 += normal;
}
[Inline]
static void NormalizeNormals(Vector3[] normals)
{
for(int i < normals.Count)
{
normals[i].Normalize();
}
}
/// Generates normals for the given model using indexed geometry
static void GenerateNormals(CGLTF.Accessor* indices, CGLTF.Accessor* positions, Vector3[] normals)
{
/// Enumerate triangle-wise
for(uint t = 0; t < indices.Count; t += 3)
{
int index0 = (.)CGLTF.AccessorReadIndex(indices, t);
int index1 = (.)CGLTF.AccessorReadIndex(indices, t + 1);
int index2 = (.)CGLTF.AccessorReadIndex(indices, t + 2);
GenerateTriangleNormals!(index0, index1, index2, positions, normals);
}
NormalizeNormals(normals);
}
/// Generates normals for the given model using nonindexed geometry
static void GenerateNormals(CGLTF.Accessor* positions, Vector3[] normals)
{
/// Enumerate triangle-wise
for(int i = 0; i < (.)positions.Count; i += 3)
{
GenerateTriangleNormals!(i, i + 1, i + 2, positions, normals);
}
NormalizeNormals(normals);
}
static Skeleton ExtractSkeleton(CGLTF.Skin* skin)
{
Skeleton skeleton = new Skeleton();
skeleton.Joints = new Joint[skin.Joints.Length];
for(int i < skin.Joints.Length)
{
ref Joint joint = ref skeleton.Joints[i];
joint.InverseBindPose = GetEntry<Matrix>(skin.InverseBindMatrices, i);
if (skin.Joints[i].Name != null)
joint.Name = new String(skin.Joints[i].Name);
int parentId = skin.Joints.IndexOf(skin.Joints[i].Parent);
Log.EngineLogger.AssertDebug(parentId < uint8.MaxValue, scope $"A skeleton must not have more than {uint8.MaxValue - 1} bones.");
if(parentId == -1)
joint.ParentID = uint8.MaxValue;
else
joint.ParentID = (uint8)parentId;
}
return skeleton;
}
static bool AnimationBelongsToSkeleton(CGLTF.Animation animation, CGLTF.Skin* skin)
{
for(var channel in animation.Channels)
{
if(skin.Joints.IndexOf(channel.TargetNode) == -1)
{
return false;
}
}
return true;
}
static void LoadAnimationClips(CGLTF.Data* data, CGLTF.Skin* skin, Skeleton skeleton, List<AnimationClip> clips)
{
for(var animation in data.Animations)
{
if(!AnimationBelongsToSkeleton(animation, skin))
continue;
AnimationClip clip = new AnimationClip(skeleton);
clips.Add(clip);
clip.IsLooping = true;
for(var channel in animation.Channels)
{
int nodeIndex = skin.Joints.IndexOf(channel.TargetNode);
Log.EngineLogger.AssertDebug(nodeIndex != -1);
ref JointAnimation jointAnimation = ref clip.JointAnimations[nodeIndex];
if(jointAnimation == null)
jointAnimation = new JointAnimation();
Log.EngineLogger.AssertDebug(channel.Sampler.Input.Count == channel.Sampler.Output.Count);
int samples = (int)channel.Sampler.Input.Count;
Log.EngineLogger.AssertDebug(channel.Sampler.Input.ComponentType == .R_32f);
Log.EngineLogger.AssertDebug(channel.Sampler.Input.Type == .Scalar);
InterpolationMode interpolationMode;
switch(channel.Sampler.Interpolation)
{
case .Step:
interpolationMode = .Step;
case .Linear:
interpolationMode = .Linear;
case .CubicSpline:
interpolationMode = .CubicSpline;
}
switch(channel.TargetPath)
{
case .Translation:
jointAnimation.TranslationChannel = new .(samples, interpolationMode);
Log.EngineLogger.AssertDebug(channel.Sampler.Output.ComponentType == .R_32f);
Log.EngineLogger.AssertDebug(channel.Sampler.Output.Type == .Vec3);
for(int i < samples)
{
float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
Vector3 sample = GetEntry<Vector3>(channel.Sampler.Output, i);
jointAnimation.TranslationChannel.TimeStamps[i] = timeStamp;
jointAnimation.TranslationChannel.Values[i] = sample;
}
case .Rotation:
jointAnimation.RotationChannel = new .(samples, interpolationMode);
Log.EngineLogger.AssertDebug(channel.Sampler.Output.ComponentType == .R_32f);
Log.EngineLogger.AssertDebug(channel.Sampler.Output.Type == .Vec4);
for(int i < samples)
{
float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
Quaternion sample = GetEntry<Quaternion>(channel.Sampler.Output, i);
jointAnimation.RotationChannel.TimeStamps[i] = timeStamp;
jointAnimation.RotationChannel.Values[i] = sample;
}
case .Scale:
jointAnimation.ScaleChannel = new .(samples, interpolationMode);
Log.EngineLogger.AssertDebug(channel.Sampler.Output.ComponentType == .R_32f);
Log.EngineLogger.AssertDebug(channel.Sampler.Output.Type == .Vec3);
for(int i < samples)
{
float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
Vector3 sample = GetEntry<Vector3>(channel.Sampler.Output, i);
jointAnimation.ScaleChannel.TimeStamps[i] = timeStamp;
jointAnimation.ScaleChannel.Values[i] = sample;
}
default:
Log.EngineLogger.Error($"Unknown channel target path \"{channel.TargetPath}\"");
}
clip.Duration = Math.Max(clip.Duration, jointAnimation.Duration);
}
}
}
static T GetEntry<T>(CGLTF.Accessor* accessor, int index)
{
Log.EngineLogger.AssertDebug((uint)index < accessor.Count);
T result = ?;
switch(typeof(T))
{
case typeof(float):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 1);
case typeof(Vector2):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 2);
case typeof(Vector3):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 3);
case typeof(Vector4), typeof(Quaternion):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 4);
case typeof(Matrix):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 16);
case typeof(uint16):
CGLTF.AccessorReadUint(accessor, (uint)index, (uint32*)&result, 2);
case default:
uint8* data = (uint8*)accessor.BufferView.Buffer.Data;
data += accessor.BufferView.Offset;
data += accessor.Offset;
data += accessor.Stride * (uint)index;
result = *(T*)data;
}
return result;
/*
uint8* data = (uint8*)accessor.BufferView.Buffer.Data;
data += accessor.BufferView.Offset;
data += accessor.Offset;
data += accessor.Stride * (uint)index;
return *(T*)data;
*/
}
/**
* Converts the vector and component type to the corresponding Format.
*/
static Format FormatFromVectorComponent(CGLTF.Type vectorType, CGLTF.ComponentType componentType)
{
switch((vectorType, componentType))
{
case (.Scalar, .R_8):
return .R8_SInt;
case (.Scalar, .R_8u):
return .R8_UInt;
case (.Scalar, .R_16):
return .R16_SInt;
case (.Scalar, .R_16u):
return .R16_UInt;
case (.Scalar, .R_32u):
return .R32_UInt;
case (.Scalar, .R_32f):
return .R32_Float;
case (.Vec2, .R_8):
return .R8G8_SInt;
case (.Vec2, .R_8u):
return .R8G8_UInt;
case (.Vec2, .R_16):
return .R16G16_SInt;
case (.Vec2, .R_16u):
return .R16G16_UInt;
case (.Vec2, .R_32u):
return .R32G32_UInt;
case (.Vec2, .R_32f):
return .R32G32_Float;
//case (.Vec3, .R_8):
//case (.Vec3, .R_8u):
//case (.Vec3, .R_16):
//case (.Vec3, .R_16u):
case (.Vec3, .R_32u):
return .R32G32B32_UInt;
case (.Vec3, .R_32f):
return .R32G32B32_Float;
case (.Vec4, .R_8):
return .R8G8B8A8_SInt;
case (.Vec4, .R_8u):
return .R8G8B8A8_UInt;
case (.Vec4, .R_16):
return .R16G16B16A16_SInt;
case (.Vec4, .R_16u):
return .R16G16B16A16_UInt;
case (.Vec4, .R_32u):
return .R32G32B32A32_UInt;
case (.Vec4, .R_32f):
return .R32G32B32A32_Float;
case (.Mat4, .R_32f):
return .R32G32B32A32_Float;
default:
Log.EngineLogger.Assert(false, scope $"Unhandled vector - componenttype combination. ({vectorType}, {componentType})");
}
return .Unknown;
}
}
}
+17
View File
@@ -0,0 +1,17 @@
using System;
namespace GlitchyEngine.Core
{
/**
* Provides a reference counter that, when reaching 0, automatically deletes itself.
* Implements the IDisposable interface so that it can be used with a using-Block so that the counter
* will be decremented automatically after leaving the block.
*/
public class RefCounter : System.RefCounted, IDisposable
{
public void Dispose()
{
ReleaseRef();
}
}
}
+95
View File
@@ -0,0 +1,95 @@
using System;
using Beefy.utils;
namespace GlitchyEngine.Debug
{
public static class Profiler
{
#if !GE_PROFILE
[SkipCall]
#endif
public static void BeginProfiling(StringView serverName = "127.0.0.1", StringView sessionName = "GlitchyEngine")
{
BeefPerf.Init(serverName, sessionName);
}
#if !GE_PROFILE
[SkipCall]
#endif
public static void EndProfiling()
{
BeefPerf.Shutdown();
}
#if !GE_PROFILE
[SkipCall]
#endif
public static mixin ProfileFunction()
{
scope:mixin PerformanceTimer()
}
#if !GE_PROFILE
[SkipCall]
#endif
public static mixin ProfileScope(char8* scopeName)
{
scope:mixin PerformanceTimer(scopeName)
}
// Extension
#if !GE_PROFILE_RENDERER
[SkipCall]
#endif
public static mixin ProfileRendererFunction()
{
scope:mixin PerformanceTimer()
}
#if !GE_PROFILE_RENDERER
[SkipCall]
#endif
public static mixin ProfileRendererScope(char8* scopeName)
{
scope:mixin PerformanceTimer(scopeName)
}
#if !GE_PROFILE_RESOURCES
[SkipCall]
#endif
public static mixin ProfileResourceFunction()
{
scope:mixin PerformanceTimer()
}
#if !GE_PROFILE_RESOURCES
[SkipCall]
#endif
public static mixin ProfileResourceScope(char8* scopeName)
{
scope:mixin PerformanceTimer(scopeName)
}
}
class PerformanceTimer
{
[Inline]
public this(char8* name = Compiler.CallerMemberName)
{
#if GE_PROFILE
[Inline]
BeefPerf.Enter(name);
#endif
}
[Inline]
public ~this()
{
#if GE_PROFILE
[Inline]
BeefPerf.Leave();
#endif
}
}
}
+42 -2
View File
@@ -26,9 +26,9 @@ namespace GlitchyEngine.Events
private bool _isResizing;
/// The new width of the window.
public int32 Width = _width;
public int32 Width => _width;
/// The new height of the window.
public int32 Height = _height;
public int32 Height => _height;
/*
* If true, indicates that the resizing is not finished.
*/
@@ -89,4 +89,44 @@ namespace GlitchyEngine.Events
strBuffer.AppendF("WindowMovedEvent: {}, {} (is moving: {})", _x, _y, _isMoving);
}
}
public class WindowActivateEvent : Event, IEvent
{
public override EventType EventType => .WindowMoved;
public override StringView Name => "WindowActivated";
public override EventCategory Category => .Application;
public static EventType StaticType => .WindowActivated;
public this()
{
}
public override void ToString(String strBuffer)
{
strBuffer.AppendF("WindowActivatedEvent");
}
}
public class WindowDeactivateEvent : Event, IEvent
{
public override EventType EventType => .WindowMoved;
public override StringView Name => "WindowDeactivated";
public override EventCategory Category => .Application;
public static EventType StaticType => .WindowDeactivated;
public this()
{
}
public override void ToString(String strBuffer)
{
strBuffer.AppendF("WindowDeactivatedEvent");
}
}
}
+2 -2
View File
@@ -4,7 +4,7 @@ namespace GlitchyEngine.Events
public enum EventType
{
None = 0,
WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved,
WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved, WindowActivated, WindowDeactivated,
AppTick, AppUpdate, AppRender,
KeyPressed, KeyReleased, KeyTyped,
MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled
@@ -50,7 +50,7 @@ namespace GlitchyEngine.Events
}
}
public class EventDispatcher
public struct EventDispatcher
{
private Event _event;
@@ -0,0 +1,49 @@
namespace System
{
public extension Array1<T>
{
/// Returns the index of the given element or -1 if it is not in this array.
public int IndexOf(T element)
{
for(int i < mLength)
if(Ptr[i] == element)
return i;
return -1;
}
/**
* Uses Binary Search to find the index of the given Element.
* @param element The element whose element will be returned.
* @returns The index of the element in the array or -1 if it is not in the array.
* @Note this function only works if the entries of the array are sorted in ascending order.
*/
public int IndexOfBinary(T element) where bool : operator T < T
{
int lowerBound = 0;
int upperBound = mLength;
while(lowerBound != upperBound)
{
int index = (lowerBound + upperBound) / 2;
T elementAtIndex = Ptr[index];
if(elementAtIndex == element)
{
return index;
}
else if(elementAtIndex < element)
{
upperBound = index;
}
else
{
lowerBound = index;
}
}
return -1;
}
}
}
+4 -1
View File
@@ -8,7 +8,7 @@ namespace GlitchyEngine
*/
public class GameTime
{
private Stopwatch _stopwatch ~ delete:append _;
private Stopwatch _stopwatch;
private uint64 _frameCount;
@@ -28,6 +28,9 @@ namespace GlitchyEngine
*/
public uint64 FrameCount => _frameCount;
/// The interval in seconds from the last frame to the current one.
public float DeltaTime => (float)_frameTime.TotalSeconds;
/**
* Initializes a new instance of a GameTime.
*/
@@ -0,0 +1,40 @@
using System;
using System.Text;
using static System.Compiler;
namespace GlitchyEngine.Generators
{
public class NewDisposableComponentGenerator : Generator
{
public override String Name => "New Disposable Component";
public override void InitUI()
{
AddEdit("name", "Component Name", "");
}
public override void Generate(String outFileName, String outText, ref Flags generateFlags)
{
var name = mParams["name"];
if (name.EndsWith(".bf", .OrdinalIgnoreCase))
name.RemoveFromEnd(3);
outFileName.Append(name);
outText.AppendF(
$"""
using GlitchyEngine.World;
namespace {Namespace}
{{
struct {name} : IDisposableComponent
{{
public void Dispose() mut
{{
// TODO: Dispose of the content
}}
}}
}}
""");
}
}
}
@@ -0,0 +1,32 @@
using System;
using static System.Compiler;
namespace GlitchyEngine.Generators
{
public class NewStructGenerator : Generator
{
public override String Name => "New Struct";
public override void InitUI()
{
AddEdit("name", "Struct Name", "");
}
public override void Generate(String outFileName, String outText, ref Flags generateFlags)
{
var name = mParams["name"];
if (name.EndsWith(".bf", .OrdinalIgnoreCase))
name.RemoveFromEnd(3);
outFileName.Append(name);
outText.AppendF(
$"""
namespace {Namespace}
{{
struct {name}
{{
}}
}}
""");
}
}
}
+41
View File
@@ -0,0 +1,41 @@
using DirectX.Common;
using GlitchyEngine.Core;
namespace GlitchyEngine
{
static
{
/// Releases the reference to destination, writes newValue into it and adds a reference to newValue.
public static mixin SetReference<T>(T destination, T newValue) where T: RefCounter
{
var oldDest = destination;
destination = newValue;
destination?.AddRef();
oldDest?.ReleaseRef();
}
/// Releases the reference to value and nullifies it.
public static mixin ReleaseRefAndNullify<T>(T value) where T: RefCounter
{
value?.ReleaseRef();
value = null;
}
public static mixin DeleteContainerAndReleaseItems(var container)
{
if (container != null)
{
for (var value in container)
value?.ReleaseRef();
delete container;
}
}
public static mixin ClearAndReleaseItems(var container)
{
for (var value in container)
value?.ReleaseRef();
container.Clear();
}
}
}
@@ -1,4 +1,6 @@
using GlitchyEngine.Math;
using GlitchyEngine.Renderer;
namespace ImGui
{
extension ImGui
@@ -14,5 +16,12 @@ namespace ImGui
public static bool ColorPicker3(char* label, ref ColorRGBA col, ColorEditFlags flags = (ColorEditFlags) 0) => ColorPicker3Impl(label, *(float[3]*)&col, flags);
public static bool ColorPicker4(char* label, ref ColorRGBA col, ColorEditFlags flags = (ColorEditFlags) 0, float* ref_col = null) => ColorPicker4Impl(label, *(float[4]*)&col, flags, ref_col);
public static extern void Image(Texture2D texture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec4 tint_col = Vec4.Ones, Vec4 border_col = Vec4.Zero);
// Wouldn't be necesseary if RenderTarget2D was Texture2D
public static extern void Image(RenderTarget2D texture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec4 tint_col = Vec4.Ones, Vec4 border_col = Vec4.Zero);
/// Releases references that accumulated calls like ImGui::Image
protected internal static extern void CleanupFrame();
}
}
+37 -12
View File
@@ -1,6 +1,16 @@
using System;
using ImGui;
using GlitchyEngine.Events;
using ImGuizmo;
using internal ImGui;
#if GE_GRAPHICS_DX11
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Platform.DX11;
#endif
namespace GlitchyEngine.ImGui
{
@@ -10,6 +20,8 @@ namespace GlitchyEngine.ImGui
public override void OnAttach()
{
Debug.Profiler.ProfileFunction!();
//ImGuiImplWin32.EnableDpiAwareness();
Log.EngineLogger.Trace("Initializing ImGui...");
@@ -17,7 +29,7 @@ namespace GlitchyEngine.ImGui
ImGui.CreateContext();
ImGui.StyleColorsDark();
ref ImGui.IO io = ref ImGui.GetIO();
ImGui.IO* io = ImGui.GetIO();
io.ConfigFlags |= .NavEnableKeyboard;
io.ConfigFlags |= .DockingEnable;
io.ConfigFlags |= .ViewportsEnable;
@@ -27,26 +39,27 @@ namespace GlitchyEngine.ImGui
//io.ConfigFlags |= .DpiEnableScaleViewports;
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
ref ImGui.Style style = ref ImGui.GetStyle();
ImGui.Style* style = ImGui.GetStyle();
if(io.ConfigFlags.HasFlag(.ViewportsEnable))
{
style.WindowRounding = 0.0f;
style.Colors[(int)ImGui.Col.WindowBg].w = 1.0f;
}
#if GE_WINDOWS
#if BF_PLATFORM_WINDOWS
// Todo: temporary, needs to be platform independent
ImGuiImplWin32.Init((Windows.HWnd)(int)Application.Get().Window.NativeWindow);
var context = Application.Get().Window.Context;
ImGuiImplDX11.Init(context.[Friend]nativeDevice, context.[Friend]nativeContext);
ImGuiImplWin32.Init((void*)(uint)(Windows.HWnd)(int)Application.Get().Window.NativeWindow);
#endif
#if GE_GRAPHICS_DX11
ImGuiImplDX11.Init(NativeDevice, NativeContext);
#endif
}
public override void OnDetach()
{
Debug.Profiler.ProfileFunction!();
ImGuiImplDX11.Shutdown();
ImGuiImplWin32.Shutdown();
ImGui.DestroyContext();
@@ -175,6 +188,8 @@ namespace GlitchyEngine.ImGui
public void Begin()
{
Debug.Profiler.ProfileFunction!();
// Todo:
//var v = DirectX.ImmediateContext;
//v.OutputMerger.SetRenderTargets(1, &DirectX.BackBufferTarget, null);
@@ -183,30 +198,40 @@ namespace GlitchyEngine.ImGui
ImGuiImplDX11.NewFrame();
ImGuiImplWin32.NewFrame();
ImGui.NewFrame();
ImGuizmo.BeginFrame();
}
bool showDemo = true;
public void ImGuiRender()
{
Debug.Profiler.ProfileFunction!();
Begin();
{
Debug.Profiler.ProfileScope!("ImGuiRenderEvent");
var event = scope ImGuiRenderEvent();
Application.Get().OnEvent(event);
ImGui.ShowDemoWindow(&showDemo);
}
//ImGui.ShowDemoWindow(&showDemo);
End();
}
public void End()
{
ref ImGui.IO io = ref ImGui.GetIO();
Debug.Profiler.ProfileFunction!();
ImGui.IO* io = ImGui.GetIO();
let window = Application.Get().Window;
io.DisplaySize = .(window.Width, window.Height);
ImGui.Render();
ImGuiImplDX11.RenderDrawData(ref ImGui.GetDrawData());
ImGuiImplDX11.RenderDrawData(ImGui.GetDrawData());
ImGui.CleanupFrame();
if(io.ConfigFlags.HasFlag(.ViewportsEnable))
{
+1 -9
View File
@@ -6,8 +6,6 @@ namespace GlitchyEngine
{
public static class Input
{
public static extern bool RawInput {get; set;}
public static extern bool IsKeyPressed(Key keycode);
public static extern bool IsKeyReleased(Key keycode);
public static extern bool IsKeyToggled(Key keycode);
@@ -40,15 +38,9 @@ namespace GlitchyEngine
public static extern int32 GetLastMouseY();
public static extern bool IsMouseButtonPressing(MouseButton button);
public static extern bool IsMouseButtonReleasiong(MouseButton button);
public static extern bool IsMouseButtonReleasing(MouseButton button);
public static extern Point GetMouseMovement();
/**
* Returns the raw mouse (that is the mouse movement taken direcly from the device instead of the movement processed by the OS).
* Requires RawInput to be true. If RawInput is false (or could not be initialized) this method will return the same value as GetMouseMovement();
*/
public static extern Int32_2 GetRawMouseMovement();
public static extern void NewFrame();
}
}
+1
View File
@@ -68,6 +68,7 @@ namespace GlitchyEngine
case Z = 90; // Z key
case LeftSuper = 91; // Left Windows key(Microsoft® Natural® keyboard)
case RightSuper = 92; // Right Windows key(Natural keyboard)
case ContextMenu = 93; // Context Menu key (VK_APPS)
case Numpad0 = 96; // Numeric keypad 0 key
case Numpad1 = 97; // Numeric keypad 1 key
case Numpad2 = 98; // Numeric keypad 2 key
-2
View File
@@ -29,7 +29,6 @@ namespace GlitchyEngine
public void PushLayer(Layer ownLayer)
{
_layers.Insert(_insertIndex++, ownLayer);
ownLayer.OnAttach();
}
/**
@@ -40,7 +39,6 @@ namespace GlitchyEngine
public void PushOverlay(Layer ownOverlay)
{
_layers.Add(ownOverlay);
ownOverlay.OnAttach();
}
/**
+3 -3
View File
@@ -13,12 +13,12 @@ namespace System
}
[Inline]
public Int32_2 XX => Int32_2((int32)this);
public Int2 XX => Int2((int32)this);
[Inline]
public Int32_3 XXX => Int32_3((int32)this);
public Int3 XXX => Int3((int32)this);
[Inline]
public Int32_4 XXXX => Int32_4((int32)this);
public Int4 XXXX => Int4((int32)this);
}
}
+81
View File
@@ -0,0 +1,81 @@
using System;
namespace GlitchyEngine.Math
{
public static class MathHelper
{
/// An optimal representation of π.
public const float Pi = 3.141592654f;
/// An optimal representation of 2*π.
public const float TwoPi = 6.283185307f;
/// An optimal representation of 1/π.
public const float OneOverPi = 0.318309886f;
/// An optimal representation of 2/π.
public const float OneOverTwoPi = 0.159154943f;
/// An optimal representation of π/2.
public const float PiOverTwo = 1.570796327f;
/// An optimal representation of π/4.
public const float PiOverFour = 0.785398163f;
/// Converts radians to degrees
const float RadToDeg = 180.0f / Pi;
/// Converts radians to degrees
const float DegToRad = Pi / 180.0f;
// Converts the given radians to degrees
public static float ToDegrees(float radians)
{
return radians * RadToDeg;
}
// Converts the given radians to degrees
public static Vector2 ToDegrees(Vector2 radians)
{
return radians * RadToDeg;
}
// Converts the given radians to degrees
public static Vector3 ToDegrees(Vector3 radians)
{
return radians * RadToDeg;
}
// Converts the given radians to degrees
public static Vector4 ToDegrees(Vector4 radians)
{
return radians * RadToDeg;
}
// Converts the given degrees to radians
public static float ToRadians(float degrees)
{
return degrees * DegToRad;
}
// Converts the given degrees to radians
public static Vector2 ToRadians(Vector2 degrees)
{
return degrees * DegToRad;
}
// Converts the given degrees to radians
public static Vector3 ToRadians(Vector3 degrees)
{
return degrees * DegToRad;
}
// Converts the given degrees to radians
public static Vector4 ToRadians(Vector4 degrees)
{
return degrees * DegToRad;
}
const float epsilon = 0.0000001f;
public static bool IsZero(float value)
{
return Math.Abs(value) < epsilon;
}
}
}
+89
View File
@@ -0,0 +1,89 @@
using System;
using GlitchyEngine.Math;
using GlitchyEngine;
namespace DirectX.Math
{
extension Matrix
{
typealias Vec3 = GlitchyEngine.Math.Vector3;
public static Self RotationQuaternion(Quaternion rotation)
{
float xSq = 2 * rotation.X * rotation.X;
float ySq = 2 * rotation.Y * rotation.Y;
float zSq = 2 * rotation.Z * rotation.Z;
float xy = 2 * rotation.X * rotation.Y;
float xz = 2 * rotation.X * rotation.Z;
float xw = 2 * rotation.X * rotation.W;
float yz = 2 * rotation.Y * rotation.Z;
float yw = 2 * rotation.Y * rotation.W;
float zw = 2 * rotation.Z * rotation.W;
Self result = ?;
result.V._11 = 1 - ySq - zSq;
result.V._21 = xy + zw;
result.V._31 = xz - yw;
result.V._41 = 0;
result.V._12 = xy - zw;
result.V._22 = 1 - xSq - zSq;
result.V._32 = yz + xw;
result.V._42 = 0;
result.V._13 = xz + yw;
result.V._23 = yz - xw;
result.V._33 = 1 - xSq - ySq;
result.V._43 = 0;
result.V._14 = 0;
result.V._24 = 0;
result.V._34 = 0;
result.V._44 = 1;
return result;
}
public static void Decompose(Self matrix, out Vec3 position, out Quaternion rotation, out Vec3 scale)
{
var matrix;
// Translation -> get last column
position = matrix.Translation;
// Zero translation for next step
matrix.Translation = .Zero;
// TODO: this doesn't detect mirroring
// Extract scaling from matrix
scale.X = (*(Vec3*)&matrix.Columns[0]).Magnitude();
scale.Y = (*(Vec3*)&matrix.Columns[1]).Magnitude();
scale.Z = (*(Vec3*)&matrix.Columns[2]).Magnitude();
if(MathHelper.IsZero(scale.X) || MathHelper.IsZero(scale.Y) || MathHelper.IsZero(scale.Z))
{
rotation = .Identity;
return;
}
// Remove scale from matrix (normalize the columns)
matrix.Columns[0] /= scale.X;
matrix.Columns[1] /= scale.Y;
matrix.Columns[2] /= scale.Z;
rotation = Quaternion.FromMatrix(matrix);
}
/*[Test]
static void TestQuaternionToMatrix()
{
// Rotation around Z-Axis by 90°
Quaternion quat = .(0, 0, 0.707107f, 0.707107f);
quat.Normalize();
}*/
}
}
+348
View File
@@ -0,0 +1,348 @@
using System;
namespace GlitchyEngine.Math
{
public struct Quaternion
{
public const Quaternion Zero = .();
public const Quaternion One = .(1.0f, 1.0f, 1.0f, 1.0f);
public const Quaternion Identity = .(0.0f, 0.0f, 0.0f, 1.0f);
public float X, Y, Z, W;
public this() => this = default;
public this(float x, float y, float z, float w)
{
X = x;
Y = y;
Z = z;
W = w;
}
public this(Vector3 xy, float z, float w)
{
X = xy.X;
Y = xy.Y;
Z = z;
W = w;
}
public this(Vector3 xyz, float w)
{
X = xyz.X;
Y = xyz.Y;
Z = xyz.Z;
W = w;
}
public this(Vector4 vector)
{
X = vector.X;
Y = vector.Y;
Z = vector.Z;
W = vector.W;
}
public Vector3 Vector
{
get => .(X, Y, Z);
set mut
{
X = value.X;
Y = value.Y;
Z = value.Z;
}
}
public float Scalar
{
get => W;
set mut => W = value;
}
public void Normalize() mut
{
float invLength = 1.0f / Length();
X *= invLength;
Y *= invLength;
Z *= invLength;
W *= invLength;
}
public static Quaternion Normalize(Quaternion q)
{
float invLength = 1.0f / Length(q);
return Quaternion(q.X * invLength, q.Y * invLength, q.Z * invLength, q.W * invLength);
}
public float Length()
{
return Math.Sqrt([Inline]LengthSquared());
}
public static float Length(Quaternion q)
{
return Math.Sqrt([Inline]LengthSquared(q));
}
public float LengthSquared()
{
return X * X + Y * Y + Z * Z + W * W;
}
public static float LengthSquared(Quaternion q)
{
return q.X * q.X + q.Y * q.Y + q.Z * q.Z + q.W * q.W;
}
public static float Dot(Self l, Self r)
{
return l.X * r.X + l.Y * r.Y + l.Z * r.Z + l.W * r.W;
}
public static Quaternion Lerp(Quaternion a, Quaternion b, float interpolationValue)
{
return a + interpolationValue * (b - a);
}
public static Quaternion Slerp(Quaternion previousQuaternion, Quaternion nextQuaternion, float interpolationValue)
{
// Based on https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_007_Animations.md
var nextQuaternion;
float dot = Dot(previousQuaternion, nextQuaternion);
//make sure we take the shortest path in case dot Product is negative
if(dot < 0.0f)
{
nextQuaternion = -nextQuaternion;
dot = -dot;
}
//if the two quaternions are too close to each other, just linear interpolate between the 4D vector
if(dot > 0.9995f)
return Normalize(previousQuaternion + interpolationValue * (nextQuaternion - previousQuaternion));
//perform the spherical linear interpolation
var theta_0 = Math.Acos(dot);
var theta = interpolationValue * theta_0;
var sin_theta = Math.Sin(theta);
var sin_theta_0 = Math.Sin(theta_0);
var scalePreviousQuat = Math.Cos(theta) - dot * sin_theta / sin_theta_0;
var scaleNextQuat = sin_theta / sin_theta_0;
return scalePreviousQuat * previousQuaternion + scaleNextQuat * nextQuaternion;
}
public static Quaternion FromMatrix(Matrix matrix)
{
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
var m = matrix.V;
Quaternion result = ?;
float tr = m._11 + m._22 + m._33;
if (tr > 0) {
float S = Math.Sqrt(tr + 1.0f) * 2f; // S=4*result.W
result.W = 0.25f * S;
result.X = (m._32 - m._23) / S;
result.Y = (m._13 - m._31) / S;
result.Z = (m._21 - m._12) / S;
} else if ((m._11 > m._22)&(m._11 > m._33)) {
float S = Math.Sqrt(1.0f + m._11 - m._22 - m._33) * 2f; // S=4*result.X
result.W = (m._32 - m._23) / S;
result.X = 0.25f * S;
result.Y = (m._12 + m._21) / S;
result.Z = (m._13 + m._31) / S;
} else if (m._22 > m._33) {
float S = Math.Sqrt(1.0f + m._22 - m._11 - m._33) * 2f; // S=4*result.Y
result.W = (m._13 - m._31) / S;
result.X = (m._12 + m._21) / S;
result.Y = 0.25f * S;
result.Z = (m._23 + m._32) / S;
} else {
float S = Math.Sqrt(1.0f + m._33 - m._11 - m._22) * 2f; // S=4*result.Z
result.W = (m._21 - m._12) / S;
result.X = (m._13 + m._31) / S;
result.Y = (m._23 + m._32) / S;
result.Z = 0.25f * S;
}
return result;
}
public static Self operator +(Self value) => value;
public static Self operator +(Self l, Self r) => Self(l.X + r.X, l.Y + r.Y, l.Z + r.Z, l.W + r.W);
public static Self operator -(Self value) => Self(-value.X, -value.Y, -value.Z, -value.W);
public static Self operator -(Self l, Self r) => Self(l.X - r.X, l.Y - r.Y, l.Z - r.Z, l.W - r.W);
public static Self operator *(float l, Self r) => Self(l * r.X, l * r.Y, l * r.Z, l * r.W);
public static Self operator /(Self l, float r) => Self(l.X * r, l.Y * r, l.Z * r, l.W * r);
public static Self operator *(Self l, Self r)
{
Quaternion result;
Vector3 v = l.Vector * r.Vector + (l.W * r.Vector) + (r.W * l.Vector);
result.X = v.X;
result.Y = v.Y;
result.Z = v.Z;
result.W = (l.W * r.W) - Vector3.Dot(l.Vector, r.Vector);
return result;
}
public static Quaternion Conjugate(Quaternion q)
{
return Quaternion(-q.Vector, q.Scalar);
}
public static Quaternion Inverse(Quaternion q)
{
Quaternion conjugate = Conjugate(q);
float magSquared = LengthSquared(q);
return conjugate / magSquared;
}
[Inline]
#unwarn
public static implicit operator Vector4(in Self value) => *(Vector4*)&value;
[Inline]
#unwarn
public static implicit operator Quaternion(in Vector4 value) => *(Quaternion*)&value;
public static bool operator ==(Quaternion l, Quaternion r)
{
return l.X == r.X && l.Y == r.Y && l.Z == r.Z && l.W == r.W;
}
public static bool operator !=(Quaternion l, Quaternion r)
{
return l.X != r.X && l.Y != r.Y && l.Z != r.Z && l.W != r.W;
}
public (Vector3 Axis, float Angle) ToAxisAngle()
{
// scalar part = cos(θ/2)
// So, we can extract the angle directly.
float angle = 2.0f * Math.Acos(W);
// vector part = axis * sin(θ/2)
// In other words, the vector part is the axis, but with length of sin(θ/2).
// We assume quaternion is unit length, so subtracting w^2 gives us length of just vector part (aka sin(θ/2)).
float length = Math.Sqrt(1.0f - (W * W));
Vector3 axis;
// Normalize vector part to get the axis!
if(length == 0)
{
axis = Vector3.Zero;
}
else
{
length = 1.0f / length;
axis.X = X * length;
axis.Y = Y * length;
axis.Z = Z * length;
}
return (axis, angle);
}
public static Quaternion FromAxisAngle(Vector3 axis, float angle)
{
float lengthSq = axis.MagnitudeSquared();
if(lengthSq == 0)
{
return .Identity;
}
float halfAngle = angle * 0.5f;
float sin = Math.Sin(halfAngle) / Math.Sqrt(lengthSq);
Quaternion result;
result.X = axis.X * sin;
result.Y = axis.Y * sin;
result.Z = axis.Z * sin;
result.W = Math.Cos(halfAngle);
return result;
}
// Assumes YZX-Order meaning Y applied first, Z second and x last
public static Quaternion FromEulerAngles(float yaw, float pitch, float roll)
{
float halfYaw = yaw / 2.0f;
float halfPitch = pitch / 2.0f;
float halfRoll = roll / 2.0f;
float cosYaw = Math.Cos(halfYaw);//heading
float sinYaw = Math.Sin(halfYaw);
float cosRoll = Math.Cos(halfRoll);//attitude
float sinRoll = Math.Sin(halfRoll);
float cosPitch = Math.Cos(halfPitch);//bank
float sinPitch = Math.Sin(halfPitch);
float cosYawCosRoll = cosYaw * cosRoll;
float sinYawSinRoll = sinYaw * sinRoll;
float cosYawSinRoll = cosYaw * sinRoll;
float sinYawCosRoll = sinYaw * cosRoll;
Quaternion result;
result.W = cosYawCosRoll * cosPitch - sinYawSinRoll * sinPitch;
result.X = cosYawCosRoll * sinPitch + sinYawSinRoll * cosPitch;
result.Y = sinYawCosRoll * cosPitch + cosYawSinRoll * sinPitch;
result.Z = cosYawSinRoll * cosPitch - sinYawCosRoll * sinPitch;
return result;
}
public static Vector3 ToEulerAngles(Quaternion q1)
{
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
Vector3 result;
float sqw = q1.W*q1.W;
float sqx = q1.X*q1.X;
float sqy = q1.Y*q1.Y;
float sqz = q1.Z*q1.Z;
float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
float test = q1.X*q1.Y + q1.Z*q1.W;
if (test > 0.499f*unit) { // singularity at north pole
result.Y = 2.0f * Math.Atan2(q1.X,q1.W);
result.Z = Math.PI_f / 2.0f;
result.X = 0.0f;
return result;
}
if (test < -0.499f*unit) { // singularity at south pole
result.Y = -2.0f * Math.Atan2(q1.X,q1.W);
result.Z = -Math.PI_f / 2.0f;
result.X = 0.0f;
return result;
}
result.Y = Math.Atan2(2*q1.Y*q1.W-2*q1.X*q1.Z , sqx - sqy - sqz + sqw);
result.Z = Math.Asin(2*test/unit);
result.X = Math.Atan2(2*q1.X*q1.W-2*q1.Y*q1.Z , -sqx + sqy - sqz + sqw);
return result;
}
}
}
-16
View File
@@ -1,16 +0,0 @@
namespace GlitchyEngine.Math
{
struct Ray
{
public Vector3 Start;
public Vector3 Direction;
public this() => this = default;
public this(Vector3 start, Vector3 direction)
{
Start = start;
Direction = direction;
}
}
}
+62
View File
@@ -0,0 +1,62 @@
using System;
namespace GlitchyEngine.Math
{
public struct ResourceBox
{
/// The x position of the left hand side of the box.
public uint32 Left;
/// The y position of the top of the box.
public uint32 Top;
/// The z position of the front of the box.
public uint32 Front;
/// The x position of the right hand side of the box.
public uint32 Right;
/// The y position of the bottom of the box.
public uint32 Bottom;
/// The z position of the back of the box.
public uint32 Back;
/**
* Returns true if the box is empty.
* A box is empty if the top value is greater than or equal to the bottom value,
* or the left value is greater than or equal to the right value,
* or the front value is greater than or equal to the back value.
*/
public bool IsEmpty => Top >= Bottom || Left >= Right || Front >= Back;
/**
* Instantiates a new instance of an uninitialized Box structure.
*/
public this()
{
this = default;
}
/**
* Instantiates a new instance of a Box structure that is initialized with the dimensions of a box.
*/
public this(uint32 left, uint32 top, uint32 front, uint32 right, uint32 bottom, uint32 back)
{
Left = left;
Top = top;
Front = front;
Right = right;
Bottom = bottom;
Back = back;
}
[Inline]
public static bool operator ==(ResourceBox l, ResourceBox r)
{
return l.Left == r.Left && l.Top == r.Top && l.Front == r.Front &&
l.Right == r.Right && l.Bottom == r.Bottom && l.Back == r.Back;
}
[Inline]
public static bool operator !=(ResourceBox l, ResourceBox r)
{
return !(l == r);
}
}
}
@@ -33,7 +33,8 @@ namespace GlitchyEngine.Math
[Comptime]
public void ApplyToType(Type type)
{
String[4] componentNames = .("X", "Y", "Z", "W");
// TODO: report bug... sized array not working
String[] componentNames = scope String[]("X", "Y", "Z", "W");
for(int swizzleCount = 2; swizzleCount <= 4; swizzleCount++)
{
@@ -49,7 +50,7 @@ namespace GlitchyEngine.Math
{
String swizzleName = scope String(swizzleCount);
String swizzleConstructor = scope String(swizzleCount * 3);
String setter = scope String();
String setter = scope String(128);
bool setterInvalid = invalidSetter(cmp, swizzleCount);
@@ -63,7 +64,7 @@ namespace GlitchyEngine.Math
}
swizzleConstructor.Append(componentNames[cmp[c]]);
if(!setterInvalid && c < _vectorSize)
if(!setterInvalid && c < _vectorSize) //
{
setter.AppendF($"\n\t\t{componentNames[cmp[c]]} = value.{componentNames[c]};");
}
+74 -16
View File
@@ -2,7 +2,7 @@ using System;
namespace GlitchyEngine.Math
{
//[SwizzleVector(2, "Vector")]
[SwizzleVector(2, "Vector")]
public struct Vector2
{
public const Vector2 Zero = .(0f, 0f);
@@ -10,7 +10,7 @@ namespace GlitchyEngine.Math
public const Vector2 UnitY = .(0f, 1f);
public const Vector2 One = .(1f, 1f);
public const int Length = 2;
public const int ComponentCount = 2;
public float X, Y;
@@ -31,18 +31,61 @@ namespace GlitchyEngine.Math
public ref float this[int index]
{
[Checked]
get
get mut
{
if(index < 0 || index >= Length)
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
#unwarn
return ref (&X)[index];
}
[Inline]
#unwarn
get => ref (&X)[index];
get mut => ref (&X)[index];
}
public float this[int index]
{
[Checked]
get
{
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
if(index == 0)
return X;
else
return Y;
}
[Inline]
get
{
if(index == 0)
return X;
else
return Y;
}
[Checked]
set mut
{
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
if(index == 0)
X = value;
else
Y = value;
}
[Inline]
set mut
{
if(index == 0)
X = value;
else
Y = value;
}
}
/**
@@ -90,7 +133,7 @@ namespace GlitchyEngine.Math
/**
* Calculates the projection of a onto b
*/
public Vector2 Project(Vector2 a, Vector2 b)
public static Vector2 Project(Vector2 a, Vector2 b)
{
return (b * (Dot(a, b) / Dot(b, b)));
}
@@ -98,11 +141,33 @@ namespace GlitchyEngine.Math
/**
* Calculates the rejection of a from b
*/
public Vector2 Reject(Vector2 a, Vector2 b)
public static Vector2 Reject(Vector2 a, Vector2 b)
{
return (a - b * (Dot(a, b) / Dot(b, b)));
}
/**
* Interpolates linearly between two given vectors.
* @param a The first vector.
* @param b The second vector.
* @param interpolationValue The value that linearly interpolates between a and b.
* (0 means a will be returned, 1 means b will be returned.)
* @returns The resulting linear interpolation.
*/
public static Vector2 Lerp(Vector2 a, Vector2 b, float interpolationValue)
{
return a + interpolationValue * (b - a);
}
public static Vector2 Min(Vector2 a, Vector2 b)
{
return .(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y));
}
public static Vector2 Max(Vector2 a, Vector2 b)
{
return .(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
}
//
// Assignment operators
@@ -217,13 +282,6 @@ namespace GlitchyEngine.Math
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1}", X, Y);
// Todo: move to extension
[Inline]
public static implicit operator DirectX.Math.Vector2(in Self value) => *(DirectX.Math.Vector2*)&value;
[Inline]
public static implicit operator Self(in DirectX.Math.Vector2 value) => *(Self*)&value;
[Inline]
public static explicit operator Self(float value) => Self(value);
}
+55 -15
View File
@@ -2,7 +2,7 @@ using System;
namespace GlitchyEngine.Math
{
//[SwizzleVector(3, "Vector")]
[SwizzleVector(3, "Vector")]
public struct Vector3
{
public const Vector3 Zero = .(0f, 0f, 0f);
@@ -18,6 +18,8 @@ namespace GlitchyEngine.Math
public const Vector3 Up = .(0f, 1f, 0f);
public const Vector3 Down = .(0f, -1f, 0f);
public const int ComponentCount = 3;
public float X, Y, Z;
public this() => this = default;
@@ -46,18 +48,41 @@ namespace GlitchyEngine.Math
public ref float this[int index]
{
[Checked]
get
get mut
{
if(index < 0 || index > 2)
Internal.ThrowIndexOutOfRange();
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
#unwarn
return ref (&X)[index];
}
[Inline]
#unwarn
get => ref (&X)[index];
get mut => ref (&X)[index];
}
public float this[int index]
{
get
{
switch(index)
{
case 0: return X;
case 1: return Y;
case 2: return Z;
default: Internal.ThrowIndexOutOfRange(1);
}
}
set mut
{
switch(index)
{
case 0: X = value;
case 1: Y = value;
case 2: Z = value;
default: Internal.ThrowIndexOutOfRange(1);
}
}
}
/**
@@ -156,6 +181,29 @@ namespace GlitchyEngine.Math
return .(Math.Floor(value.X), Math.Floor(value.Y), Math.Floor(value.Z));
}
/**
* Interpolates linearly between two given vectors.
* @param a The first vector.
* @param b The second vector.
* @param interpolationValue The value that linearly interpolates between a and b.
* (0 means a will be returned, 1 means b will be returned.)
* @returns The resulting linear interpolation.
*/
public static Vector3 Lerp(Vector3 a, Vector3 b, float interpolationValue)
{
return a + interpolationValue * (b - a);
}
public static Vector3 Min(Vector3 a, Vector3 b)
{
return .(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Min(a.Z, b.Z));
}
public static Vector3 Max(Vector3 a, Vector3 b)
{
return .(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y), Math.Min(a.Z, b.Z));
}
//
// Assignment operators
//
@@ -286,14 +334,6 @@ namespace GlitchyEngine.Math
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1} Z:{2}", X, Y, Z);
// Todo: move to extension
[Inline]
public static implicit operator DirectX.Math.Vector3(in Self value) => *(DirectX.Math.Vector3*)&value;
[Inline]
public static implicit operator Self(in DirectX.Math.Vector3 value) => *(Self*)&value;
[Inline]
public static explicit operator Self(float value) => Self(value);
}
+58 -15
View File
@@ -2,7 +2,7 @@ using System;
namespace GlitchyEngine.Math
{
//[SwizzleVector(4, "Vector")]
[SwizzleVector(4, "Vector")]
public struct Vector4
{
public const Vector4 Zero = .(0f, 0f, 0f, 0f);
@@ -12,6 +12,8 @@ namespace GlitchyEngine.Math
public const Vector4 UnitW = .(0f, 0f, 0f, 1f);
public const Vector4 One = .(1f, 1f, 1f, 1f);
public const int ComponentCount = 4;
public float X, Y, Z, W;
public this() => this = default;
@@ -59,17 +61,43 @@ namespace GlitchyEngine.Math
public ref float this[int index]
{
[Checked]
get
get mut
{
if(index < 0 || index > 3)
Internal.ThrowIndexOutOfRange();
#unwarn
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
return ref (&X)[index];
}
[Inline]
#unwarn
get => ref (&X)[index];
get mut => ref (&X)[index];
}
public float this[int index]
{
get
{
switch(index)
{
case 0: return X;
case 1: return Y;
case 2: return Z;
case 3: return W;
default: Internal.ThrowIndexOutOfRange(1);
}
}
set mut
{
switch(index)
{
case 0: X = value;
case 1: Y = value;
case 2: Z = value;
case 3: W = value;
default: Internal.ThrowIndexOutOfRange(1);
}
}
}
/**
@@ -138,6 +166,29 @@ namespace GlitchyEngine.Math
return (a - b * (Dot(a, b) / Dot(b, b)));
}
/**
* Interpolates linearly between two given vectors.
* @param a The first vector.
* @param b The second vector.
* @param interpolationValue The value that linearly interpolates between a and b.
* (0 means a will be returned, 1 means b will be returned.)
* @returns The resulting linear interpolation.
*/
public static Vector4 Lerp(Vector4 a, Vector4 b, float interpolationValue)
{
return a + interpolationValue * (b - a);
}
public static Vector4 Min(Vector4 a, Vector4 b)
{
return .(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Min(a.Z, b.Z), Math.Min(a.W, b.W));
}
public static Vector4 Max(Vector4 a, Vector4 b)
{
return .(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y), Math.Min(a.Z, b.Z), Math.Min(a.W, b.W));
}
//
// Assignment operators
//
@@ -267,14 +318,6 @@ namespace GlitchyEngine.Math
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W);
// Todo: move to extension
[Inline]
public static implicit operator DirectX.Math.Vector4(in Self value) => *(DirectX.Math.Vector4*)&value;
[Inline]
public static implicit operator Self(in DirectX.Math.Vector4 value) => *(Self*)&value;
[Inline]
public static explicit operator Self(float value) => Self(value);
}
+162
View File
@@ -0,0 +1,162 @@
#pragma warning disable 4204
using System;
using System.Diagnostics;
namespace GlitchyEngine.Math
{
/**
* A Vector with two components of type int32.
*/
[SwizzleVector(2, "Int")]
public struct Int2 : IHashable
{
public const Int2 Zero = .(0, 0);
public const Int2 UnitX = .(1, 0);
public const Int2 UnitY = .(0, 1);
public const Int2 One = .(1, 1);
public int32 X, Y;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
}
public this(int32 x, int32 y)
{
X = x;
Y = y;
}
public this(int x, int y)
{
X = (.)x;
Y = (.)y;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 2)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y;
public float Magnitude() => Math.Sqrt(X * X + Y * Y);
public Int2 Abs() => .(Math.Abs(X), Math.Abs(Y));
//
// Assignment operators
//
public void operator +=(Int2 value) mut
{
X += value.X;
Y += value.Y;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
}
public void operator -=(Int2 value) mut
{
X -= value.X;
Y -= value.Y;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
}
public void operator *=(Int2 value) mut
{
X *= value.X;
Y *= value.Y;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
}
public void operator /=(Int2 value) mut
{
X /= value.X;
Y /= value.Y;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
}
// Operators
public static Int2 operator +(Int2 value) => value;
public static Int2 operator +(Int2 left, Int2 right) => .(left.X + right.X, left.Y + right.Y);
public static Int2 operator +(Int2 left, int32 right) => .(left.X + right, left.Y + right);
public static Int2 operator +(int32 left, Int2 right) => .(left + right.X, left + right.Y);
public static Int2 operator -(Int2 value) => .(-value.X, -value.Y);
public static Int2 operator -(Int2 left, Int2 right) => .(left.X - right.X, left.Y - right.Y);
public static Int2 operator -(Int2 left, int32 right) => .(left.X - right, left.Y - right);
public static Int2 operator -(int32 left, Int2 right) => .(left - right.X, left - right.Y);
public static Int2 operator *(Int2 left, Int2 right) => .(left.X * right.X, left.Y * right.Y);
public static Int2 operator *(Int2 left, int32 right) => .(left.X * right, left.Y * right);
public static Int2 operator *(int32 left, Int2 right) => .(left * right.X, left * right.Y);
public static Int2 operator /(Int2 left, Int2 right) => .(left.X / right.X, left.Y / right.Y);
public static Int2 operator /(Int2 left, int32 right) => .(left.X / right, left.Y / right);
public static Int2 operator /(int32 left, Int2 right) => .(left / right.X, left / right.Y);
public static Int2 operator %(Int2 left, Int2 right) => .(left.X % right.X, left.Y % right.Y);
public static Int2 operator %(Int2 left, int32 right) => .(left.X % right, left.Y % right);
public static Int2 operator %(int32 left, Int2 right) => .(left % right.X, left % right.Y);
public static bool operator ==(Int2 left, Int2 right) => left.X == right.X && left.Y == right.Y;
public static bool operator ==(Int2 left, int32 right) => left.X == right && left.Y == right;
public static bool operator ==(int32 left, Int2 right) => left == right.X && left == right.Y;
public static bool operator !=(Int2 left, Int2 right) => left.X != right.X || left.Y != right.Y;
public static bool operator !=(Int2 left, int32 right) => left.X != right || left.Y != right;
public static bool operator !=(int32 left, Int2 right) => left != right.X || left != right.Y;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y}");
public static explicit operator Vector2(Int2 point) => .(point.X, point.Y);
public static explicit operator Int2(Vector2 point) => .((int32)point.X, (int32)point.Y);
public int GetHashCode()
{
return (X * 39) ^ Y;
}
}
}
+206
View File
@@ -0,0 +1,206 @@
#pragma warning disable 4204
using System;
using System.Diagnostics;
namespace GlitchyEngine.Math
{
/**
* A Vector with three components of type int32.
*/
[SwizzleVector(3, "Int")]
public struct Int3 : IHashable
{
public const Int3 Zero = .(0, 0, 0);
public const Int3 UnitX = .(1, 0, 0);
public const Int3 UnitY = .(0, 1, 0);
public const Int3 UnitZ = .(0, 0, 1);
public const Int3 One = .(1, 1, 1);
public int32 X, Y, Z;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
Z = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
Z = (.)value;
}
public this(int32 x, int32 y, int32 z)
{
X = x;
Y = y;
Z = z;
}
public this(Int2 xy, int32 z)
{
X = xy.X;
Y = xy.Y;
Z = z;
}
public this(int32 x, Int2 yz)
{
X = x;
Y = yz.X;
Z = yz.Y;
}
public this(int x, int y, int z)
{
X = (.)x;
Y = (.)y;
Z = (.)z;
}
public this(Int2 xy, int z)
{
X = xy.X;
Y = xy.Y;
Z = (.)z;
}
public this(int x, Int2 yz)
{
X = (.)x;
Y = yz.X;
Z = yz.Y;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 3)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y + Z * Z;
public float Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z);
public Int3 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z));
//
// Assignment operators
//
public void operator +=(Int3 value) mut
{
X += value.X;
Y += value.Y;
Z += value.Z;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
Z += value;
}
public void operator -=(Int3 value) mut
{
X -= value.X;
Y -= value.Y;
Z -= value.Z;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
Z -= value;
}
public void operator *=(Int3 value) mut
{
X *= value.X;
Y *= value.Y;
Z *= value.Z;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
Z *= value;
}
public void operator /=(Int3 value) mut
{
X /= value.X;
Y /= value.Y;
Z /= value.Z;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
Z /= value;
}
// Operators
public static Int3 operator +(Int3 value) => value;
public static Int3 operator +(Int3 left, Int3 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
public static Int3 operator +(Int3 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right);
public static Int3 operator +(int32 left, Int3 right) => .(left + right.X, left + right.Y, left + right.Z);
public static Int3 operator -(Int3 value) => .(-value.X, -value.Y, -value.Z);
public static Int3 operator -(Int3 left, Int3 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
public static Int3 operator -(Int3 left, int32 right) => .(left.X - right, left.Y - right, left.Z- right);
public static Int3 operator -(int32 left, Int3 right) => .(left - right.X, left - right.Y, left - right.Z);
public static Int3 operator *(Int3 left, Int3 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
public static Int3 operator *(Int3 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right);
public static Int3 operator *(int32 left, Int3 right) => .(left * right.X, left * right.Y, left * right.Z);
public static Int3 operator /(Int3 left, Int3 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
public static Int3 operator /(Int3 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right);
public static Int3 operator /(int32 left, Int3 right) => .(left / right.X, left / right.Y, left / right.Z);
public static Int3 operator %(Int3 left, Int3 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z);
public static Int3 operator %(Int3 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right);
public static Int3 operator %(int32 left, Int3 right) => .(left % right.X, left % right.Y, left % right.Z);
public static bool operator ==(Int3 left, Int3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
public static bool operator ==(Int3 left, int32 right) => left.X == right && left.Y == right && left.Z == right;
public static bool operator ==(int32 left, Int3 right) => left == right.X && left == right.Y && left == right.Z;
public static bool operator !=(Int3 left, Int3 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
public static bool operator !=(Int3 left, int32 right) => left.X != right || left.Y != right || left.Z != right;
public static bool operator !=(int32 left, Int3 right) => left != right.X || left != right.Y || left != right.Z;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y} Z:{Z}");
public static explicit operator Vector3(Int3 point) => .(point.X, point.Y, point.Z);
public static explicit operator Int3(Vector3 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z);
[Inline]
public static explicit operator Int2(in Int3 point) => *(Int2*)&point;
public int GetHashCode()
{
return (((X * 39) ^ Y) * 39) ^ Z;
}
}
}
-162
View File
@@ -1,162 +0,0 @@
#pragma warning disable 4204
using System;
using System.Diagnostics;
namespace GlitchyEngine.Math
{
/**
* A Vector with two components of type int32.
*/
[SwizzleVector(2, "Int32_")]
public struct Int32_2 : IHashable
{
public const Int32_2 Zero = .(0, 0);
public const Int32_2 UnitX = .(1, 0);
public const Int32_2 UnitY = .(0, 1);
public const Int32_2 One = .(1, 1);
public int32 X, Y;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
}
public this(int32 x, int32 y)
{
X = x;
Y = y;
}
public this(int x, int y)
{
X = (.)x;
Y = (.)y;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 2)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y;
public float Magnitude() => Math.Sqrt(X * X + Y * Y);
public Int32_2 Abs() => .(Math.Abs(X), Math.Abs(Y));
//
// Assignment operators
//
public void operator +=(Int32_2 value) mut
{
X += value.X;
Y += value.Y;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
}
public void operator -=(Int32_2 value) mut
{
X -= value.X;
Y -= value.Y;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
}
public void operator *=(Int32_2 value) mut
{
X *= value.X;
Y *= value.Y;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
}
public void operator /=(Int32_2 value) mut
{
X /= value.X;
Y /= value.Y;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
}
// Operators
public static Int32_2 operator +(Int32_2 value) => value;
public static Int32_2 operator +(Int32_2 left, Int32_2 right) => .(left.X + right.X, left.Y + right.Y);
public static Int32_2 operator +(Int32_2 left, int32 right) => .(left.X + right, left.Y + right);
public static Int32_2 operator +(int32 left, Int32_2 right) => .(left + right.X, left + right.Y);
public static Int32_2 operator -(Int32_2 value) => .(-value.X, -value.Y);
public static Int32_2 operator -(Int32_2 left, Int32_2 right) => .(left.X - right.X, left.Y - right.Y);
public static Int32_2 operator -(Int32_2 left, int32 right) => .(left.X - right, left.Y - right);
public static Int32_2 operator -(int32 left, Int32_2 right) => .(left - right.X, left - right.Y);
public static Int32_2 operator *(Int32_2 left, Int32_2 right) => .(left.X * right.X, left.Y * right.Y);
public static Int32_2 operator *(Int32_2 left, int32 right) => .(left.X * right, left.Y * right);
public static Int32_2 operator *(int32 left, Int32_2 right) => .(left * right.X, left * right.Y);
public static Int32_2 operator /(Int32_2 left, Int32_2 right) => .(left.X / right.X, left.Y / right.Y);
public static Int32_2 operator /(Int32_2 left, int32 right) => .(left.X / right, left.Y / right);
public static Int32_2 operator /(int32 left, Int32_2 right) => .(left / right.X, left / right.Y);
public static Int32_2 operator %(Int32_2 left, Int32_2 right) => .(left.X % right.X, left.Y % right.Y);
public static Int32_2 operator %(Int32_2 left, int32 right) => .(left.X % right, left.Y % right);
public static Int32_2 operator %(int32 left, Int32_2 right) => .(left % right.X, left % right.Y);
public static bool operator ==(Int32_2 left, Int32_2 right) => left.X == right.X && left.Y == right.Y;
public static bool operator ==(Int32_2 left, int32 right) => left.X == right && left.Y == right;
public static bool operator ==(int32 left, Int32_2 right) => left == right.X && left == right.Y;
public static bool operator !=(Int32_2 left, Int32_2 right) => left.X != right.X || left.Y != right.Y;
public static bool operator !=(Int32_2 left, int32 right) => left.X != right || left.Y != right;
public static bool operator !=(int32 left, Int32_2 right) => left != right.X || left != right.Y;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y}");
public static explicit operator Vector2(Int32_2 point) => .(point.X, point.Y);
public static explicit operator Int32_2(Vector2 point) => .((int32)point.X, (int32)point.Y);
public int GetHashCode()
{
return (X * 39) ^ Y;
}
}
}
-206
View File
@@ -1,206 +0,0 @@
#pragma warning disable 4204
using System;
using System.Diagnostics;
namespace GlitchyEngine.Math
{
/**
* A Vector with three components of type int32.
*/
[SwizzleVector(3, "Int32_")]
public struct Int32_3 : IHashable
{
public const Int32_3 Zero = .(0, 0, 0);
public const Int32_3 UnitX = .(1, 0, 0);
public const Int32_3 UnitY = .(0, 1, 0);
public const Int32_3 UnitZ = .(0, 0, 1);
public const Int32_3 One = .(1, 1, 1);
public int32 X, Y, Z;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
Z = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
Z = (.)value;
}
public this(int32 x, int32 y, int32 z)
{
X = x;
Y = y;
Z = z;
}
public this(Int32_2 xy, int32 z)
{
X = xy.X;
Y = xy.Y;
Z = z;
}
public this(int32 x, Int32_2 yz)
{
X = x;
Y = yz.X;
Z = yz.Y;
}
public this(int x, int y, int z)
{
X = (.)x;
Y = (.)y;
Z = (.)z;
}
public this(Int32_2 xy, int z)
{
X = xy.X;
Y = xy.Y;
Z = (.)z;
}
public this(int x, Int32_2 yz)
{
X = (.)x;
Y = yz.X;
Z = yz.Y;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 3)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y + Z * Z;
public float Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z);
public Int32_3 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z));
//
// Assignment operators
//
public void operator +=(Int32_3 value) mut
{
X += value.X;
Y += value.Y;
Z += value.Z;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
Z += value;
}
public void operator -=(Int32_3 value) mut
{
X -= value.X;
Y -= value.Y;
Z -= value.Z;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
Z -= value;
}
public void operator *=(Int32_3 value) mut
{
X *= value.X;
Y *= value.Y;
Z *= value.Z;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
Z *= value;
}
public void operator /=(Int32_3 value) mut
{
X /= value.X;
Y /= value.Y;
Z /= value.Z;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
Z /= value;
}
// Operators
public static Int32_3 operator +(Int32_3 value) => value;
public static Int32_3 operator +(Int32_3 left, Int32_3 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
public static Int32_3 operator +(Int32_3 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right);
public static Int32_3 operator +(int32 left, Int32_3 right) => .(left + right.X, left + right.Y, left + right.Z);
public static Int32_3 operator -(Int32_3 value) => .(-value.X, -value.Y, -value.Z);
public static Int32_3 operator -(Int32_3 left, Int32_3 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
public static Int32_3 operator -(Int32_3 left, int32 right) => .(left.X - right, left.Y - right, left.Z- right);
public static Int32_3 operator -(int32 left, Int32_3 right) => .(left - right.X, left - right.Y, left - right.Z);
public static Int32_3 operator *(Int32_3 left, Int32_3 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
public static Int32_3 operator *(Int32_3 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right);
public static Int32_3 operator *(int32 left, Int32_3 right) => .(left * right.X, left * right.Y, left * right.Z);
public static Int32_3 operator /(Int32_3 left, Int32_3 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
public static Int32_3 operator /(Int32_3 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right);
public static Int32_3 operator /(int32 left, Int32_3 right) => .(left / right.X, left / right.Y, left / right.Z);
public static Int32_3 operator %(Int32_3 left, Int32_3 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z);
public static Int32_3 operator %(Int32_3 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right);
public static Int32_3 operator %(int32 left, Int32_3 right) => .(left % right.X, left % right.Y, left % right.Z);
public static bool operator ==(Int32_3 left, Int32_3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
public static bool operator ==(Int32_3 left, int32 right) => left.X == right && left.Y == right && left.Z == right;
public static bool operator ==(int32 left, Int32_3 right) => left == right.X && left == right.Y && left == right.Z;
public static bool operator !=(Int32_3 left, Int32_3 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
public static bool operator !=(Int32_3 left, int32 right) => left.X != right || left.Y != right || left.Z != right;
public static bool operator !=(int32 left, Int32_3 right) => left != right.X || left != right.Y || left != right.Z;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y} Z:{Z}");
public static explicit operator Vector3(Int32_3 point) => .(point.X, point.Y, point.Z);
public static explicit operator Int32_3(Vector3 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z);
[Inline]
public static explicit operator Int32_2(in Int32_3 point) => *(Int32_2*)&point;
public int GetHashCode()
{
return (((X * 39) ^ Y) * 39) ^ Z;
}
}
}
-234
View File
@@ -1,234 +0,0 @@
#pragma warning disable 4204
using System;
using System.Diagnostics;
namespace GlitchyEngine.Math
{
/**
* A Vector with four components of type int32.
*/
[SwizzleVector(4, "Int32_")]
public struct Int32_4 : IHashable
{
public const Int32_4 Zero = .(0, 0, 0, 0);
public const Int32_4 UnitX = .(1, 0, 0, 0);
public const Int32_4 UnitY = .(0, 1, 0, 0);
public const Int32_4 UnitZ = .(0, 0, 1, 0);
public const Int32_4 UnitW = .(0, 0, 0, 1);
public const Int32_4 One = .(1, 1, 1, 1);
public int32 X, Y, Z, W;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
Z = value;
W = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
Z = (.)value;
W = (.)value;
}
public this(int32 x, int32 y, int32 z, int32 w)
{
X = x;
Y = y;
Z = z;
W = w;
}
public this(Int32_2 xy, int32 z, int32 w)
{
X = xy.X;
Y = xy.Y;
Z = z;
W = w;
}
public this(int32 x, Int32_2 yz, int32 w)
{
X = x;
Y = yz.X;
Z = yz.Y;
W = w;
}
public this(int32 x, int32 y, Int32_2 zw)
{
X = x;
Y = y;
Z = zw.X;
W = zw.Y;
}
public this(Int32_2 xy, Int32_2 zw)
{
X = xy.X;
Y = xy.Y;
Z = zw.X;
W = zw.Y;
}
public this(Int32_3 xyz, int32 w)
{
X = xyz.X;
Y = xyz.Y;
Z = xyz.Z;
W = w;
}
public this(int32 x, Int32_3 yzw)
{
X = x;
Y = yzw.X;
Z = yzw.Y;
W = yzw.Z;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 3)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y + Z * Z + W * W;
public float Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
public Int32_4 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z), Math.Abs(W));
//
// Assignment operators
//
public void operator +=(Int32_4 value) mut
{
X += value.X;
Y += value.Y;
Z += value.Z;
W += value.W;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
Z += value;
W += value;
}
public void operator -=(Int32_4 value) mut
{
X -= value.X;
Y -= value.Y;
Z -= value.Z;
W -= value.W;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
Z -= value;
W -= value;
}
public void operator *=(Int32_4 value) mut
{
X *= value.X;
Y *= value.Y;
Z *= value.Z;
W *= value.W;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
Z *= value;
W *= value;
}
public void operator /=(Int32_4 value) mut
{
X /= value.X;
Y /= value.Y;
Z /= value.Z;
W /= value.W;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
Z /= value;
W /= value;
}
// Operators
public static Int32_4 operator +(Int32_4 value) => value;
public static Int32_4 operator +(Int32_4 left, Int32_4 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
public static Int32_4 operator +(Int32_4 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right, left.W + right);
public static Int32_4 operator +(int32 left, Int32_4 right) => .(left + right.X, left + right.Y, left + right.Z, left + right.W);
public static Int32_4 operator -(Int32_4 value) => .(-value.X, -value.Y, -value.Z, -value.W);
public static Int32_4 operator -(Int32_4 left, Int32_4 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W);
public static Int32_4 operator -(Int32_4 left, int32 right) => .(left.X - right, left.Y - right, left.Z - right, left.W - right);
public static Int32_4 operator -(int32 left, Int32_4 right) => .(left - right.X, left - right.Y, left - right.Z, left - right.W);
public static Int32_4 operator *(Int32_4 left, Int32_4 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W);
public static Int32_4 operator *(Int32_4 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right, left.W * right);
public static Int32_4 operator *(int32 left, Int32_4 right) => .(left * right.X, left * right.Y, left * right.Z, left * right.W);
public static Int32_4 operator /(Int32_4 left, Int32_4 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W);
public static Int32_4 operator /(Int32_4 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right, left.W / right);
public static Int32_4 operator /(int32 left, Int32_4 right) => .(left / right.X, left / right.Y, left / right.Z, left / right.W);
public static Int32_4 operator %(Int32_4 left, Int32_4 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z, left.W % right.W);
public static Int32_4 operator %(Int32_4 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right, left.W % right);
public static Int32_4 operator %(int32 left, Int32_4 right) => .(left % right.X, left % right.Y, left % right.Z, left % right.W);
public static bool operator ==(Int32_4 left, Int32_4 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z && left.W == right.W;
public static bool operator ==(Int32_4 left, int32 right) => left.X == right && left.Y == right && left.Z == right && left.W == right;
public static bool operator ==(int32 left, Int32_4 right) => left == right.X && left == right.Y && left == right.Z && left == right.W;
public static bool operator !=(Int32_4 left, Int32_4 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z || left.W != right.W;
public static bool operator !=(Int32_4 left, int32 right) => left.X != right || left.Y != right || left.Z != right || left.W != right;
public static bool operator !=(int32 left, Int32_4 right) => left != right.X || left != right.Y || left != right.Z || left != right.W;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y} Z:{Z} W:{W}");
public static explicit operator Vector4(Int32_4 point) => .(point.X, point.Y, point.Z, point.W);
public static explicit operator Int32_4(Vector4 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z, (int32)point.W);
[Inline]
public static explicit operator Int32_2(in Int32_4 point) => *(Int32_2*)&point;
[Inline]
public static explicit operator Int32_3(in Int32_4 point) => *(Int32_3*)&point;
public int GetHashCode()
{
return (((((X * 39) ^ Y) * 39) ^ Z) * 39) ^ W;
}
}
}
+234
View File
@@ -0,0 +1,234 @@
#pragma warning disable 4204
using System;
using System.Diagnostics;
namespace GlitchyEngine.Math
{
/**
* A Vector with four components of type int32.
*/
[SwizzleVector(4, "Int")]
public struct Int4 : IHashable
{
public const Int4 Zero = .(0, 0, 0, 0);
public const Int4 UnitX = .(1, 0, 0, 0);
public const Int4 UnitY = .(0, 1, 0, 0);
public const Int4 UnitZ = .(0, 0, 1, 0);
public const Int4 UnitW = .(0, 0, 0, 1);
public const Int4 One = .(1, 1, 1, 1);
public int32 X, Y, Z, W;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
Z = value;
W = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
Z = (.)value;
W = (.)value;
}
public this(int32 x, int32 y, int32 z, int32 w)
{
X = x;
Y = y;
Z = z;
W = w;
}
public this(Int2 xy, int32 z, int32 w)
{
X = xy.X;
Y = xy.Y;
Z = z;
W = w;
}
public this(int32 x, Int2 yz, int32 w)
{
X = x;
Y = yz.X;
Z = yz.Y;
W = w;
}
public this(int32 x, int32 y, Int2 zw)
{
X = x;
Y = y;
Z = zw.X;
W = zw.Y;
}
public this(Int2 xy, Int2 zw)
{
X = xy.X;
Y = xy.Y;
Z = zw.X;
W = zw.Y;
}
public this(Int3 xyz, int32 w)
{
X = xyz.X;
Y = xyz.Y;
Z = xyz.Z;
W = w;
}
public this(int32 x, Int3 yzw)
{
X = x;
Y = yzw.X;
Z = yzw.Y;
W = yzw.Z;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 3)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y + Z * Z + W * W;
public float Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
public Int4 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z), Math.Abs(W));
//
// Assignment operators
//
public void operator +=(Int4 value) mut
{
X += value.X;
Y += value.Y;
Z += value.Z;
W += value.W;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
Z += value;
W += value;
}
public void operator -=(Int4 value) mut
{
X -= value.X;
Y -= value.Y;
Z -= value.Z;
W -= value.W;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
Z -= value;
W -= value;
}
public void operator *=(Int4 value) mut
{
X *= value.X;
Y *= value.Y;
Z *= value.Z;
W *= value.W;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
Z *= value;
W *= value;
}
public void operator /=(Int4 value) mut
{
X /= value.X;
Y /= value.Y;
Z /= value.Z;
W /= value.W;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
Z /= value;
W /= value;
}
// Operators
public static Int4 operator +(Int4 value) => value;
public static Int4 operator +(Int4 left, Int4 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
public static Int4 operator +(Int4 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right, left.W + right);
public static Int4 operator +(int32 left, Int4 right) => .(left + right.X, left + right.Y, left + right.Z, left + right.W);
public static Int4 operator -(Int4 value) => .(-value.X, -value.Y, -value.Z, -value.W);
public static Int4 operator -(Int4 left, Int4 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W);
public static Int4 operator -(Int4 left, int32 right) => .(left.X - right, left.Y - right, left.Z - right, left.W - right);
public static Int4 operator -(int32 left, Int4 right) => .(left - right.X, left - right.Y, left - right.Z, left - right.W);
public static Int4 operator *(Int4 left, Int4 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W);
public static Int4 operator *(Int4 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right, left.W * right);
public static Int4 operator *(int32 left, Int4 right) => .(left * right.X, left * right.Y, left * right.Z, left * right.W);
public static Int4 operator /(Int4 left, Int4 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W);
public static Int4 operator /(Int4 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right, left.W / right);
public static Int4 operator /(int32 left, Int4 right) => .(left / right.X, left / right.Y, left / right.Z, left / right.W);
public static Int4 operator %(Int4 left, Int4 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z, left.W % right.W);
public static Int4 operator %(Int4 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right, left.W % right);
public static Int4 operator %(int32 left, Int4 right) => .(left % right.X, left % right.Y, left % right.Z, left % right.W);
public static bool operator ==(Int4 left, Int4 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z && left.W == right.W;
public static bool operator ==(Int4 left, int32 right) => left.X == right && left.Y == right && left.Z == right && left.W == right;
public static bool operator ==(int32 left, Int4 right) => left == right.X && left == right.Y && left == right.Z && left == right.W;
public static bool operator !=(Int4 left, Int4 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z || left.W != right.W;
public static bool operator !=(Int4 left, int32 right) => left.X != right || left.Y != right || left.Z != right || left.W != right;
public static bool operator !=(int32 left, Int4 right) => left != right.X || left != right.Y || left != right.Z || left != right.W;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y} Z:{Z} W:{W}");
public static explicit operator Vector4(Int4 point) => .(point.X, point.Y, point.Z, point.W);
public static explicit operator Int4(Vector4 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z, (int32)point.W);
[Inline]
public static explicit operator Int2(in Int4 point) => *(Int2*)&point;
[Inline]
public static explicit operator Int3(in Int4 point) => *(Int3*)&point;
public int GetHashCode()
{
return (((((X * 39) ^ Y) * 39) ^ Z) * 39) ^ W;
}
}
}
@@ -0,0 +1,134 @@
using GlitchyEngine.Renderer;
using GlitchyEngine.Events;
using GlitchyEngine.Math;
using System;
namespace GlitchyEngine
{
/// A simple controller for an orthographic camera
public class OrthographicCameraController
{
private OrthographicCamera _camera ~ delete _;
private float _aspectRatio;
private float _zoomLevel = 1.0f;
private bool _rotation;
private Vector3 _cameraPosition;
private float _cameraRotation;
private float _cameraTranslationSpeed = 1.0f;
private float _cameraRotationSpeed = 1.0f;
public OrthographicCamera Camera => _camera;
public this(float aspectRatio, bool rotation = false)
{
Debug.Profiler.ProfileFunction!();
_aspectRatio = aspectRatio;
_rotation = rotation;
_camera = new OrthographicCamera();
_camera.NearPlane = -10f;
_camera.FarPlane = 10f;
UpdateCamera();
}
public void Update(GameTime gameTime)
{
Debug.Profiler.ProfileFunction!();
if(Application.Get().Window.IsActive)
{
Vector3 movement = .();
if(Input.IsKeyPressed(Key.W))
{
movement.Y += 1;
}
if(Input.IsKeyPressed(Key.S))
{
movement.Y -= 1;
}
if(Input.IsKeyPressed(Key.A))
{
movement.X -= 1;
}
if(Input.IsKeyPressed(Key.D))
{
movement.X += 1;
}
if(movement != .Zero)
movement.Normalize();
movement *= (float)(gameTime.FrameTime.TotalSeconds) * _cameraTranslationSpeed * _zoomLevel;
_cameraPosition += movement;
if(_rotation)
{
if(Input.IsKeyPressed(Key.Q))
{
_cameraRotation += (float)(gameTime.FrameTime.TotalSeconds) * _cameraRotationSpeed;
}
if(Input.IsKeyPressed(Key.E))
{
_cameraRotation -= (float)(gameTime.FrameTime.TotalSeconds) * _cameraRotationSpeed;
}
}
UpdateCamera();
}
}
public void OnEvent(Event e)
{
Debug.Profiler.ProfileFunction!();
EventDispatcher dispatcher = EventDispatcher(e);
dispatcher.Dispatch<MouseScrolledEvent>(scope => OnMouseScrolled);
dispatcher.Dispatch<WindowResizeEvent>(scope => OnWindowResized);
}
private bool OnMouseScrolled(MouseScrolledEvent e)
{
Debug.Profiler.ProfileFunction!();
_zoomLevel -= e.YOffset * 0.25f;
_zoomLevel = Math.Max(_zoomLevel, 0.25f);
UpdateCamera();
return false;
}
private bool OnWindowResized(WindowResizeEvent e)
{
Debug.Profiler.ProfileFunction!();
_aspectRatio = (float)e.Width / (float)e.Height;
UpdateCamera();
return false;
}
private void UpdateCamera()
{
Debug.Profiler.ProfileFunction!();
_camera.Left = -_aspectRatio * _zoomLevel;
_camera.Right = _aspectRatio * _zoomLevel;
_camera.Top = _zoomLevel;
_camera.Bottom = -_zoomLevel;
_camera.Rotation = .(_camera.Rotation.X, _camera.Rotation.Y, _cameraRotation);
_camera.Position = _cameraPosition;
_camera.Update();
}
}
}
@@ -0,0 +1,186 @@
using System;
using GlitchyEngine.Events;
using GlitchyEngine.Math;
using GlitchyEngine.Renderer;
namespace GlitchyEngine
{
/// A simple controller for a perspective camera
public class PerspectiveCameraController
{
private PerspectiveCamera _camera ~ delete _;
private float _aspectRatio;
private float _fovY = Math.PI_f / 4;
private Vector3 _cameraPosition;
private Vector3 _cameraRotation;
private float _cameraTranslationSpeed = 1.0f;
private float _cameraRotationSpeedX = 0.001f;
private float _cameraRotationSpeedY = 0.001f;
public PerspectiveCamera Camera => _camera;
public float TranslationSpeed
{
get => _cameraTranslationSpeed;
set => _cameraTranslationSpeed = value;
}
public Vector2 RotationSpeed
{
get => .(_cameraRotationSpeedX, _cameraRotationSpeedY);
set
{
_cameraRotationSpeedX = value.X;
_cameraRotationSpeedY = value.Y;
}
}
public Vector3 CameraPosition
{
get => _cameraPosition;
set
{
_cameraPosition = value;
UpdateCamera();
}
}
public Vector3 CameraRotation
{
get => _cameraRotation;
set
{
_cameraRotation = value;
UpdateCamera();
}
}
public float FovY
{
get => _fovY;
set
{
_fovY = value;
UpdateCamera();
}
}
public float AspectRatio
{
get => _aspectRatio;
set
{
_aspectRatio = value;
UpdateCamera();
}
}
public this(float aspectRatio)
{
Debug.Profiler.ProfileFunction!();
_aspectRatio = aspectRatio;
_camera = new PerspectiveCamera();
_camera.ProjectionType = .Infinite;
_camera.NearPlane = 0.01f;
UpdateCamera();
}
public void Update(GameTime gameTime)
{
Debug.Profiler.ProfileFunction!();
if(Application.Get().Window.IsActive)
{
Vector3 movement = .();
if(Input.IsKeyPressed(Key.W))
{
movement.Z += 1;
}
if(Input.IsKeyPressed(Key.S))
{
movement.Z -= 1;
}
if(Input.IsKeyPressed(Key.A))
{
movement.X -= 1;
}
if(Input.IsKeyPressed(Key.D))
{
movement.X += 1;
}
if(Input.IsKeyPressed(Key.Space))
{
movement.Y += 1;
}
if(Input.IsKeyPressed(Key.Control))
{
movement.Y -= 1;
}
if(movement != .Zero)
movement.Normalize();
movement *= (float)(gameTime.FrameTime.TotalSeconds) * _cameraTranslationSpeed;
Vector4 delta = Vector4(movement, 1.0f) * _camera.View;
_cameraPosition += Vector3(delta.X, delta.Y, delta.Z);
//_cameraPosition += movement;
// Camera rotation
var mouseDelta = Input.GetMouseMovement();
float rotY = mouseDelta.X * _cameraRotationSpeedX;
float rotX = mouseDelta.Y * _cameraRotationSpeedY;
_cameraRotation.X += rotX;
_cameraRotation.Y += rotY;
UpdateCamera();
}
}
public void OnEvent(Event e)
{
Debug.Profiler.ProfileFunction!();
EventDispatcher dispatcher = EventDispatcher(e);
dispatcher.Dispatch<WindowResizeEvent>(scope => OnWindowResized);
}
private bool OnWindowResized(WindowResizeEvent e)
{
Debug.Profiler.ProfileFunction!();
_aspectRatio = (float)e.Width / (float)e.Height;
UpdateCamera();
return false;
}
private void UpdateCamera()
{
Debug.Profiler.ProfileFunction!();
_camera.AspectRatio = _aspectRatio;
_camera.FovY = _fovY;
_camera.Rotation = _cameraRotation;
_camera.Position = _cameraPosition;
_camera.Update();
}
}
}
@@ -0,0 +1,87 @@
#if GE_GRAPHICS_DX11
#if !BF_PLATFORM_WINDOWS
#error DirectX 11 (GE_GRAPHICS_DX11) can on be used on Windows.
#endif
using DirectX.Common;
using DirectX.D3D11;
using DirectX.D3D11.SDKLayers;
namespace GlitchyEngine.Platform.DX11
{
static
{
/// Gets whether or not the DX11 device is initialized.
protected internal static bool IsDx11Initialized => NativeDevice != null;
protected internal static ID3D11Device* NativeDevice;
protected internal static ID3D11DeviceContext* NativeContext;
protected internal static ID3D11Debug* DebugDevice;
internal static void Dx11Init()
{
Debug.Profiler.ProfileFunction!();
if(!IsDx11Initialized)
{
Log.EngineLogger.Trace("Creating D3D11 Device and Context...");
DeviceCreationFlags deviceFlags = .None;
#if DEBUG
deviceFlags |= .Debug;
#endif
FeatureLevel[] levels = scope .(.Level_11_0);
FeatureLevel deviceLevel = ?;
HResult deviceResult;
{
Debug.Profiler.ProfileScope!("D3D11.CreateDevice");
deviceResult = D3D11.CreateDevice(null, .Hardware, 0, deviceFlags, levels, &NativeDevice, &deviceLevel, &NativeContext);
}
Log.EngineLogger.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message({(int32)deviceResult}): {deviceResult}");
#if DEBUG
{
Debug.Profiler.ProfileScope!("Query for ID3D11Debug");
if(NativeDevice.QueryInterface<ID3D11Debug>(out DebugDevice).Succeeded)
{
ID3D11InfoQueue* infoQueue;
if(NativeDevice.QueryInterface<ID3D11InfoQueue>(out infoQueue).Succeeded)
{
infoQueue.SetBreakOnSeverity(.Corruption, true);
infoQueue.SetBreakOnSeverity(.Error, true);
infoQueue.Release();
}
}
}
#endif
Log.EngineLogger.Trace($"D3D11 Device and Context created (Feature level: {deviceLevel})");
}
else
{
// We created a second GraphicsContext (for some reason?) just increment references.
NativeDevice.AddRef();
NativeContext.AddRef();
DebugDevice.AddRef();
}
}
internal static void Dx11Release()
{
Debug.Profiler.ProfileFunction!();
NativeDevice.Release();
NativeContext.Release();
DebugDevice.ReportLiveDeviceObjects(.Detail);
DebugDevice.Release();
}
}
}
#endif
@@ -0,0 +1,43 @@
#if GE_GRAPHICS_DX11
using GlitchyEngine.Renderer;
using System.Collections;
using DirectX.D3D11;
namespace ImGui
{
using internal GlitchyEngine.Renderer;
extension ImGui
{
static List<ID3D11ShaderResourceView*> _resourceViews = new .() ~ delete _;
public static override void Image(Texture2D texture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec4 tint_col = Vec4.Ones, Vec4 border_col = Vec4.Zero)
{
var view = texture.nativeResourceView..AddRef();
_resourceViews.Add(view);
ImGui.Image(view, size, uv0, uv1, tint_col, border_col);
}
public static override void Image(RenderTarget2D texture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec4 tint_col = Vec4.Ones, Vec4 border_col = Vec4.Zero)
{
var view = texture._nativeResourceView..AddRef();
_resourceViews.Add(view);
ImGui.Image(view, size, uv0, uv1, tint_col, border_col);
}
protected internal static override void CleanupFrame()
{
for(var view in _resourceViews)
{
view.Release();
}
_resourceViews.Clear();
}
}
}
#endif
@@ -1,9 +1,12 @@
#if GE_GRAPHICS_DX11
// Disable warning when taking pointer of in-Parameter
#pragma warning disable 4204
using System;
namespace GlitchyEngine.Math
{
/*
extension Vector2
{
[Inline]
@@ -30,5 +33,6 @@ namespace GlitchyEngine.Math
[Inline]
public static implicit operator Self(in DirectX.Math.Vector4 value) => *(Self*)&value;
}
*/
}
#endif
@@ -1,22 +1,24 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using DirectX.Common;
using GlitchyEngine.Math;
using internal GlitchyEngine.Renderer;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
extension BlendState
{
internal ID3D11BlendState* nativeBlendState ~ _?.Release();
public override void Bind(ColorRGBA blendFactor)
{
_context.nativeContext.OutputMerger.SetBlendState(nativeBlendState, blendFactor);
}
protected override void PlatformCreateBlendState()
{
Debug.Profiler.ProfileResourceFunction!();
BlendDescription nativeDesc = .Default;
nativeDesc.IndependentBlendEnable = _desc.IndependentBlendEnable;
nativeDesc.AlphaToCoverageEnable = _desc.AlphaToCoverageEnable;
@@ -33,9 +35,11 @@ namespace GlitchyEngine.Renderer
nativeDesc.RenderTarget[i].RenderTargetWriteMask = _desc.RenderTarget[i].RenderTargetWriteMask;
}
HResult result = _context.nativeDevice.CreateBlendState(ref nativeDesc, &nativeBlendState);
HResult result = NativeDevice.CreateBlendState(ref nativeDesc, &nativeBlendState);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create blend state. Error({(int)result}): {result}");
}
}
}
#endif
@@ -1,27 +1,16 @@
#if GE_GRAPHICS_DX11
using System.Diagnostics;
using System;
using DirectX.D3D11;
using internal GlitchyEngine.Renderer;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
extension CPUAccessFlags
{
public static explicit operator DirectX.D3D11.CpuAccessFlags(Self cpuAccessFlags)
{
DirectX.D3D11.CpuAccessFlags flags = .None;
if(cpuAccessFlags.HasFlag(.Read))
flags |= .Read;
if(cpuAccessFlags.HasFlag(.Write))
flags |= .Write;
return flags;
}
}
extension BufferDescription
{
public static operator DirectX.D3D11.BufferDescription(Self desc)
@@ -71,6 +60,8 @@ namespace GlitchyEngine.Renderer
private Result<void> InternalCreateBuffer(void* data, uint32 byteLength, uint32 dstByteOffset)
{
Debug.Profiler.ProfileResourceFunction!();
nativeDescription = (.)_description;
uint8* byteData = (.)data;
@@ -79,11 +70,11 @@ namespace GlitchyEngine.Renderer
{
byteData = new uint8[nativeDescription.ByteWidth]*;
defer:: delete byteData;
Internal.MemCpy(byteData + dstByteOffset, data, byteLength);
Internal.MemCpy(byteData + dstByteOffset, data, Math.Min(nativeDescription.ByteWidth, byteLength));
}
SubresourceData srData = .(byteData, byteLength, 0);
var result = _context.nativeDevice.CreateBuffer(ref nativeDescription, &srData, &nativeBuffer);
var result = NativeDevice.CreateBuffer(ref nativeDescription, &srData, &nativeBuffer);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create buffer. Message({(int)result}):{result}");
@@ -95,6 +86,8 @@ namespace GlitchyEngine.Renderer
protected override Result<void> PlatformSetData(void* data, uint32 byteLength, uint32 dstByteOffset, GlitchyEngine.Renderer.MapType mapType)
{
Debug.Profiler.ProfileResourceFunction!();
if(nativeBuffer == null)
{
// We can pass the data while creating the buffer, so we can return here.
@@ -107,14 +100,14 @@ namespace GlitchyEngine.Renderer
{
case .Default:
Box dataBox = .(dstByteOffset, 0, 0, dstByteOffset + byteLength, 1, 1);
_context.nativeContext.UpdateSubresource(nativeBuffer, 0, &dataBox, data, byteLength, byteLength);
NativeContext.UpdateSubresource(nativeBuffer, 0, &dataBox, data, byteLength, byteLength);
case .Dynamic:
Debug.Assert(mapType.CanWrite, "The map type has to have write access.");
// Todo: DoNotWaitFlag
MappedSubresource map = ?;
_context.nativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map);
NativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map);
Internal.MemCpy(((uint8*)map.Data) + dstByteOffset, data, byteLength);
_context.nativeContext.Unmap(nativeBuffer, 0);
NativeContext.Unmap(nativeBuffer, 0);
case .Immutable:
Log.EngineLogger.Error("Can't set the data of an immutable resource.");
return .Err;
@@ -127,3 +120,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -1,3 +1,5 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using internal GlitchyEngine.Renderer;
@@ -10,6 +12,8 @@ namespace GlitchyEngine.Renderer
internal void PlatformFetchNativeBuffers()
{
Debug.Profiler.ProfileRendererFunction!();
for(let buffer in _buffers)
{
nativeBuffers[buffer.Index] = buffer.Buffer.nativeBuffer;
@@ -17,3 +21,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -0,0 +1,22 @@
#if GE_GRAPHICS_DX11
namespace GlitchyEngine.Renderer
{
extension CPUAccessFlags
{
public static explicit operator DirectX.D3D11.CpuAccessFlags(Self cpuAccessFlags)
{
DirectX.D3D11.CpuAccessFlags flags = .None;
if(cpuAccessFlags.HasFlag(.Read))
flags |= .Read;
if(cpuAccessFlags.HasFlag(.Write))
flags |= .Write;
return flags;
}
}
}
#endif
@@ -1,3 +1,5 @@
#if GE_GRAPHICS_DX11
using DirectX.Common;
using DirectX.D3D11Shader;
using System;
@@ -10,6 +12,8 @@ namespace GlitchyEngine.Renderer
{
internal this(ConstantBuffer constantBuffer, ID3D11ShaderReflectionVariable* variableReflection)
{
Debug.Profiler.ProfileResourceFunction!();
_constantBuffer = constantBuffer;
HResult result = variableReflection.GetDescription(let variableDescription);
@@ -27,14 +31,19 @@ namespace GlitchyEngine.Renderer
switch(shaderTypeDescription.Type)
{
case .Bool:
_type = .Bool;
case .Float:
_type = .Float;
case .Int:
_type = .Int;
default:
Log.EngineLogger.Assert(false, scope $"Unhandled shader variable type: {shaderTypeDescription.Type}");
}
_columns = shaderTypeDescription.Columns;
_rows = shaderTypeDescription.Rows;
_elements = shaderTypeDescription.Elements;
SetRawData(variableDescription.DefaultValue);
}
@@ -42,8 +51,10 @@ namespace GlitchyEngine.Renderer
extension ConstantBuffer
{
internal this(GraphicsContext context, ID3D11ShaderReflectionConstantBuffer* bufferReflection) : base(context)
internal this(ID3D11ShaderReflectionConstantBuffer* bufferReflection)
{
Debug.Profiler.ProfileResourceFunction!();
Reflect(bufferReflection);
ConstructBuffer();
@@ -53,6 +64,8 @@ namespace GlitchyEngine.Renderer
private void Reflect(ID3D11ShaderReflectionConstantBuffer* bufferReflection)
{
Debug.Profiler.ProfileResourceFunction!();
HResult result = bufferReflection.GetDescription(let bufferDescription);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to get buffer description. Error({(int)result}): {result}");
@@ -75,3 +88,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -0,0 +1,64 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
internal typealias D3DDepthStencilStateDesc = DirectX.D3D11.DepthStencilStateDescription;
internal typealias DxDSSDesc = DirectX.D3D11.DepthStencilStateDescription;
internal typealias GEDSSDesc = GlitchyEngine.Renderer.DepthStencilStateDescription;
internal typealias DxDepthStencilOpDesc = DirectX.D3D11.DepthStencilOperationDescription;
extension StencilOperation
{
public static explicit operator DirectX.D3D11.StencilOperation(Self self)
{
return (.)self.Underlying;
}
}
extension DepthStencilOperationDescription
{
public static explicit operator DxDepthStencilOpDesc(Self desc)
{
return DxDepthStencilOpDesc((.)desc.StencilFailOperation, (.)desc.StencilDepthFailOperation,
(.)desc.StencilPassOperation, (.)desc.StencilFunction);
}
}
extension DepthStencilStateDescription
{
public static explicit operator DxDSSDesc(Self desc)
{
return DxDSSDesc(desc.DepthEnabled, desc.WriteDepth ? .All : .Zero, (.)desc.DepthFunction, desc.StencilEnabled,
desc.StencilReadMask, desc.StencilWriteMask, (.)desc.FrontFace, (.)desc.BackFace);
}
}
extension DepthStencilState
{
internal DxDSSDesc nativeDescription;
internal ID3D11DepthStencilState* nativeDepthStencilState ~ _?.Release();
public this(GEDSSDesc description)
{
Debug.Profiler.ProfileResourceFunction!();
_description = description;
nativeDescription = (.)description;
var result = NativeDevice.CreateDepthStencilState(ref nativeDescription, &nativeDepthStencilState);
if (result.Failed)
{
Log.EngineLogger.Error($"Failed to create depth stencil state. Message({(int)result}): {result}");
}
}
}
}
#endif
@@ -1,16 +1,45 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using internal GlitchyEngine.Renderer;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
extension DepthStencilFormat
{
public static explicit operator Format(Self self)
{
switch(self)
{
case .D16_UNorm:
return .D16_UNorm;
case .D24_UNorm_S8_UInt:
return .D24_UNorm_S8_UInt;
case .D32_Float:
return .D32_Float;
case .D32_Float_S8X24_UInt:
return .D32_Float_S8X24_UInt;
case .None:
Log.EngineLogger.Assert(false, "None is not a valid depth stencil format");
return .Unknown;
}
}
}
// TODO: Depth stencil target is technically just a RenderTarget
extension DepthStencilTarget
{
internal ID3D11DepthStencilView* nativeView ~ _?.Release();
protected override void PlatformCreate()
{
Debug.Profiler.ProfileResourceFunction!();
Texture2DDescription desc = .();
desc.Format = .D32_Float;
desc.Format = (.)_format;
desc.ArraySize = 1;
desc.BindFlags = .DepthStencil;
desc.Width = _width;
@@ -18,21 +47,13 @@ namespace GlitchyEngine.Renderer
desc.SampleDesc = .(1, 0);
ID3D11Texture2D* tex = ?;
_context.nativeDevice.CreateTexture2D(ref desc, null, &tex);
NativeDevice.CreateTexture2D(ref desc, null, &tex);
_context.nativeDevice.CreateDepthStencilView(tex, null, &nativeView);
NativeDevice.CreateDepthStencilView(tex, null, &nativeView);
tex.Release();
}
public override void Bind()
{
_context.SetDepthStencilTarget(this);
}
public override void Clear(float depthValue, uint8 stencilValue, DepthStencilClearFlag clearFlags)
{
_context.nativeContext.ClearDepthStencilView(nativeView, (.)clearFlags, depthValue, stencilValue);
}
}
}
#endif
@@ -1,3 +1,5 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX;
using DirectX.D3D11;
@@ -10,9 +12,11 @@ namespace GlitchyEngine.Renderer
{
protected override void Compile(String vsPath, String vsEntry, String psPath, String psEntry)
{
Debug.Profiler.ProfileResourceFunction!();
// Todo: macros
VertexShader = new VertexShader(_context, vsPath, vsEntry);
PixelShader = new PixelShader(_context, psPath, psEntry);
VertexShader = new VertexShader(vsPath, vsEntry);
PixelShader = new PixelShader(psPath, psEntry);
Reflect();
}
@@ -23,3 +27,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -1,10 +1,14 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
using internal GlitchyEngine.Renderer;
extension GeometryBinding
{
internal ID3D11Buffer*[DirectX.D3D11.D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT] nativeBuffers;
@@ -27,6 +31,8 @@ namespace GlitchyEngine.Renderer
protected override void PlatformSetVertexBuffer(VertexBufferBinding binding, uint32 slot)
{
Debug.Profiler.ProfileResourceFunction!();
Log.EngineLogger.Assert(slot < nativeBuffers.Count, "The buffer slot has to be in the range from 0 to 31.");
VertexBuffer vertexBuffer = binding.Buffer;
@@ -40,8 +46,8 @@ namespace GlitchyEngine.Renderer
nativeBuffers[slot] = vertexBuffer.nativeBuffer..AddRef();
bufferStrides[slot] = vertexBuffer.Binding.Stride;
bufferOffsets[slot] = vertexBuffer.Binding.Offset;
bufferStrides[slot] = binding.Stride;
bufferOffsets[slot] = binding.Offset;
}
else
{
@@ -55,12 +61,16 @@ namespace GlitchyEngine.Renderer
protected override void PlatformSetVertexLayout(VertexLayout vertexLayout)
{
Debug.Profiler.ProfileResourceFunction!();
nativeVertexLayout?.Release();
nativeVertexLayout = vertexLayout?.nativeLayout..AddRef();
}
protected override void PlatformSetIndexBuffer(IndexBuffer indexBuffer)
{
Debug.Profiler.ProfileResourceFunction!();
Log.EngineLogger.Assert(indexBuffer.nativeDescription.BindFlags.HasFlag(.IndexBuffer),
scope $"Buffer ({indexBuffer.nativeBuffer.GetDebugName(.. scope .())}) must have IndexBuffer-flag set to be bound as an index buffer.");
@@ -73,32 +83,33 @@ namespace GlitchyEngine.Renderer
}
public override void Bind(GraphicsContext context = null)
public override void Bind()
{
var context;
context ??= _context;
Debug.Profiler.ProfileRendererFunction!();
context.nativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nativeBuffers, &bufferStrides, &bufferOffsets);
context.nativeContext.InputAssembler.SetInputLayout(_vertexLayout.nativeLayout);
context.nativeContext.InputAssembler.SetPrimitiveTopology((.)_primitiveTopology);
NativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nativeBuffers, &bufferStrides, &bufferOffsets);
NativeContext.InputAssembler.SetInputLayout(_vertexLayout.nativeLayout);
NativeContext.InputAssembler.SetPrimitiveTopology((.)_primitiveTopology);
context.nativeContext.InputAssembler.SetIndexBuffer(_indexBuffer.nativeBuffer, _indexBuffer.Format == .Index32Bit ? .R32_UInt : .R16_UInt, _indexByteOffset);
if(_indexBuffer != null)
NativeContext.InputAssembler.SetIndexBuffer(_indexBuffer.nativeBuffer, _indexBuffer.Format == .Index32Bit ? .R32_UInt : .R16_UInt, _indexByteOffset);
}
public void Unbind(GraphicsContext context = null)
public override void Unbind()
{
var context;
context ??= _context;
Debug.Profiler.ProfileRendererFunction!();
ID3D11Buffer*[nativeBuffers.Count] nullBuffers = .();
uint32[nativeBuffers.Count] zeroStrides = .();
uint32[nativeBuffers.Count] zeroOffsets = .();
context.nativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nullBuffers, &zeroStrides, &zeroOffsets);
context.nativeContext.InputAssembler.SetInputLayout(null);
context.nativeContext.InputAssembler.SetPrimitiveTopology(.Undefined);
NativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nullBuffers, &zeroStrides, &zeroOffsets);
NativeContext.InputAssembler.SetInputLayout(null);
NativeContext.InputAssembler.SetPrimitiveTopology(.Undefined);
context.nativeContext.InputAssembler.SetIndexBuffer(null, .R16_UNorm, 0);
NativeContext.InputAssembler.SetIndexBuffer(null, .R16_UNorm, 0);
}
}
}
#endif
@@ -1,3 +1,5 @@
#if GE_GRAPHICS_DX11
using System;
using System.Diagnostics;
using DirectX.Common;
@@ -6,9 +8,12 @@ using DirectX.D3D11.SDKLayers;
using DirectX.DXGI.DXGI1_2;
using GlitchyEngine.Renderer;
using GlitchyEngine.Math;
using DirectX.D3D11.DeviceContextStages;
using internal GlitchyEngine.Renderer;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
/// DirectX 11 specific implementation of the GraphicsContext
@@ -18,10 +23,12 @@ namespace GlitchyEngine.Renderer
internal Windows.HWnd nativeWindowHandle;
internal ID3D11Device* nativeDevice;
internal ID3D11DeviceContext* nativeContext;
/// Gets whether or not the DX11 device is initialized.
//internal bool IsDx11Initialized => nativeDevice != null;
//internal ID3D11Device* nativeDevice => NativeDevice;
//internal ID3D11DeviceContext* nativeContext => NativeContext;
private ID3D11Debug* _debugDevice;
//internal ID3D11Debug* debugDevice => DebugDevice;
public override SwapChain SwapChain => _swapChain;
@@ -31,24 +38,29 @@ namespace GlitchyEngine.Renderer
public this(Windows.HWnd windowHandle)
{
Debug.Profiler.ProfileFunction!();
nativeWindowHandle = windowHandle;
_swapChain = new SwapChain(this);
s_GraphicsContext = this;
}
public ~this()
{
Debug.Profiler.ProfileFunction!();
delete _swapChain;
nativeDevice?.Release();
nativeContext?.Release();
_debugDevice.ReportLiveDeviceObjects(.Detail);
_debugDevice?.Release();
Dx11Release();
}
public override void Init()
{
InitDevice();
Debug.Profiler.ProfileFunction!();
Dx11Init();
SwapChain.Init();
}
@@ -56,11 +68,11 @@ namespace GlitchyEngine.Renderer
/**
* Initializes the Device and ImmediateContext.
*/
/*
void InitDevice()
{
nativeDevice?.Release();
nativeContext?.Release();
if(!IsDx11Initialized)
{
Log.EngineLogger.Trace("Creating D3D11 Device and Context...");
DeviceCreationFlags deviceFlags = .None;
@@ -72,10 +84,10 @@ namespace GlitchyEngine.Renderer
FeatureLevel deviceLevel = ?;
var deviceResult = D3D11.CreateDevice(null, .Hardware, 0, deviceFlags, levels, &nativeDevice, &deviceLevel, &nativeContext);
Debug.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message(0x{(int32)deviceResult}): {deviceResult}");
Log.EngineLogger.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message({(int32)deviceResult}): {deviceResult}");
#if DEBUG
if(nativeDevice.QueryInterface<ID3D11Debug>(out _debugDevice).Succeeded)
if(nativeDevice.QueryInterface<ID3D11Debug>(out debugDevice).Succeeded)
{
ID3D11InfoQueue* infoQueue;
if(nativeDevice.QueryInterface<ID3D11InfoQueue>(out infoQueue).Succeeded)
@@ -90,104 +102,149 @@ namespace GlitchyEngine.Renderer
Log.EngineLogger.Trace($"D3D11 Device and Context created (Feature level: {deviceLevel})");
}
else
{
// We created a second GraphicsContext (for some reason?) just increment references.
nativeDevice.AddRef();
nativeContext.AddRef();
debugDevice.AddRef();
}
}
*/
private ID3D11DepthStencilView* _depthStencilTarget;
private ID3D11RenderTargetView*[MaxRTVCount] _renderTargets;
internal void SetDepthStencilTarget(DepthStencilTarget target)
public override void SetDepthStencilTarget(DepthStencilTarget target)
{
_depthStencilTarget = target?.nativeView;
}
public override void SetRenderTarget(RenderTarget renderTarget, int slot = 0)
public override void SetRenderTarget(RenderTarget2D renderTarget, int slot, bool setDepthTarget)
{
if(renderTarget == null)
_renderTargets[slot] = (renderTarget ?? _swapChain.BackBuffer)._nativeRenderTargetView;
if(slot == 0 && setDepthTarget)
{
_renderTargets[slot] = _swapChain.nativeBackBufferTarget;
}
else
{
Runtime.NotImplemented();
SetDepthStencilTarget((renderTarget ?? _swapChain.BackBuffer).DepthStencilTarget);
}
}
public override void BindRenderTargets()
{
nativeContext.OutputMerger.SetRenderTargets(MaxRTVCount, &_renderTargets, _depthStencilTarget);
NativeContext.OutputMerger.SetRenderTargets(MaxRTVCount, &_renderTargets, _depthStencilTarget);
}
public override void ClearRenderTarget(RenderTarget renderTarget, ColorRGBA color)
public override void ClearRenderTarget(RenderTarget2D renderTarget, ColorRGBA color)
{
if(renderTarget == null)
{
nativeContext.ClearRenderTargetView(_swapChain.nativeBackBufferTarget, color);
}
else
{
Runtime.NotImplemented();
}
NativeContext.ClearRenderTargetView((renderTarget ?? _swapChain.BackBuffer)._nativeRenderTargetView, color);
}
public override void SetVertexBuffer(uint32 slot, Buffer buffer, uint32 stride, uint32 offset = 0)
{
// make stride and offset mutable so that we can take their pointers.
var stride, offset;
nativeContext.InputAssembler.SetVertexBuffers(slot, 1, &buffer.nativeBuffer, &stride, &offset);
NativeContext.InputAssembler.SetVertexBuffers(slot, 1, &buffer.nativeBuffer, &stride, &offset);
}
public override void Draw(uint32 vertexCount, uint32 startVertexIndex = 0)
{
nativeContext.Draw(vertexCount, startVertexIndex);
NativeContext.Draw(vertexCount, startVertexIndex);
}
public override void DrawIndexed(uint32 indexCount, uint32 startIndexLocation = 0, int32 vertexOffset = 0)
{
nativeContext.DrawIndexed(indexCount, startIndexLocation, vertexOffset);
NativeContext.DrawIndexed(indexCount, startIndexLocation, vertexOffset);
}
public override void SetIndexBuffer(Buffer buffer, IndexFormat indexFormat = .Index16Bit, uint32 byteOffset = 0)
{
nativeContext.InputAssembler.SetIndexBuffer(buffer.nativeBuffer, indexFormat == .Index32Bit ? .R32_UInt : .R16_UInt, byteOffset);
}
protected override void SetRasterizerStateImpl()
{
nativeContext.Rasterizer.SetState(_currentRasterizerState.nativeRasterizerState);
NativeContext.InputAssembler.SetIndexBuffer(buffer.nativeBuffer, indexFormat == .Index32Bit ? .R32_UInt : .R16_UInt, byteOffset);
}
public override void SetViewports(uint32 viewportsCount, GlitchyEngine.Renderer.Viewport* viewports)
{
nativeContext.Rasterizer.SetViewports(viewportsCount, (.)viewports);
NativeContext.Rasterizer.SetViewports(viewportsCount, (.)viewports);
}
public override void SetVertexLayout(VertexLayout vertexLayout)
{
nativeContext.InputAssembler.SetInputLayout(vertexLayout.nativeLayout);
NativeContext.InputAssembler.SetInputLayout(vertexLayout.nativeLayout);
}
public override void SetPrimitiveTopology(GlitchyEngine.Renderer.PrimitiveTopology primitiveTopology)
{
nativeContext.InputAssembler.SetPrimitiveTopology((DirectX.Common.PrimitiveTopology)primitiveTopology);
NativeContext.InputAssembler.SetPrimitiveTopology((DirectX.Common.PrimitiveTopology)primitiveTopology);
}
/**
* Binds the given shader to the corresponding shader stage.
* @param shader The shader that will be bound to the graphics context.
*/
private void BindShaderToStage<TShader>(TShader shader) where TShader : Shader
{
Debug.Profiler.ProfileRendererFunction!();
uint32 _firstTexture = D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
uint32 _textureCount = 0;
ID3D11ShaderResourceView*[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] _textures = .();
ID3D11SamplerState*[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] _samplers = .();
for(let entry in shader.Textures)
{
_textures[entry.Index] = entry.Texture?.nativeResourceView;
_samplers[entry.Index] = entry.Texture?.SamplerState?.nativeSamplerState;
if(entry.Index >= _textureCount)
_textureCount = entry.Index + 1;
if(entry.Index < _firstTexture)
_firstTexture = entry.Index;
}
if(_textureCount > 0)
{
switch(typeof(TShader))
{
// TODO: Add remaining shader stages
case typeof(PixelShader):
// TODO: bind uavs
NativeContext.PixelShader.SetShaderResources(_firstTexture, _textureCount, &_textures[_firstTexture]);
NativeContext.PixelShader.SetSamplers(_firstTexture, _textureCount, &_samplers[_firstTexture]);
case typeof(VertexShader):
NativeContext.VertexShader.SetShaderResources(_firstTexture, _textureCount, &_textures[_firstTexture]);
NativeContext.VertexShader.SetSamplers(_firstTexture, _textureCount, &_samplers[_firstTexture]);
default:
Runtime.FatalError(scope $"Shader stage \"{typeof(TShader)}\" not implemented.");
}
}
shader.Buffers.PlatformFetchNativeBuffers();
switch(typeof(TShader))
{
// TODO: Add remaining shader stages
case typeof(PixelShader):
NativeContext.PixelShader.SetConstantBuffers(0, shader.Buffers.nativeBuffers.Count, &shader.Buffers.nativeBuffers);
[IgnoreErrors]{ NativeContext.PixelShader.SetShader(((PixelShader)shader).nativeShader); }
case typeof(VertexShader):
NativeContext.VertexShader.SetConstantBuffers(0, shader.Buffers.nativeBuffers.Count, &shader.Buffers.nativeBuffers);
[IgnoreErrors]{ NativeContext.VertexShader.SetShader(((VertexShader)shader).nativeShader); }
default:
Runtime.FatalError(scope $"Shader stage \"{typeof(TShader)}\" not implemented.");
}
}
public override void SetVertexShader(VertexShader vertexShader)
{
//Todo: nativeContext.VertexShader.SetSamplers();
vertexShader.Buffers.PlatformFetchNativeBuffers();
nativeContext.VertexShader.SetConstantBuffers(0, vertexShader.Buffers.nativeBuffers.Count, &vertexShader.Buffers.nativeBuffers);
//Todo: nativeContext.VertexShader.SetShaderResources();
nativeContext.VertexShader.SetShader(vertexShader.nativeShader);
BindShaderToStage(vertexShader);
}
public override void SetPixelShader(PixelShader pixelShader)
{
//Todo: nativeContext.PixelShader.SetSamplers();
pixelShader.Buffers.PlatformFetchNativeBuffers();
nativeContext.PixelShader.SetConstantBuffers(0, pixelShader.Buffers.nativeBuffers.Count, &pixelShader.Buffers.nativeBuffers);
//Todo: nativeContext.PixelShader.SetShaderResources();
nativeContext.PixelShader.SetShader(pixelShader.nativeShader);
BindShaderToStage(pixelShader);
}
}
}
#endif
@@ -1,8 +1,12 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX.D3D11;
using DirectX.D3DCompiler;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
@@ -12,15 +16,23 @@ namespace GlitchyEngine.Renderer
public override void CompileFromSource(String code, String entryPoint, ShaderDefine[] macros = null)
{
Debug.Profiler.ProfileRendererFunction!();
Shader.PlattformCompileShaderFromSource(code, macros, entryPoint, "ps_5_0", DefaultCompileFlags, out nativeCode);
var result = _context.nativeDevice.CreatePixelShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
{
Debug.Profiler.ProfileResourceScope!("CreateNativePixelShader");
var result = NativeDevice.CreatePixelShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create pixel shader: Message ({(int)result}): {result}");
}
}
Reflect(nativeCode);
}
}
}
#endif
@@ -1,6 +1,10 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
@@ -48,12 +52,14 @@ namespace GlitchyEngine.Renderer
internal DirectX.D3D11.RasterizerStateDescription nativeDescription;
internal ID3D11RasterizerState* nativeRasterizerState ~ _?.Release();
public override this(GraphicsContext context, GlitchyEngine.Renderer.RasterizerStateDescription description) : this(context)
public override this(GlitchyEngine.Renderer.RasterizerStateDescription description)
{
Debug.Profiler.ProfileRendererFunction!();
_description = description;
nativeDescription = (.)_description;
var result = context.nativeDevice.CreateRasterizerState(ref nativeDescription, &nativeRasterizerState);
var result = NativeDevice.CreateRasterizerState(ref nativeDescription, &nativeRasterizerState);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create rasterizer state. Message({(int)result}): {result}");
@@ -61,3 +67,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -0,0 +1,125 @@
#if GE_GRAPHICS_DX11
using DirectX.Common;
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using DirectX.DXGI.DXGI1_2;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
using internal GlitchyEngine.Renderer;
extension RenderTarget2D
{
protected internal ID3D11Texture2D* _nativeTexture ~ _?.Release();
protected internal ID3D11ShaderResourceView* _nativeResourceView ~ _?.Release();
protected internal ID3D11RenderTargetView* _nativeRenderTargetView ~ _?.Release();
private void ReleaseAndNullify()
{
Debug.Profiler.ProfileResourceFunction!();
ReleaseAndNullify!(_nativeTexture);
ReleaseAndNullify!(_nativeResourceView);
ReleaseAndNullify!(_nativeRenderTargetView);
ReleaseRefAndNullify!(_depthStenilTarget);
}
public override void Resize(uint32 width, uint32 height)
{
Debug.Profiler.ProfileResourceFunction!();
ReleaseAndNullify();
_description.Width = width;
_description.Height = height;
ApplyChanges();
}
protected override void PlatformApplyChanges()
{
Debug.Profiler.ProfileResourceFunction!();
ReleaseAndNullify();
if(_description.IsSwapchainTarget)
{
// TODO: if the engine supports multiple windows it has to support multiple swap chains.
// kinda dirty...
var context = GraphicsContext.Get();
context.SwapChain.GetBackbuffer(out _nativeTexture);
CreateViews();
}
else
{
PlatformCreateTexture();
}
if(_description.DepthStencilFormat != .None)
{
_depthStenilTarget = new DepthStencilTarget(_description.Width, _description.Height, _description.DepthStencilFormat);
}
}
private void PlatformCreateTexture()
{
Debug.Profiler.ProfileResourceFunction!();
Texture2DDescription desc = .()
{
Width = _description.Width,
Height = _description.Height,
ArraySize = _description.ArraySize,
MipLevels = _description.MipLevels,
Format = _description.PixelFormat,
// Always bindable as ShaderResource and RenderTarget
BindFlags = .ShaderResource | .RenderTarget,
CpuAccessFlags = (.)_description.CpuAccess,
// 2D RenderTarget never has misc flags
MiscFlags = .None,
SampleDesc = .(_description.SampleCount, _description.SampleQuality),
// RenderTarget has always Default usage
Usage = .Default
};
var result = NativeDevice.CreateTexture2D(ref desc, null, &_nativeTexture);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create RenderTarget2D");
CreateViews();
}
private void CreateViews()
{
Debug.Profiler.ProfileResourceFunction!();
if(_description.IsSwapchainTarget)
{
var result = NativeDevice.CreateShaderResourceView(_nativeTexture, null, &_nativeResourceView);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create resource view");
// TODO: SRGB...
//RenderTargetViewDescription rtvDesc = .(_nativeTexture, .Texture2D, .R8G8B8A8_UNorm_SRGB);
//result = NativeDevice.CreateRenderTargetView(_nativeTexture, &rtvDesc, &_nativeRenderTargetView);
result = NativeDevice.CreateRenderTargetView(_nativeTexture, null, &_nativeRenderTargetView);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create render target view");
}
else
{
var result = NativeDevice.CreateShaderResourceView(_nativeTexture, null, &_nativeResourceView);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create resource view");
result = NativeDevice.CreateRenderTargetView(_nativeTexture, null, &_nativeRenderTargetView);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create render target view");
}
}
}
}
#endif
@@ -1,39 +1,141 @@
#if GE_GRAPHICS_DX11
using GlitchyEngine.Math;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
using internal GlitchyEngine.Renderer;
extension RendererAPI
{
private GraphicsContext _context;
private GraphicsContext _context ~ _?.ReleaseRef();
public GraphicsContext Context
{
get => _context;
set => _context = value;
set => SetReference!(_context, value);
}
public static override API Api => .D3D11;
public override void Init()
{
Debug.Profiler.ProfileFunction!();
}
public override void Clear(RenderTarget renderTarget, ColorRGBA clearColor)
private mixin RtOrBackbuffer(RenderTarget2D renderTarget)
{
_context.ClearRenderTarget(renderTarget, clearColor);
renderTarget ?? _context.SwapChain.BackBuffer
}
public override void Clear(DepthStencilTarget target, float depthValue, uint8 stencilValue, DepthStencilClearFlag clearFlags)
public override void Clear(RenderTarget2D renderTarget, ColorRGBA color)
{
_context.nativeContext.ClearDepthStencilView(target.nativeView, (.)clearFlags, depthValue, stencilValue);
Debug.Profiler.ProfileRendererFunction!();
NativeContext.ClearRenderTargetView(RtOrBackbuffer!(renderTarget)._nativeRenderTargetView, color);
}
public override void Clear(DepthStencilTarget target, ClearOptions clearOptions, float depth, uint8 stencil)
{
Debug.Profiler.ProfileRendererFunction!();
if(target == null)
return;
DirectX.D3D11.ClearFlag flags = default;
if(clearOptions.HasFlag(.Depth))
{
flags |= .Depth;
}
if(clearOptions.HasFlag(.Stencil))
{
flags |= .Stencil;
}
NativeContext.ClearDepthStencilView(target.nativeView, flags, depth, stencil);
}
public override void SetRenderTarget(RenderTarget2D renderTarget, int slot, bool setDepthBuffer)
{
Debug.Profiler.ProfileRendererFunction!();
_context.SetRenderTarget(renderTarget, slot, setDepthBuffer);
}
public override void SetDepthStencilTarget(DepthStencilTarget target)
{
Debug.Profiler.ProfileRendererFunction!();
_context.SetDepthStencilTarget(target);
}
public override void BindRenderTargets()
{
Debug.Profiler.ProfileRendererFunction!();
_context.BindRenderTargets();
}
private RasterizerState _currentRasterizerState ~ _?.ReleaseRef();
public override void SetRasterizerState(RasterizerState rasterizerState)
{
Debug.Profiler.ProfileRendererFunction!();
SetReference!(_currentRasterizerState, rasterizerState);
NativeContext.Rasterizer.SetState(_currentRasterizerState.nativeRasterizerState);
}
private BlendState _currentBlendState ~ _?.ReleaseRef();
public override void SetBlendState(BlendState blendState, ColorRGBA blendFactor)
{
Debug.Profiler.ProfileRendererFunction!();
SetReference!(_currentBlendState, blendState);
NativeContext.OutputMerger.SetBlendState(_currentBlendState.nativeBlendState, blendFactor);
}
private DepthStencilState _currentDepthStencilState ~ _?.ReleaseRef();
public override void SetDepthStencilState(DepthStencilState depthStencilState, uint8 stencilReference)
{
Debug.Profiler.ProfileRendererFunction!();
SetReference!(_currentDepthStencilState, depthStencilState);
NativeContext.OutputMerger.SetDepthStencilState(_currentDepthStencilState.nativeDepthStencilState, stencilReference);
}
public override void DrawIndexed(GeometryBinding geometry)
{
Debug.Profiler.ProfileRendererFunction!();
if(geometry.IsIndexed)
_context.DrawIndexed(geometry.IndexCount, geometry.IndexByteOffset, 0);
else
_context.Draw(geometry.VertexCount, 0);
}
public override void DrawIndexedInstanced(GeometryBinding geometry)
{
Debug.Profiler.ProfileRendererFunction!();
NativeContext.DrawIndexedInstanced(geometry.IndexCount, geometry.InstanceCount, geometry.IndexByteOffset, 0, 0);
}
public override void SetViewport(Viewport viewport)
{
Debug.Profiler.ProfileRendererFunction!();
_context.SetViewport(viewport);
}
}
}
#endif
@@ -1,5 +1,11 @@
using DirectX.D3D11;
#if GE_GRAPHICS_DX11
using DirectX.Common;
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace DirectX.D3D11
{
@@ -73,8 +79,6 @@ namespace DirectX.D3D11
}
}
using internal GlitchyEngine.Renderer;
namespace GlitchyEngine.Renderer
{
extension SamplerStateDescription
@@ -107,17 +111,21 @@ namespace GlitchyEngine.Renderer
protected override void PlatformCreateSamplerState()
{
Debug.Profiler.ProfileResourceFunction!();
_desc.ToNative(var nativeDesc);
HResult result = _context.nativeDevice.CreateSamplerState(ref nativeDesc, &nativeSamplerState);
HResult result = NativeDevice.CreateSamplerState(ref nativeDesc, &nativeSamplerState);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create SamplerState. Error({(int)result}): {result}");
}
public override void Bind(uint32 slot)
{
_context.nativeContext.VertexShader.SetSamplers(slot, 1, &nativeSamplerState);
_context.nativeContext.PixelShader.SetSamplers(slot, 1, &nativeSamplerState);
NativeContext.VertexShader.SetSamplers(slot, 1, &nativeSamplerState);
NativeContext.PixelShader.SetSamplers(slot, 1, &nativeSamplerState);
}
}
}
#endif
@@ -1,3 +1,5 @@
#if GE_GRAPHICS_DX11
using System;
using GlitchyEngine.Renderer;
using DirectX.Common;
@@ -16,7 +18,7 @@ namespace GlitchyEngine.Renderer
*/
internal ID3DBlob* nativeCode ~ _?.Release();
protected const ShaderCompileFlags DefaultCompileFlags =
protected const ShaderCompileFlags DefaultCompileFlags = .EnableStrictness |
#if DEBUG
.Debug;
#else
@@ -25,6 +27,8 @@ namespace GlitchyEngine.Renderer
internal static void PlattformCompileShaderFromSource(String code, ShaderDefine[] macros, String entryPoint, String target, ShaderCompileFlags compileFlags, out ID3DBlob* shaderBlob)
{
Debug.Profiler.ProfileResourceFunction!();
ShaderMacro* nativeMacros = macros == null ? null : new:ScopedAlloc! ShaderMacro[macros.Count]*;
for(int i < macros?.Count ?? 0)
@@ -49,65 +53,60 @@ namespace GlitchyEngine.Renderer
protected internal void Reflect(ID3DBlob* shaderCode)
{
Debug.Profiler.ProfileResourceFunction!();
ID3D11ShaderReflection* reflection = null;
{
Debug.Profiler.ProfileResourceScope!("D3DReflect");
var result = D3DCompiler.D3DReflect(shaderCode.GetBufferPointer(), shaderCode.GetBufferSize(), &reflection);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to reflect shader: Message ({(int)result}): {result}");
}
}
reflection.GetDescription(let desc);
uint32 cBufferCount = desc.ConstantBuffers;
for(uint32 i < cBufferCount)
uint32 resourceCount = desc.BoundResources;
for (uint32 i < resourceCount)
{
reflection.GetResourceBindingDescription(i, let bindDesc);
var res = reflection.GetResourceBindingDescription(i, let bindDesc);
var bufferReflection = reflection.GetConstantBufferByIndex(i);
if (res.Failed)
{
Log.EngineLogger.Error($"Error({(int)res}) {res}: Failed to get resource binding desc for resource {i}");
continue;
}
switch(bindDesc.Type)
{
case .ConstantBuffer:
var bufferReflection = reflection.GetConstantBufferByName(bindDesc.Name);
bufferReflection.GetDescription(let bufferDesc);
// ConstantBuffer
if(bufferDesc.Type == .D3D11_CT_CBUFFER)
{
let buffer = new ConstantBuffer(_context, bufferReflection);
let buffer = new ConstantBuffer(bufferReflection);
_buffers.Add(bindDesc.BindPoint, buffer.Name, buffer);
buffer.ReleaseRef();
}
/*
bufferReflection.GetDescription(let bufferDesc);
// ConstantBuffer
if(bufferDesc.Type == .D3D11_CT_CBUFFER)
{
GlitchyEngine.Renderer.BufferDescription cBufferDesc = .(bufferDesc.Size, .Constant, .Dynamic, .Write);
let buffer = new Buffer(_context, cBufferDesc);
_buffers.Add(bindDesc.BindPoint, StringView(bufferDesc.Name), buffer);//, true
buffer.ReleaseRef();
// Buffer for default values
uint8* rawData = new:ScopedAlloc! uint8[bufferDesc.Size]*;
// Get default values
for(uint32 variable < bufferDesc.Variables)
{
let variableReflection = bufferReflection.GetVariableByIndex(variable);
variableReflection.GetDescription(let varDesc);
if(varDesc.DefaultValue != null)
Internal.MemCpy(rawData + varDesc.StartOffset, varDesc.DefaultValue, varDesc.Size);
case .Texture:
_textures.Add(scope String(bindDesc.Name), bindDesc.BindPoint, null);
case .Sampler:
// TODO: do we have to do something for samplers?
default:
Log.EngineLogger.Warning($"Unhandled shader resource type: \"{bindDesc.Type}\"");
}
}
buffer.SetData(Span<uint8>(rawData, bufferDesc.Size));
}
*/
}
reflection.Release();
}
}
}
#endif
@@ -1,56 +1,24 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using DirectX.DXGI.DXGI1_2;
using System.Diagnostics;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
public extension SwapChain
{
private GraphicsContext _context;
private bool _changed;
private uint32 _width, _height;
internal DirectX.DXGI.IDXGIDevice* nativeDxgiDevice;
internal IDXGISwapChain1* nativeSwapChain;
internal ID3D11RenderTargetView* nativeBackBufferTarget;
public override uint32 Width
{
get => _width;
set
{
if(_width == value)
return;
_width = value;
_changed = true;
}
}
public override uint32 Height
{
get => _height;
set
{
if(_height == value)
return;
_height = value;
_changed = true;
}
}
public override GraphicsContext Context => _context;
public this(GraphicsContext context)
{
Debug.Profiler.ProfileFunction!();
_context = context;
SetResolutionFromWindow();
@@ -58,9 +26,10 @@ namespace GlitchyEngine.Renderer
public ~this()
{
Debug.Profiler.ProfileFunction!();
nativeDxgiDevice?.Release();
nativeSwapChain?.Release();
nativeBackBufferTarget?.Release();
}
void SetResolutionFromWindow()
@@ -73,11 +42,15 @@ namespace GlitchyEngine.Renderer
public override void Init()
{
Debug.Profiler.ProfileFunction!();
ApplyChanges();
}
public override void ApplyChanges()
{
Debug.Profiler.ProfileFunction!();
if(!_changed)
return;
@@ -89,6 +62,8 @@ namespace GlitchyEngine.Renderer
*/
public void UpdateSwapchain()
{
Debug.Profiler.ProfileFunction!();
Log.EngineLogger.Trace($"Updating swap chain ({_width}, {_height})");
uint32 backBufferCount = 2;
@@ -97,7 +72,8 @@ namespace GlitchyEngine.Renderer
if(nativeSwapChain != null)
{
nativeBackBufferTarget.Release();
//nativeBackBufferTarget.Release();
_backBuffer.ReleaseRef();
var resizeResult = nativeSwapChain.ResizeBuffers(backBufferCount, _width, _height, backBufferFormat, .None);
if(resizeResult.Failed)
@@ -107,7 +83,7 @@ namespace GlitchyEngine.Renderer
}
else
{
_context.nativeDevice.QueryInterface(out nativeDxgiDevice);
NativeDevice.QueryInterface(out nativeDxgiDevice);
nativeDxgiDevice.GetAdapter(let adapter);
adapter.GetParent<DirectX.DXGI.IDXGIFactory2>(let factory);
@@ -118,14 +94,14 @@ namespace GlitchyEngine.Renderer
swDesc.Height = _height;
swDesc.Format = backBufferFormat;
swDesc.SampleDescription = .(1, 0);
swDesc.BufferUsage = .RenderTargetOutput;
swDesc.BufferUsage = .RenderTargetOutput | .ShaderInput;
swDesc.BufferCount = backBufferCount;
swDesc.SwapEffect = .FlipDiscard;
SwapChainFullscreenDescription fsSwapChainDesc = .();
fsSwapChainDesc.Windowed = true;
var createResult = factory.CreateSwapChainForHwnd((.)_context.nativeDevice, _context.nativeWindowHandle, ref swDesc, &fsSwapChainDesc, null, &nativeSwapChain);
var createResult = factory.CreateSwapChainForHwnd((.)NativeDevice, _context.nativeWindowHandle, ref swDesc, &fsSwapChainDesc, null, &nativeSwapChain);
if(createResult.Failed)
{
Log.EngineLogger.Error($"Failed to create swap chain. Message({(int)createResult}):{createResult}");
@@ -134,19 +110,31 @@ namespace GlitchyEngine.Renderer
factory.Release();
}
nativeSwapChain.GetBuffer<ID3D11Texture2D>(0, let backBuffer);
RenderTarget2DDescription desc = .(backBufferFormat, _width, _height);
desc.DepthStencilFormat = _depthStencilFormat;
desc.IsSwapchainTarget = true;
RenderTargetViewDescription rtvDesc = .(backBuffer, .Texture2D, backBufferViewFormat);
_context.nativeDevice.CreateRenderTargetView(backBuffer, &rtvDesc, &nativeBackBufferTarget);
// Todo: perhaps just update the render target
_backBuffer = new RenderTarget2D(desc);
_backBufferViewport = GlitchyEngine.Renderer.Viewport(0, 0, _width, _height, 0.0f, 1.0f);
}
backBuffer.Release();
internal void GetBackbuffer(out ID3D11Texture2D* texture)
{
Debug.Profiler.ProfileResourceFunction!();
var result = nativeSwapChain.GetBuffer<ID3D11Texture2D>(0, out texture);
Log.EngineLogger.Assert(result.Succeeded, "Failed to get backbuffer.");
}
public override void Present()
{
Debug.Profiler.ProfileFunction!();
nativeSwapChain.Present(Application.Get().Window.IsVSync ? 1 : 0, .None);
}
}
}
#endif
@@ -1,22 +1,58 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX.D3D11;
using DirectX.Common;
using DirectXTK;
using GlitchyEngine.Math;
using System.Collections;
using internal GlitchyEngine.Renderer;
typealias NativeTex2DDesc = DirectX.D3D11.Texture2DDescription;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
extension Texture
{
protected internal ID3D11ShaderResourceView* nativeView ~ _?.Release();
protected internal ID3D11ShaderResourceView* nativeResourceView ~ _?.Release();
protected override void ImplBind(uint32 slot)
{
_context.nativeContext.VertexShader.SetShaderResources(slot, 1, &nativeView);
_context.nativeContext.PixelShader.SetShaderResources(slot, 1, &nativeView);
// TODO: textures don't bind themselves!
NativeContext.VertexShader.SetShaderResources(slot, 1, &nativeResourceView);
NativeContext.PixelShader.SetShaderResources(slot, 1, &nativeResourceView);
}
/** \brief Loads the texture from the specified path.
* @param path The path of the texture to load.
* @param texture The reference to the pointer that will hold the texture.
* @returns true if the texture was loaded successfully; false otherwise.
*/
protected bool LoadResourcePlatform<T>(StringView path, ref T* texture) where T : ID3D11Resource
{
Debug.Profiler.ProfileResourceFunction!();
((ID3D11Resource*)texture)?.Release();
nativeResourceView?.Release();
HResult loadResult = DDSTextureLoader.CreateDDSTextureFromFile(NativeDevice, path.ToScopedNativeWChar!(),
(.)&texture, &nativeResourceView);
if(loadResult.Failed)
{
Log.EngineLogger.Error($"Failed to load texture \"{path}\". Error({(int)loadResult}): {loadResult}");
ReleaseAndNullify!(texture);
ReleaseAndNullify!(nativeResourceView);
return false;
}
return true;
}
}
@@ -34,15 +70,16 @@ namespace GlitchyEngine.Renderer
// TODO: missing options
desc.SampleDesc = .(1, 0);
desc.Usage = .Immutable;
desc.Usage = (.)Usage;
desc.BindFlags = .ShaderResource;
desc.CpuAccessFlags = .None;
desc.CpuAccessFlags = (.)CpuAccess;
desc.MiscFlags = .None;
return desc;
}
public static implicit operator NativeTex2DDesc(Self desc) => desc.ToNative();
public static explicit operator NativeTex2DDesc(Self desc) => desc.ToNative();
}
extension Texture2D
@@ -57,21 +94,9 @@ namespace GlitchyEngine.Renderer
protected override void LoadTexturePlatform()
{
nativeTexture?.Release();
nativeView?.Release();
Debug.Profiler.ProfileResourceFunction!();
HResult loadResult = DDSTextureLoader.CreateDDSTextureFromFile(_context.nativeDevice, _path.ToScopedNativeWChar!(),
(.)&nativeTexture, &nativeView);
if(loadResult.Failed)
{
Log.EngineLogger.Error($"Failed to load texture \"{_path}\". Error({(int)loadResult}): {loadResult}");
nativeTexture?.Release();
nativeView?.Release();
// TODO: load fallback texture
}
LoadResourcePlatform(_path, ref nativeTexture);
let resType = nativeTexture.GetResourceType();
Log.EngineLogger.Assert(resType == .Texture2D, scope $"The texture \"{_path}\" is not a 2D texture (it is {resType}).");
@@ -79,22 +104,156 @@ namespace GlitchyEngine.Renderer
nativeTexture.GetDescription(out nativeDesc);
}
protected override void CreateTexturePlatform(Texture2DDesc desc, void* data, uint32 linePitch)
protected override void CreateTexturePlatform(Texture2DDesc desc, bool isRenderTarget, void* data, uint32 linePitch)
{
nativeTexture?.Release();
nativeView?.Release();
Debug.Profiler.ProfileResourceFunction!();
nativeDesc = desc;
SubresourceData resData = .(data, linePitch, 0);
PrepareTexturePlatform(desc, isRenderTarget);
InternalCreateTexture(data, linePitch, 0);
}
var result = _context.[Friend]nativeDevice.CreateTexture2D(ref nativeDesc, &resData, &nativeTexture);
private void InternalCreateTexture(void* data, uint32 linePitch, uint32 slicePitch)
{
Debug.Profiler.ProfileResourceFunction!();
SubresourceData resData = .(data, linePitch, slicePitch);
// If data is null, set subresourceData null
SubresourceData* resDataPtr = data == null ? null : &resData;
var result = NativeDevice.CreateTexture2D(ref nativeDesc, resDataPtr, &nativeTexture);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture 2D. Error ({result.Underlying}): {result}");
result = _context.[Friend]nativeDevice.CreateShaderResourceView(nativeTexture, null, &nativeView);
result = NativeDevice.CreateShaderResourceView(nativeTexture, null, &nativeResourceView);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture view. Error ({result.Underlying}): {result}");
}
protected override void PrepareTexturePlatform(Texture2DDesc desc, bool isRenderTarget)
{
Debug.Profiler.ProfileResourceFunction!();
nativeTexture?.Release();
nativeTexture = null;
nativeResourceView?.Release();
nativeResourceView = null;
nativeDesc = (NativeTex2DDesc)desc;
if(isRenderTarget)
nativeDesc.BindFlags |= .RenderTarget;
}
// TODO: Update Texture Arrays!
protected override System.Result<void> PlatformSetData(void* data, uint32 elementSize, uint32 destX,
uint32 destY, uint32 destWidth, uint32 destHeight, uint32 arraySlice, uint32 mipLevel, GlitchyEngine.Renderer.MapType mapType)
{
Debug.Profiler.ProfileResourceFunction!();
if(nativeTexture == null)
{
if(destX == 0 && destY == 0 && destWidth == nativeDesc.Width && destHeight == nativeDesc.Height)
{
// We can pass the data while creating the buffer, so we can return here.
var rowPitch = elementSize * destWidth;
return InternalCreateTexture(data, rowPitch, rowPitch * destHeight);
}
else
{
// If we don't set the entire texture we need to create the texture with unknown content and set it manually
Log.EngineLogger.Assert(nativeDesc.Usage != .Immutable, "The entire immutable texture has to be initialized.");
InternalCreateTexture(null, 0, 0);
}
}
//Log.EngineLogger.AssertDebug();
// TODO: Debug.Assert(dstByteOffset + byteLength <= _description.Size, "The destination offset and byte length are too long for the target buffer.");
uint32 subresourceIndex = D3D11.CalcSubresource(mipLevel, arraySlice, nativeDesc.MipLevels);
switch(nativeDesc.Usage)
{
case .Default:
Box dataBox = .(destX, destY, 0, destX + destWidth, destY + destHeight, 1);
var rowPitch = elementSize * destWidth;
NativeContext.UpdateSubresource(nativeTexture, subresourceIndex, &dataBox, data, rowPitch, rowPitch * destHeight);
case .Dynamic:
Runtime.NotImplemented();
/*
Log.EngineLogger.Assert(mapType.CanWrite, "The map type has to have write access.");
// Todo: DoNotWaitFlag
MappedSubresource map = ?;
_context.nativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map);
Internal.MemCpy(((uint8*)map.Data) + dstByteOffset, data, byteLength);
_context.nativeContext.Unmap(nativeBuffer, 0);
*/
case .Immutable:
Log.EngineLogger.Error("Can't set the data of an immutable resource.");
return .Err;
default:
Log.EngineLogger.Error($"Unknown resource usage: {nativeDesc.Usage}");
return .Err;
}
return .Ok;
}
public static override void CopySubresourceRegion(Texture2D source, Texture2D destination,
ResourceBox sourceBox = default, uint32 destX = 0, uint32 destY = 0,
uint32 srcArraySlice = 0, uint32 srcMipSlice = 0, uint32 destArraySlice = 0, uint32 destMipSlice = 0)
{
Debug.Profiler.ProfileResourceFunction!();
Log.EngineLogger.AssertDebug(source != destination || srcArraySlice != destArraySlice
|| srcMipSlice != destMipSlice, "Cannot copy from and to the same sub resource.");
Log.EngineLogger.AssertDebug(source.nativeDesc.Format == destination.nativeDesc.Format,
"Source and destination resources must have the same texel-format.");
var sourceBox;
if(sourceBox.Right == 0)
{
sourceBox.Right = source.Width;
sourceBox.Bottom = source.Height;
sourceBox.Back = 1;
}
// Make sure the destination was initialized
if(destination.nativeTexture == null)
destination.InternalCreateTexture(null, 0, 0);
NativeContext.CopySubresourceRegion(destination.nativeTexture,
D3D11.CalcSubresource(destMipSlice, destArraySlice, destination.MipLevels), destX, destY, 0,
source.nativeTexture, D3D11.CalcSubresource(srcMipSlice, srcArraySlice, source.MipLevels), (.)&sourceBox);
}
public override void CopyTo(Texture2D destination)
{
Debug.Profiler.ProfileResourceFunction!();
if(nativeTexture == null)
return;
Log.EngineLogger.AssertDebug(nativeDesc.Format == destination.nativeDesc.Format,
"Source and destination resources must have the same texel-format.");
// Make sure the destination was initialized
if(destination.nativeTexture == null)
destination.InternalCreateTexture(null, 0, 0);
uint32 arraySlices = Math.Min(ArraySize, destination.ArraySize);
uint32 mipSlices = Math.Min(MipLevels, destination.MipLevels);
for(uint32 arraySlice < arraySlices)
for(uint32 mipSlice < mipSlices)
{
Box sourceBox = .(0, 0, 0, Width, Height, 1);
NativeContext.CopySubresourceRegion(destination.nativeTexture,
D3D11.CalcSubresource(mipSlice, arraySlice, destination.MipLevels), 0, 0, 0,
nativeTexture, D3D11.CalcSubresource(mipSlice, arraySlice, MipLevels), (.)&sourceBox);
}
}
}
extension TextureCube
@@ -109,21 +268,9 @@ namespace GlitchyEngine.Renderer
protected override void LoadTexturePlatform()
{
nativeTexture?.Release();
nativeView?.Release();
Debug.Profiler.ProfileResourceFunction!();
HResult loadResult = DDSTextureLoader.CreateDDSTextureFromFile(_context.nativeDevice, _path.ToScopedNativeWChar!(),
(.)&nativeTexture, &nativeView);
if(loadResult.Failed)
{
Log.EngineLogger.Error($"Failed to load texture \"{_path}\". Error({(int)loadResult}): {loadResult}");
nativeTexture?.Release();
nativeView?.Release();
// TODO: load fallback texture
}
LoadResourcePlatform(_path, ref nativeTexture);
let resType = nativeTexture.GetResourceType();
Log.EngineLogger.Assert(resType == .Texture2D, scope $"The texture \"{_path}\" is not a texture cube (it is {resType}).");
@@ -135,3 +282,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -1,9 +1,13 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX.D3D11;
using System.Diagnostics;
using DirectX.Common;
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
@@ -13,12 +17,12 @@ namespace GlitchyEngine.Renderer
public ID3DBlob* nativeShaderCode ~ _?.Release();
public this(GraphicsContext context, VertexElement[] elements, VertexShader vertexShader)
public this(VertexElement[] elements, bool ownsElements, VertexShader vertexShader)
{
nativeShaderCode = vertexShader.nativeCode..AddRef();
_context = context..AddRef();
_elements = elements;
_ownsElements = ownsElements;
CreateNativeLayout();
}
@@ -28,16 +32,18 @@ namespace GlitchyEngine.Renderer
Debug.Assert(input.Count == output.Count);
for(int i < input.Count)
output[i] = .(input[i].SemanticName, input[i].SemanticIndex, input[i].Format, input[i].InputSlot, input[i].AlignedByteOffset, (.)input[i].InputSlotClass);
output[i] = .(input[i].SemanticName, input[i].SemanticIndex, input[i].Format, input[i].InputSlot, input[i].AlignedByteOffset, (.)input[i].InputSlotClass, input[i].InstanceDataStepRate);
}
protected override void CreateNativeLayout()
{
Debug.Profiler.ProfileResourceFunction!();
var nativeElements = scope InputElementDescription[_elements.Count];
ToNativeLayout(_elements, nativeElements);
var result = _context.nativeDevice.CreateInputLayout(nativeElements.CArray(), (.)nativeElements.Count, nativeShaderCode.GetBufferPointer(), nativeShaderCode.GetBufferSize(), &nativeLayout);
var result = NativeDevice.CreateInputLayout(nativeElements.CArray(), (.)nativeElements.Count, nativeShaderCode.GetBufferPointer(), nativeShaderCode.GetBufferSize(), &nativeLayout);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create D3D11 input layout: Message({(int)result}): {result}");
@@ -45,3 +51,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -1,10 +1,13 @@
#if GE_GRAPHICS_DX11
using System;
using GlitchyEngine.Renderer;
using DirectX.Common;
using DirectX.D3D11;
using DirectX.D3DCompiler;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
@@ -14,15 +17,23 @@ namespace GlitchyEngine.Renderer
public override void CompileFromSource(String code, String entryPoint, ShaderDefine[] macros = null)
{
Debug.Profiler.ProfileResourceFunction!();
Shader.PlattformCompileShaderFromSource(code, macros, entryPoint, "vs_5_0", DefaultCompileFlags, out nativeCode);
var result = _context.nativeDevice.CreateVertexShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
{
Debug.Profiler.ProfileResourceScope!("CreateNativeVertexShader");
var result = NativeDevice.CreateVertexShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create vertex shader: Message ({(int)result}): {result}");
}
}
Reflect(nativeCode);
}
}
}
#endif
@@ -1,49 +0,0 @@
#if BF_PLATFORM_WINDOWS
using static System.Windows;
namespace GlitchyEngine.Threading
{
extension Mutex
{
internal Handle nativeHandle;
public this(bool initialyOwned = false)
{
nativeHandle = CreateMutexW(null, initialyOwned, null);
Log.EngineLogger.AssertDebug(nativeHandle != 0, scope $"Failed to create mutex. Error code: {GetLastError()}");
}
public ~this()
{
CloseHandle(nativeHandle);
}
public override LockResult Lock(uint32 timeout = InfiniteTimeout)
{
int i = WaitForSingleObject(nativeHandle, timeout);
switch(i)
{
case 0x00000000L:
return .Released;
case 0x00000080L:
return .Abandoned;
case 0x00000102L:
return .Timeout;
case 0xFFFFFFFF:
return .Failed;
default:
return .Unknown;
}
}
public bool Unlock()
{
return ReleaseMutex(nativeHandle);
}
}
}
#endif
@@ -1,55 +0,0 @@
#if BF_PLATFORM_WINDOWS
using static System.Windows;
namespace GlitchyEngine.Threading
{
extension Semaphore
{
internal Handle nativeHandle;
public override this(int32 initialCount = 1, int32 maximumCount = 1)
{
nativeHandle = (.)CreateSemaphoreW(null, initialCount, maximumCount, null);
Log.EngineLogger.AssertDebug(nativeHandle != 0, scope $"Failed to create semaphore. Error code: {GetLastError()}");
}
public ~this()
{
CloseHandle(nativeHandle);
}
public override LockResult Lock(uint32 timeout = InfiniteTimeout)
{
int i = WaitForSingleObject(nativeHandle, timeout);
switch(i)
{
case 0x00000000L:
return .Released;
case 0x00000080L:
return .Abandoned;
case 0x00000102L:
return .Timeout;
case 0xFFFFFFFF:
return .Failed;
default:
return .Unknown;
}
}
public bool Unlock(int32 releaseCount = 1)
{
return ReleaseSemaphore(nativeHandle, releaseCount, null);
}
public bool Unlock(out int32 previousCount, int32 releaseCount = 1)
{
previousCount = ?;
return ReleaseSemaphore(nativeHandle, releaseCount, &previousCount);
}
}
}
#endif
@@ -1,38 +0,0 @@
namespace System
{
extension Windows
{
public struct SecurityAttributes
{
public uint32 Length;
public SECURITY_DESCRIPTOR* SecurityDescriptor;
public IntBool InheritHandle;
}
[LinkName(.C)]
public static extern Windows.Handle CreateSemaphoreW(SecurityAttributes* semaphoreAttributes, int32 initialCount, int32 maximumCount, char16* name);
[LinkName(.C)]
public static extern uint32 WaitForSingleObject(Handle handle, uint32 timeout);
[LinkName(.C)]
public static extern uint32 WaitForMultipleObjects(uint32 count, Handle *handles, IntBool bWaitAll, uint32 timeout);
public const uint32 InfiniteTimeout = 0xFFFFFFFF;
[Inline]
public static uint32 WaitForMultipleObjects(Handle[] handles, bool waitForAll, uint32 timeout = InfiniteTimeout)
{
return WaitForMultipleObjects((.)handles.Count, handles.CArray(), waitForAll, timeout);
}
[LinkName(.C)]
public static extern IntBool ReleaseSemaphore(Handle semaphore, int32 releaseCount, int32* previousCount);
[LinkName(.C)]
public static extern Handle CreateMutexW(SecurityAttributes* mutexAttributes, IntBool initialOwner, char16* name);
[LinkName(.C)]
public static extern IntBool ReleaseMutex(Handle mutex);
}
}
@@ -1,3 +1,5 @@
#if BF_PLATFORM_WINDOWS
using System;
using GlitchyEngine.Events;
using DirectX.Windows.VirtualKeyCodes;
@@ -7,39 +9,21 @@ using static System.Windows;
namespace GlitchyEngine
{
#if GE_WINDOWS
/// Windows (WinApi) specific implementation of the Input-class
extension Input
{
internal static bool sUseRawInput = true;
public override static bool RawInput
{
get => sUseRawInput;
set
{
if(sUseRawInput == value)
return;
sUseRawInput = value;
//if(sUseRawInput)
// TODO: init raw input
}
}
/// Represents the state of the input devices on the windows platform (WinApi that is).
struct WindowsInputState
{
public int8[256] KeyStates;
public Point CursorPosition;
public Point CursorPositionDifference;
public Int32_2 RawMovement;
}
static WindowsInputState* CurrentState = new WindowsInputState() ~ delete _;
static WindowsInputState* LastState = new WindowsInputState() ~ delete _;
static WindowsInputState[2] IputStates;
static WindowsInputState* CurrentState = &IputStates[0];
static WindowsInputState* LastState = &IputStates[1];
//
// Current Keyboard state
@@ -67,13 +51,13 @@ namespace GlitchyEngine
//
public override static bool WasKeyPressed(Key keycode)
{
int8 state = CurrentState.KeyStates[(int)keycode];
int8 state = LastState.KeyStates[(int)keycode];
return state < 0;
}
public override static bool WasKeyReleased(Key keycode)
{
int8 state = CurrentState.KeyStates[(int)keycode];
int8 state = LastState.KeyStates[(int)keycode];
return state >= 0;
}
@@ -166,24 +150,10 @@ namespace GlitchyEngine
//
public override static bool IsMouseButtonPressing(MouseButton button) => IsMouseButtonPressed(button) && WasMouseButtonReleased(button);
public override static bool IsMouseButtonReleasiong(MouseButton button) => IsMouseButtonReleased(button) && WasMouseButtonPressed(button);
public override static bool IsMouseButtonReleasing(MouseButton button) => IsMouseButtonReleased(button) && WasMouseButtonPressed(button);
public override static Point GetMouseMovement() => CurrentState.CursorPositionDifference;
public override static Int32_2 GetRawMouseMovement()
{
if(sUseRawInput)
{
return CurrentState.RawMovement;
}
else
{
var v = GetMouseMovement();
return *(Int32_2*)&v;
}
}
//[CLink, CallingConvention(.Stdcall)]
//static extern int16 GetKeyState(int32 keycode);
[CLink, CallingConvention(.Stdcall)]
@@ -191,14 +161,11 @@ namespace GlitchyEngine
[CLink, CallingConvention(.Stdcall)]
static extern IntBool ScreenToClient(HWnd hWnd, ref Point p);
internal static Int32_2 rawMovement;
public override static void NewFrame()
{
// Switch last and current states
var buffer = LastState;
LastState = CurrentState;
CurrentState = buffer;
Debug.Profiler.ProfileFunction!();
Swap!(CurrentState, LastState);
// Get current keyboard state
if(DirectX.Windows.Winuser.GetKeyboardState((uint8*)&CurrentState.KeyStates) == 0)
@@ -225,9 +192,8 @@ namespace GlitchyEngine
// Calculate cursor movement
CurrentState.CursorPositionDifference = CurrentState.CursorPosition - LastState.CursorPosition;
CurrentState.RawMovement = rawMovement;
rawMovement = .Zero;
}
}
#endif
}
#endif
@@ -0,0 +1,48 @@
using System;
using DirectX.Windows.Winuser;
using DirectX.Common;
namespace GlitchyEngine
{
extension Key
{
// Todo: make overriding method
public void GetKeyName(String strBuffer)
{
var scanCode = MapVirtualKeyW((.)this, MAPVK_VK_TO_VSC);
char16[128] name = ?;
int result;
switch (this)
{
case .Left, .Up, .Right, .Down, .RightControl, .RightAlt,
.LeftSuper, .RightSuper, ContextMenu,
.Prior, .Next, .End, .Home, .Insert, .Delete, .Divide, .Numlock:
// set extended flag
scanCode |= 0x0100;
fallthrough;
default:
result = GetKeyNameTextW((.)(scanCode << 16), &name, name.Count);
}
if(result == 0)
{
HResult error = (.)DirectX.Windows.Kernel32.GetLastError();
Log.EngineLogger.Error($"Failed to convert keycode {(int)this}({this}) to string. WinAPI error({(int)error}): {error}");
return;
}
strBuffer.Append(Span<char16>(&name, result));
}
/// Converts the given windows specific virtual key code to a GlitchyEngine Key.
[Inline]
internal static Key FromWindowsKeyCode(int32 vKeyCode)
{
return (.)vKeyCode;
}
}
}
@@ -1,4 +1,5 @@
#if GE_WINDOWS
#if BF_PLATFORM_WINDOWS
using System;
using DirectX.Common;
using DirectX.Windows;
@@ -9,9 +10,10 @@ using GlitchyEngine.Events;
using System.Diagnostics;
using GlitchyEngine.Math;
using GlitchyEngine.Renderer;
using DirectX.Windows.Winuser.RawInput;
using static System.Windows;
using internal GlitchyEngine;
namespace GlitchyEngine
{
/// The windows specific Window implementation
@@ -32,6 +34,8 @@ namespace GlitchyEngine
private bool _isVSync = true;
private bool _isActive;
private GraphicsContext _graphicsContext ~ _?.ReleaseRef();
public override GraphicsContext Context => _graphicsContext;
@@ -150,10 +154,14 @@ namespace GlitchyEngine
set => _isVSync = value;
}
public override bool IsActive => _isActive;
public override void* NativeWindow => (void*)(int)_windowHandle;
public override this(WindowDescription desc)
{
Debug.Profiler.ProfileFunction!();
_minMaxInfo.MaximumTrackingSize.x = int32.MaxValue;
_minMaxInfo.MaximumTrackingSize.y = int32.MaxValue;
@@ -168,6 +176,8 @@ namespace GlitchyEngine
public ~this()
{
Debug.Profiler.ProfileFunction!();
if(!DestroyWindow(_windowHandle))
{
HResult res = (.)GetLastError();
@@ -177,8 +187,13 @@ namespace GlitchyEngine
UnregisterClass(WindowClassName.ToScopedNativeWChar!(), _instanceHandle);
}
[LinkName(.C)]
static extern HWnd GetActiveWindow();
private void Init(WindowDescription desc)
{
Debug.Profiler.ProfileFunction!();
Log.EngineLogger.Trace($"Creating window \"{desc.Title}\" ({desc.Width}, {desc.Height})...");
_instanceHandle = (.)GetModuleHandleW(null);
@@ -189,7 +204,11 @@ namespace GlitchyEngine
_windowClass.HInstance = (.)_instanceHandle;
if (desc.Icon.Ptr != null)
{
Debug.Profiler.ProfileScope!("LoadIcon");
_windowClass.Icon = LoadImageW(0, desc.Icon.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile);
}
else
_windowClass.Icon = 0;
@@ -204,8 +223,12 @@ namespace GlitchyEngine
Runtime.FatalError("Failed to register window class");
}
{
Debug.Profiler.ProfileScope!("CreateWindow");
_windowHandle = CreateWindowExW(.None, _windowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, (.)_instanceHandle, null);
}
_graphicsContext = new GraphicsContext(_windowHandle);
_graphicsContext.Init();
@@ -217,40 +240,8 @@ namespace GlitchyEngine
Log.EngineLogger.Trace($"Created window \"{Title}\" ({Width}, {Height})");
if(Input.RawInput)
{
InitRawInput();
}
}
[CLink, CallingConvention(.Stdcall)]
static extern IntBool RegisterRawInputDevices(RAWINPUTDEVICE* rawInputDevices, uint32 numDevices, uint32 size);
private void InitRawInput()
{
RAWINPUTDEVICE[1] Rid;
Rid[0].UsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
Rid[0].Usage = 0x02; // HID_USAGE_GENERIC_MOUSE
Rid[0].Flags = 0;//RIDEV_NOLEGACY; // adds mouse and also ignores legacy mouse messages
Rid[0].Target = 0;
/*
Rid[1].UsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
Rid[1].Usage = 0x06; // HID_USAGE_GENERIC_KEYBOARD
Rid[1].Flags = RIDEV_NOLEGACY; // adds keyboard and also ignores legacy keyboard messages
Rid[1].Target = 0;
*/
if (!RegisterRawInputDevices(&Rid, Rid.Count, sizeof(RAWINPUTDEVICE)))
{
DirectX.Common.HResult errorCode = (.)GetLastError();
Log.EngineLogger.Error($"Failed to register raw input devices. Message({(int32)errorCode}):{errorCode}");
// Disable raw input
Input.RawInput = false;
}
else
{
Log.EngineLogger.Trace($"Registered raw input devices.");
}
// Determine whether or not the window is currently active
_isActive = GetActiveWindow() == _windowHandle;
}
private bool _isResizingOrMoving;
@@ -259,6 +250,18 @@ namespace GlitchyEngine
[CLink]
static extern IntBool IsWindowUnicode(HWND whnd);
public Result<void> SetIcon(StringView filePath)
{
HICON hIcon = LoadImageW(0, filePath.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile);
if (hIcon == 0)
return .Err;
// WM_SETICON 0x0080
SendMessageW(_windowHandle, 0x0080, 1 /* ICON_BIG */, (int)hIcon);
return .Ok;
}
private static LRESULT MessageHandler(HWND hwnd, uint32 uMsg, WPARAM wParam, LPARAM lParam)
{
void* windowPtr = (void*)GetWindowLongPtrW(hwnd, GWL_USERDATA);
@@ -287,27 +290,22 @@ namespace GlitchyEngine
// Window size changed
case WM_SIZE:
{
if (wParam == (.)ResizingType.Minimized)
{
window._isMinimized = true;
}
else if (window._isMinimized)
{
window._isMinimized = false;
}
else
{
window._isMinimized = wParam == (.)ResizingType.Minimized;
SplitHighAndLowOrder!(lParam, out window._clientRect.Width, out window._clientRect.Height);
if(window._clientRect.Width != 0 && window._clientRect.Height != 0)
{
window._graphicsContext.SwapChain.Width = (.)window._clientRect.Width;
window._graphicsContext.SwapChain.Height = (.)window._clientRect.Height;
window._graphicsContext.SwapChain.ApplyChanges();
}
WindowResizeEvent event = scope WindowResizeEvent(window._clientRect.Width, window._clientRect.Height, window._isResizingOrMoving);
window._eventCallback(event);
}
}
// Window position changed
case WM_MOVE:
{
@@ -341,18 +339,39 @@ namespace GlitchyEngine
window._eventCallback(moveEvent);
}
case 0x0006: //WM_ACTIVATE
{
SplitHighAndLowOrder!((int64)wParam, let lowWParam, let highWParam);
bool isActivate = lowWParam > 0;
//HWnd windowHandle = (.)lParam;
if(window._isActive != isActivate)
{
window._isActive = isActivate;
if(window._isActive)
window._eventCallback(scope WindowActivateEvent());
else
window._eventCallback(scope WindowDeactivateEvent());
}
}
////
//// Keyboard input
////
case WM_KEYDOWN:
case WM_KEYDOWN, WM_SYSKEYDOWN:
{
var event = scope KeyPressedEvent((Key)wParam, (int32)lParam & 0xFFFF);
Key key = WindowMessageToKey(wParam, lParam);
var event = scope KeyPressedEvent(key, (int32)lParam & 0xFFFF);
window._eventCallback(event);
}
case WM_KEYUP:
case WM_KEYUP, WM_SYSKEYUP:
{
var event = scope KeyReleasedEvent((Key)wParam);
Key key = WindowMessageToKey(wParam, lParam);
var event = scope KeyReleasedEvent(key);
window._eventCallback(event);
}
@@ -474,130 +493,38 @@ namespace GlitchyEngine
return 0;
}
// Raw Input
case 0x00FF: //WM_INPUT
{
uint32 dataSize = ?;
GetRawInputData((.)lParam, RID_INPUT, null, &dataSize, sizeof(RAWINPUTHEADER));
if (dataSize > 0)
{
uint8[] rawData = scope .[dataSize];
if (GetRawInputData((.)lParam, RID_INPUT, rawData.CArray(), &dataSize, sizeof(RAWINPUTHEADER)) == dataSize)
{
RAWINPUT* raw = (.)rawData.CArray();
if (raw.Header.Type == RIM_TYPEMOUSE)
{
int32 movementX = raw.Data.Mouse.lLastX;
int32 movementY = raw.Data.Mouse.lLastY;
// TODO: raw.Data.Mouse.usFlags defines whether movement is abosulte, relative, etc...
Input.[Friend]rawMovement += .(movementX, movementY);
/*
var event = scope MouseMovedEvent(movementX, movementY);
window._eventCallback(event);
// Convert button transition flags to a more manageable type
var buttonTransitions = (RawMouseButtonTransition)raw.Data.Mouse.DUMMYUNIONNAME.DUMMYSTRUCTNAME.usButtonFlags;
if(buttonTransitions.HasFlag(.LeftDown))
{
var buttonEvent = scope MouseButtonPressedEvent(.LeftButton);
window._eventCallback(buttonEvent);
}
else if(buttonTransitions.HasFlag(.LeftUp))
{
var buttonEvent = scope MouseButtonReleasedEvent(.LeftButton);
window._eventCallback(buttonEvent);
}
if(buttonTransitions.HasFlag(.RightDown))
{
var buttonEvent = scope MouseButtonPressedEvent(.RightButton);
window._eventCallback(buttonEvent);
}
else if(buttonTransitions.HasFlag(.RightUp))
{
var buttonEvent = scope MouseButtonReleasedEvent(.RightButton);
window._eventCallback(buttonEvent);
}
if(buttonTransitions.HasFlag(.MiddleDown))
{
var buttonEvent = scope MouseButtonPressedEvent(.MiddleButton);
window._eventCallback(buttonEvent);
}
else if(buttonTransitions.HasFlag(.MiddleUp))
{
var buttonEvent = scope MouseButtonReleasedEvent(.MiddleButton);
window._eventCallback(buttonEvent);
}
if(buttonTransitions.HasFlag(.XButton1Down))
{
var buttonEvent = scope MouseButtonPressedEvent(.XButton1);
window._eventCallback(buttonEvent);
}
else if(buttonTransitions.HasFlag(.XButton1Up))
{
var buttonEvent = scope MouseButtonReleasedEvent(.XButton1);
window._eventCallback(buttonEvent);
}
if(buttonTransitions.HasFlag(.XButton2Down))
{
var buttonEvent = scope MouseButtonPressedEvent(.XButton2);
window._eventCallback(buttonEvent);
}
else if(buttonTransitions.HasFlag(.XButton2Up))
{
var buttonEvent = scope MouseButtonReleasedEvent(.XButton2);
window._eventCallback(buttonEvent);
}
if(buttonTransitions.HasFlag(.MouseWheel))
{
int32 rotation = raw.Data.Mouse.DUMMYUNIONNAME.DUMMYSTRUCTNAME.usButtonData / WHEEL_DELTA;
var scrollEvent = scope MouseScrolledEvent(0, rotation);
window._eventCallback(scrollEvent);
}
if(buttonTransitions.HasFlag(.MouseHWheel))
{
int32 rotation = raw.Data.Mouse.DUMMYUNIONNAME.DUMMYSTRUCTNAME.usButtonData / WHEEL_DELTA;
var scrollEvent = scope MouseScrolledEvent(rotation, 0);
window._eventCallback(scrollEvent);
}
*/
}
}
}
}
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
enum RawMouseButtonTransition
/**
* Converts the wParam and lParam of a WM_KEYDOWN or WM_KEYUP message to the corresponding engine keycode.
*/
static Key WindowMessageToKey(WPARAM wParam, LPARAM lParam)
{
LeftDown = 0x0001,
LeftUp = 0x0002,
MiddleDown = 0x0010,
MiddleUp = 0x0020,
RightDown = 0x0004,
RightUp = 0x0008,
XButton1Down = 0x0040,
XButton1Up = 0x0080,
XButton2Down = 0x0100,
XButton2Up = 0x0200,
MouseWheel = 0x0400,
MouseHWheel = 0x0800
Key key = Key.FromWindowsKeyCode((.)wParam);
var scancode = (lParam & 0x00ff0000) >> 16;
bool isExtended = (lParam & 0x01000000) > 0;
switch(key)
{
case .Shift:
return Key.FromWindowsKeyCode((.)MapVirtualKeyW((.)scancode, MAPVK_VSC_TO_VK_EX));
case .Control:
return isExtended ? .RightControl : .LeftControl;
case .Alt:
return isExtended ? .RightAlt : .LeftAlt;
default:
return key;
}
}
public override void Update()
{
Debug.Profiler.ProfileFunction!();
Message message = .();
while (PeekMessageW(&message, 0, 0, 0, .Remove))
{
@@ -641,4 +568,5 @@ namespace GlitchyEngine
}
}
}
#endif
+15 -1
View File
@@ -1,5 +1,6 @@
using System;
using GlitchLog;
using GlitchyEngine.Debug;
using System.Diagnostics;
namespace GlitchyEngine
@@ -11,24 +12,37 @@ namespace GlitchyEngine
public static int Main(String[] args)
{
Debug.Profiler.BeginProfiling();
Application app;
{
Debug.Profiler.ProfileScope!("Initialize");
Log.EngineLogger.Info("Initializing Application...");
Stopwatch initWatch = scope Stopwatch();
var app = CreateApplication();
app = CreateApplication();
initWatch.Stop();
Log.EngineLogger.Info($"Application initialized ({initWatch.ElapsedMilliseconds}ms).");
Log.EngineLogger.Info("Running App...");
}
app.Run();
{
Debug.Profiler.ProfileScope!("Shutdown");
Log.EngineLogger.Info("Uninitializing Application...");
delete app;
Log.EngineLogger.Info("Application uninitialized.");
}
Debug.Profiler.EndProfiling();
return 0;
}
@@ -0,0 +1,279 @@
using System;
using GlitchyEngine.Core;
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer.Animation
{
class AnimationPlayer
{
public Skeleton Skeleton ~ _.ReleaseRef();
public AnimationClip CurrentClip ~ _.ReleaseRef();
public float TimeStamp;
public SkeletonPose Pose ~ delete _;
public Matrix[] SkinningMatricies ~ delete _;
public Matrix3x3[] InvTransSkinningMatricies ~ delete _;
public this(Skeleton skeleton, AnimationClip clip)
{
Skeleton = skeleton;
CurrentClip = clip;
Pose = new SkeletonPose(Skeleton);
SkinningMatricies = new Matrix[Skeleton.Joints.Count];
InvTransSkinningMatricies = new Matrix3x3[Skeleton.Joints.Count];
}
public void Update(GameTime gameTime)
{
TimeStamp += (float)gameTime.FrameTime.TotalSeconds;
if(CurrentClip.IsLooping && CurrentClip.Duration != 0)
{
TimeStamp %= CurrentClip.Duration;
}
for(int i < Skeleton.Joints.Count)
{
ref JointPose localPose = ref Pose.LocalPose[i];
localPose = CurrentClip.JointAnimations[i].GetCurrentPose(TimeStamp);
Matrix jointToParent =
Matrix.Translation(localPose.Translation) *
Matrix.RotationQuaternion(localPose.Rotation) *
Matrix.Scaling(localPose.Scale);
uint8 parentIndex = Skeleton.Joints[i].ParentID;
ref Matrix globalPose = ref Pose.GlobalPose[i];
if(parentIndex == uint8.MaxValue)
{
globalPose = jointToParent;
}
else
{
globalPose = Pose.GlobalPose[parentIndex] * jointToParent;
}
SkinningMatricies[i] = globalPose * Skeleton.Joints[i].InverseBindPose;
InvTransSkinningMatricies[i] = ((Matrix3x3)SkinningMatricies[i]).Inverse().Transpose();
}
}
}
class AnimationClip : RefCounter
{
private Skeleton _skeleton ~ _.ReleaseRef();
public JointAnimation[] JointAnimations ~ delete _;
public bool IsLooping;
public float Duration;
public Skeleton Skeleton => _skeleton;
[AllowAppend]
public this(Skeleton skeleton)
{
var jointAnimations = new JointAnimation[skeleton.Joints.Count];
Log.EngineLogger.AssertDebug(skeleton != null);
_skeleton = skeleton..AddRef();
JointAnimations = jointAnimations;
}
// TODO: check
public ~this()
{
for(var jointAnimation in JointAnimations)
{
//jointAnimation.Dispose();
delete jointAnimation;
}
}
}
public enum InterpolationMode
{
/**
* No keyframe interpolation.
* The keyframe with the greates timestamp that is smaller than the current time will be used.
*/
Step,
/**
* Linear interpolation between the two keyframes that are closest to the current time.
*/
Linear,
/**
* Cubic spline interpolation between the two keyframes that are closest to the current time.
*/
CubicSpline
}
class JointAnimationChannel<T> where T : struct // : IDisposable
{
public float[] TimeStamps;
public T[] Values;
public InterpolationMode InterpolationMode = .Step;
public float Duration => TimeStamps[TimeStamps.Count - 1];
public this(int samples, InterpolationMode interpolationMode)
{
TimeStamps = new float[samples];
Values = new T[samples];
InterpolationMode = interpolationMode;
}
public ~this()
{
delete TimeStamps;
delete Values;
}
public (T, T, float) GetSample(float currentTime)
{
let (previousIndex, previousTime) = FindPreviousTimeStamp(currentTime);
T previousSample = Values[previousIndex];
if(InterpolationMode == .Step)
{
return (previousSample, default(T), 0f);
}
else
{
int nextIndex = previousIndex + 1;
if(nextIndex >= Values.Count)
nextIndex = Values.Count - 1;
float nextTime = TimeStamps[nextIndex];
T nextSample = Values[nextIndex];
float interpolationValue = (currentTime - previousTime) / (nextTime - previousTime);
return (previousSample, nextSample, interpolationValue);
}
}
public (int Index, float timeStamp) FindPreviousTimeStamp(float currentTime)
{
// TODO use binary search to speed things up?
int index = TimeStamps.Count - 1;
for(int i < TimeStamps.Count)
{
if(TimeStamps[i] > currentTime)
{
index = i - 1;
break;
}
}
if(index == -1)
index = 0;
// Don't return -1 but return first frame
return (index, TimeStamps[index]);
//return array.Count - 1;
}
}
class JointAnimation// : IDisposable
{
public JointAnimationChannel<Vector3> TranslationChannel ~ delete _;
public JointAnimationChannel<Quaternion> RotationChannel ~ delete _;
public JointAnimationChannel<Vector3> ScaleChannel ~ delete _;
public float Duration
{
get
{
return Math.Max(Math.Max(
TranslationChannel?.Duration ?? 0, RotationChannel?.Duration ?? 0), ScaleChannel?.Duration ?? 0);
}
}
/*
public void Dispose()
{
delete TranslationChannel;
delete RotationChannel;
delete ScaleChannel;
}
*/
public JointPose GetCurrentPose(float timeStamp)
{
JointPose result;
if(TranslationChannel != null)
{
let (previous, next, interpolationValue) = TranslationChannel.GetSample(timeStamp);
switch(TranslationChannel.InterpolationMode)
{
case .Step:
result.Translation = previous;
case .Linear:
result.Translation = Vector3.Lerp(previous, next, interpolationValue);
default:
result.Rotation = ?;
Runtime.NotImplemented();
}
}
else
{
result.Translation = .Zero;
}
if(RotationChannel != null)
{
let (previous, next, interpolationValue) = RotationChannel.GetSample(timeStamp);
switch(RotationChannel.InterpolationMode)
{
case .Step:
result.Rotation = previous;
case .Linear:
result.Rotation = Quaternion.Slerp(previous, next, interpolationValue);
default:
result.Rotation = ?;
Runtime.NotImplemented();
}
}
else
{
result.Rotation = .Identity;
}
if(ScaleChannel != null)
{
let (previous, next, interpolationValue) = ScaleChannel.GetSample(timeStamp);
//switch(ScaleChannel.InterpolationMode)
switch(InterpolationMode.Step)
{
case .Step:
result.Scale = previous;
case .Linear:
result.Scale = Vector3.Lerp(previous, next, interpolationValue);
default:
result.Rotation = ?;
Runtime.NotImplemented();
}
}
else
{
result.Scale = .One;
}
return result;
}
}
}
@@ -0,0 +1,20 @@
using GlitchyEngine.Math;
using System;
namespace GlitchyEngine.Renderer.Animation
{
public struct Joint
{
/// Converts vertices from model space to joint space
public Matrix InverseBindPose;
public String Name;
public uint8 ParentID;
}
public struct JointPose
{
public Quaternion Rotation;
public Vector3 Translation;
public Vector3 Scale;
}
}
@@ -0,0 +1,51 @@
using GlitchyEngine.Core;
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer.Animation
{
public class Skeleton : RefCounter
{
public Joint[] Joints ~ delete _;
public Joint? GetParent(Joint joint)
{
if(joint.ParentID == uint8.MaxValue)
return null;
return Joints[joint.ParentID];
}
public ~this()
{
for(var joint in Joints)
{
delete joint.Name;
}
}
}
public class SkeletonPose
{
private Skeleton _skeleton ~ _.ReleaseRef();
public JointPose[] LocalPose ~ delete _;
public Matrix[] GlobalPose ~ delete _;
public Matrix[] SkinningMatricies ~ delete _;
public Matrix3x3[] InvTransSkinningMatricies ~ delete _;
public Skeleton Skeleton => _skeleton;
public this(Skeleton skeleton)
{
_skeleton = skeleton..AddRef();
int jointCount = _skeleton.Joints.Count;
LocalPose = new JointPose[jointCount];
GlobalPose = new Matrix[jointCount];
SkinningMatricies = new Matrix[jointCount];
InvTransSkinningMatricies = new Matrix3x3[jointCount];
}
}
}
+10 -6
View File
@@ -1,5 +1,7 @@
using System;
using GlitchyEngine.Core;
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer
{
public struct BlendStateDescription
@@ -70,23 +72,25 @@ namespace GlitchyEngine.Renderer
public static readonly Self Default = .(false, false, .(.Default, .Default, .Default, .Default, .Default, .Default, .Default, .Default));
}
public class BlendState : RefCounted
public class BlendState : RefCounter
{
protected GraphicsContext _context ~ _?.ReleaseRef();
protected BlendStateDescription _desc;
public BlendStateDescription Description => _desc;
public this(GraphicsContext context, BlendStateDescription desc)
public this(BlendStateDescription desc)
{
_context = context..AddRef();
Debug.Profiler.ProfileResourceFunction!();
_desc = desc;
PlatformCreateBlendState();
}
public extern void Bind(ColorRGBA blendFactor = .White);
public ~this()
{
Debug.Profiler.ProfileResourceFunction!();
}
protected extern void PlatformCreateBlendState();
}
+5 -52
View File
@@ -1,47 +1,8 @@
using System;
using GlitchyEngine.Core;
namespace GlitchyEngine.Renderer
{
/**
* Identifies expected resource use during rendering.
* The usage directly reflects whether a resource is accessible by the CPU and/or the graphics processing unit (GPU).
*/
public enum Usage
{
/**
* A resource that requires read and write access by the GPU.
* This is likely to be the most common usage choice.
*/
Default = 0,
/**
* A resource that can only be read by the GPU. It cannot be written by the GPU, and cannot be accessed at all by the CPU.
*/
Immutable = 1,
/**
* A resource that is accessible by both the GPU (read only) and the CPU (write only).
* A dynamic resource is a good choice for a resource that will be updated by the CPU at least once per frame.
* To update a dynamic resource, use a Map method.
*/
Dynamic = 2,
/**
* A resource that supports data transfer (copy) from the GPU to the CPU.
*/
Staging = 3
}
/**
* Defines how the CPU can access a resource.
*/
public enum CPUAccessFlags
{
/// The CPU has no access to the resource.
None = 0,
/// The CPU has read access to the resource.
Read = 1,
/// The CPU has write access to the resource.
Write = 2
}
/**
* Defines how to bind a buffer to the pipeline.
*/
@@ -120,26 +81,19 @@ namespace GlitchyEngine.Renderer
}
/// Represents a buffer containing binary data on the GPU.
public class Buffer : RefCounted
public class Buffer : RefCounter
{
internal GraphicsContext _context ~ _?.ReleaseRef();
protected BufferDescription _description;
public GraphicsContext Context => _context;
public BufferDescription Description => _description;
protected this(GraphicsContext context)
{
_context = context..AddRef();
}
protected this() { }
/**
* Creates a new instance of a Buffer.
* @param description The buffer description.
*/
public this(GraphicsContext context, BufferDescription description) : this(context)
public this(BufferDescription description)
{
_description = description;
}
@@ -201,10 +155,9 @@ namespace GlitchyEngine.Renderer
/**
* Creates a new instance of a Buffer<T>
* @param context The graphics context.
* @param description Describes the Buffer. Note: description.Size is ignored, as it will always be sizeof(T).
*/
public this(GraphicsContext context, BufferDescription description) : base(context)
public this(BufferDescription description)
{
_description = description;
_description.Size = (uint32)sizeof(T);
@@ -15,6 +15,8 @@ namespace GlitchyEngine.Renderer
[AllowAppend]
public this()
{
Debug.Profiler.ProfileResourceFunction!();
// Todo: append allocate as soon as it's fixed
let buffers = new List<BufferEntry>();
let strToBuf = new Dictionary<String, BufferEntry*>();
@@ -25,6 +27,11 @@ namespace GlitchyEngine.Renderer
_idxToBuf = idxToBuf;
}
public ~this()
{
Debug.Profiler.ProfileResourceFunction!();
}
mixin DeleteBufferEntries(List<BufferEntry> entries)
{
if(entries == null)
@@ -54,6 +61,8 @@ namespace GlitchyEngine.Renderer
public BufferEntry* TryGetBufferEntry(String name)
{
Debug.Profiler.ProfileResourceFunction!();
if(_strToBuf.TryGetValue(name, let buffer))
{
return buffer;
@@ -64,6 +73,8 @@ namespace GlitchyEngine.Renderer
public BufferEntry* TryGetBufferEntry(int index)
{
Debug.Profiler.ProfileResourceFunction!();
if(_idxToBuf.TryGetValue(index, let buffer))
{
return buffer;
@@ -80,6 +91,8 @@ namespace GlitchyEngine.Renderer
*/
public bool TryReplaceBuffer(int idx, Buffer buffer)
{
Debug.Profiler.ProfileResourceFunction!();
if(_idxToBuf.TryGetValue(idx, let bufferEntry))
{
Log.EngineLogger.Assert(idx == bufferEntry.Index);
@@ -105,6 +118,8 @@ namespace GlitchyEngine.Renderer
*/
public bool TryReplaceBuffer(String name, Buffer buffer)
{
Debug.Profiler.ProfileResourceFunction!();
if(_strToBuf.TryGetValue(name, let bufferEntry))
{
Log.EngineLogger.AssertDebug(name == bufferEntry.Name);
@@ -129,6 +144,8 @@ namespace GlitchyEngine.Renderer
public void Add(BufferEntry entry)
{
Debug.Profiler.ProfileResourceFunction!();
BufferEntry copy = (new String(entry.Name), entry.Index, entry.Buffer..AddRef());
_buffers.Add(copy);
@@ -146,6 +163,8 @@ namespace GlitchyEngine.Renderer
*/
int GetIndexOfBuffer(Buffer buffer)
{
Debug.Profiler.ProfileResourceFunction!();
for(int i < _buffers.Count)
{
// Only check for reference equality.
@@ -165,6 +184,8 @@ namespace GlitchyEngine.Renderer
*/
String GetNameOfBuffer(Buffer buffer)
{
Debug.Profiler.ProfileResourceFunction!();
for(int i < _buffers.Count)
{
// Only check for reference equality.
+102 -53
View File
@@ -12,10 +12,12 @@ namespace GlitchyEngine.Renderer
private ShaderVariableType _type;
private uint32 _columns;
private uint32 _rows;
internal uint32 _columns;
internal uint32 _rows;
private uint32 _offset;
private uint32 _sizeInBytes;
internal uint32 _sizeInBytes;
// Number of elements in the array
internal uint32 _elements;
private bool _isUsed;
@@ -25,13 +27,15 @@ namespace GlitchyEngine.Renderer
public String Name => _name;
public bool IsUsed => _isUsed;
/**
* Gets a pointer to the start of the variable in the constant buffers backing data.
*/
[Inline]
internal uint8* firstByte => _constantBuffer.rawData.CArray() + _offset;
public this(ConstantBuffer constantBuffer, ShaderVariableType type, uint32 columns, uint32 rows, uint32 offset, uint32 sizeInBytes, bool isUsed)
public this(ConstantBuffer constantBuffer, ShaderVariableType type, uint32 columns, uint32 rows, uint32 offset, uint32 sizeInBytes, uint32 elements, bool isUsed)
{
_constantBuffer = constantBuffer..AddRef();
_type = type;
@@ -39,97 +43,140 @@ namespace GlitchyEngine.Renderer
_rows = rows;
_offset = offset;
_sizeInBytes = sizeInBytes;
_elements = elements;
_isUsed = isUsed;
}
public void EnsureTypeMatch(int rows, int cols, ShaderVariableType type)
{
#if GE_ERROR_SHADER_MATRIX_MISMATCH
#if GE_SHADER_MATRIX_MISMATCH_IS_ERROR
Log.EngineLogger.Assert(rows == _rows || cols == _columns, scope $"The matrix-dimensions do not match: Expected {_rows} rows and {_rows} columns but Received {rows} rows and {cols} columns instead. Variable: \"{_name}\" of buffer: \"{_constantBuffer.Name}\"");
#elif GE_WARN_SHADER_MATRIX_MISMATCH
#elif GE_SHADER_MATRIX_MISMATCH_IS_WARNING
if (rows != _rows || cols != _columns)
Log.EngineLogger.Warning($"The matrix-dimensions do not match: Expected {_rows} rows and {_rows} columns but Received {rows} rows and {cols} columns instead. Variable: \"{_name}\" of buffer: \"{_constantBuffer.Name}\"");
#endif
#if GE_ERROR_SHADER_VAR_TYPE_MISMATCH
#if GE_SHADER_VAR_TYPE_MISMATCH_IS_ERROR
Log.EngineLogger.Assert(type == _type, scope $"The types do not match: Expected \"{_type}\" but Received \"{type}\" instead. Variable: \"{_name}\" of buffer: \"{_constantBuffer.Name}\"");
#elif GE_WARN_SHADER_VAR_TYPE_MISMATCH
#elif GE_SHADER_VAR_TYPE_MISMATCH_IS_WARNING
if (type != _type)
Log.EngineLogger.Warning($"The types do not match: Expected \"{_type}\" but Received \"{type}\" instead. Variable: \"{_name}\" of buffer: \"{_constantBuffer.Name}\"");
#endif
}
public void SetData(float value)
public void EnsureTypeMatch<T>()
{
switch(typeof(T))
{
case typeof(bool):
EnsureTypeMatch(1, 1, .Bool);
case typeof(float):
EnsureTypeMatch(1, 1, .Float);
*(float*)firstByte = value;
}
public void SetData(Vector2 value)
{
case typeof(Vector2):
EnsureTypeMatch(1, 2, .Float);
*(Vector2*)firstByte = value;
}
public void SetData(Vector3 value)
{
case typeof(Vector3):
EnsureTypeMatch(1, 3, .Float);
*(Vector3*)firstByte = value;
}
public void SetData(Vector4 value)
{
case typeof(Vector4):
EnsureTypeMatch(1, 4, .Float);
*(Vector4*)firstByte = value;
case typeof(int32):
EnsureTypeMatch(1, 1, .Int);
case typeof(Int2):
EnsureTypeMatch(1, 2, .Int);
case typeof(Int3):
EnsureTypeMatch(1, 3, .Int);
case typeof(Int4):
EnsureTypeMatch(1, 4, .Int);
case typeof(Matrix4x3):
EnsureTypeMatch(4, 3, .Float);
case typeof(Matrix3x3):
EnsureTypeMatch(3, 3, .Float);
case typeof(Matrix):
EnsureTypeMatch(4, 4, .Float);
case typeof(ColorRGB):
EnsureTypeMatch(1, 3, .Float);
case typeof(ColorRGBA):
EnsureTypeMatch(1, 4, .Float);
case typeof(Color):
EnsureTypeMatch(1, 4, .Float);
}
}
public void SetData(Matrix4x3 value)
[Inline]
private void SetData<T>(T value)
{
// I think this is right
EnsureTypeMatch(4, 3, .Float);
EnsureTypeMatch<T>();
*(Matrix4x3*)firstByte = value;
*(T*)firstByte = value;
}
[Inline]
private T GetData<T>()
{
return *(T*)firstByte;
}
public void SetData(bool value) => SetData<bool>(value);
public void SetData(float value) => SetData<float>(value);
public void SetData(Vector2 value) => SetData<Vector2>(value);
public void SetData(Vector3 value) => SetData<Vector3>(value);
public void SetData(Vector4 value) => SetData<Vector4>(value);
public void SetData(int32 value) => SetData<int32>(value);
public void SetData(Int2 value) => SetData<Int2>(value);
public void SetData(Int3 value) => SetData<Int3>(value);
public void SetData(Int4 value) => SetData<Int4>(value);
public void SetData(ColorRGB value) => SetData<ColorRGB>(value);
public void SetData(ColorRGBA value) => SetData<ColorRGBA>(value);
public void SetData(Color value) => SetData<ColorRGBA>((ColorRGBA)value);
public void SetData(Matrix4x3 value) => SetData<Matrix4x3>(value);
public void SetData(Matrix4x3[] value)
{
EnsureTypeMatch<Matrix4x3>();
// TODO: assert length
Internal.MemCpy(firstByte, value.Ptr, sizeof(Matrix4x3) * Math.Min(value.Count, _elements));
}
public void SetData(Matrix3x3 value)
{
EnsureTypeMatch(3, 3, .Float);
EnsureTypeMatch<Matrix3x3>();
*(Matrix4x3*)firstByte = Matrix4x3(value);
// Todo: maybe manual copy
}
public void SetData(Matrix value)
public void SetData(Matrix3x3[] value)
{
EnsureTypeMatch(4, 4, .Float);
EnsureTypeMatch<Matrix3x3>();
*(Matrix*)firstByte = value;
// TODO: assert length
for(int i < Math.Min(value.Count, _elements))
{
((Matrix4x3*)firstByte)[i] = Matrix4x3(value[i]);
}
}
public void SetData(ColorRGB value)
public void SetData(Matrix value) => SetData<Matrix>(value);
public void SetData(Matrix[] value)
{
EnsureTypeMatch(1, 3, .Float);
EnsureTypeMatch<Matrix>();
*(ColorRGB*)firstByte = value;
}
// TODO: assert length
public void SetData(ColorRGBA value)
{
EnsureTypeMatch(1, 4, .Float);
*(ColorRGBA*)firstByte = value;
}
public void SetData(Color value)
{
EnsureTypeMatch(1, 4, .Float);
*(ColorRGBA*)firstByte = (ColorRGBA)value;
Internal.MemCpy(firstByte, value.Ptr, sizeof(Matrix) * Math.Min(value.Count, _elements));
}
// Todo: add all the other SetData-Methods
@@ -140,7 +187,9 @@ namespace GlitchyEngine.Renderer
*/
internal void SetRawData(void* rawData)
{
#if DEBUG && !GE_IGNORE_UNUSED_VARIABLE
#if GE_SHADER_UNUSED_VARIABLE_IS_ERROR
Log.EngineLogger.Assert(_isUsed, scope $"Setting data for unused Variable \"{_name}\" of constant buffer \"{_constantBuffer.Name}\".");
#elif GE_SHADER_UNUSED_VARIABLE_IS_WARNING
if(!_isUsed)
{
Log.EngineLogger.Warning($"Setting data for unused Variable \"{_name}\" of constant buffer \"{_constantBuffer.Name}\".");
@@ -0,0 +1,16 @@
namespace GlitchyEngine.Renderer
{
/**
* Defines how the CPU can access a resource.
*/
public enum CPUAccessFlags
{
/// The CPU has no access to the resource.
None = 0,
/// The CPU has read access to the resource.
Read = 1,
/// The CPU has write access to the resource.
Write = 2
}
}
@@ -0,0 +1,98 @@
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer
{
public struct CameraComponent
{
public enum Projection
{
/**
* A perspective projection with near and far plane.
*/
Perspective,
/**
* A perspective projection with near and far plane,
* except that the closer a pixel is to the far plane the smaller its depth-value gets.
*/
PerspectiveReversed,
/**
* A perspective projection with a near plane and a far plane at infinite distance.
*/
PerspectiveInfinite,
/**
* A perspective projection with a near plane and a far plane at infinite distance,
* except that the closer a pixel is to the far plane the smaller its depth-value gets.
*/
PerspectiveReversedInfinite,
/**
* An orthographic projection.
*/
Orthographic
}
float _nearPlane;
float _farPlane;
Projection _projectionType;
float _fovY;
float _aspect;
public float NearPlane
{
get => _nearPlane;
set mut => _nearPlane = value;
}
public float FarPlane
{
get => _farPlane;
set mut => _farPlane = value;
}
public Projection ProjectionType
{
get => _projectionType;
set mut => _projectionType = value;
}
public float FovY
{
get => _fovY;
set mut => _fovY = value;
}
public float FovX
{
get => _fovY * _aspect;
set mut => _fovY = value / _aspect;
}
public float Aspect
{
get => _aspect;
set mut => _aspect = value;
}
public Matrix Projection => CalculateProjection();
public Matrix CalculateProjection()
{
Log.EngineLogger.Assert(_nearPlane < _farPlane, "The near plane must be smaller than the far plane.");
switch(_projectionType)
{
case .Perspective:
return Matrix.PerspectiveProjection(_fovY, _aspect, _nearPlane, _farPlane);
case .PerspectiveReversed:
return Matrix.ReversedPerspectiveProjection(_fovY, _aspect, _nearPlane, _farPlane);
case .PerspectiveInfinite:
return Matrix.InfinitePerspectiveProjection(_fovY, _aspect, _nearPlane);
case .PerspectiveReversedInfinite:
return Matrix.ReversedInfinitePerspectiveProjection(_fovY, _aspect, _nearPlane);
default:
Log.EngineLogger.Assert(false, "Unknown projection type.");
}
return .Identity;
}
}
}
+3 -1
View File
@@ -69,7 +69,7 @@ namespace GlitchyEngine.Renderer
public BufferVariableCollection Variables => _variables;
protected this(GraphicsContext context) : base(context) {}
protected this() {}
protected internal void AddVariable(BufferVariable ownVariable)
{
@@ -99,7 +99,9 @@ namespace GlitchyEngine.Renderer
public enum ShaderVariableType
{
Bool,
Float,
Int,
// todo
}
}
@@ -0,0 +1,62 @@
using GlitchyEngine.Math;
using GlitchyEngine.World;
namespace GlitchyEngine.Renderer
{
/// Provides functionality for debugging
public static class DebugRenderer
{
private static GeometryBinding CoordinateCross;
public static bool DrawEntityTransforms = true;
public static void Init()
{
CreateCoordinateCross();
}
private static void CreateCoordinateCross()
{
CoordinateCross = new GeometryBinding();
//CoordinateCross.PrimitiveTopology
}
public static void Deinit()
{
CoordinateCross.ReleaseRef();
}
/**
* Draws a coordinate cross for the given matrix.
* @param transform The transform matrix.
* @param length Can be used to set a fixed length for the rendered lines that represent the axes.
* Set to 0 if you want transform to affect the length of the lines.
*/
public static void DrawCoordinateCross(Matrix transform, float length = 0.0f)
{
var transform;
if(length != 0.0f)
{
transform.Columns[0]..Normalize() *= length;
transform.Columns[1]..Normalize() *= length;
transform.Columns[2]..Normalize() *= length;
}
Renderer.DrawLine(.Zero, .Right, .Red, transform);
Renderer.DrawLine(.Zero, .Up, .Lime, transform);
Renderer.DrawLine(.Zero, .Forward, .Blue, transform);
}
public static void Render(EcsWorld world)
{
if(DrawEntityTransforms)
{
for(var (entity, transform) in world.Enumerate<TransformComponent>())
{
DrawCoordinateCross(transform.WorldTransform);
}
}
}
}
}
@@ -0,0 +1,93 @@
using GlitchyEngine.Core;
namespace GlitchyEngine.Renderer
{
/// The stencil operations that can be performed during depth-stencil testing.
public enum StencilOperation : uint32
{
/// Keep the existing stencil data.
Keep = 1,
/// Set the stencil data to 0.
Zero= 2,
/// Set the stencil data to the reference value set by calling ID3D11DeviceContext::OMSetDepthStencilState.
Replace = 3,
/// Increment the stencil value by 1, and clamp the result.
IncrementAndClamp = 4,
/// Decrement the stencil value by 1, and clamp the result.
DecrementAndClamp = 5,
/// Invert the stencil data.
Invert = 6,
/// Increment the stencil value by 1, and wrap the result if necessary.
Increment = 7,
/// Decrement the stencil value by 1, and wrap the result if necessary.
Decrement = 8
}
public struct DepthStencilOperationDescription
{
/// The stencil operation to perform when stencil testing fails.
public StencilOperation StencilFailOperation = .Keep;
/// The stencil operation to perform when stencil testing passes and depth testing fails.
public StencilOperation StencilDepthFailOperation = .Keep;
/// The stencil operation to perform when stencil testing and depth testing both pass.
public StencilOperation StencilPassOperation = .Keep;
/// A function that compares stencil data against existing stencil data.
public ComparisonFunction StencilFunction = .Always;
public this(StencilOperation stencilFailOperation = .Keep, StencilOperation stencilDepthFailOperation = .Keep,
StencilOperation stencilPassOperation = .Keep, ComparisonFunction stencilFunction = .Always)
{
StencilFunction = stencilFunction;
}
public const Self Default = Self();
}
public struct DepthStencilStateDescription
{
/// True if depth testing is enabled.
public bool DepthEnabled = true;
/// If true writing to the depth-stencil buffer is enabled.
public bool WriteDepth = true;
/// The function that is used to compare depth data against the existing depth data.
public ComparisonFunction DepthFunction = .Less;
/// True if stencil testing is enabled.
public bool StencilEnabled = false;
public uint8 StencilReadMask = 255;
public uint8 StencilWriteMask = 255;
public DepthStencilOperationDescription FrontFace = .();
public DepthStencilOperationDescription BackFace = .();
public this(bool depthEnabled = true, bool writeDepth = true, ComparisonFunction depthFunction = .Less,
bool stencilEnabled = false, uint8 stencilReadMask = 255, uint8 stencilWriteMask = 255,
DepthStencilOperationDescription frontFace = .(),
DepthStencilOperationDescription backFace = .())
{
DepthEnabled = depthEnabled;
WriteDepth = writeDepth;
DepthFunction = depthFunction;
StencilEnabled = stencilEnabled;
StencilReadMask = stencilReadMask;
StencilWriteMask = stencilWriteMask;
FrontFace = frontFace;
BackFace = backFace;
}
public const DepthStencilStateDescription Default = Self();
}
class DepthStencilState : RefCounter
{
public DepthStencilStateDescription _description;
public extern this(DepthStencilStateDescription description);
}
}
@@ -1,29 +1,37 @@
using System;
using GlitchyEngine.Core;
namespace GlitchyEngine.Renderer
{
// TODO: add all features.
public class DepthStencilTarget : RefCounted
public enum DepthStencilFormat
{
protected internal GraphicsContext _context ~ _?.ReleaseRef();
D16_UNorm,
D24_UNorm_S8_UInt,
D32_Float,
D32_Float_S8X24_UInt,
None
}
// TODO: add all features.
public class DepthStencilTarget : RefCounter
{
protected uint32 _width, _height;
protected DepthStencilFormat _format;
public uint32 Width => _width;
public uint32 Height => _height;
public DepthStencilFormat Format => _format;
public this(GraphicsContext context, uint32 width, uint32 height)
public this(uint32 width, uint32 height, DepthStencilFormat format = .D32_Float)
{
_context = context..AddRef();
Log.EngineLogger.Assert(format != .None, "DepthStencilFormat None is only valid for RenderTarget");
_width = width;
_height = height;
_format = format;
PlatformCreate();
}
protected extern void PlatformCreate();
public extern void Bind();
public extern void Clear(float depthValue, uint8 stencilValue, DepthStencilClearFlag clearFlags);
}
}
+157 -34
View File
@@ -1,19 +1,18 @@
using System;
using System.IO;
using System.Collections;
using GlitchyEngine.Core;
namespace GlitchyEngine.Renderer
{
public class EffectLibrary
{
private GraphicsContext _context ~ _?.ReleaseRef();
private Dictionary<String, Effect> _effects = new .() ~ delete _;
private List<String> _ownedStrings = new .() ~ DeleteContainerAndItems!(_);
public this(GraphicsContext context)
public this()
{
_context = context..AddRef();
}
public ~this()
@@ -26,6 +25,8 @@ namespace GlitchyEngine.Renderer
public void Add(Effect effect, String effectName = null)
{
Debug.Profiler.ProfileResourceFunction!();
String name;
if(effectName == null)
@@ -52,6 +53,8 @@ namespace GlitchyEngine.Renderer
*/
public Effect Load(String filepath, String effectName = null)
{
Debug.Profiler.ProfileResourceFunction!();
String name = effectName;
if(name == null)
@@ -62,7 +65,7 @@ namespace GlitchyEngine.Renderer
Log.EngineLogger.AssertDebug(!Exists(name), "Can't add two effects with the same name to library.");
Effect effect = new Effect(_context, filepath, name);
Effect effect = new Effect(filepath, name);
Add(effect);
return effect;
@@ -81,6 +84,8 @@ namespace GlitchyEngine.Renderer
public Effect Get(String effectName)
{
Debug.Profiler.ProfileResourceFunction!();
Log.EngineLogger.AssertDebug(Exists(effectName), "Effect not found!");
return _effects.GetValue(effectName).Get()..AddRef();
@@ -89,9 +94,8 @@ namespace GlitchyEngine.Renderer
public bool Exists(String effectName) => _effects.ContainsKey(effectName);
}
public class Effect : RefCounted
public class Effect : RefCounter
{
protected GraphicsContext _context ~ _?.ReleaseRef();
internal VertexShader _vs ~ _?.ReleaseRef();
internal PixelShader _ps ~ _?.ReleaseRef();
protected String _name ~ delete _;
@@ -100,7 +104,10 @@ namespace GlitchyEngine.Renderer
BufferVariableCollection _variables ~ delete _;
public GraphicsContext Context => _context;
typealias TextureEntry = (Texture Texture, ShaderTextureCollection.ResourceEntry* VsSlot, ShaderTextureCollection.ResourceEntry* PsSlot);
Dictionary<String, TextureEntry> _textures ~ delete _;
public Dictionary<String, TextureEntry> Textures => _textures;
public VertexShader VertexShader
{
@@ -129,33 +136,15 @@ namespace GlitchyEngine.Renderer
public String Name => _name;
public void ApplyChanges()
{
for(let buffer in _bufferCollection)
{
if(let cbuffer = buffer.Buffer as ConstantBuffer)
{
cbuffer.Update();
}
}
}
public void Bind(GraphicsContext context)
{
ApplyChanges();
context.SetVertexShader(_vs);
context.SetPixelShader(_ps);
}
[Obsolete("Will be removed in the future", false)]
public this()
{
}
public this(GraphicsContext context, String filename, String vsEntry, String psEntry, String shaderName = null)
public this(String filename, String vsEntry, String psEntry, String shaderName = null)
{
_context = context..AddRef();
Debug.Profiler.ProfileResourceFunction!();
CompileFromFile(filename, vsEntry, psEntry);
if(shaderName == null)
@@ -169,15 +158,16 @@ namespace GlitchyEngine.Renderer
}
}
public this(GraphicsContext context, String filename, String shaderName = null)
public this(String filename, String shaderName = null)
{
_context = context..AddRef();
Debug.Profiler.ProfileResourceFunction!();
String fileContent = scope String();
String vsName = scope String();
String psName = scope String();
ProcessFile(filename, fileContent, vsName, psName);
Compile(fileContent, vsName, psName);
MergeResources();
@@ -195,28 +185,97 @@ namespace GlitchyEngine.Renderer
public this(String shaderName, String vsPath, String vsEntry, String psPath, String psEntry)
{
Debug.Profiler.ProfileResourceFunction!();
Compile(vsPath, vsEntry, psPath, psEntry);
_name = new String(shaderName);
}
public ~this()
{
Debug.Profiler.ProfileResourceFunction!();
for(let entry in _textures)
{
entry.value.Texture?.ReleaseRef();
}
}
public void SetTexture(String name, Texture texture)
{
Debug.Profiler.ProfileRendererFunction!();
ref TextureEntry entry = ref _textures[name];
entry.Texture?.ReleaseRef();
entry.Texture = texture;
entry.Texture?.AddRef();
}
private void ApplyTextures()
{
Debug.Profiler.ProfileRendererFunction!();
for(let (name, entry) in _textures)
{
entry.VsSlot?.Texture?.ReleaseRef();
entry.VsSlot?.Texture = entry.Texture;
entry.VsSlot?.Texture?.AddRef();
entry.PsSlot?.Texture?.ReleaseRef();
entry.PsSlot?.Texture = entry.Texture;
entry.PsSlot?.Texture?.AddRef();
}
}
private void ApplyChanges()
{
Debug.Profiler.ProfileRendererFunction!();
for(let buffer in _bufferCollection)
{
if(let cbuffer = buffer.Buffer as ConstantBuffer)
{
cbuffer.Update();
}
}
}
public void Bind(GraphicsContext context)
{
Debug.Profiler.ProfileRendererFunction!();
ApplyTextures();
ApplyChanges();
context.SetVertexShader(_vs);
context.SetPixelShader(_ps);
}
private void CompileFromFile(String filename, String vsEntry, String psEntry)
{
let vs = Shader.FromFile!<VertexShader>(_context, filename, vsEntry);
Debug.Profiler.ProfileResourceFunction!();
let vs = Shader.FromFile!<VertexShader>(filename, vsEntry);
VertexShader = vs;
vs.ReleaseRef();
let ps = Shader.FromFile!<PixelShader>(_context, filename, psEntry);
let ps = Shader.FromFile!<PixelShader>(filename, psEntry);
PixelShader = ps;
ps.ReleaseRef();
}
private void Compile(String fileContent, String vsEntry, String psEntry)
{
Debug.Profiler.ProfileResourceFunction!();
// TODO: vsEntry and psEntry could be empty (which is a valid case.)
let vs = new VertexShader(_context, fileContent, vsEntry);
let vs = new VertexShader(fileContent, vsEntry);
VertexShader = vs;
vs.ReleaseRef();
let ps = new PixelShader(_context, fileContent, psEntry);
let ps = new PixelShader(fileContent, psEntry);
PixelShader = ps;
ps.ReleaseRef();
}
@@ -232,6 +291,8 @@ namespace GlitchyEngine.Renderer
*/
private static void ProcessFile(String filename, String fileContent, String vsName, String psName)
{
Debug.Profiler.ProfileResourceFunction!();
File.ReadAllText(filename, fileContent, true);
// append line ending just in case the file doesn't end with one.
fileContent.Append('\n');
@@ -291,12 +352,17 @@ namespace GlitchyEngine.Renderer
private void MergeResources()
{
Debug.Profiler.ProfileResourceFunction!();
MergeConstantBuffers();
MergeBufferVariables();
MergeTextures();
}
private void MergeConstantBuffers()
{
Debug.Profiler.ProfileResourceFunction!();
_bufferCollection = new BufferCollection();
HashSet<String> bufferNames = scope HashSet<String>();
@@ -340,6 +406,8 @@ namespace GlitchyEngine.Renderer
private void MergeBufferVariables()
{
Debug.Profiler.ProfileResourceFunction!();
_variables = new BufferVariableCollection(false);
for(let buffer in _bufferCollection)
@@ -356,6 +424,8 @@ namespace GlitchyEngine.Renderer
private void AddShaderBuffers(Shader shader, HashSet<String> bufferNames)
{
Debug.Profiler.ProfileResourceFunction!();
if(shader != null)
{
for(let buffer in shader.Buffers)
@@ -364,5 +434,58 @@ namespace GlitchyEngine.Renderer
}
}
}
/// Merges the texture slots of all shaders into one dictionary.
private void MergeTextures()
{
Debug.Profiler.ProfileResourceFunction!();
delete _textures;
_textures = new .();
EnumerateShaderTextures(_vs);
EnumerateShaderTextures(_ps);
}
/** @brief Merges all textures of the given shader into the _textures dictionary.
* @param shader The shader whose textures will be merged into the dictionary.
*/
private void EnumerateShaderTextures<T>(T shader) where T : Shader
{
Debug.Profiler.ProfileResourceFunction!();
//for(var (name, index, texture) in shader.Resources)
for(var shaderEntry in ref shader.Textures)
{
TextureEntry entry;
// Get existing entry or create new
if(!_textures.TryGetValue(shaderEntry.Name, out entry))
{
entry = (shaderEntry.Texture, null, null);
entry.Texture?.AddRef();
}
// Set the corresponding shader resource slot
if(typeof(T) == typeof(VertexShader))
{
entry.VsSlot = &shaderEntry;
}
else if(typeof(T) == typeof(PixelShader))
{
entry.PsSlot = &shaderEntry;
}
// If the entry has no texture but the shader has one -> set texture
if(entry.Texture == null && shaderEntry.Texture != null)
{
entry.Texture = shaderEntry.Texture;
entry.Texture?.AddRef();
}
// save entry
_textures[shaderEntry.Name] = entry;
}
}
}
}
+30 -14
View File
@@ -1,22 +1,24 @@
using System;
using System.Collections;
using GlitchyEngine.Core;
namespace GlitchyEngine.Renderer
{
public class GeometryBinding : RefCounted
public class GeometryBinding : RefCounter
{
internal GraphicsContext _context ~ _?.ReleaseRef();
internal List<VertexBufferBinding> _vertexBuffers = new .() ~ delete _;
internal IndexBuffer _indexBuffer;
internal VertexLayout _vertexLayout;
internal IndexBuffer _indexBuffer ~ _?.ReleaseRef();
internal VertexLayout _vertexLayout ~ _?.ReleaseRef();
internal uint32 _indexByteOffset;
internal uint32 _indexCount;
internal uint32 _instanceCount;
internal PrimitiveTopology _primitiveTopology;
internal uint32 _vertexCount;
public this(GraphicsContext context)
public bool IsIndexed => _indexBuffer != null;
public this()
{
_context = context..AddRef();
}
public ~this()
@@ -25,8 +27,6 @@ namespace GlitchyEngine.Renderer
{
binding.Buffer?.ReleaseRef();
}
_indexBuffer?.ReleaseRef();
}
public VertexBufferBinding GetVertexBuffer(uint32 slot)
@@ -50,6 +50,22 @@ namespace GlitchyEngine.Renderer
[Inline]
public uint32 IndexCount => _indexCount;
public uint32 InstanceCount
{
[Inline]
get => _instanceCount;
[Inline]
set => _instanceCount = value;
}
public uint32 VertexCount
{
[Inline]
get => _vertexCount;
[Inline]
set => _vertexCount = value;
}
[Inline]
public PrimitiveTopology PrimitiveTopology => _primitiveTopology;
@@ -65,7 +81,8 @@ namespace GlitchyEngine.Renderer
public void SetVertexLayout(VertexLayout vertexLayout)
{
_vertexLayout = vertexLayout;
_vertexLayout?.ReleaseRef();
_vertexLayout = vertexLayout..AddRef();
PlatformSetVertexLayout(vertexLayout);
}
@@ -82,8 +99,7 @@ namespace GlitchyEngine.Renderer
public void SetIndexBuffer(IndexBuffer indexBuffer, uint32 byteOffset = 0, uint32 indexCount = (.)-1)
{
_indexBuffer?.ReleaseRef();
_indexBuffer = indexBuffer;
_indexBuffer.AddRef();
_indexBuffer = indexBuffer..AddRef();
_indexByteOffset = byteOffset;
@@ -101,8 +117,8 @@ namespace GlitchyEngine.Renderer
protected extern void PlatformSetIndexBuffer(IndexBuffer indexBuffer);
public extern void Bind(GraphicsContext context = null);
public extern void Bind();
public extern void Unbind(GraphicsContext context = null);
public extern void Unbind();
}
}
+21 -25
View File
@@ -1,16 +1,18 @@
using GlitchyEngine.Math;
using System;
using GlitchyEngine.Core;
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer
{
public class RenderTarget;
public class GraphicsContext : RefCounted
public class GraphicsContext : RefCounter
{
private RasterizerState _currentRasterizerState;
private static GraphicsContext s_GraphicsContext;
public static GraphicsContext Get() => s_GraphicsContext;
public extern SwapChain SwapChain {get;}
protected extern void PlatformConstruct();
/**
* The maximum number of simultaneous rendertargets supported.
*/
@@ -18,14 +20,20 @@ namespace GlitchyEngine.Renderer
public extern void Init();
/**
* Sets a rendertarget.
* @param renderTarget The render target.
* @param slot The slot to which the rendertarget will be bound.
* @remarks When @RenderTarget is null the backbuffer will be bound.
/** @brief Sets a depthtarget.
* @BindRenderTargets has to be called in order to bind the rendertargets.
* @param renderTarget The render target to set. If null, the backbuffer will be set.
* @param slot The slot to which the rendertarget will be bound. If 0 the depth buffer of the current render target will be set. If it has no depthbuffer the current will be unset.
*/
public extern void SetRenderTarget(RenderTarget renderTarget, int slot = 0);
public extern void SetDepthStencilTarget(DepthStencilTarget target);
/** @brief Sets a rendertarget to the given slot.
* @BindRenderTargets has to be called in order to bind the rendertargets.
* @param renderTarget The render target to set. If null, the backbuffer will be set.
* @param slot The slot to which the rendertarget will be bound. If 0 the depth buffer of the current render target will be set. If it has no depthbuffer the current will be unset.
* @param setDepthTarget If set to true the depth stencil target of the given renderTarget will be bound (only applies if slot is 0).
*/
public extern void SetRenderTarget(RenderTarget2D renderTarget, int slot = 0, bool setDepthTarget = true);
/**
* Binds all.
@@ -41,7 +49,7 @@ namespace GlitchyEngine.Renderer
* @param color The color to clear the render target with.
* @remarks When @renderTarget is null the backbuffer will be cleared.
*/
public extern void ClearRenderTarget(RenderTarget renderTarget, ColorRGBA color);
public extern void ClearRenderTarget(RenderTarget2D renderTarget, ColorRGBA color);
//public abstract void SetViewport(Viewport viewport);
@@ -74,21 +82,9 @@ namespace GlitchyEngine.Renderer
public extern void SetIndexBuffer(Buffer buffer, IndexFormat indexFormat = .Index16Bit, uint32 byteOffset = 0);
public void SetRasterizerState(RasterizerState rasterizerState)
{
_currentRasterizerState = rasterizerState;
SetRasterizerStateImpl();
}
protected extern void SetRasterizerStateImpl();
public RasterizerState GetRasterizerState()
{
return _currentRasterizerState;
}
[Inline]
public void SetViewport(Viewport viewport)
internal void SetViewport(Viewport viewport)
{
var viewport;
SetViewports(1, &viewport);

Some files were not shown because too many files have changed in this diff Show More