219 Commits
Author SHA1 Message Date
Simon Lübeß 4874cfe511 Cleanup 2022-05-03 22:52:37 +02:00
Simon Lübeß 63b9270710 DotNet script changes transform 2022-04-27 23:58:50 +02:00
Simon Lübeß 2bac6a8d05 Cleanup + Attach scripts 2022-04-27 13:00:55 +02:00
Simon Lübeß e51fc77c3f Use Init for dotnet commandline 2022-04-27 12:22:53 +02:00
Simon Lübeß 8c53f21690 Even more DotNet stuff, help 2022-04-26 22:36:27 +02:00
Simon Lübeß ad1b37dc64 Some more dotnet stuff 2022-04-23 01:15:33 +02:00
Simon Lübeß 8e46945362 Improved makefile and nethost structure 2022-04-14 13:39:25 +02:00
Simon Lübeß 6a250d4927 DotNet hosting test 2022-04-14 02:24:36 +02:00
Simon Lübeß 9c93ccee47 Removed redundant submodule-entry 2022-04-13 12:32:42 +02:00
Simon Lübeß f1ce63ee3b Removed submodule GlitchyEngine/vendor/DirectXTK 2022-04-13 00:01:05 +02:00
Simon Lübeß e685b4fbba Added DirectXTK to GlitchyEngineHelper 2022-04-13 00:00:41 +02:00
Simon Lübeß 9105c12e33 Added buildcommand 2022-04-12 14:32:00 +02:00
Simon Lübeß 863c264001 UI Tweaks, ChildEnumerator, ComponentAdded-Handlers 2022-04-12 00:48:20 +02:00
Simon Lübeß bec202e23a Settings / SettingsWindow 2022-04-10 13:12:09 +02:00
Simon Lübeß bca72cf0d4 Add / Remove Components 2022-04-10 12:18:43 +02:00
Simon Lübeß af37154071 Better Transform component and edit gui 2022-04-09 12:35:36 +02:00
Simon Lübeß c7fde476e8 Updated DX11 Bindings 2022-04-09 12:34:28 +02:00
Simon Lübeß 604e34a12a Start of ContentManager 2022-04-09 12:34:09 +02:00
Simon Lübeß b94c84d304 GlitchyEngineHelper project 2022-04-09 12:33:05 +02:00
Simon Lübeß d47bc37886 SpriteRenderer Editor, enabled alpha-blending and depth 2022-04-02 00:10:00 +02:00
Simon Lübeß c13b0a26d1 Overhauled component editors 2022-04-01 23:29:35 +02:00
Simon Lübeß e82f70ba02 Native scripting 2022-03-03 01:39:04 +01:00
Simon Lübeß cbee9f59d8 Many ecs, component and editor changes 2022-03-02 01:59:51 +01:00
Simon Lübeß afe36b19df Removed unnecessary "override extern"s 2022-03-02 01:57:04 +01:00
Simon Lübeß 9197a209d6 Ecs changes
- BitArray has always at least sizeof(uint) bits
- Renamed Entity to EcsEntity
- Added value parameter to EcsWorld.AssignComponent
- Matched EcsWorld generic parameters
- Added Scene
- Added Entity
2022-02-28 23:34:52 +01:00
Simon Lübeß ba5ccd7107 Editor window 2022-02-28 18:08:13 +01:00
Simon Lübeß c80ca4d9a1 Cleaned up DX11 report 2022-02-28 12:13:44 +01:00
Simon Lübeß 92fd8bb572 Removed Bind from Texture 2022-02-27 12:30:09 +01:00
Simon Lübeß b341c70a26 Editor changes
- Updated Renderer2D shaders
- Fixed scene viewport having resolution
- Show Grid even if no entity is selected
- Explicit conversion from ImGui.VecX <-> VectorX
- Simplified EditorLayer
2022-02-27 12:21:30 +01:00
Simon Lübeß 8c8f0d9fc7 Basic tilemap 2022-02-26 18:05:56 +01:00
Simon Lübeß 8638b7ac3b ImGuiLayer cleanup 2022-02-26 18:05:47 +01:00
Simon Lübeß be7c3d1d8b Subtextures 2022-02-23 13:41:59 +01:00
Simon Lübeß 699522ff67 Fixed textureviewer 2022-02-22 20:18:37 +01:00
Simon Lübeß 6f999da1b8 Fixed fallback font rendering 2022-02-22 20:18:06 +01:00
Simon Lübeß 373980d5ad SwizzleVectorAttribute no longer generates invalid setters 2022-02-21 17:06:48 +01:00
Simon Lübeß 0db85de067 Release build fixes + ImGui build flag
Fonts still not working
2022-02-18 16:03:24 +01:00
Simon Lübeß fae2bdf084 Updated submodules
Updated submodule GlitchyEngine/vendor/imgui
Updated submodule GlitchyEngine/vendor/msdfgen
2022-02-17 01:12:02 +01:00
Simon Lübeß 0f0afc2a90 Renderer2D improvements + stats 2022-02-16 20:44:31 +01:00
Simon Lübeß 66dad82dc6 Added ColorHSV 2022-02-14 20:40:50 +01:00
Simon Lübeß 0e684af17d Added Window.SetIcon 2022-02-14 20:38:01 +01:00
Simon Lübeß 82898f1d5c Added DeltaTime 2022-02-14 20:37:31 +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
226 changed files with 18717 additions and 1474 deletions
+1 -1
View File
@@ -4,4 +4,4 @@ build/
.vs/
recovery/
vendor/directx
Sandbox/imgui.ini
imgui.ini
+24 -6
View File
@@ -1,9 +1,27 @@
[submodule "GlitchyEngine/vendor/directx"]
path = GlitchyEngine/vendor/directx
url = https://github.com/aharabada/directx-beef.git
[submodule "GlitchyEngine/vendor/imgui-beef"]
path = GlitchyEngine/vendor/imgui-beef
url = https://github.com/aharabada/imgui-beef
[submodule ".\\GlitchyEngine\\vendor\\DirectXTK"]
path = .\\GlitchyEngine\\vendor\\DirectXTK
url = https://github.com/aharabada/DirectXTK-beef.git
[submodule "vendor/lodepng-beef"]
path = vendor/lodepng-beef
url = https://github.com/aharabada/lodepng-beef.git
[submodule "GlitchyEngine/vendor/freetype"]
path = GlitchyEngine/vendor/freetype
url = https://github.com/aharabada/FreeType-beef.git
[submodule "GlitchyEngine/vendor/gltf"]
path = GlitchyEngine/vendor/gltf
url = https://github.com/aharabada/cgltf-beef.git
[submodule "GlitchyEngine/vendor/msdfgen"]
path = GlitchyEngine/vendor/msdfgen
url = https://github.com/aharabada/msdfgen-beef.git
[submodule "GlitchyEngine/vendor/imgui"]
path = GlitchyEngine/vendor/imgui
url = https://github.com/aharabada/ImGui_GlitchyEngine.git
[submodule "GlitchyEngineHelper/vendor/xxHash"]
path = GlitchyEngineHelper/vendor/xxHash
url = https://github.com/Cyan4973/xxHash.git
[submodule "GlitchyEngine/vendor/bon"]
path = GlitchyEngine/vendor/bon
url = https://github.com/EinScott/bon.git
[submodule "GlitchyEngineHelper/vendor/DirectXTK"]
path = GlitchyEngineHelper/vendor/DirectXTK
url = https://github.com/microsoft/DirectXTK.git
+3 -2
View File
@@ -1,5 +1,6 @@
FileVersion = 1
Projects = {Sandbox = {Path = "Sandbox"}, GlitchyEngine = {Path = "GlitchyEngine"}, GlitchLog = {Path = "GlitchLog"}, DirectX = {Path = "GlitchyEngine/vendor/directx/DirectX"}, ImGui = {Path = "GlitchyEngine/vendor/imgui-beef/ImGui"}, ImGuiImplWin32 = {Path = "GlitchyEngine/vendor/imgui-beef/ImGuiImplWin32"}, ImGuiImplDX11 = {Path = "GlitchyEngine/vendor/imgui-beef/ImGuiImplDX11"}, DirectXTK = {Path = "GlitchyEngine/vendor/DirectXTK/DirectXTK-beef"}}
Projects = {Sandbox = {Path = "Sandbox"}, GlitchyEngine = {Path = "GlitchyEngine"}, GlitchLog = {Path = "GlitchLog"}, DirectX = {Path = "GlitchyEngine/vendor/directx/DirectX"}, LodePng = {Path = "vendor/lodepng-beef/lodepng-beef"}, FreeType = {Path = "GlitchyEngine/vendor/freetype"}, cgltf-beef = {Path = "GlitchyEngine/vendor/gltf/cgltf-beef"}, GlitchyEditor = {Path = "GlitchyEditor"}, msdfgen-beef = {Path = "GlitchyEngine/vendor/msdfgen/msdfgen-beef"}, ImGui = {Path = "GlitchyEngine/vendor/imgui/ImGui"}, ImGuiImplDX11 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplDX11"}, ImGuiImplWin32 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplWin32"}, ImGuizmo = {Path = "GlitchyEngine/vendor/imgui/ImGuizmo"}, GlitchyEngineHelper = {Path = "GlitchyEngineHelper"}, bon = {Path = "GlitchyEngine/vendor/bon"}}
WorkspaceFolders = {GlitchyEngine = ["GlitchyEngine", "GlitchLog", "GlitchyEngineHelper"], "GlitchyEngine/Dependencies" = ["cgltf-beef", "DirectX", "FreeType", "ImGui", "ImGuiImplDX11", "ImGuiImplWin32", "ImGuizmo", "LodePng", "msdfgen-beef", "bon"]}
[Workspace]
StartupProject = "Sandbox"
StartupProject = "GlitchyEditor"
+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"
Binary file not shown.
@@ -0,0 +1,62 @@
Texture2D Texture : register(t0);
SamplerState Sampler : register(s0);
cbuffer Constants : register(b0)
{
float4x4 ViewProjection;
};
struct VS_Input
{
float2 Position : POSITION;
float2 Texcoord : TEXCOORD0;
float4x4 Transform : TRANSFORM;
float4 Color : COLOR;
float4 UVTransform : TEXCOORD1;
float InnerRadius : TEXCOORD2;
};
struct PS_Input
{
float4 Position : SV_Position;
float2 RawPos : TEXCOORD0;
float2 Texcoord : TEXCOORD1;
float4 Color : COLOR;
float InnerRadius : TEXCOORD2;
};
PS_Input VS(VS_Input input)
{
PS_Input output;
output.Position = mul(ViewProjection, mul(input.Transform, float4(input.Position, 0.0f, 1.0f)));
output.Texcoord = input.UVTransform.xy + input.UVTransform.zw * input.Texcoord;
output.RawPos = input.Position;
output.Color = input.Color;
output.InnerRadius = input.InnerRadius;
return output;
}
float4 PS(PS_Input input) : SV_Target0
{
float2 uv = input.RawPos * 2;
float distance = 1.0f - length(uv);
// Smoothing edges based on derivative (not sure if RawPos is the best value for this)
float f = fwidth(input.RawPos.x) + fwidth(input.RawPos.y);
float amount = smoothstep(0.0f, f, distance);
amount *= smoothstep(input.InnerRadius + f, input.InnerRadius, distance);
// Discard invisible pixels
clip(amount - 0.5f);
float4 color = Texture.Sample(Sampler, input.Texcoord) * input.Color;
color.a *= amount;
return color;
}
#effect[VS=VS, PS=PS]
@@ -0,0 +1,31 @@
cbuffer Constants : register(b0)
{
float4x4 ViewProjection;
float4 Color;
};
struct VS_Input
{
float4 Position : POSITION;
};
struct PS_Input
{
float4 Position : SV_Position;
};
PS_Input VS(VS_Input input)
{
PS_Input output;
output.Position = mul(ViewProjection, input.Position);
return output;
}
float4 PS(PS_Input input) : SV_Target0
{
return Color;
}
#effect[VS=VS, PS=PS]
@@ -0,0 +1,93 @@
Texture2D<float3> Texture : register(t0);
SamplerState Sampler : register(s0);
cbuffer Constants
{
float4x4 ViewProjection;
float2 UnitRange;
float screenPixelRange = 2;
}
struct VS_Input
{
float2 Position : POSITION;
float2 Texcoord : TEXCOORD0;
float4x4 Tranform : TRANSFORM;
float4 Color : COLOR;
float4 UVTransform : TEXCOORD1;
};
struct PS_Input
{
float4 Position : SV_Position;
float2 TexCoord : TEXCOORD0;
float4 Color : COLOR;
};
PS_Input VS(VS_Input input)
{
PS_Input output;
output.Position = mul(ViewProjection, mul(input.Tranform, float4(input.Position, 0.0f, 1.0f)));
output.TexCoord = input.UVTransform.xy + input.UVTransform.zw * input.Texcoord;
output.Color = input.Color;
return output;
}
/*
in vec2 texCoord;
out vec4 color;
uniform sampler2D msdf;
uniform vec4 bgColor;
uniform vec4 fgColor;
float median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
void main() {
vec3 msd = texture(msdf, texCoord).rgb;
float sd = median(msd.r, msd.g, msd.b);
float screenPxDistance = screenPxRange()*(sd - 0.5);
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
color = mix(bgColor, fgColor, opacity);
}
*/
float median(float r, float g, float b)
{
return max(min(r, g), min(max(r, g), b));
}
float ScreenPxRange(float2 texcoord)
{
float2 screenTexSize = 1.0f / fwidth(texcoord);
return max(0.5f * dot(UnitRange, screenTexSize), 1.0f);
}
float4 PS(PS_Input input) : SV_Target0
{
float3 msd = Texture.Sample(Sampler, input.TexCoord);
float sd = median(msd.r, msd.g, msd.b);
float screenPxDistance = ScreenPxRange(input.TexCoord) * (sd - 0.5);
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
return float4(input.Color.rgb, opacity * input.Color.a);
}
/*
// 2D
float4 PS(PS_Input input) : SV_Target0
{
float3 msd = Texture.Sample(Sampler, input.TexCoord).rgb;
float sd = median(msd.r, msd.g, msd.b);
float screenPxDistance = screenPixelRange*(sd - 0.5);
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
return float4(input.Color.rgb, opacity * input.Color.a);
}
*/
#effect[VS=VS, PS=PS]
@@ -0,0 +1,41 @@
Texture2D Texture : register(t0);
SamplerState Sampler : register(s0);
cbuffer Constants : register(b0)
{
float4x4 ViewProjection;
};
struct VS_Input
{
float2 Position : POSITION;
float2 Texcoord : TEXCOORD0;
float4x4 Transform : TRANSFORM;
float4 Color : COLOR;
float4 UVTransform : TEXCOORD1;
};
struct PS_Input
{
float4 Position : SV_Position;
float2 Texcoord : TEXCOORD;
float4 Color : COLOR;
};
PS_Input VS(VS_Input input)
{
PS_Input output;
output.Position = mul(ViewProjection, mul(input.Transform, float4(input.Position, 0.0f, 1.0f)));
output.Texcoord = input.UVTransform.xy + input.UVTransform.zw * input.Texcoord;
output.Color = input.Color;
return output;
}
float4 PS(PS_Input input) : SV_Target0
{
return Texture.Sample(Sampler, input.Texcoord) * input.Color;
}
#effect[VS=VS, PS=PS]
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

@@ -0,0 +1,298 @@
using ImGui;
using GlitchyEngine.World;
using System;
using GlitchyEngine.Math;
using System.Collections;
namespace GlitchyEditor.EditWindows
{
using internal GlitchyEngine.World.TransformComponent;
class ComponentEditWindow : EditorWindow
{
public const String s_WindowTitle = "Components";
private EntityHierarchyWindow _entityHierarchyWindow;
private List<(Type, ComponentAttribute attribute)> _componentTypes = new .() ~ delete _;
public this(EntityHierarchyWindow entityHierarchyWindow)
{
_entityHierarchyWindow = entityHierarchyWindow;
for (let type in Type.Types)
{
if (let componentAttribute = type.GetCustomAttribute<ComponentAttribute>())
{
_componentTypes.Add((type, componentAttribute));
}
}
}
protected override void InternalShow()
{
ImGui.PushStyleVar(.WindowMinSize, ImGui.Vec2(1000, 100));
if(!ImGui.Begin(s_WindowTitle, &_open, .None))
{
ImGui.PopStyleVar();
ImGui.End();
return;
}
if(_entityHierarchyWindow.SelectedEntities.Count == 1)
{
Entity entity = _entityHierarchyWindow.SelectedEntities.Front;
ShowComponents(entity);
}
ImGui.PopStyleVar();
ImGui.End();
}
private void ShowComponents(Entity entity)
{
ShowNameComponentEditor(entity);
ShowComponentEditor<TransformComponent>("Transform", entity, => ShowTransformComponentEditor);
ShowComponentEditor<CameraComponent>("Camera", entity, => ShowCameraComponentEditor, => ShowComponentContextMenu<CameraComponent>);
ShowComponentEditor<SpriterRendererComponent>("Sprite Renderer", entity, => ShowSpriteRendererComponentEditor, => ShowComponentContextMenu<SpriterRendererComponent>);
ShowAddComponentButton(entity);
}
private static void ShowComponentContextMenu<TComponent>(Entity entity, TComponent* component) where TComponent: struct, new
{
if (ImGui.Selectable("Remove Component"))
entity.RemoveComponent<TComponent>();
}
private static void ShowComponentEditor<TComponent>(String header, Entity entity, function void(Entity, TComponent*) showComponentEditor, function void(Entity, TComponent*) showComponentContextMenu = null) where TComponent: struct, new
{
if (!entity.HasComponent<TComponent>())
return;
TComponent* component = entity.GetComponent<TComponent>();
ImGui.PushID(header);
bool nodeOpen = ImGui.TreeNodeEx(header.CStr(), .DefaultOpen | .AllowItemOverlap | .Framed | .SpanFullWidth);
if (showComponentContextMenu != null)
{
ImGui.SameLine(ImGui.GetWindowContentRegionMax().x - ImGui.CalcTextSize("...").x - 2 * ImGui.GetStyle().FramePadding.x);
if (ImGui.SmallButton("..."))
{
ImGui.OpenPopup("component_popup");
}
if (ImGui.BeginPopup("component_popup"))
{
showComponentContextMenu(entity, component);
ImGui.EndPopup();
}
}
if (nodeOpen)
{
showComponentEditor(entity, component);
ImGui.TreePop();
}
ImGui.PopID();
}
private static void ShowNameComponentEditor(Entity entity)
{
if (!entity.HasComponent<DebugNameComponent>())
return;
char8[256] nameBuffer = default;
DebugNameComponent* component = entity.GetComponent<DebugNameComponent>();
String name = null;
if(component != null)
{
name = component.DebugName;
}
else
{
name = scope:: $"Entity {entity.Handle.[Friend]Index}";
}
// Copy name to buffer
Internal.MemCpy(&nameBuffer, name.Ptr, Math.Min(nameBuffer.Count, name.Length));
if(ImGui.InputText("Name", &nameBuffer, nameBuffer.Count))
{
if(component == null)
{
component = entity.AddComponent<DebugNameComponent>();
}
component.DebugName.Clear();
component.DebugName.Append(&nameBuffer);
}
}
private static void ShowTransformComponentEditor(Entity entity, TransformComponent* transform)
{
float textWidth = ImGui.CalcTextSize("Position".CStr()).x;
textWidth = Math.Max(textWidth, ImGui.CalcTextSize("Rotation".CStr()).x);
textWidth = Math.Max(textWidth, ImGui.CalcTextSize("Scale".CStr()).x);
textWidth += ImGui.GetStyle().FramePadding.x * 3.0f;
Vector3 position = transform.Position;
if (ImGui.EditVector3("Position", ref position, .Zero, 0.1f, textWidth))
transform.Position = position;
Vector3 rotationEuler = MathHelper.ToDegrees(transform.EditorRotationEuler);
if (ImGui.EditVector3("Rotation", ref rotationEuler, .Zero, 0.1f, textWidth))
transform.EditorRotationEuler = MathHelper.ToRadians(rotationEuler);
Vector3 scale = transform.Scale;
if (ImGui.EditVector3("Scale", ref scale, .One, 0.1f, textWidth))
transform.Scale = scale;
}
static String[] strings = new String[]("Orthographic", "Perspective", "Perspective (Infinite)") ~ delete _;
private static void ShowCameraComponentEditor(Entity entity, CameraComponent* cameraComponent)
{
ImGui.Checkbox("Primary", &cameraComponent.Primary);
var camera = ref cameraComponent.Camera;
String typeName = strings[camera.ProjectionType.Underlying];
if (ImGui.BeginCombo("Projection", typeName.CStr()))
{
for (int i = 0; i < 3; i++)
{
bool isSelected = (typeName == strings[i]);
if (ImGui.Selectable(strings[i], isSelected))
{
camera.ProjectionType = (.)i;
}
if (isSelected)
ImGui.SetItemDefaultFocus();
}
ImGui.EndCombo();
}
if (camera.ProjectionType == .Perspective)
{
float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY);
if (ImGui.DragFloat("Fov Y", &fovY, 0.1f))
camera.PerspectiveFovY = MathHelper.ToRadians(fovY);
float near = camera.PerspectiveNearPlane;
if (ImGui.DragFloat("Near", &near, 0.1f))
camera.PerspectiveNearPlane = near;
float far = camera.PerspectiveFarPlane;
if (ImGui.DragFloat("Far", &far, 0.1f))
camera.PerspectiveFarPlane = far;
}
else if (camera.ProjectionType == .InfinitePerspective)
{
float fovY = MathHelper.ToDegrees(camera.PerspectiveFovY);
if (ImGui.DragFloat("Vertical FOV", &fovY, 0.1f))
camera.PerspectiveFovY = MathHelper.ToRadians(fovY);
float near = camera.PerspectiveNearPlane;
if (ImGui.DragFloat("Near", &near, 0.1f))
camera.PerspectiveNearPlane = near;
}
else if (camera.ProjectionType == .Orthographic)
{
float size = camera.OrthographicHeight;
if (ImGui.DragFloat("Size", &size, 0.1f))
camera.OrthographicHeight = size;
float near = camera.OrthographicNearPlane;
if (ImGui.DragFloat("Near", &near, 0.1f))
camera.OrthographicNearPlane = near;
float far = camera.OrthographicFarPlane;
if (ImGui.DragFloat("Far", &far, 0.1f))
camera.OrthographicFarPlane = far;
}
bool fixedAspectRatio = camera.FixedAspectRatio;
if (ImGui.Checkbox("Fixed Aspect Ratio", &fixedAspectRatio))
camera.FixedAspectRatio = fixedAspectRatio;
if (fixedAspectRatio)
{
float aspect = camera.AspectRatio;
if (ImGui.DragFloat("Aspect Ratio", &aspect, 0.1f))
camera.AspectRatio = aspect;
}
}
private static void ShowSpriteRendererComponentEditor(Entity entity, SpriterRendererComponent* spriteRendererComponent)
{
ImGui.ColorEdit4("Color", ref spriteRendererComponent.Color);
}
private static void ShowAddComponentButton(Entity entity)
{
static char8[128] searchBuffer = .();
static StringView searchFilter = .();
static float buttonWidth = 100;
ImGui.NewLine();
ImGui.Separator();
ImGui.NewLine();
void ShowComponentButton<TComponent>(String name) where TComponent : struct, new
{
float textWidth = ImGui.CalcTextSize(name.CStr()).x;
buttonWidth = Math.Max(buttonWidth, textWidth + ImGui.GetStyle().FramePadding.x * 2);
if (name.Contains(searchFilter, true) && ImGui.Selectable(name))
{
if (!entity.HasComponent<TComponent>())
entity.AddComponent<TComponent>();
}
}
float textWidth = ImGui.CalcTextSize("Add Component...").x;
buttonWidth = Math.Max(buttonWidth, textWidth + ImGui.GetStyle().FramePadding.x * 2);
ImGui.PushItemWidth(buttonWidth);
ImGui.NewLine();
ImGui.SameLine(ImGui.GetContentRegionMax().x / 2 - buttonWidth / 2);
if (ImGui.BeginCombo("##add_component_combo", "Add Component...", .NoArrowButton))
{
if (ImGui.InputText("##search_component_name", &searchBuffer, (.)searchBuffer.Count))
{
searchFilter = .(&searchBuffer);
}
ImGui.Separator();
ShowComponentButton<CameraComponent>("Camera");
ShowComponentButton<SpriterRendererComponent>("Sprite Renderer");
ImGui.EndCombo();
}
ImGui.PopItemWidth();
}
}
}
@@ -0,0 +1,31 @@
namespace GlitchyEditor.EditWindows
{
abstract class EditorWindow
{
protected Editor _editor;
protected bool _open = true;
protected bool _hasFocus;
public bool Open
{
get => _open;
set => _open = value;
}
public bool HasFocus
{
get => _hasFocus;
}
public void Show()
{
if(!_open)
return;
InternalShow();
}
protected abstract void InternalShow();
}
}
@@ -0,0 +1,477 @@
using GlitchyEngine.Collections;
using GlitchyEngine.World;
using ImGui;
using System;
using System.Collections;
using GlitchyEngine;
namespace GlitchyEditor.EditWindows
{
using internal GlitchyEditor;
/// A window for viewing and editing the scene hierarchy
class EntityHierarchyWindow : EditorWindow
{
public const String s_WindowTitle = "Entity Hierarchy";
/// Buffer for the entity search string.
private char8[64] _entitySearchChars;
private Scene _scene;
private List<Entity> _selectedEntities = new .() ~ delete _;
public List<Entity> SelectedEntities => _selectedEntities;
public this(Scene scene)
{
SetContext(scene);
}
public void SetContext(Scene scene)
{
_scene = scene;
}
protected override void InternalShow()
{
if(!ImGui.Begin(s_WindowTitle, &_open, .MenuBar))
{
ImGui.End();
return;
}
ShowEntityHierarchyMenuBar();
if (ImGui.BeginPopupContextWindow(s_WindowTitle))
{
Show_ContextMenu_Create(true, false, false);
ImGui.EndPopup();
}
ShowEntityHierarchy();
if ((ImGui.IsMouseDown(.Left) || ImGui.IsMouseDown(.Right)) && !ImGui.IsAnyItemHovered() && !ImGui.GetIO().KeyCtrl && ImGui.IsWindowHovered(.AllowWhenBlockedByPopup))
_selectedEntities.Clear();
ImGui.End();
}
/// Returns whether or not all selected entities have the same parent.
internal bool AllSelectionsOnSameLevel()
{
EcsEntity? parent = .InvalidEntity;
for(var selectedEntity in _selectedEntities)
{
var transformComponent = selectedEntity.GetComponent<TransformComponent>();
if(parent == .InvalidEntity)
{
parent = transformComponent.Parent;
}
else if(transformComponent.Parent != parent)
{
return false;
}
}
return true;
}
/// Finds all children of the given entity and stores their IDs in the given list.
internal void FindChildren(EcsEntity entity, List<EcsEntity> entities)
{
for(var (child, childTransform) in _scene.[Friend]_ecsWorld.Enumerate<TransformComponent>())
{
if(childTransform.Parent == entity)
{
if(!entities.Contains(child))
entities.Add(child);
FindChildren(child, entities);
}
}
}
/// Deletes all selected entities and their children.
internal void DeleteSelectedEntities()
{
for (var entity in _selectedEntities)
{
_scene.DestroyEntity(entity, true);
}
}
private void ShowEntityHierarchyMenuBar()
{
if(ImGui.BeginMenuBar())
{
Show_ContextMenu_Create(true, true, true);
Show_ContextMenu_Delete();
if(ImGui.MenuItem("Delete", null, false, !_selectedEntities.IsEmpty) ||
(Input.IsKeyPressed(.Delete) && ImGui.IsWindowHovered()))
{
DeleteSelectedEntities();
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Deletes the selected Entities and their children.");
ImGui.Text("Search:");
ImGui.InputText(String.Empty, &_entitySearchChars, (.)_entitySearchChars.Count);
ImGui.EndMenuBar();
}
}
/// Creates a new entity that is a child of the given entity.
private void CreateChild(Entity? entity)
{
var newEntity = _scene.CreateEntity();
var transformCmp = newEntity.GetComponent<TransformComponent>();
// Last entity in list is the entity that has been selected last.
transformCmp.Parent = entity?.Handle ?? .InvalidEntity;
}
/// Creates a new entity that is a parent of the selected entities.
private void CreateParent()
{
if (_selectedEntities.IsEmpty || !AllSelectionsOnSameLevel())
{
Log.EngineLogger.Error("Cannot create parent entity.");
return;
}
var commonParent = _selectedEntities.Front.GetComponent<TransformComponent>();
var newEntity = _scene.CreateEntity();
if(commonParent != null)
{
// parent of selected entities is parent of the new entity.
// (which is why this doesn't work if the entities don't have the same parent)
var newEntityTransform = newEntity.GetComponent<TransformComponent>();
newEntityTransform.Parent = commonParent.Parent;
}
// new entity is parent of all selected entities.
for(var selectedEntity in _selectedEntities)
{
var selectedTransform = selectedEntity.GetComponent<TransformComponent>();
selectedTransform.Parent = newEntity.Handle;
}
}
private void Show_ContextMenu_Create(bool allowEmpty = true, bool allowChild = true, bool allowParent = true)
{
if (ImGui.BeginMenu("Create"))
{
if (allowEmpty)
{
if(ImGui.MenuItem("Empty Entity"))
{
if (_selectedEntities.IsEmpty)
_scene.CreateEntity();
else
{
Entity? parent = _selectedEntities.Back.Parent;
CreateChild(parent);
}
}
if(ImGui.IsItemHovered())
{
ImGui.SetTooltip("Create a new Entity.");
}
}
if (allowChild)
{
if(ImGui.MenuItem("Empty Child", null, false, !_selectedEntities.IsEmpty))
{
CreateChild(_selectedEntities.Back);
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity that is a child of the selected entity.");
}
if (allowParent)
{
if(ImGui.MenuItem("Parent", null, false, !_selectedEntities.IsEmpty && AllSelectionsOnSameLevel()))
{
CreateParent();
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity that is the parent of the selected entities.");
}
ImGui.EndMenu();
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Create a new Entity.");
}
private bool Show_ContextMenu_Delete()
{
bool deleted = false;
if(ImGui.MenuItem("Delete", null, false, !_selectedEntities.IsEmpty))
{
DeleteSelectedEntities();
deleted = true;
}
if(ImGui.IsItemHovered())
ImGui.SetTooltip("Deletes the selected Entities and their children.");
return deleted;
}
private void ImGuiPrintEntityTree(TreeNode<Entity> tree)
{
String name = null;
var nameComponent = tree.Value.GetComponent<DebugNameComponent>();
if(nameComponent != null)
{
name = nameComponent.DebugName;
}
else
{
name = scope:: $"Entity {(tree.Value.Handle.[Friend]Index)}";
}
ImGui.TreeNodeFlags flags = .OpenOnArrow | .DefaultOpen | .SpanAvailWidth;
if(tree.Children.Count == 0)
flags |= .Leaf;
bool inSelectedList = _selectedEntities.Contains(tree.Value);
if(inSelectedList)
flags |= .Selected;
bool isOpen = ImGui.TreeNodeEx((void*)(uint)tree.Value.Handle.[Friend]Index, flags, $"{name}");
ImGui.PushID((void*)(uint)tree.Value.Handle.[Friend]Index);
bool deleted = false;
if (ImGui.BeginPopupContextItem("treeNodePopup"))
{
Show_ContextMenu_Create(true, true, true);
deleted = Show_ContextMenu_Delete();
ImGui.EndPopup();
}
ImGui.PopID();
if (deleted)
return;
if(ImGui.BeginDragDropSource())
{
ImGui.SetDragDropPayload("DND_Entity", &tree.Value, sizeof(Entity));
ImGui.Text(name);
ImGui.EndDragDropSource();
}
if(ImGui.BeginDragDropTarget())
{
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("DND_Entity");
if(payload != null)
{
Log.ClientLogger.AssertDebug(payload.DataSize == sizeof(Entity));
Entity movedEntity = *(Entity*)payload.Data;
bool dropLegal = true;
Entity walker = tree.Value;
// make sure the dropped entity is not a parent of the entity we dropped it on.
while(true)
{
var parentTransform = walker.GetComponent<TransformComponent>();
if(parentTransform.Parent == .InvalidEntity)
{
dropLegal = true;
break;
}
else if(parentTransform.Parent == movedEntity.Handle)
{
dropLegal = false;
break;
}
walker = .(parentTransform.Parent, _scene);
}
if(dropLegal)
{
var movedEntityTransform = movedEntity.GetComponent<TransformComponent>();
movedEntityTransform.Parent = tree.Value.Handle;
}
}
ImGui.EndDragDropTarget();
}
bool clicked = ImGui.IsItemClicked(.Left);
bool clickedRight = ImGui.IsItemClicked(.Right);
if(isOpen)
{
for(var child in tree.Children)
{
ImGuiPrintEntityTree(child);
}
ImGui.TreePop();
}
if (clicked || clickedRight)
{
if (inSelectedList && !clickedRight)
{
_selectedEntities.Remove(tree.Value);
inSelectedList = false;
}
else
{
if (!ImGui.GetIO().KeyCtrl && !clickedRight)
{
_selectedEntities.Clear();
}
_selectedEntities.Add(tree.Value);
inSelectedList = true;
}
}
}
private void ShowEntityHierarchy()
{
StringView searchString = StringView(&_entitySearchChars);
if(searchString.IsWhiteSpace)
{
// Show entity hierarchy as tree
TreeNode<Entity> root = scope .(Entity(.InvalidEntity, _scene));
TreeNode<Entity> InsertIntoTree(Entity entity)
{
var transform = entity.GetComponent<TransformComponent>();
if(transform == null)
{
return root.AddChild(entity);
}
else
{
var parentEntity = Entity(transform.Parent, _scene);
var parentNode = root.FindNode(parentEntity);
if(parentNode == null)
parentNode = InsertIntoTree(parentEntity);
return parentNode.AddChild(entity);
}
}
for(var entityId in _scene.[Friend]_ecsWorld.Enumerate())
{
Entity entity = .(entityId, _scene);
//if (!entity.HasComponent<EditorComponent>())
InsertIntoTree(entity);
}
if(ImGui.TreeNodeEx("Scene", .DefaultOpen))
{
if(ImGui.BeginDragDropTarget())
{
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("DND_Entity");
if(payload != null)
{
Log.ClientLogger.AssertDebug(payload.DataSize == sizeof(Entity));
Entity movedEntity = *(Entity*)payload.Data;
// Also mark transform as dirty
var transformComponent = movedEntity.GetComponent<TransformComponent>();
transformComponent.Parent = .InvalidEntity;
//transformComponent?.IsDirty = true;
}
ImGui.EndDragDropTarget();
}
for(var child in root.Children)
{
ImGuiPrintEntityTree(child);
}
ImGui.TreePop();
}
}
// Otherwise
else
{
// Show search results as flat list
List<StringView> searchTokens = new:ScopedAlloc! .(searchString.Split(' ', .RemoveEmptyEntries));
worldEnumeration:
for(var entityId in _scene.[Friend]_ecsWorld.Enumerate())
{
Entity entity = .(entityId, _scene);
String name = null;
var nameComponent = entity.GetComponent<DebugNameComponent>();
if(nameComponent != null)
{
name = nameComponent.DebugName;
}
else
{
name = scope:worldEnumeration $"Entity {entityId.[Friend]Index}";
}
StringView nameView = StringView(name);
for(var token in searchTokens)
{
if(nameView.IndexOf(token, true) == -1)
{
continue worldEnumeration;
}
}
ImGuiPrintEntityTree(scope .(entity));
}
}
}
}
}
@@ -0,0 +1,117 @@
using System;
using ImGui;
using GlitchyEngine.Renderer;
using GlitchyEngine.Math;
using GlitchyEngine;
using GlitchyEngine.World;
using ImGuizmo;
namespace GlitchyEditor.EditWindows
{
class SceneViewportWindow : EditorWindow
{
//public OldCamera _camera;
public const String s_WindowTitle = "Scene";
private RenderTarget2D _renderTarget ~ _?.ReleaseRef();
public Event<EventHandler<Vector2>> ViewportSizeChangedEvent ~ _.Dispose();
public RenderTarget2D RenderTarget
{
get => _renderTarget;
set
{
if(_renderTarget == value)
return;
SetReference!(_renderTarget, value);
}
}
public this(Editor editor)
{
_editor = editor;
}
private ImGui.Vec2 oldViewportSize;
private bool viewPortChanged;
public Entity CameraEntity { get; set; }
protected override void InternalShow()
{
ImGui.PushStyleVar(.WindowPadding, ImGui.Vec2(1, 1));
defer ImGui.PopStyleVar();
if(!ImGui.Begin(s_WindowTitle, &_open, .NoScrollbar))
{
ImGui.End();
return;
}
if(ImGui.IsWindowHovered() && Input.IsMouseButtonPressing(.RightButton))
{
var currentWindow = ImGui.GetCurrentWindow();
ImGui.FocusWindow(currentWindow);
}
_hasFocus = ImGui.IsWindowFocused();
var viewportSize = ImGui.GetContentRegionAvail();
if(_renderTarget != null)
{
ImGui.Image(_renderTarget, viewportSize);
}
DrawImGuizmo(viewportSize);
ImGui.End();
if(oldViewportSize != viewportSize)
{
ViewportSizeChangedEvent.Invoke(this, (Vector2)viewportSize);
viewPortChanged = true;
oldViewportSize = viewportSize;
}
}
private void DrawImGuizmo(ImGui.Vec2 viewportSize)
{
ImGuizmo.SetDrawlist();
var topLeft = ImGui.GetWindowPos();
var cntMin = ImGui.GetWindowContentRegionMin();
topLeft.x += cntMin.x;
topLeft.y += cntMin.y;
ImGuizmo.SetRect(topLeft.x, topLeft.y, viewportSize.x, viewportSize.y);
var cameraTransformCmp = CameraEntity.GetComponent<TransformComponent>();
var view = cameraTransformCmp.WorldTransform.Invert();
var cameraCmp = CameraEntity.GetComponent<CameraComponent>();
var projection = cameraCmp.Camera.Projection;
Matrix mat = .Identity;
ImGuizmo.DrawGrid((.)&view, (.)&projection, (.)&mat, 10);
if(_editor.SelectedEntities.Count > 0)
{
var entity = _editor.SelectedEntities.Front;
var transformCmp = _editor.World.GetComponent<TransformComponent>(entity);
var transform = transformCmp.LocalTransform;
ImGuizmo.SetRect(topLeft.x, topLeft.y, viewportSize.x, viewportSize.y);
ImGuizmo.Manipulate((.)&view, (.)&projection, .TRANSLATE, .LOCAL, (.)&transform);
transformCmp.LocalTransform = transform;
}
}
}
}
+136
View File
@@ -0,0 +1,136 @@
using GlitchyEngine.World;
using ImGui;
using System;
using System.Collections;
using GlitchyEngine.Collections;
using GlitchyEditor.EditWindows;
using GlitchyEngineHelper;
using System.IO;
using GlitchyEngineHelper.DotNet;
namespace GlitchyEditor
{
class Editor
{
private EcsWorld _ecsWorld;
private Scene _scene;
private EntityHierarchyWindow _entityHierarchyWindow ~ delete _;
private ComponentEditWindow _componentEditWindow ~ delete _;
private SceneViewportWindow _sceneViewportWindow = new .(this) ~ delete _;
private List<EcsEntity> _selectedEntities = new .() ~ delete _;
public EcsWorld World => _ecsWorld;
public List<EcsEntity> SelectedEntities => _selectedEntities;
public EntityHierarchyWindow EntityHierarchyWindow => _entityHierarchyWindow;
public ComponentEditWindow ComponentEditWindow => _componentEditWindow;
public SceneViewportWindow SceneViewportWindow => _sceneViewportWindow;
/// Creates a new editor for the given world
public this(Scene scene)
{
String exePath = Environment.GetExecutableFilePath(.. scope String());
String exeDir = Path.GetDirectoryPath(exePath, .. scope String());
String assemblyPath = scope String(exeDir, "/DotNetScriptingHelper.dll");
DotNetContext dotty = new DotNetContext(assemblyPath);
defer delete dotty;
dotty.Init();
dotty.GetFunctionPointerUnmanagedCallersOnly("DotNetScriptingHelper.ScriptableEntity, DotNetScriptingHelper", "CreateInstance", out DotNetScriptComponent.CreateInstanceFn);
dotty.GetFunctionPointerUnmanagedCallersOnly("DotNetScriptingHelper.ScriptableEntity, DotNetScriptingHelper", "UpdateEntity", out DotNetScriptComponent.UpdateInstanceFn);
dotty.GetFunctionPointerUnmanagedCallersOnly("DotNetScriptingHelper.ScriptableEntity, DotNetScriptingHelper", "DestroyEntity", out DotNetScriptComponent.DestroyInstanceFn);
_scene = scene;
_ecsWorld = _scene.[Friend]_ecsWorld;
_entityHierarchyWindow = new EntityHierarchyWindow(_scene);
_componentEditWindow = new ComponentEditWindow(_entityHierarchyWindow);
}
public void Update()
{
_entityHierarchyWindow.Show();
_componentEditWindow.Show();
_sceneViewportWindow.Show();
}
/// Creates a new entity with a transform component.
internal EcsEntity CreateEntityWithTransform()
{
var entity = _ecsWorld.NewEntity();
var transformComponent = ref *_ecsWorld.AssignComponent<TransformComponent>(entity);
transformComponent = TransformComponent();
var nameComponent = ref *_ecsWorld.AssignComponent<DebugNameComponent>(entity);
nameComponent.SetName("Entity");
return entity;
}
/// Returns whether or not all selected entities have the same parent.
internal bool AllSelectionsOnSameLevel()
{
EcsEntity? parent = .InvalidEntity;
for(var selectedEntity in _selectedEntities)
{
var parentComponent = _ecsWorld.GetComponent<ParentComponent>(selectedEntity);
if(parent == .InvalidEntity)
{
parent = parentComponent?.Entity;
}
else if(parentComponent?.Entity != parent)
{
return false;
}
}
return true;
}
/// Finds all children of the given entity and stores their IDs in the given list.
internal void FindChildren(EcsEntity entity, List<EcsEntity> entities)
{
for(var (child, childParent) in _ecsWorld.Enumerate<ParentComponent>())
{
if(childParent.Entity == entity)
{
if(!entities.Contains(child))
entities.Add(child);
FindChildren(child, entities);
}
}
}
/// Deletes all selected entities and their children.
internal void DeleteSelectedEntities()
{
List<EcsEntity> entities = scope .();
for(var entity in _selectedEntities)
{
entities.Add(entity);
FindChildren(entity, entities);
}
for(var entity in entities)
{
_ecsWorld.RemoveEntity(entity);
}
_selectedEntities.Clear();
}
}
}
+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();
}
}
}
@@ -0,0 +1,63 @@
using GlitchyEngine;
using GlitchyEngine.Math;
using GlitchyEngine.World;
namespace GlitchyEditor
{
class EditorCameraController : ScriptableEntity
{
private float _cameraTranslationSpeed = 2.0f;
private float _cameraRotationSpeedX = 0.001f;
private float _cameraRotationSpeedY = 0.001f;
public bool IsEnabled = false;
protected override void OnUpdate(GameTime gt)
{
if (!IsEnabled)
return;
Debug.Profiler.ProfileFunction!();
Vector3 movement = .();
if(Input.IsKeyPressed(Key.W))
movement.Z += 1;
if(Input.IsKeyPressed(Key.S))
movement.Z -= 1;
if(Input.IsKeyPressed(Key.A))
movement.X -= 1;
if(Input.IsKeyPressed(Key.D))
movement.X += 1;
if(Input.IsKeyPressed(Key.Space))
movement.Y += 1;
if(Input.IsKeyPressed(Key.Control))
movement.Y -= 1;
var transformComponent = transform;
if(movement != .Zero)
{
movement.Normalize();
movement *= (float)(gt.FrameTime.TotalSeconds) * _cameraTranslationSpeed;
Matrix view = transformComponent.WorldTransform.Invert();
Vector4 delta = Vector4(movement, 1.0f) * view;
transformComponent.Position = transformComponent.Position + delta.XYZ;
}
// Camera rotation
var mouseDelta = Input.GetMouseMovement();
float rotY = mouseDelta.X * _cameraRotationSpeedX;
float rotX = mouseDelta.Y * _cameraRotationSpeedY;
transformComponent.RotationEuler = transformComponent.RotationEuler + .(rotX, rotY, 0);
}
}
}
+270
View File
@@ -0,0 +1,270 @@
using System;
using ImGui;
using GlitchyEngine;
using GlitchyEditor.EditWindows;
using GlitchyEngine.Events;
using GlitchyEngine.ImGui;
using GlitchyEngine.Math;
using GlitchyEngine.Renderer;
using GlitchyEngine.World;
namespace GlitchyEditor
{
class EditorLayer : Layer
{
RasterizerState _rasterizerState ~ _?.ReleaseRef();
RasterizerState _rasterizerStateClockWise ~ _?.ReleaseRef();
GraphicsContext _context ~ _.ReleaseRef();
BlendState _alphaBlendState ~ _.ReleaseRef();
BlendState _opaqueBlendState ~ _.ReleaseRef();
DepthStencilState _depthStencilState ~ _.ReleaseRef();
Scene _scene = new Scene() ~ delete _;
Editor _editor ~ delete _;
RenderTarget2D _viewportTarget ~ _?.ReleaseRef();
SettingsWindow _settingsWindow = new .() ~ delete _;
Entity _cameraEntity;
Entity _otherCameraEntity;
class CameraController : ScriptableEntity
{
protected override void OnCreate()
{
Log.EngineLogger.Trace("Cam controller created!");
}
protected override void OnUpdate(GameTime gameTime)
{
var transformCmp = GetComponent<TransformComponent>();
Vector3 position = transformCmp.Position;
if (Input.IsKeyPressed(Key.A))
{
position.X -= gameTime.DeltaTime;
}
if (Input.IsKeyPressed(Key.D))
{
position.X += gameTime.DeltaTime;
}
if (Input.IsKeyPressed(Key.W))
{
position.Y += gameTime.DeltaTime;
}
if (Input.IsKeyPressed(Key.S))
{
position.Y -= gameTime.DeltaTime;
}
transformCmp.Position = position;
}
protected override void OnDestroy()
{
Log.EngineLogger.Trace("Cam controller destroyed!");
}
}
public this() : base("Example")
{
Application.Get().Window.IsVSync = false;
InitGraphics();
{
_cameraEntity = _scene.CreateEntity("Camera Entity");
let camera = _cameraEntity.AddComponent<CameraComponent>();
camera.Camera.SetPerspective(MathHelper.ToRadians(75), 0.1f, 10000.0f);
camera.Primary = true;
camera.FixedAspectRatio = false;
let transform = _cameraEntity.GetComponent<TransformComponent>();
transform.Position = .(0, 0, -5);
_cameraEntity.AddComponent<NativeScriptComponent>().Bind<EditorCameraController>();
_cameraEntity.AddComponent<EditorComponent>();
}
{
_otherCameraEntity = _scene.CreateEntity("Other Camera Entity");
let camera = _otherCameraEntity.AddComponent<CameraComponent>();
camera.Camera.SetPerspective(MathHelper.ToRadians(45), 0.1f, 1000.0f);
camera.Primary = false;
camera.FixedAspectRatio = false;
let transform = _otherCameraEntity.GetComponent<TransformComponent>();
transform.Position = .(0, 0, -5);
_otherCameraEntity.AddComponent<NativeScriptComponent>().Bind<EditorCameraController>();
_otherCameraEntity.AddComponent<EditorComponent>();
}
InitEditor();
}
private void InitGraphics()
{
_context = Application.Get().Window.Context..AddRef();
RasterizerStateDescription rsDesc = .(.Solid, .Back, true);
_rasterizerState = new RasterizerState(rsDesc);
rsDesc.FrontCounterClockwise = false;
_rasterizerStateClockWise = new RasterizerState(rsDesc);
BlendStateDescription blendDesc = .();
blendDesc.RenderTarget[0] = .(true, .SourceAlpha, .InvertedSourceAlpha, .Add, .SourceAlpha, .InvertedSourceAlpha, .Add, .All);
_alphaBlendState = new BlendState(blendDesc);
_opaqueBlendState = new BlendState(.Default);
DepthStencilStateDescription dsDesc = .();
_depthStencilState = new DepthStencilState(dsDesc);
_viewportTarget = new RenderTarget2D(RenderTarget2DDescription(.R8G8B8A8_UNorm, 100, 100) {DepthStencilFormat = .D32_Float});
_viewportTarget.SamplerState = SamplerStateManager.LinearClamp;
}
private void InitEditor()
{
_editor = new Editor(_scene);
_editor.SceneViewportWindow.ViewportSizeChangedEvent.Add(new (s, e) => ViewportSizeChanged(s, e));
//_editor.[Friend]CreateEntityWithTransform();
_editor.SceneViewportWindow.CameraEntity = _cameraEntity;
}
public override void Update(GameTime gameTime)
{
var scriptComponent = _cameraEntity.GetComponent<NativeScriptComponent>();
if (var camController = scriptComponent.Instance as EditorCameraController)
{
camController.IsEnabled = (_editor.SceneViewportWindow.HasFocus && Input.IsMouseButtonPressed(.RightButton));
}
//TransformSystem.Update(_world);
RenderCommand.Clear(_viewportTarget, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0);
RenderCommand.SetRenderTarget(_viewportTarget, 0, true);
RenderCommand.BindRenderTargets();
RenderCommand.SetViewport(Viewport(0, 0, _viewportTarget.Width, _viewportTarget.Height));
RenderCommand.SetBlendState(_alphaBlendState);
RenderCommand.SetDepthStencilState(_depthStencilState);
//Renderer.BeginScene(_cameraController.Camera);
//DebugRenderer.Render(_scene.[Friend]_ecsWorld);
//Renderer.EndScene();
_scene.Update(gameTime);
RenderCommand.Clear(null, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0);
RenderCommand.SetRenderTarget(null, 0, true);
RenderCommand.BindRenderTargets();
RenderCommand.SetViewport(_context.SwapChain.BackbufferViewport);
}
public override void OnEvent(Event event)
{
EventDispatcher dispatcher = EventDispatcher(event);
dispatcher.Dispatch<ImGuiRenderEvent>(scope (e) => OnImGuiRender(e));
dispatcher.Dispatch<WindowResizeEvent>(scope (e) => OnWindowResize(e));
}
ImGui.ID _mainDockspaceId;
private bool OnImGuiRender(ImGuiRenderEvent event)
{
ImGui.Begin("Test");
static bool cameraA = true;
if (ImGui.Checkbox("Camera A", &cameraA))
{
_cameraEntity.GetComponent<CameraComponent>().Primary = cameraA;
_otherCameraEntity.GetComponent<CameraComponent>().Primary = !cameraA;
}
ImGui.End();
ImGui.Viewport* viewport = ImGui.GetMainViewport();
ImGui.DockSpaceOverViewport(viewport);
DrawMainMenuBar();
_editor.SceneViewportWindow.RenderTarget = _viewportTarget;
_editor.Update();
_settingsWindow.Show();
return false;
}
private void DrawMainMenuBar()
{
ImGui.BeginMainMenuBar();
if(ImGui.BeginMenu("File", true))
{
if (ImGui.MenuItem("Settings"))
_settingsWindow.Open = true;
ImGui.EndMenu();
}
if(ImGui.BeginMenu("View", true))
{
if(ImGui.MenuItem(EntityHierarchyWindow.s_WindowTitle))
{
_editor.EntityHierarchyWindow.Open = true;
}
if(ImGui.MenuItem(ComponentEditWindow.s_WindowTitle))
{
_editor.ComponentEditWindow.Open = true;
}
if(ImGui.MenuItem(SceneViewportWindow.s_WindowTitle))
{
_editor.SceneViewportWindow.Open = true;
}
ImGui.EndMenu();
}
ImGui.EndMainMenuBar();
}
private bool OnWindowResize(WindowResizeEvent e)
{
return false;
}
private void ViewportSizeChanged(Object sender, Vector2 viewportSize)
{
uint32 sizeX = (uint32)viewportSize.X;
uint32 sizeY = (uint32)viewportSize.Y;
if(sizeX == 0 || sizeY == 0)
return;
_viewportTarget.Resize(sizeX, sizeY);
_scene.OnViewportResize(sizeX, sizeY);
}
}
}
+253
View File
@@ -0,0 +1,253 @@
using GlitchyEditor.EditWindows;
using ImGui;
using GlitchyEngine;
using System;
using System.Collections;
using System.Reflection;
namespace GlitchyEditor
{
class SettingsWindow : EditorWindow
{
class Binding
{
public Object SettingsObject;
public String Name ~ delete _;
public String FieldName ~ delete _;
public this(StringView name, StringView fieldName, Object settingsObject)
{
Name = new String(name);
FieldName = new String(fieldName);
SettingsObject = settingsObject;
}
}
class Category
{
public List<Binding> _bindings = new .() ~ DeleteContainerAndItems!(_);
public String Header ~ delete _;
public Object SettingsObject;
public this(String header)
{
Header = new String(header);
}
public void AddSetting(StringView name, StringView fieldName, Object settingsObject = null)
{
Binding binding = new .(name, fieldName, settingsObject ?? SettingsObject);
_bindings.Add(binding);
}
}
Settings _settings;
bool _settingsChanged = false;
private Dictionary<String, Category> _categories = new .() ~ DeleteDictionaryAndValues!(_);
public this()
{
_open = false;
_settings = Application.Get().Settings;
Create();
}
void Create()
{
ScanForSettings(_settings);
/*for (ISettings settings in _settings.[Friend]_userSettings)
{
ScanForSettings(settings);
}*/
}
Category AddCategory(String header)
{
if (_categories.TryGetValue(header, let category))
return category;
Category newCat = new Category(header);
_categories.Add(newCat.Header, newCat);
return newCat;
}
void ScanForSettings(Object container)
{
Type type = container.GetType();
for (var field in type.GetFields())
{
Result<SettingAttribute> settingResult = field.GetCustomAttribute<SettingAttribute>();
if (settingResult case .Ok(let settingInfo))
{
AddSetting(settingInfo.Category, settingInfo.Name, field.Name, container);
}
Result<SettingContainerAttribute> containerResult = field.GetCustomAttribute<SettingContainerAttribute>();
if (containerResult case .Ok(let containerInfo))
{
var res = field.GetValue<Object>(container, let childContainer);
if (res case .Err(let error))
{
Log.EngineLogger.Error($"Failed to get settings container. Error: {error}");
continue;
}
ScanForSettings(childContainer);
}
}
}
void AddSetting(String categoryName, String name, StringView fieldName, Object container)
{
Category category = AddCategory(categoryName);
category.AddSetting(name, fieldName, container);
}
protected override void InternalShow()
{
ImGui.Begin("Settings", &_open, .NoDocking);
defer ImGui.End();
// Leave room for 1 line below us
ImGui.BeginChild("item view", ImGui.Vec2(0, -ImGui.GetFrameHeightWithSpacing()));
ImGui.BeginTabBar("##Tabs");
for (Category category in _categories.Values)
{
if (ImGui.BeginTabItem(category.Header))
{
ImGui.Columns(2);
defer ImGui.Columns(1);
ImGui.SetColumnWidth(0, 100);
for (Binding setting in category._bindings)
{
ImGui.TextUnformatted(setting.Name);
ImGui.NextColumn();
Type settingsObjectType = setting.SettingsObject.GetType();
Result<FieldInfo> result = settingsObjectType.GetField(setting.FieldName);
if (result case .Err)
{
ImGui.PushStyleColor(.Text, ImGui.Vec4(1f, 0f, 0f, 1f));
ImGui.Text($"Field {setting.FieldName} not found.");
ImGui.PopStyleColor();
continue;
}
FieldInfo fieldInfo = result.Get();
Type fieldType = fieldInfo.FieldType;
mixin GetSettingValue<T>()
{
var error = fieldInfo.GetValue<T>(setting.SettingsObject, var value);
if (error case .Err(let err))
{
Log.EngineLogger.Error($"Could not get value of setting {setting.FieldName}. Error: {err}");
ImGui.PushStyleColor(.Text, ImGui.Vec4(1f, 0f, 0f, 1f));
ImGui.Text($"Could not get value of setting {setting.FieldName}. Error: {err}");
ImGui.PopStyleColor();
break;
}
value
}
mixin SetSettingValue<T>(T value)
{
var error = fieldInfo.SetValue(setting.SettingsObject, value);
if (error case .Err(let err))
{
Log.EngineLogger.Error($"Could not set value of setting {setting.FieldName}. Error: {err}");
}
}
switch (fieldType)
{
case typeof(int32):
int32 value = GetSettingValue!<int32>();
if (!ImGui.InputInt(scope $"##{setting.Name}", &value))
break;
SetSettingValue!(value);
_settingsChanged = true;
case typeof(String):
String value = GetSettingValue!<String>();
char8[256] buffer = .();
value.CopyTo(buffer);
if (ImGui.InputText(scope $"##{setting.Name}", &buffer, buffer.Count))
{
value..Clear().Append(&buffer);
_settingsChanged = true;
}
}
ImGui.NextColumn();
}
ImGui.EndTabItem();
}
}
ImGui.EndTabBar();
ImGui.EndChild();
ImGui.BeginDisabled(!_settingsChanged);
if (ImGui.Button("Save"))
{
_settings.Save();
_settings.Apply();
_open = false;
}
ImGui.SameLine();
if (ImGui.Button("Apply"))
{
_settings.Apply();
}
ImGui.EndDisabled();
ImGui.SameLine();
if (ImGui.Button("Cancel"))
{
Settings.Load();
_open = false;
}
}
}
}
+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 = "*", FreeType = "*", cgltf-beef = "*", msdfgen-beef = "*", ImGui = "*", ImGuiImplDX11 = "*", ImGuiImplWin32 = "*", ImGuizmo = "*", Beefy2D = "*", LodePng = "*", GlitchyEngineHelper = "*", bon = "*"}
[Project]
Name = "GlitchyEngine"
ProcessorMacros = ["GE_ERROR_SHADER_MATRIX_MISMATCH", "GE_ERROR_SHADER_VAR_TYPE_MISMATCH"]
[Configs.Debug.Win32]
PreprocessorMacros = ["DEBUG", "GE_WINDOWS"]
[Configs.Debug.Win64]
CLibType = "DynamicDebug"
PreprocessorMacros = ["DEBUG", "GE_WINDOWS"]
[Configs.Release.Win32]
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
[Configs.Release.Win64]
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
ProcessorMacros = ["GE_GRAPHICS_DX11", "GE_SHADER_MATRIX_MISMATCH_IS_ERROR", "GE_SHADER_VAR_TYPE_MISMATCH_IS_ERROR", "GE_SHADER_UNUSED_VARIABLE_IS_WARNING", "GE_WINDOWS"]
[Configs.Paranoid.Win32]
PreprocessorMacros = ["DEBUG", "PARANOID", "GE_WINDOWS"]
@@ -24,8 +11,17 @@ PreprocessorMacros = ["DEBUG", "PARANOID", "GE_WINDOWS"]
[Configs.Paranoid.Win64]
PreprocessorMacros = ["DEBUG", "PARANOID", "GE_WINDOWS"]
[Configs.Release.Win32]
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
[Configs.Release.Win64]
PreprocessorMacros = ["IMGUI", "RELEASE", "GE_PROFILE"]
[Configs.Test.Win32]
PreprocessorMacros = ["TEST", "GE_WINDOWS"]
[Configs.Test.Win64]
PreprocessorMacros = ["TEST", "GE_WINDOWS"]
[Configs.Debug.Win64]
PreprocessorMacros = ["DEBUG", "GE_PROFILE", "GE_PROFILE_RENDERER", "GE_PROFILE_RESOURCES", "IMGUI"]
+123 -14
View File
@@ -2,6 +2,8 @@ using System;
using GlitchyEngine.Events;
using GlitchyEngine.ImGui;
using GlitchyEngine.Renderer;
using GlitchyEngine.Debug;
using GlitchyEngine.Content;
namespace GlitchyEngine
{
@@ -9,53 +11,102 @@ namespace GlitchyEngine
{
static Application s_Instance = null;
private Window _window ~ delete _;
private RendererAPI _rendererApi ~ delete _;
private Window _window;
private RendererAPI _rendererApi;
private EffectLibrary _effectLibrary;
private bool _running = true;
private bool _isMinimized = false;
private LayerStack _layerStack = new LayerStack() ~ delete _;
private LayerStack _layerStack;
#if IMGUI
private ImGuiLayer _imGuiLayer;
#endif
private GameTime _gameTime = new GameTime(true) ~ delete _;
private GameTime _gameTime;
private IContentManager _contentManager;
public bool IsRunning => _running;
public Window Window => _window;
public EffectLibrary EffectLibrary => _effectLibrary;
public IContentManager ContentManager => _contentManager;
public bool IsMinimized => _isMinimized;
[Inline]
public static Application Get() => s_Instance;
public Settings Settings {get; private set;} = new .() ~ delete _;
public this()
{
Profiler.ProfileFunction!();
Log.EngineLogger.Assert(s_Instance == null, "Tried to create a second application.");
s_Instance = this;
_layerStack = new LayerStack();
_gameTime = new GameTime(true);
_window = new Window(.Default);
_window.EventCallback = new => OnEvent;
_rendererApi = new RendererAPI();
_rendererApi.Context = _window.Context;
SamplerStateManager.Init(_window.Context);
_contentManager = new ContentManager("./content");
SamplerStateManager.Init();
RenderCommand.RendererAPI = _rendererApi;
Renderer.Init(_window.Context);
_effectLibrary = new EffectLibrary();
Renderer.Init(_window.Context, _effectLibrary);
#if IMGUI
_imGuiLayer = new ImGuiLayer();
PushOverlay(_imGuiLayer);
#endif
GlitchyEngine.Settings.Load();
Settings.Apply();
}
public ~this()
{
Profiler.ProfileFunction!();
SamplerStateManager.Uninit();
Renderer.Deinit();
delete _effectLibrary;
delete _contentManager;
delete _rendererApi;
delete _window;
delete _gameTime;
delete _layerStack;
}
public void OnEvent(Event e)
{
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 +115,91 @@ 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)
{
_gameTime.NewFrame();
Debug.Profiler.ProfileScope!("Loop");
if(allowFrame)
{
_gameTime.NewFrame();
}
Input.NewFrame();
for(Layer layer in _layerStack)
layer.Update(_gameTime);
#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();
_imGuiLayer.ImGuiRender();
if(allowFrame)
{
#if IMGUI
_imGuiLayer.ImGuiRender();
#endif
_window.Context.SwapChain.Present();
_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;
}
}
}
@@ -0,0 +1,68 @@
using System;
using System.IO;
using xxHash;
namespace GlitchyEngine.Content
{
class ContentId
{
private readonly String _string;
private readonly XXH64_hash _hash;
public String String => _string;
public XXH64_hash Hash => _hash;
[AllowAppend]
public this(StringView id)
{
String str = append String(id);
_string = str;
_hash = xxHash.ComputeHash(id);
}
}
interface IContentManager
{
void GetFilePath(String outFilename, String filename);
Stream GetFile(String filename);
}
class ContentManager : IContentManager
{
private String _contentRoot;
[AllowAppend]
public this(String contentRoot)
{
String cntRoot = append String(contentRoot);
_contentRoot = cntRoot;
Runtime.Assert(Directory.Exists(contentRoot), "Content root directory doesn't exist.");
}
public void GetFilePath(String outFilename, String filename)
{
Path.InternalCombine(outFilename, _contentRoot, filename);
}
public Stream GetFile(String filename)
{
String fullpath = scope .(_contentRoot.Length + 1 + filename.Length);
GetFilePath(fullpath, filename);
FileStream stream = new FileStream();
var result = stream.Open(fullpath, .Read, .Read);
if (result case .Err(let error))
{
Log.EngineLogger.Error($"Failed to open file \"{fullpath}\". Error: {error}");
return null;
}
return stream;
}
}
}
+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, EcsEntity? parentEntity, EcsWorld world, Effect validationEffect, Material material, List<AnimationClip> clips)
{
EcsEntity entity = world.NewEntity();
#if DEBUG
var nameComponent = world.AssignComponent<DebugNameComponent>(entity);
if (node.Name != null)
{
nameComponent.SetName(StringView(node.Name));
}
else
{
nameComponent.SetName("Unnamed Node");
}
#endif
if(parentEntity.HasValue)
{
var childParent = world.AssignComponent<ParentComponent>(entity);
childParent.Entity = parentEntity.Value;
}
var childTransform = world.AssignComponent<TransformComponent>(entity);
if(node.HasMatrix)
{
childTransform.LocalTransform = *(Matrix*)&node.Matrix;
}
else
{
if(node.HasTranslation)
childTransform.Position = *(Vector3*)&node.Translation;
else
childTransform.Position = .Zero;
if(node.HasRotation)
childTransform.Rotation = *(Quaternion*)&node.Rotation;
else
childTransform.Rotation = .Identity;
if(node.HasScale)
childTransform.Scale = *(Vector3*)&node.Scale;
else
childTransform.Scale = .(1, 1, 1);
}
// Invert the Z-Axis of the root Node to convert the coordinate system from right-handed to left-handed
if(parentEntity == null)
childTransform.Scale *= .(1, 1, -1);
Skeleton skeleton = null;
if(node.Skin != null)
{
skeleton = ExtractSkeleton(node.Skin);
LoadAnimationClips(data, node.Skin, skeleton, clips);
}
if(node.Mesh != null)
{
// If we have only one primitive, add it directly to the entity
if(node.Mesh.Primitives.Length == 1)
{
var mesh = world.AssignComponent<MeshComponent>(entity);
using (var geo = PrimitiveToGeoBinding(node.Mesh.Primitives[0], validationEffect))
{
mesh.Mesh = geo;
}
if(skeleton == null)
{
var meshRenderer = world.AssignComponent<MeshRendererComponent>(entity);
meshRenderer.Material = material;
}
else
{
var meshRenderer = world.AssignComponent<SkinnedMeshRendererComponent>(entity);
meshRenderer.Material = material;
meshRenderer.Skeleton = skeleton;
}
}
// otherwise one child-entity per primitive
else
{
for(var primitive in node.Mesh.Primitives)
{
EcsEntity meshEntity = world.NewEntity();
var meshParent = world.AssignComponent<ParentComponent>(meshEntity);
meshParent.Entity = entity;
var mesh = world.AssignComponent<MeshComponent>(meshEntity);
mesh.Mesh = PrimitiveToGeoBinding(primitive, validationEffect);
if(skeleton == null)
{
var meshRenderer = world.AssignComponent<MeshRendererComponent>(meshEntity);
meshRenderer.Material = material;
}
else
{
var meshRenderer = world.AssignComponent<SkinnedMeshRendererComponent>(meshEntity);
meshRenderer.Material = material;
meshRenderer.Skeleton = skeleton;
}
}
}
}
skeleton?.ReleaseRef();
for(var child in node.Children)
{
NodesToEntities(data, child, entity, world, validationEffect, material, clips);
}
}
public static GeometryBinding PrimitiveToGeoBinding(CGLTF.Primitive primitive, Effect validationEffect)
{
GeometryBinding binding = new GeometryBinding();
// primitive topology
switch(primitive.Type)
{
case .Points:
binding.SetPrimitiveTopology(.PointList);
case .Lines:
binding.SetPrimitiveTopology(.LineList);
case .LineStrip:
binding.SetPrimitiveTopology(.LineStrip);
case .Triangles:
binding.SetPrimitiveTopology(.TriangleList);
case .TriangleStrip:
binding.SetPrimitiveTopology(.TriangleStrip);
default:
Log.EngineLogger.Assert(false, scope $"{primitive.Type} not supported.");
}
// indices
{
CGLTF.Accessor* indices = primitive.Indices;
if(indices != null)
{
bool is16bit = indices.ComponentType == .R_16u || indices.ComponentType == .R_16;
IndexBuffer ib = new IndexBuffer((.)indices.Count, .Immutable, .None, is16bit ? .Index16Bit : .Index32Bit);
uint8* bufferData = (uint8*)indices.BufferView.Buffer.Data;
bufferData += indices.BufferView.Offset;
// TODO: this is a mess
ib.SetData<uint8>(bufferData, (.)indices.BufferView.Size);
binding.SetIndexBuffer(ib);
ib.ReleaseRef();
}
}
bool hasNormals = false;
bool hasTangents = false;
// vertices
{
List<VertexElement> elements = scope .(primitive.Attributes.Length);
Dictionary<void*, VertexBuffer> buffers = scope .();
List<VertexBufferBinding> bindings = scope .();
for(var attribute in primitive.Attributes)
{
{
StringView attributeName = StringView(attribute.Name);
if(attributeName.Equals("NORMAL"))
{
hasNormals = true;
}
else if(attributeName.Equals("TANGENT"))
{
hasTangents = true;
}
}
// Get Input Element format
Format format = FormatFromVectorComponent(attribute.Data.Type, attribute.Data.ComponentType);
Log.EngineLogger.AssertDebug(format != .Unknown, "Vertex element format must not be \"Unknown.\"");
VertexBuffer vertexBuffer = null;
binding.VertexCount = (uint32)attribute.Data.Count;
// Get vertex buffer
{
CGLTF.BufferView* bufferView = attribute.Data.BufferView;
// if buffer doesn't exist -> create
if(!buffers.TryGetValue(bufferView, out vertexBuffer))
{
vertexBuffer = new VertexBuffer(1, (uint32)bufferView.Size, .Immutable)..ReleaseRefNoDelete();
uint8* bufferData = (uint8*)bufferView.Buffer.Data;
bufferData += bufferView.Offset;
vertexBuffer.SetData(bufferData, (.)bufferView.Size);
buffers.Add(attribute.Data.BufferView, vertexBuffer);
}
Log.EngineLogger.AssertDebug(vertexBuffer != null);
}
VertexBufferBinding bufferBinding = .(vertexBuffer, (.)attribute.Data.Stride, (.)attribute.Data.Offset);
// get slot of bufferBinding
int bindingSlot = bindings.IndexOf(bufferBinding);
// binding has no slot -> add to list
if(bindingSlot == -1)
{
bindingSlot = bindings.Count;
bindings.Add(bufferBinding);
binding.SetVertexBufferSlot(bufferBinding, (.)bindingSlot);
}
StringView strView = .(attribute.Name);
// Remove number from end of name
while((*(strView.EndPtr - 1)).IsDigit)
{
strView.Length--;
}
VertexElement element = .(format, new String(strView), true, (.)attribute.Index, (.)bindingSlot);
elements.Add(element);
}
// Generate normals if missing
if(!hasNormals)
{
CGLTF.Accessor* positions = null;
// Find position accessor
for(var attribute in primitive.Attributes)
{
if(StringView(attribute.Name).Equals("POSITION"))
{
positions = attribute.Data;
break;
}
}
Log.EngineLogger.AssertDebug(positions != null, "The model appears to have no position data?!");
Vector3[] normals = new Vector3[positions.Count];
if(primitive.Indices != null)
GenerateNormals(primitive.Indices, positions, normals);
else
GenerateNormals(positions, normals);
VertexBuffer vertexBuffer = new VertexBuffer((uint32)sizeof(Vector3), (uint32)normals.Count, .Immutable);
vertexBuffer.SetData<Vector3>(normals);
bindings.Add(vertexBuffer.Binding);
uint32 bindingSlot = (.)bindings.Count - 1;
binding.SetVertexBufferSlot(vertexBuffer.Binding, (.)bindingSlot);
VertexElement element = .(.R32G32B32_Float, "NORMAL", false, 0, bindingSlot);
elements.Add(element);
}
// TODO: validate vertex layout somewhere else
VertexElement[] vertexElements = new VertexElement[elements.Count];
for(int i < elements.Count)
{
vertexElements[i] = elements[i];
}
VertexLayout layout = new VertexLayout(vertexElements, true, validationEffect.VertexShader);
binding.SetVertexLayout(layout..ReleaseRefNoDelete());
}
// TODO: calculate tangents if missing (MikkTSpace algorithm)
// Note: Bitangent = cross(normal, tangent.xyz) * tangent.w
return binding;
}
/// Generates the normals for one triangle
static mixin GenerateTriangleNormals(int index0, int index1, int index2, CGLTF.Accessor* positions, Vector3[] normals)
{
Vector3 position0 = GetEntry<Vector3>(positions, (.)index0);
Vector3 position1 = GetEntry<Vector3>(positions, (.)index1);
Vector3 position2 = GetEntry<Vector3>(positions, (.)index2);
ref Vector3 normal0 = ref normals[(.)index0];
ref Vector3 normal1 = ref normals[(.)index1];
ref Vector3 normal2 = ref normals[(.)index2];
Vector3 e0 = position1 - position0;
Vector3 e1 = position2 - position0;
Vector3 normal = Vector3.Cross(e0, e1);
normal0 += normal;
normal1 += normal;
normal2 += normal;
}
[Inline]
static void NormalizeNormals(Vector3[] normals)
{
for(int i < normals.Count)
{
normals[i].Normalize();
}
}
/// Generates normals for the given model using indexed geometry
static void GenerateNormals(CGLTF.Accessor* indices, CGLTF.Accessor* positions, Vector3[] normals)
{
/// Enumerate triangle-wise
for(uint t = 0; t < indices.Count; t += 3)
{
int index0 = (.)CGLTF.AccessorReadIndex(indices, t);
int index1 = (.)CGLTF.AccessorReadIndex(indices, t + 1);
int index2 = (.)CGLTF.AccessorReadIndex(indices, t + 2);
GenerateTriangleNormals!(index0, index1, index2, positions, normals);
}
NormalizeNormals(normals);
}
/// Generates normals for the given model using nonindexed geometry
static void GenerateNormals(CGLTF.Accessor* positions, Vector3[] normals)
{
/// Enumerate triangle-wise
for(int i = 0; i < (.)positions.Count; i += 3)
{
GenerateTriangleNormals!(i, i + 1, i + 2, positions, normals);
}
NormalizeNormals(normals);
}
static Skeleton ExtractSkeleton(CGLTF.Skin* skin)
{
Skeleton skeleton = new Skeleton();
skeleton.Joints = new Joint[skin.Joints.Length];
for(int i < skin.Joints.Length)
{
ref Joint joint = ref skeleton.Joints[i];
joint.InverseBindPose = GetEntry<Matrix>(skin.InverseBindMatrices, i);
if (skin.Joints[i].Name != null)
joint.Name = new String(skin.Joints[i].Name);
int parentId = skin.Joints.IndexOf(skin.Joints[i].Parent);
Log.EngineLogger.AssertDebug(parentId < uint8.MaxValue, scope $"A skeleton must not have more than {uint8.MaxValue - 1} bones.");
if(parentId == -1)
joint.ParentID = uint8.MaxValue;
else
joint.ParentID = (uint8)parentId;
}
return skeleton;
}
static bool AnimationBelongsToSkeleton(CGLTF.Animation animation, CGLTF.Skin* skin)
{
for(var channel in animation.Channels)
{
if(skin.Joints.IndexOf(channel.TargetNode) == -1)
{
return false;
}
}
return true;
}
static void LoadAnimationClips(CGLTF.Data* data, CGLTF.Skin* skin, Skeleton skeleton, List<AnimationClip> clips)
{
for(var animation in data.Animations)
{
if(!AnimationBelongsToSkeleton(animation, skin))
continue;
AnimationClip clip = new AnimationClip(skeleton);
clips.Add(clip);
clip.IsLooping = true;
for(var channel in animation.Channels)
{
int nodeIndex = skin.Joints.IndexOf(channel.TargetNode);
Log.EngineLogger.AssertDebug(nodeIndex != -1);
ref JointAnimation jointAnimation = ref clip.JointAnimations[nodeIndex];
if(jointAnimation == null)
jointAnimation = new JointAnimation();
Log.EngineLogger.AssertDebug(channel.Sampler.Input.Count == channel.Sampler.Output.Count);
int samples = (int)channel.Sampler.Input.Count;
Log.EngineLogger.AssertDebug(channel.Sampler.Input.ComponentType == .R_32f);
Log.EngineLogger.AssertDebug(channel.Sampler.Input.Type == .Scalar);
InterpolationMode interpolationMode;
switch(channel.Sampler.Interpolation)
{
case .Step:
interpolationMode = .Step;
case .Linear:
interpolationMode = .Linear;
case .CubicSpline:
interpolationMode = .CubicSpline;
}
switch(channel.TargetPath)
{
case .Translation:
jointAnimation.TranslationChannel = new .(samples, interpolationMode);
Log.EngineLogger.AssertDebug(channel.Sampler.Output.ComponentType == .R_32f);
Log.EngineLogger.AssertDebug(channel.Sampler.Output.Type == .Vec3);
for(int i < samples)
{
float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
Vector3 sample = GetEntry<Vector3>(channel.Sampler.Output, i);
jointAnimation.TranslationChannel.TimeStamps[i] = timeStamp;
jointAnimation.TranslationChannel.Values[i] = sample;
}
case .Rotation:
jointAnimation.RotationChannel = new .(samples, interpolationMode);
Log.EngineLogger.AssertDebug(channel.Sampler.Output.ComponentType == .R_32f);
Log.EngineLogger.AssertDebug(channel.Sampler.Output.Type == .Vec4);
for(int i < samples)
{
float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
Quaternion sample = GetEntry<Quaternion>(channel.Sampler.Output, i);
jointAnimation.RotationChannel.TimeStamps[i] = timeStamp;
jointAnimation.RotationChannel.Values[i] = sample;
}
case .Scale:
jointAnimation.ScaleChannel = new .(samples, interpolationMode);
Log.EngineLogger.AssertDebug(channel.Sampler.Output.ComponentType == .R_32f);
Log.EngineLogger.AssertDebug(channel.Sampler.Output.Type == .Vec3);
for(int i < samples)
{
float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
Vector3 sample = GetEntry<Vector3>(channel.Sampler.Output, i);
jointAnimation.ScaleChannel.TimeStamps[i] = timeStamp;
jointAnimation.ScaleChannel.Values[i] = sample;
}
default:
Log.EngineLogger.Error($"Unknown channel target path \"{channel.TargetPath}\"");
}
clip.Duration = Math.Max(clip.Duration, jointAnimation.Duration);
}
}
}
static T GetEntry<T>(CGLTF.Accessor* accessor, int index)
{
Log.EngineLogger.AssertDebug((uint)index < accessor.Count);
T result = ?;
switch(typeof(T))
{
case typeof(float):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 1);
case typeof(Vector2):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 2);
case typeof(Vector3):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 3);
case typeof(Vector4), typeof(Quaternion):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 4);
case typeof(Matrix):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 16);
case typeof(uint16):
CGLTF.AccessorReadUint(accessor, (uint)index, (uint32*)&result, 2);
case default:
uint8* data = (uint8*)accessor.BufferView.Buffer.Data;
data += accessor.BufferView.Offset;
data += accessor.Offset;
data += accessor.Stride * (uint)index;
result = *(T*)data;
}
return result;
/*
uint8* data = (uint8*)accessor.BufferView.Buffer.Data;
data += accessor.BufferView.Offset;
data += accessor.Offset;
data += accessor.Stride * (uint)index;
return *(T*)data;
*/
}
/**
* Converts the vector and component type to the corresponding Format.
*/
static Format FormatFromVectorComponent(CGLTF.Type vectorType, CGLTF.ComponentType componentType)
{
switch((vectorType, componentType))
{
case (.Scalar, .R_8):
return .R8_SInt;
case (.Scalar, .R_8u):
return .R8_UInt;
case (.Scalar, .R_16):
return .R16_SInt;
case (.Scalar, .R_16u):
return .R16_UInt;
case (.Scalar, .R_32u):
return .R32_UInt;
case (.Scalar, .R_32f):
return .R32_Float;
case (.Vec2, .R_8):
return .R8G8_SInt;
case (.Vec2, .R_8u):
return .R8G8_UInt;
case (.Vec2, .R_16):
return .R16G16_SInt;
case (.Vec2, .R_16u):
return .R16G16_UInt;
case (.Vec2, .R_32u):
return .R32G32_UInt;
case (.Vec2, .R_32f):
return .R32G32_Float;
//case (.Vec3, .R_8):
//case (.Vec3, .R_8u):
//case (.Vec3, .R_16):
//case (.Vec3, .R_16u):
case (.Vec3, .R_32u):
return .R32G32B32_UInt;
case (.Vec3, .R_32f):
return .R32G32B32_Float;
case (.Vec4, .R_8):
return .R8G8B8A8_SInt;
case (.Vec4, .R_8u):
return .R8G8B8A8_UInt;
case (.Vec4, .R_16):
return .R16G16B16A16_SInt;
case (.Vec4, .R_16u):
return .R16G16B16A16_UInt;
case (.Vec4, .R_32u):
return .R32G32B32A32_UInt;
case (.Vec4, .R_32f):
return .R32G32B32A32_Float;
case (.Mat4, .R_32f):
return .R32G32B32A32_Float;
default:
Log.EngineLogger.Assert(false, scope $"Unhandled vector - componenttype combination. ({vectorType}, {componentType})");
}
return .Unknown;
}
}
}
+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;
}
}
}
@@ -0,0 +1,13 @@
namespace System
{
extension Runtime
{
[NoReturn, Warn("The method is not implemented.")]
#unwarn
public static void NotImplemented(String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum)
{
String failStr = scope .()..AppendF("Not Implemented at line {} in {}", line, filePath);
Internal.FatalError(failStr, 1);
}
}
}
@@ -0,0 +1,21 @@
namespace System
{
extension String
{
[Inline]
public void CopyTo(Span<char8> target)
{
CopyTo(target.Ptr, target.Length);
}
[Inline]
public void CopyTo(char8* target, int targetLength)
{
int copiedChars = Math.Min(targetLength - 1, Length);
Internal.MemCpy(target, Ptr, copiedChars);
target[copiedChars] = '\0';
}
}
}
+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();
}
}
}
+109 -1
View File
@@ -1,10 +1,24 @@
using GlitchyEngine.Math;
using GlitchyEngine.Renderer;
using System;
namespace ImGui
{
extension ImGui
{
// TODO: Color-functions
extension Vec2
{
public static explicit operator Vector2(Vec2 v) => .(v.x, v.y);
public static explicit operator Vec2(Vector2 v) => .(v.X, v.Y);
}
extension Vec4
{
public static explicit operator Vector4(Vec4 v) => .(v.x, v.y, v.z, v.w);
public static explicit operator Vec4(Vector4 v) => .(v.X, v.Y, v.Z, v.W);
}
// TODO: Color-functions
public static bool ColorEdit3(char* label, ref ColorRGB col, ColorEditFlags flags = (ColorEditFlags) 0) => ColorEdit3Impl(label, *(float[3]*)&col, flags);
public static bool ColorEdit3(char* label, ref ColorRGBA col, ColorEditFlags flags = (ColorEditFlags) 0) => ColorEdit3Impl(label, *(float[3]*)&col, flags);
@@ -14,5 +28,99 @@ namespace ImGui
public static bool ColorPicker3(char* label, ref ColorRGBA col, ColorEditFlags flags = (ColorEditFlags) 0) => ColorPicker3Impl(label, *(float[3]*)&col, flags);
public static bool ColorPicker4(char* label, ref ColorRGBA col, ColorEditFlags flags = (ColorEditFlags) 0, float* ref_col = null) => ColorPicker4Impl(label, *(float[4]*)&col, flags, ref_col);
public static extern void Image(Texture2D texture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec4 tint_col = Vec4.Ones, Vec4 border_col = Vec4.Zero);
// Wouldn't be necesseary if RenderTarget2D was Texture2D
public static extern void Image(RenderTarget2D texture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec4 tint_col = Vec4.Ones, Vec4 border_col = Vec4.Zero);
public static void TextUnformatted(StringView text) => TextUnformattedImpl(text.Ptr, text.Ptr + text.Length);
public static void PushID(StringView id) => PushID(id.Ptr, id.Ptr + id.Length);
/// Releases references that accumulated calls like ImGui::Image
protected internal static extern void CleanupFrame();
/// Control to edit a vector 3 with drag functionality and reset buttons
public static bool EditVector3(StringView label, ref Vector3 value, Vector3 resetValues = .Zero, float dragSpeed = 0.1f, float columnWidth = 100f)
{
bool changed = false;
PushID(label);
defer PopID();
Columns(2);
defer Columns(1);
SetColumnWidth(0, columnWidth);
TextUnformatted(label);
NextColumn();
PushMultiItemsWidths(3, CalcItemWidth());
PushStyleVar(.ItemSpacing, Vec2.Zero);
defer PopStyleVar();
float lineHeight = GetFont().FontSize + GetStyle().FramePadding.y * 2.0f;
ImGui.Vec2 buttonSize = .(lineHeight + 3.0f, lineHeight);
PushStyleColor(.Button, Color(230, 25, 45).Value);
PushStyleColor(.ButtonHovered, Color(150, 25, 45).Value);
PushStyleColor(.ButtonActive, Color(230, 120, 130).Value);
if (Button("X", buttonSize))
{
value.X = resetValues.X;
changed = true;
}
SameLine();
if (DragFloat("##X", &value.X, dragSpeed))
changed = true;
PopItemWidth();
SameLine();
PopStyleColor(3);
PushStyleColor(.Button, Color(50, 190, 15).Value);
PushStyleColor(.ButtonHovered, Color(50, 120, 15).Value);
PushStyleColor(.ButtonActive, Color(116, 190, 99).Value);
if (Button("Y", buttonSize))
{
value.Y = resetValues.Y;
changed = true;
}
SameLine();
if (DragFloat("##Y", &value.Y, dragSpeed))
changed = true;
PopItemWidth();
SameLine();
PopStyleColor(3);
PushStyleColor(.Button, Color(55, 55, 230).Value);
PushStyleColor(.ButtonHovered, Color(55, 55, 150).Value);
PushStyleColor(.ButtonActive, Color(90, 90, 230).Value);
if (Button("Z", buttonSize))
{
value.Z = resetValues.Z;
changed = true;
}
SameLine();
if (DragFloat("##Z", &value.Z, dragSpeed))
changed = true;
PopItemWidth();
PopStyleColor(3);
return changed;
}
}
}
+82 -148
View File
@@ -1,15 +1,33 @@
using System;
using ImGui;
using GlitchyEngine.Events;
using ImGuizmo;
using GlitchyEngine.Renderer;
using System.IO;
using internal ImGui;
#if GE_GRAPHICS_DX11
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Platform.DX11;
#endif
namespace GlitchyEngine.ImGui
{
public class ImGuiLayer : Layer
{
ImGui.IO* _io;
public bool SettingsInvalid;
public this() : base("ImGuiLayer") { }
public override void OnAttach()
{
Debug.Profiler.ProfileFunction!();
//ImGuiImplWin32.EnableDpiAwareness();
Log.EngineLogger.Trace("Initializing ImGui...");
@@ -17,198 +35,114 @@ namespace GlitchyEngine.ImGui
ImGui.CreateContext();
ImGui.StyleColorsDark();
ref ImGui.IO io = ref ImGui.GetIO();
io.ConfigFlags |= .NavEnableKeyboard;
io.ConfigFlags |= .DockingEnable;
io.ConfigFlags |= .ViewportsEnable;
_io = ImGui.GetIO();
_io.ConfigFlags |= .NavEnableKeyboard;
_io.ConfigFlags |= .DockingEnable;
_io.ConfigFlags |= .ViewportsEnable;
// Todo: currently broken in ImGui
//io.ConfigFlags |= .DpiEnableScaleFonts;
//io.ConfigFlags |= .DpiEnableScaleViewports;
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
ref ImGui.Style style = ref ImGui.GetStyle();
if(io.ConfigFlags.HasFlag(.ViewportsEnable))
ImGui.Style* style = ImGui.GetStyle();
if(_io.ConfigFlags.HasFlag(.ViewportsEnable))
{
style.WindowRounding = 0.0f;
style.Colors[(int)ImGui.Col.WindowBg].w = 1.0f;
}
#if GE_WINDOWS
// Todo: temporary, needs to be platform independent
ImGuiImplWin32.Init((Windows.HWnd)(int)Application.Get().Window.NativeWindow);
var context = Application.Get().Window.Context;
ImGuiImplDX11.Init(context.[Friend]nativeDevice, context.[Friend]nativeContext);
#if BF_PLATFORM_WINDOWS
ImGuiImplWin32.Init(Application.Get().Window.NativeWindow);
#endif
#if GE_GRAPHICS_DX11
ImGuiImplDX11.Init(NativeDevice, NativeContext);
#endif
}
public override void OnDetach()
{
Debug.Profiler.ProfileFunction!();
#if GE_GRAPHICS_DX11
ImGuiImplDX11.Shutdown();
#endif
#if BF_PLATFORM_WINDOWS
ImGuiImplWin32.Shutdown();
#endif
ImGui.DestroyContext();
}
public override void OnEvent(Event event)
{
/*
EventDispatcher dispatcher = scope EventDispatcher(event);
dispatcher.Dispatch<WindowResizeEvent>(scope (e) =>
{
//ref ImGui.IO io = ref ImGui.GetIO();
//io.DisplaySize = .(e.Width, e.Height);
//io.DisplayFramebufferScale = .(1.0f, 1.0f);
return false;
});
dispatcher.Dispatch<MouseMovedEvent>(scope (e) =>
{
ref ImGui.IO io = ref ImGui.GetIO();
io.MousePos = ImGui.Vec2(e.PositionX, e.PositionY);
return false;
});
dispatcher.Dispatch<MouseButtonPressedEvent>(scope (e) =>
{
int button = 0;
switch(e.MouseButton)
{
case .LeftButton:
button = (uint)ImGui.MouseButton.Left;
case .RightButton:
button = (uint)ImGui.MouseButton.Right;
case .MiddleButton:
button = (uint)ImGui.MouseButton.Middle;
case .XButton1:
button = (uint)3;
case .XButton2:
button = (uint)4;
default:
}
ref ImGui.IO io = ref ImGui.GetIO();
io.MouseDown[button] = true;
return false;
});
dispatcher.Dispatch<MouseButtonReleasedEvent>(scope (e) =>
{
int button = 0;
switch(e.MouseButton)
{
case .LeftButton:
button = (uint)ImGui.MouseButton.Left;
case .RightButton:
button = (uint)ImGui.MouseButton.Right;
case .MiddleButton:
button = (uint)ImGui.MouseButton.Middle;
case .XButton1:
button = (uint)3;
case .XButton2:
button = (uint)4;
default:
}
ref ImGui.IO io = ref ImGui.GetIO();
io.MouseDown[button] = false;
return false;
});
dispatcher.Dispatch<MouseScrolledEvent>(scope (e) =>
{
ref ImGui.IO io = ref ImGui.GetIO();
io.MouseWheel += e.YOffset;
io.MouseWheelH += e.XOffset; // Todo: horizontal mousewheel inverted?!
return false;
});
dispatcher.Dispatch<KeyPressedEvent>(scope (e) =>
{
if(e.KeyCode >= (.)256)
return false;
ref ImGui.IO io = ref ImGui.GetIO();
io.KeysDown[(int32)e.KeyCode] = true;
io.KeyCtrl = io.KeysDown[(int32)Key.Control];
io.KeyShift = io.KeysDown[(int32)Key.Shift];
io.KeyAlt = io.KeysDown[(int32)Key.Alt];
io.KeySuper = io.KeysDown[(int32)Key.LeftSuper] || io.KeysDown[(int32)Key.RightSuper];
return false;
});
dispatcher.Dispatch<KeyReleasedEvent>(scope (e) =>
{
if(e.KeyCode >= (.)256)
return false;
ref ImGui.IO io = ref ImGui.GetIO();
io.KeysDown[(int32)e.KeyCode] = false;
io.KeyCtrl = io.KeysDown[(int32)Key.Control];
io.KeyShift = io.KeysDown[(int32)Key.Shift];
io.KeyAlt = io.KeysDown[(int32)Key.Alt];
io.KeySuper = io.KeysDown[(int32)Key.LeftSuper] || io.KeysDown[(int32)Key.RightSuper];
return false;
});
dispatcher.Dispatch<KeyTypedEvent>(scope (e) =>
{
ref ImGui.IO io = ref ImGui.GetIO();
io.AddInputCharacterUTF16((uint16)e.Char);
return false;
});
*/
}
public void Begin()
{
// Todo:
//var v = DirectX.ImmediateContext;
//v.OutputMerger.SetRenderTargets(1, &DirectX.BackBufferTarget, null);
Application.Get().Window.Context.SetRenderTarget(null);
Debug.Profiler.ProfileFunction!();
ImGuiImplDX11.NewFrame();
ImGuiImplWin32.NewFrame();
ImGui.NewFrame();
ImGuizmo.BeginFrame();
}
bool showDemo = true;
public void ImGuiRender()
{
Debug.Profiler.ProfileFunction!();
if (SettingsInvalid)
{
var settings = Application.Get().Settings.ImGuiSettings;
ImGui.GetIO().Fonts.Clear();
String fullpath = scope String();
Application.Get().ContentManager.GetFilePath(fullpath, settings.FontName);
if (File.Exists(fullpath))
{
ImGui.GetIO().Fonts.AddFontFromFileTTF(fullpath, settings.FontSize);
}
else
{
ImGui.GetIO().Fonts.AddFontDefault();
}
#if GE_GRAPHICS_DX11
ImGuiImplDX11.CreateDeviceObjects();
#endif
SettingsInvalid = false;
}
Begin();
var event = scope ImGuiRenderEvent();
Application.Get().OnEvent(event);
{
Debug.Profiler.ProfileScope!("ImGuiRenderEvent");
ImGui.ShowDemoWindow(&showDemo);
var event = scope ImGuiRenderEvent();
Application.Get().OnEvent(event);
}
End();
}
public void End()
{
ref ImGui.IO io = ref ImGui.GetIO();
let window = Application.Get().Window;
io.DisplaySize = .(window.Width, window.Height);
Debug.Profiler.ProfileFunction!();
ImGui.Render();
ImGuiImplDX11.RenderDrawData(ref ImGui.GetDrawData());
if(io.ConfigFlags.HasFlag(.ViewportsEnable))
RenderCommand.SetDepthStencilTarget(null);
RenderCommand.SetRenderTarget(null);
RenderCommand.BindRenderTargets();
#if GE_GRAPHICS_DX11
ImGuiImplDX11.RenderDrawData(ImGui.GetDrawData());
#endif
ImGui.CleanupFrame();
if(_io.ConfigFlags.HasFlag(.ViewportsEnable))
{
ImGui.UpdatePlatformWindows();
ImGui.RenderPlatformWindowsDefault();
+1 -1
View File
@@ -38,7 +38,7 @@ namespace GlitchyEngine
public static extern int32 GetLastMouseY();
public static extern bool IsMouseButtonPressing(MouseButton button);
public static extern bool IsMouseButtonReleasiong(MouseButton button);
public static extern bool IsMouseButtonReleasing(MouseButton button);
public static extern Point GetMouseMovement();
public static extern void NewFrame();
+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();
}
/**
+154
View File
@@ -0,0 +1,154 @@
using System;
namespace GlitchyEngine.Math
{
public class BitArray
{
const int BitsPerInt = sizeof(uint) * 8;
private uint* _bits ~ Free(_);
private int _intCount;
private int _capacity;
public int Capacity
{
get => _capacity;
set => EnsureCapacity(value);
}
public this(int initialCapacity = sizeof(uint))
{
var initialCapacity;
if (initialCapacity < sizeof(uint))
initialCapacity = sizeof(uint);
EnsureCapacity(initialCapacity);
}
[Inline]
static int IntCount(int bits)
{
int count = bits / BitsPerInt;
if(bits % BitsPerInt > 0)
count++;
return count;
}
[LinkName("free")]
static extern void Free(void* memoryBlock);
[LinkName("realloc")]
static extern void* Realloc(void* memoryBlock, int size);
private void EnsureCapacity(int requestedCapacity)
{
if(requestedCapacity <= _capacity)
return;
int newIntCount = IntCount(requestedCapacity);
if(_intCount >= newIntCount)
return;
int oldIntCount = _intCount;
_intCount = newIntCount;
_capacity = _intCount * BitsPerInt;
_bits = (.)Realloc(_bits, _intCount * sizeof(uint));
// Set new bits to 0
for(int i = oldIntCount; i < _intCount; i++)
{
_bits[i] = 0;
}
}
public bool this[int index]
{
get
{
Log.EngineLogger.AssertDebug(index >= 0);
if(index > _capacity)
return false;
int arrayIndex = index / _capacity;
int bitIndex = index % _capacity;
return ((_bits[arrayIndex] >> bitIndex) & 1) == 1;
}
set
{
Log.EngineLogger.AssertDebug(index >= 0);
if(index > _capacity)
EnsureCapacity(index);
int arrayIndex = index / _capacity;
int bitIndex = index % _capacity;
if(value)
{
_bits[arrayIndex] |= (1 << bitIndex);
}
else
{
// create a mask that is all 1 except for the bit at the specified index
uint mask = uint.MaxValue;
mask ^= (1 << bitIndex);
_bits[arrayIndex] &= mask;
}
}
}
/**
* Sets all bits to given value.
*/
public void Clear(bool value = false)
{
if(value)
{
for(int i < _intCount)
{
_bits[i] = (uint)-1;
}
}
else
{
for(int i < _intCount)
{
_bits[i] = 0;
}
}
}
/**
* Returns whether or not this bitarray is 1 for every 1 in mask.
* i.e. mask == (this & mask)
*/
public bool MaskMatch(BitArray mask)
{
// The number of ints we can compare binary
int intCompares = Math.Min(mask._intCount, _intCount);
for(int i < intCompares)
{
if(mask._bits[i] != (_bits[i] & mask._bits[i]))
return false;
}
// if mask has more integers than "this", these integers must be 0
for(int i = _intCount; i < mask._intCount; i++)
{
if(mask._bits[i] > 0)
return false;
}
return true;
}
}
}
+24
View File
@@ -0,0 +1,24 @@
using GlitchyEngine.Math;
namespace System
{
extension Float
{
public float X
{
[Inline]
get => (float)this;
[Inline]
set mut => this = value;
}
[Inline]
public Vector2 XX => Vector2((float)this);
[Inline]
public Vector3 XXX => Vector3((float)this);
[Inline]
public Vector4 XXXX => Vector4((float)this);
}
}
+24
View File
@@ -0,0 +1,24 @@
using GlitchyEngine.Math;
namespace System
{
extension Int32
{
public int32 X
{
[Inline]
get => (int32)this;
[Inline]
set mut => this = value;
}
[Inline]
public Int2 XX => Int2((int32)this);
[Inline]
public Int3 XXX => Int3((int32)this);
[Inline]
public Int4 XXXX => Int4((int32)this);
}
}
+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
public const float RadToDeg = 180.0f / Pi;
/// Converts radians to degrees
public const float DegToRad = Pi / 180.0f;
// Converts the given radians to degrees
public static float ToDegrees(float radians)
{
return radians * RadToDeg;
}
// Converts the given radians to degrees
public static Vector2 ToDegrees(Vector2 radians)
{
return radians * RadToDeg;
}
// Converts the given radians to degrees
public static Vector3 ToDegrees(Vector3 radians)
{
return radians * RadToDeg;
}
// Converts the given radians to degrees
public static Vector4 ToDegrees(Vector4 radians)
{
return radians * RadToDeg;
}
// Converts the given degrees to radians
public static float ToRadians(float degrees)
{
return degrees * DegToRad;
}
// Converts the given degrees to radians
public static Vector2 ToRadians(Vector2 degrees)
{
return degrees * DegToRad;
}
// Converts the given degrees to radians
public static Vector3 ToRadians(Vector3 degrees)
{
return degrees * DegToRad;
}
// Converts the given degrees to radians
public static Vector4 ToRadians(Vector4 degrees)
{
return degrees * DegToRad;
}
const float epsilon = 0.0000001f;
public static bool IsZero(float value)
{
return Math.Abs(value) < epsilon;
}
}
}
+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();
}*/
}
}
+378
View File
@@ -0,0 +1,378 @@
using System;
namespace GlitchyEngine.Math
{
public struct Quaternion
{
public const Quaternion Zero = .();
public const Quaternion One = .(1.0f, 1.0f, 1.0f, 1.0f);
public const Quaternion Identity = .(0.0f, 0.0f, 0.0f, 1.0f);
public float X, Y, Z, W;
public this() => this = default;
public this(float x, float y, float z, float w)
{
X = x;
Y = y;
Z = z;
W = w;
}
public this(Vector3 xy, float z, float w)
{
X = xy.X;
Y = xy.Y;
Z = z;
W = w;
}
public this(Vector3 xyz, float w)
{
X = xyz.X;
Y = xyz.Y;
Z = xyz.Z;
W = w;
}
public this(Vector4 vector)
{
X = vector.X;
Y = vector.Y;
Z = vector.Z;
W = vector.W;
}
public Vector3 Vector
{
get => .(X, Y, Z);
set mut
{
X = value.X;
Y = value.Y;
Z = value.Z;
}
}
public float Scalar
{
get => W;
set mut => W = value;
}
public void Normalize() mut
{
float invLength = 1.0f / Length();
X *= invLength;
Y *= invLength;
Z *= invLength;
W *= invLength;
}
public static Quaternion Normalize(Quaternion q)
{
float invLength = 1.0f / Length(q);
return Quaternion(q.X * invLength, q.Y * invLength, q.Z * invLength, q.W * invLength);
}
public float Length()
{
return Math.Sqrt([Inline]LengthSquared());
}
public static float Length(Quaternion q)
{
return Math.Sqrt([Inline]LengthSquared(q));
}
public float LengthSquared()
{
return X * X + Y * Y + Z * Z + W * W;
}
public static float LengthSquared(Quaternion q)
{
return q.X * q.X + q.Y * q.Y + q.Z * q.Z + q.W * q.W;
}
public static float Dot(Self l, Self r)
{
return l.X * r.X + l.Y * r.Y + l.Z * r.Z + l.W * r.W;
}
public static Quaternion Lerp(Quaternion a, Quaternion b, float interpolationValue)
{
return a + interpolationValue * (b - a);
}
public static Quaternion Slerp(Quaternion previousQuaternion, Quaternion nextQuaternion, float interpolationValue)
{
// Based on https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_007_Animations.md
var nextQuaternion;
float dot = Dot(previousQuaternion, nextQuaternion);
//make sure we take the shortest path in case dot Product is negative
if(dot < 0.0f)
{
nextQuaternion = -nextQuaternion;
dot = -dot;
}
//if the two quaternions are too close to each other, just linear interpolate between the 4D vector
if(dot > 0.9995f)
return Normalize(previousQuaternion + interpolationValue * (nextQuaternion - previousQuaternion));
//perform the spherical linear interpolation
var theta_0 = Math.Acos(dot);
var theta = interpolationValue * theta_0;
var sin_theta = Math.Sin(theta);
var sin_theta_0 = Math.Sin(theta_0);
var scalePreviousQuat = Math.Cos(theta) - dot * sin_theta / sin_theta_0;
var scaleNextQuat = sin_theta / sin_theta_0;
return scalePreviousQuat * previousQuaternion + scaleNextQuat * nextQuaternion;
}
public static Quaternion FromMatrix(Matrix matrix)
{
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
var m = matrix.V;
Quaternion result = ?;
float tr = m._11 + m._22 + m._33;
if (tr > 0) {
float S = Math.Sqrt(tr + 1.0f) * 2f; // S=4*result.W
result.W = 0.25f * S;
result.X = (m._32 - m._23) / S;
result.Y = (m._13 - m._31) / S;
result.Z = (m._21 - m._12) / S;
} else if ((m._11 > m._22)&(m._11 > m._33)) {
float S = Math.Sqrt(1.0f + m._11 - m._22 - m._33) * 2f; // S=4*result.X
result.W = (m._32 - m._23) / S;
result.X = 0.25f * S;
result.Y = (m._12 + m._21) / S;
result.Z = (m._13 + m._31) / S;
} else if (m._22 > m._33) {
float S = Math.Sqrt(1.0f + m._22 - m._11 - m._33) * 2f; // S=4*result.Y
result.W = (m._13 - m._31) / S;
result.X = (m._12 + m._21) / S;
result.Y = 0.25f * S;
result.Z = (m._23 + m._32) / S;
} else {
float S = Math.Sqrt(1.0f + m._33 - m._11 - m._22) * 2f; // S=4*result.Z
result.W = (m._21 - m._12) / S;
result.X = (m._13 + m._31) / S;
result.Y = (m._23 + m._32) / S;
result.Z = 0.25f * S;
}
return result;
}
public static Self operator +(Self value) => value;
public static Self operator +(Self l, Self r) => Self(l.X + r.X, l.Y + r.Y, l.Z + r.Z, l.W + r.W);
public static Self operator -(Self value) => Self(-value.X, -value.Y, -value.Z, -value.W);
public static Self operator -(Self l, Self r) => Self(l.X - r.X, l.Y - r.Y, l.Z - r.Z, l.W - r.W);
public static Self operator *(float l, Self r) => Self(l * r.X, l * r.Y, l * r.Z, l * r.W);
public static Self operator /(Self l, float r) => Self(l.X * r, l.Y * r, l.Z * r, l.W * r);
public static Self operator *(Self l, Self r)
{
Quaternion result;
Vector3 v = l.Vector * r.Vector + (l.W * r.Vector) + (r.W * l.Vector);
result.X = v.X;
result.Y = v.Y;
result.Z = v.Z;
result.W = (l.W * r.W) - Vector3.Dot(l.Vector, r.Vector);
return result;
}
public static Quaternion Conjugate(Quaternion q)
{
return Quaternion(-q.Vector, q.Scalar);
}
public static Quaternion Inverse(Quaternion q)
{
Quaternion conjugate = Conjugate(q);
float magSquared = LengthSquared(q);
return conjugate / magSquared;
}
[Inline]
#unwarn
public static implicit operator Vector4(in Self value) => *(Vector4*)&value;
[Inline]
#unwarn
public static implicit operator Quaternion(in Vector4 value) => *(Quaternion*)&value;
public static bool operator ==(Quaternion l, Quaternion r)
{
return l.X == r.X && l.Y == r.Y && l.Z == r.Z && l.W == r.W;
}
public static bool operator !=(Quaternion l, Quaternion r)
{
return l.X != r.X && l.Y != r.Y && l.Z != r.Z && l.W != r.W;
}
public (Vector3 Axis, float Angle) ToAxisAngle()
{
// scalar part = cos(θ/2)
// So, we can extract the angle directly.
float angle = 2.0f * Math.Acos(W);
// vector part = axis * sin(θ/2)
// In other words, the vector part is the axis, but with length of sin(θ/2).
// We assume quaternion is unit length, so subtracting w^2 gives us length of just vector part (aka sin(θ/2)).
float length = Math.Sqrt(1.0f - (W * W));
Vector3 axis;
// Normalize vector part to get the axis!
if(length == 0)
{
axis = Vector3.Zero;
}
else
{
length = 1.0f / length;
axis.X = X * length;
axis.Y = Y * length;
axis.Z = Z * length;
}
return (axis, angle);
}
public static Quaternion FromAxisAngle(Vector3 axis, float angle)
{
float lengthSq = axis.MagnitudeSquared();
if(lengthSq == 0)
{
return .Identity;
}
float halfAngle = angle * 0.5f;
float sin = Math.Sin(halfAngle) / Math.Sqrt(lengthSq);
Quaternion result;
result.X = axis.X * sin;
result.Y = axis.Y * sin;
result.Z = axis.Z * sin;
result.W = Math.Cos(halfAngle);
return result;
}
// Assumes YZX-Order meaning Y applied first, Z second and x last
public static Quaternion FromEulerAngles(float yaw, float pitch, float roll)
{
float halfYaw = yaw / 2.0f;
float halfPitch = pitch / 2.0f;
float halfRoll = roll / 2.0f;
float cosYaw = Math.Cos(halfYaw);//heading
float sinYaw = Math.Sin(halfYaw);
float cosRoll = Math.Cos(halfRoll);//attitude
float sinRoll = Math.Sin(halfRoll);
float cosPitch = Math.Cos(halfPitch);//bank
float sinPitch = Math.Sin(halfPitch);
float cosYawCosRoll = cosYaw * cosRoll;
float sinYawSinRoll = sinYaw * sinRoll;
float cosYawSinRoll = cosYaw * sinRoll;
float sinYawCosRoll = sinYaw * cosRoll;
Quaternion result;
result.W = cosYawCosRoll * cosPitch - sinYawSinRoll * sinPitch;
result.X = cosYawCosRoll * sinPitch + sinYawSinRoll * cosPitch;
result.Y = sinYawCosRoll * cosPitch + cosYawSinRoll * sinPitch;
result.Z = cosYawSinRoll * cosPitch - sinYawCosRoll * sinPitch;
return result;
}
public static Vector3 ToEulerAngles(Quaternion q)
{
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
Vector3 result;
float sqw = q.W*q.W;
float sqx = q.X*q.X;
float sqy = q.Y*q.Y;
float sqz = q.Z*q.Z;
float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
float test = q.X*q.Y + q.Z*q.W;
if (test > 0.4999f*unit) { // singularity at north pole
result.Y = 2.0f * Math.Atan2(q.X,q.W);
result.Z = Math.PI_f / 2.0f;
result.X = 0.0f;
return result;
}
if (test < -0.4999f*unit) { // singularity at south pole
result.Y = -2.0f * Math.Atan2(q.X,q.W);
result.Z = -Math.PI_f / 2.0f;
result.X = 0.0f;
return result;
}
result.Y = Math.Atan2(2*q.Y*q.W-2*q.X*q.Z , sqx - sqy - sqz + sqw);
result.Z = Math.Asin(2*test/unit);
result.X = Math.Atan2(2*q.X*q.W-2*q.Y*q.Z , -sqx + sqy - sqz + sqw);
return result;
//return .(q.Pitch(), q.Yaw(), q.Roll());
}
/*
public float Pitch()
{
float y = 2.0f * (Y * Z + W * X);
float x = W * W - X * X - Y * Y + Z * Z;
if (Vector2(x, y).Equals(.Zero)) //avoid atan2(0,0) - handle singularity - Matiis
return 2.0f * Math.Atan2(X, W);
return Math.Atan2(y, x);
}
public float Yaw()
{
return Math.Asin(Math.Clamp(-2.0f * (X * Z - W * Y), -1.0f, 1.0f));
}
public float Roll()
{
float y = 2.0f * (X * Y + W * Z);
float x = W * W + X * X - Y * Y - Z * Z;
if (Vector2(x, y).Equals(.Zero)) //avoid atan2(0,0) - handle singularity - Matiis
return 0;
return Math.Atan2(y, x);
}
*/
}
}
+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);
}
}
}
@@ -12,15 +12,18 @@ namespace GlitchyEngine.Math
public int VectorSize => _vectorSize;
this(int vectorSize)
private String _vectorTypeName;
this(int vectorSize, String vectorTypeName)
{
_vectorSize = vectorSize;
_vectorTypeName = vectorTypeName;
}
/**
* Determines whether or not a swizzle operator can have a valid setter (e.g. no component assigned twice)
* Returns true if the swizzle operator is invalid (same component assigned twice).
*/
static bool invalidSetter(int[4] cmp, int vecSize)
static bool IsSetterValid(int[4] cmp, int vecSize)
{
return cmp[0] == cmp[1] || (vecSize >= 3 && cmp[0] == cmp[2]) || (vecSize == 4 && cmp[0] == cmp[3]) ||
(vecSize >= 3 && cmp[1] == cmp[2]) || (vecSize == 4 && cmp[1] == cmp[3]) ||
@@ -30,8 +33,7 @@ namespace GlitchyEngine.Math
[Comptime]
public void ApplyToType(Type type)
{
// TODO: report bug... sized array not working
String[] componentNames = scope String[]("X", "Y", "Z", "W");
String[4] componentNames = .("X", "Y", "Z", "W");
for(int swizzleCount = 2; swizzleCount <= 4; swizzleCount++)
{
@@ -49,7 +51,16 @@ namespace GlitchyEngine.Math
String swizzleConstructor = scope String(swizzleCount * 3);
String setter = scope String(128);
bool setterInvalid = invalidSetter(cmp, swizzleCount);
bool setterInvalid = IsSetterValid(cmp, swizzleCount);
if (!setterInvalid)
{
setter.Append(
"""
set mut
{
""");
}
for(int c = 0; c < swizzleCount; c++)
{
@@ -61,21 +72,26 @@ namespace GlitchyEngine.Math
}
swizzleConstructor.Append(componentNames[cmp[c]]);
if(!setterInvalid && c < _vectorSize) //
if(!setterInvalid && c < _vectorSize)
{
setter.AppendF($"\n\t\t{componentNames[cmp[c]]} = value.{componentNames[c]};");
}
}
//{(setterInvalid ? "[Error(\"Cannot assign multiple values to same component.\")]" : String.Empty)}
if (!setterInvalid)
{
setter.Append(
"""
}
""");
}
String swizzleString = scope $"""
public Vector{swizzleCount} {swizzleName}
public {_vectorTypeName}{swizzleCount} {swizzleName}
{{
get => .({swizzleConstructor});
set mut
{{{setter}
}}
{setter}
}}
""";
+79 -13
View File
@@ -2,7 +2,7 @@ using System;
namespace GlitchyEngine.Math
{
[SwizzleVector(2)]
[SwizzleVector(2, "Vector")]
public struct Vector2
{
public const Vector2 Zero = .(0f, 0f);
@@ -10,7 +10,7 @@ namespace GlitchyEngine.Math
public const Vector2 UnitY = .(0f, 1f);
public const Vector2 One = .(1f, 1f);
public const int Length = 2;
public const int ComponentCount = 2;
public float X, Y;
@@ -31,18 +31,61 @@ namespace GlitchyEngine.Math
public ref float this[int index]
{
[Checked]
get
get mut
{
if(index < 0 || index >= Length)
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
#unwarn
return ref (&X)[index];
}
[Inline]
#unwarn
get => ref (&X)[index];
get mut => ref (&X)[index];
}
public float this[int index]
{
[Checked]
get
{
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
if(index == 0)
return X;
else
return Y;
}
[Inline]
get
{
if(index == 0)
return X;
else
return Y;
}
[Checked]
set mut
{
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
if(index == 0)
X = value;
else
Y = value;
}
[Inline]
set mut
{
if(index == 0)
X = value;
else
Y = value;
}
}
/**
@@ -90,7 +133,7 @@ namespace GlitchyEngine.Math
/**
* Calculates the projection of a onto b
*/
public Vector2 Project(Vector2 a, Vector2 b)
public static Vector2 Project(Vector2 a, Vector2 b)
{
return (b * (Dot(a, b) / Dot(b, b)));
}
@@ -98,11 +141,33 @@ namespace GlitchyEngine.Math
/**
* Calculates the rejection of a from b
*/
public Vector2 Reject(Vector2 a, Vector2 b)
public static Vector2 Reject(Vector2 a, Vector2 b)
{
return (a - b * (Dot(a, b) / Dot(b, b)));
}
/**
* Interpolates linearly between two given vectors.
* @param a The first vector.
* @param b The second vector.
* @param interpolationValue The value that linearly interpolates between a and b.
* (0 means a will be returned, 1 means b will be returned.)
* @returns The resulting linear interpolation.
*/
public static Vector2 Lerp(Vector2 a, Vector2 b, float interpolationValue)
{
return a + interpolationValue * (b - a);
}
public static Vector2 Min(Vector2 a, Vector2 b)
{
return .(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y));
}
public static Vector2 Max(Vector2 a, Vector2 b)
{
return .(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
}
//
// Assignment operators
@@ -217,11 +282,12 @@ namespace GlitchyEngine.Math
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1}", X, Y);
// Todo: move to extension
[Inline]
public static implicit operator DirectX.Math.Vector2(in Self value) => *(DirectX.Math.Vector2*)&value;
public static explicit operator Self(float value) => Self(value);
[Inline]
public static implicit operator Self(in DirectX.Math.Vector2 value) => *(Self*)&value;
public bool Equals(Vector2 v, float epsilon = Math.[Friend]sMachineEpsilonFloat)
{
return (Math.Abs(v.X - X) < epsilon) && (Math.Abs(v.Y - Y) < epsilon);
}
}
}
+105 -23
View File
@@ -2,21 +2,23 @@ using System;
namespace GlitchyEngine.Math
{
[SwizzleVector(3)]
[SwizzleVector(3, "Vector")]
public struct Vector3
{
public const Vector3 Zero = .(0f, 0f, 0f);
public const Vector3 UnitX = .(1f, 0f, 0f);
public const Vector3 UnitY = .(0f, 1f, 0f);
public const Vector3 UnitZ = .(0f, 0f, 1f);
public const Vector3 One = .(1f, 1f, 1f);
public const Vector3 Zero = .(0f, 0f, 0f);
public const Vector3 UnitX = .(1f, 0f, 0f);
public const Vector3 UnitY = .(0f, 1f, 0f);
public const Vector3 UnitZ = .(0f, 0f, 1f);
public const Vector3 One = .(1f, 1f, 1f);
public const Vector3 Forward = .(0f, 0f, 1f);
public const Vector3 Forward = .(0f, 0f, 1f);
public const Vector3 Backward = .(0f, 0f, -1f);
public const Vector3 Left = .(-1f, 0f, 0f);
public const Vector3 Right = .(1f, 0f, 0f);
public const Vector3 Up = .(0f, 1f, 0f);
public const Vector3 Down = .(0f, -1f, 0f);
public const Vector3 Left = .(-1f, 0f, 0f);
public const Vector3 Right = .(1f, 0f, 0f);
public const Vector3 Up = .(0f, 1f, 0f);
public const Vector3 Down = .(0f, -1f, 0f);
public const int ComponentCount = 3;
public float X, Y, Z;
@@ -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);
}
}
}
/**
@@ -77,6 +102,9 @@ namespace GlitchyEngine.Math
return X * X + Y * Y + Z * Z;
}
/**
* Normalizes this vector.
*/
[Checked]
public void Normalize() mut
{
@@ -86,11 +114,34 @@ namespace GlitchyEngine.Math
this /= Magnitude();
}
/**
* Normalizes this vector.
*/
public void Normalize() mut
{
this /= Magnitude();
}
/**
* Returns a copy of this Vector with a magnitude of 1.
*/
[Checked]
public Vector3 Normalized()
{
if(this == .Zero)
return .Zero;
return this / Magnitude();
}
/**
* Returns a copy of this Vector with a magnitude of 1.
*/
public Vector3 Normalized()
{
return this / Magnitude();
}
public static Vector3 Normalize(Vector3 v)
{
return v / v.Magnitude();
@@ -125,6 +176,34 @@ namespace GlitchyEngine.Math
return (a - b * (Dot(a, b) / Dot(b, b)));
}
public static Vector3 Floor(Vector3 value)
{
return .(Math.Floor(value.X), Math.Floor(value.Y), Math.Floor(value.Z));
}
/**
* 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
//
@@ -239,6 +318,14 @@ namespace GlitchyEngine.Math
public static Vector3 operator /(float scalar, Vector3 value) => Vector3(scalar / value.X, scalar / value.Y, scalar / value.Z);
// Modulo
public static Vector3 operator %(Vector3 left, Vector3 right) => Vector3(left.X % right.X, left.Y % right.Y, left.Z % right.Z);
public static Vector3 operator %(Vector3 value, float scalar) => Vector3(value.X % scalar, value.Y % scalar, value.Z % scalar);
public static Vector3 operator %(float scalar, Vector3 value) => Vector3(scalar % value.X, scalar % value.Y, scalar % value.Z);
// Equality
public static bool operator ==(Vector3 left, Vector3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
@@ -247,12 +334,7 @@ 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;
public static explicit operator Self(float value) => Self(value);
}
}
+59 -13
View File
@@ -2,7 +2,7 @@ using System;
namespace GlitchyEngine.Math
{
[SwizzleVector(4)]
[SwizzleVector(4, "Vector")]
public struct Vector4
{
public const Vector4 Zero = .(0f, 0f, 0f, 0f);
@@ -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,12 +318,7 @@ namespace GlitchyEngine.Math
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W);
// Todo: move to extension
[Inline]
public static implicit operator DirectX.Math.Vector4(in Self value) => *(DirectX.Math.Vector4*)&value;
[Inline]
public static implicit operator Self(in DirectX.Math.Vector4 value) => *(Self*)&value;
public static explicit operator Self(float value) => Self(value);
}
}
+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;
}
}
}
+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,108 @@
#if GE_GRAPHICS_DX11
#if !BF_PLATFORM_WINDOWS
#error DirectX 11 (GE_GRAPHICS_DX11) can only be used on Windows.
#endif
using DirectX.Common;
using DirectX.D3D11;
using DirectX.D3D11.SDKLayers;
using System.Diagnostics;
namespace GlitchyEngine.Platform.DX11
{
static
{
/// Gets whether or not the DX11 device is initialized.
protected internal static bool IsDx11Initialized => NativeDevice != null;
protected internal static ID3D11Device* NativeDevice;
protected internal static ID3D11DeviceContext* NativeContext;
#if DEBUG
protected internal static ID3D11Debug* DebugDevice;
#endif
internal static void Dx11Init()
{
Debug.Profiler.ProfileFunction!();
if(!IsDx11Initialized)
{
Log.EngineLogger.Trace("Creating D3D11 Device and Context...");
DeviceCreationFlags deviceFlags = .None;
#if DEBUG
deviceFlags |= .Debug;
#endif
FeatureLevel[] levels = scope .(.Level_11_0);
FeatureLevel deviceLevel = ?;
HResult deviceResult;
{
Debug.Profiler.ProfileScope!("D3D11.CreateDevice");
deviceResult = D3D11.CreateDevice(null, .Hardware, 0, deviceFlags, levels, &NativeDevice, &deviceLevel, &NativeContext);
}
Log.EngineLogger.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message({(int32)deviceResult}): {deviceResult}");
#if DEBUG
{
Debug.Profiler.ProfileScope!("Query for ID3D11Debug");
if(NativeDevice.QueryInterface<ID3D11Debug>(out DebugDevice).Succeeded)
{
ID3D11InfoQueue* infoQueue;
if(NativeDevice.QueryInterface<ID3D11InfoQueue>(out infoQueue).Succeeded)
{
infoQueue.SetBreakOnSeverity(.Corruption, true);
infoQueue.SetBreakOnSeverity(.Error, true);
infoQueue.Release();
}
}
}
#endif
Log.EngineLogger.Trace($"D3D11 Device and Context created (Feature level: {deviceLevel})");
}
else
{
// We created a second GraphicsContext (for some reason?) just increment references.
NativeDevice.AddRef();
NativeContext.AddRef();
#if DEBUG
DebugDevice.AddRef();
#endif
}
}
internal static void Dx11Release()
{
Debug.Profiler.ProfileFunction!();
NativeDevice.Release();
NativeContext.Release();
#if DEBUG
Debug.WriteLine("""
-------------------------
Live DX Objects Report:
""");
DebugDevice.ReportLiveDeviceObjects(.Detail | .IgnoreInternal);
Debug.WriteLine("""
-------------------------
""");
DebugDevice.Release();
#endif
}
}
}
#endif
@@ -0,0 +1,43 @@
#if GE_GRAPHICS_DX11
using GlitchyEngine.Renderer;
using System.Collections;
using DirectX.D3D11;
namespace ImGui
{
using internal GlitchyEngine.Renderer;
extension ImGui
{
static List<ID3D11ShaderResourceView*> _resourceViews = new .() ~ delete _;
public static override void Image(Texture2D texture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec4 tint_col = Vec4.Ones, Vec4 border_col = Vec4.Zero)
{
var view = texture.nativeResourceView..AddRef();
_resourceViews.Add(view);
ImGui.Image(view, size, uv0, uv1, tint_col, border_col);
}
public static override void Image(RenderTarget2D texture, Vec2 size, Vec2 uv0 = Vec2.Zero, Vec2 uv1 = Vec2.Ones, Vec4 tint_col = Vec4.Ones, Vec4 border_col = Vec4.Zero)
{
var view = texture._nativeResourceView..AddRef();
_resourceViews.Add(view);
ImGui.Image(view, size, uv0, uv1, tint_col, border_col);
}
protected internal static override void CleanupFrame()
{
for(var view in _resourceViews)
{
view.Release();
}
_resourceViews.Clear();
}
}
}
#endif
@@ -1,9 +1,12 @@
#if GE_GRAPHICS_DX11
// Disable warning when taking pointer of in-Parameter
#pragma warning disable 4204
using System;
namespace GlitchyEngine.Math
{
/*
extension Vector2
{
[Inline]
@@ -30,5 +33,6 @@ namespace GlitchyEngine.Math
[Inline]
public static implicit operator Self(in DirectX.Math.Vector4 value) => *(Self*)&value;
}
*/
}
#endif
@@ -1,22 +1,24 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using DirectX.Common;
using GlitchyEngine.Math;
using internal GlitchyEngine.Renderer;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
extension BlendState
{
internal ID3D11BlendState* nativeBlendState ~ _?.Release();
public override void Bind(ColorRGBA blendFactor)
{
_context.nativeContext.OutputMerger.SetBlendState(nativeBlendState, blendFactor);
}
protected override void PlatformCreateBlendState()
{
Debug.Profiler.ProfileResourceFunction!();
BlendDescription nativeDesc = .Default;
nativeDesc.IndependentBlendEnable = _desc.IndependentBlendEnable;
nativeDesc.AlphaToCoverageEnable = _desc.AlphaToCoverageEnable;
@@ -33,9 +35,11 @@ namespace GlitchyEngine.Renderer
nativeDesc.RenderTarget[i].RenderTargetWriteMask = _desc.RenderTarget[i].RenderTargetWriteMask;
}
HResult result = _context.nativeDevice.CreateBlendState(ref nativeDesc, &nativeBlendState);
HResult result = NativeDevice.CreateBlendState(ref nativeDesc, &nativeBlendState);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create blend state. Error({(int)result}): {result}");
}
}
}
#endif
@@ -1,27 +1,16 @@
#if GE_GRAPHICS_DX11
using System.Diagnostics;
using System;
using DirectX.D3D11;
using internal GlitchyEngine.Renderer;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
extension CPUAccessFlags
{
public static explicit operator DirectX.D3D11.CpuAccessFlags(Self cpuAccessFlags)
{
DirectX.D3D11.CpuAccessFlags flags = .None;
if(cpuAccessFlags.HasFlag(.Read))
flags |= .Read;
if(cpuAccessFlags.HasFlag(.Write))
flags |= .Write;
return flags;
}
}
extension BufferDescription
{
public static operator DirectX.D3D11.BufferDescription(Self desc)
@@ -71,6 +60,8 @@ namespace GlitchyEngine.Renderer
private Result<void> InternalCreateBuffer(void* data, uint32 byteLength, uint32 dstByteOffset)
{
Debug.Profiler.ProfileResourceFunction!();
nativeDescription = (.)_description;
uint8* byteData = (.)data;
@@ -79,11 +70,11 @@ namespace GlitchyEngine.Renderer
{
byteData = new uint8[nativeDescription.ByteWidth]*;
defer:: delete byteData;
Internal.MemCpy(byteData + dstByteOffset, data, byteLength);
Internal.MemCpy(byteData + dstByteOffset, data, Math.Min(nativeDescription.ByteWidth, byteLength));
}
SubresourceData srData = .(byteData, byteLength, 0);
var result = _context.nativeDevice.CreateBuffer(ref nativeDescription, &srData, &nativeBuffer);
var result = NativeDevice.CreateBuffer(ref nativeDescription, &srData, &nativeBuffer);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create buffer. Message({(int)result}):{result}");
@@ -95,6 +86,8 @@ namespace GlitchyEngine.Renderer
protected override Result<void> PlatformSetData(void* data, uint32 byteLength, uint32 dstByteOffset, GlitchyEngine.Renderer.MapType mapType)
{
Debug.Profiler.ProfileResourceFunction!();
if(nativeBuffer == null)
{
// We can pass the data while creating the buffer, so we can return here.
@@ -107,14 +100,14 @@ namespace GlitchyEngine.Renderer
{
case .Default:
Box dataBox = .(dstByteOffset, 0, 0, dstByteOffset + byteLength, 1, 1);
_context.nativeContext.UpdateSubresource(nativeBuffer, 0, &dataBox, data, byteLength, byteLength);
NativeContext.UpdateSubresource(nativeBuffer, 0, &dataBox, data, byteLength, byteLength);
case .Dynamic:
Debug.Assert(mapType.CanWrite, "The map type has to have write access.");
// Todo: DoNotWaitFlag
MappedSubresource map = ?;
_context.nativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map);
NativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map);
Internal.MemCpy(((uint8*)map.Data) + dstByteOffset, data, byteLength);
_context.nativeContext.Unmap(nativeBuffer, 0);
NativeContext.Unmap(nativeBuffer, 0);
case .Immutable:
Log.EngineLogger.Error("Can't set the data of an immutable resource.");
return .Err;
@@ -127,3 +120,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -1,3 +1,5 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using internal GlitchyEngine.Renderer;
@@ -10,6 +12,8 @@ namespace GlitchyEngine.Renderer
internal void PlatformFetchNativeBuffers()
{
Debug.Profiler.ProfileRendererFunction!();
for(let buffer in _buffers)
{
nativeBuffers[buffer.Index] = buffer.Buffer.nativeBuffer;
@@ -17,3 +21,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -0,0 +1,22 @@
#if GE_GRAPHICS_DX11
namespace GlitchyEngine.Renderer
{
extension CPUAccessFlags
{
public static explicit operator DirectX.D3D11.CpuAccessFlags(Self cpuAccessFlags)
{
DirectX.D3D11.CpuAccessFlags flags = .None;
if(cpuAccessFlags.HasFlag(.Read))
flags |= .Read;
if(cpuAccessFlags.HasFlag(.Write))
flags |= .Write;
return flags;
}
}
}
#endif
@@ -1,3 +1,5 @@
#if GE_GRAPHICS_DX11
using DirectX.Common;
using DirectX.D3D11Shader;
using System;
@@ -10,6 +12,8 @@ namespace GlitchyEngine.Renderer
{
internal this(ConstantBuffer constantBuffer, ID3D11ShaderReflectionVariable* variableReflection)
{
Debug.Profiler.ProfileResourceFunction!();
_constantBuffer = constantBuffer;
HResult result = variableReflection.GetDescription(let variableDescription);
@@ -27,14 +31,19 @@ namespace GlitchyEngine.Renderer
switch(shaderTypeDescription.Type)
{
case .Bool:
_type = .Bool;
case .Float:
_type = .Float;
case .Int:
_type = .Int;
default:
Log.EngineLogger.Assert(false, scope $"Unhandled shader variable type: {shaderTypeDescription.Type}");
}
_columns = shaderTypeDescription.Columns;
_rows = shaderTypeDescription.Rows;
_elements = shaderTypeDescription.Elements;
SetRawData(variableDescription.DefaultValue);
}
@@ -42,8 +51,10 @@ namespace GlitchyEngine.Renderer
extension ConstantBuffer
{
internal this(GraphicsContext context, ID3D11ShaderReflectionConstantBuffer* bufferReflection) : base(context)
internal this(ID3D11ShaderReflectionConstantBuffer* bufferReflection)
{
Debug.Profiler.ProfileResourceFunction!();
Reflect(bufferReflection);
ConstructBuffer();
@@ -53,6 +64,8 @@ namespace GlitchyEngine.Renderer
private void Reflect(ID3D11ShaderReflectionConstantBuffer* bufferReflection)
{
Debug.Profiler.ProfileResourceFunction!();
HResult result = bufferReflection.GetDescription(let bufferDescription);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to get buffer description. Error({(int)result}): {result}");
@@ -75,3 +88,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -0,0 +1,64 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
internal typealias D3DDepthStencilStateDesc = DirectX.D3D11.DepthStencilStateDescription;
internal typealias DxDSSDesc = DirectX.D3D11.DepthStencilStateDescription;
internal typealias GEDSSDesc = GlitchyEngine.Renderer.DepthStencilStateDescription;
internal typealias DxDepthStencilOpDesc = DirectX.D3D11.DepthStencilOperationDescription;
extension StencilOperation
{
public static explicit operator DirectX.D3D11.StencilOperation(Self self)
{
return (.)self.Underlying;
}
}
extension DepthStencilOperationDescription
{
public static explicit operator DxDepthStencilOpDesc(Self desc)
{
return DxDepthStencilOpDesc((.)desc.StencilFailOperation, (.)desc.StencilDepthFailOperation,
(.)desc.StencilPassOperation, (.)desc.StencilFunction);
}
}
extension DepthStencilStateDescription
{
public static explicit operator DxDSSDesc(Self desc)
{
return DxDSSDesc(desc.DepthEnabled, desc.WriteDepth ? .All : .Zero, (.)desc.DepthFunction, desc.StencilEnabled,
desc.StencilReadMask, desc.StencilWriteMask, (.)desc.FrontFace, (.)desc.BackFace);
}
}
extension DepthStencilState
{
internal DxDSSDesc nativeDescription;
internal ID3D11DepthStencilState* nativeDepthStencilState ~ _?.Release();
public this(GEDSSDesc description)
{
Debug.Profiler.ProfileResourceFunction!();
_description = description;
nativeDescription = (.)description;
var result = NativeDevice.CreateDepthStencilState(ref nativeDescription, &nativeDepthStencilState);
if (result.Failed)
{
Log.EngineLogger.Error($"Failed to create depth stencil state. Message({(int)result}): {result}");
}
}
}
}
#endif
@@ -0,0 +1,59 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using internal GlitchyEngine.Renderer;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
extension DepthStencilFormat
{
public static explicit operator Format(Self self)
{
switch(self)
{
case .D16_UNorm:
return .D16_UNorm;
case .D24_UNorm_S8_UInt:
return .D24_UNorm_S8_UInt;
case .D32_Float:
return .D32_Float;
case .D32_Float_S8X24_UInt:
return .D32_Float_S8X24_UInt;
case .None:
Log.EngineLogger.Assert(false, "None is not a valid depth stencil format");
return .Unknown;
}
}
}
// TODO: Depth stencil target is technically just a RenderTarget
extension DepthStencilTarget
{
internal ID3D11DepthStencilView* nativeView ~ _?.Release();
protected override void PlatformCreate()
{
Debug.Profiler.ProfileResourceFunction!();
Texture2DDescription desc = .();
desc.Format = (.)_format;
desc.ArraySize = 1;
desc.BindFlags = .DepthStencil;
desc.Width = _width;
desc.Height = _height;
desc.SampleDesc = .(1, 0);
ID3D11Texture2D* tex = ?;
NativeDevice.CreateTexture2D(ref desc, null, &tex);
NativeDevice.CreateDepthStencilView(tex, null, &nativeView);
tex.Release();
}
}
}
#endif
@@ -1,3 +1,5 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX;
using DirectX.D3D11;
@@ -10,9 +12,11 @@ namespace GlitchyEngine.Renderer
{
protected override void Compile(String vsPath, String vsEntry, String psPath, String psEntry)
{
Debug.Profiler.ProfileResourceFunction!();
// Todo: macros
VertexShader = new VertexShader(_context, vsPath, vsEntry);
PixelShader = new PixelShader(_context, psPath, psEntry);
VertexShader = new VertexShader(vsPath, vsEntry);
PixelShader = new PixelShader(psPath, psEntry);
Reflect();
}
@@ -23,3 +27,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -1,10 +1,14 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
using internal GlitchyEngine.Renderer;
extension GeometryBinding
{
internal ID3D11Buffer*[DirectX.D3D11.D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT] nativeBuffers;
@@ -27,6 +31,8 @@ namespace GlitchyEngine.Renderer
protected override void PlatformSetVertexBuffer(VertexBufferBinding binding, uint32 slot)
{
Debug.Profiler.ProfileResourceFunction!();
Log.EngineLogger.Assert(slot < nativeBuffers.Count, "The buffer slot has to be in the range from 0 to 31.");
VertexBuffer vertexBuffer = binding.Buffer;
@@ -40,8 +46,8 @@ namespace GlitchyEngine.Renderer
nativeBuffers[slot] = vertexBuffer.nativeBuffer..AddRef();
bufferStrides[slot] = vertexBuffer.Binding.Stride;
bufferOffsets[slot] = vertexBuffer.Binding.Offset;
bufferStrides[slot] = binding.Stride;
bufferOffsets[slot] = binding.Offset;
}
else
{
@@ -55,12 +61,16 @@ namespace GlitchyEngine.Renderer
protected override void PlatformSetVertexLayout(VertexLayout vertexLayout)
{
Debug.Profiler.ProfileResourceFunction!();
nativeVertexLayout?.Release();
nativeVertexLayout = vertexLayout?.nativeLayout..AddRef();
}
protected override void PlatformSetIndexBuffer(IndexBuffer indexBuffer)
{
Debug.Profiler.ProfileResourceFunction!();
Log.EngineLogger.Assert(indexBuffer.nativeDescription.BindFlags.HasFlag(.IndexBuffer),
scope $"Buffer ({indexBuffer.nativeBuffer.GetDebugName(.. scope .())}) must have IndexBuffer-flag set to be bound as an index buffer.");
@@ -73,32 +83,33 @@ namespace GlitchyEngine.Renderer
}
public override void Bind(GraphicsContext context = null)
public override void Bind()
{
var context;
context ??= _context;
Debug.Profiler.ProfileRendererFunction!();
context.nativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nativeBuffers, &bufferStrides, &bufferOffsets);
context.nativeContext.InputAssembler.SetInputLayout(_vertexLayout.nativeLayout);
context.nativeContext.InputAssembler.SetPrimitiveTopology((.)_primitiveTopology);
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,132 +68,183 @@ namespace GlitchyEngine.Renderer
/**
* Initializes the Device and ImmediateContext.
*/
/*
void InitDevice()
{
nativeDevice?.Release();
nativeContext?.Release();
if(!IsDx11Initialized)
{
Log.EngineLogger.Trace("Creating D3D11 Device and Context...");
Log.EngineLogger.Trace("Creating D3D11 Device and Context...");
DeviceCreationFlags deviceFlags = .None;
DeviceCreationFlags deviceFlags = .None;
#if DEBUG
deviceFlags |= .Debug;
deviceFlags |= .Debug;
#endif
FeatureLevel[] levels = scope .(.Level_11_0);
FeatureLevel[] levels = scope .(.Level_11_0);
FeatureLevel deviceLevel = ?;
var deviceResult = D3D11.CreateDevice(null, .Hardware, 0, deviceFlags, levels, &nativeDevice, &deviceLevel, &nativeContext);
Debug.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message(0x{(int32)deviceResult}): {deviceResult}");
FeatureLevel deviceLevel = ?;
var deviceResult = D3D11.CreateDevice(null, .Hardware, 0, deviceFlags, levels, &nativeDevice, &deviceLevel, &nativeContext);
Log.EngineLogger.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message({(int32)deviceResult}): {deviceResult}");
#if DEBUG
if(nativeDevice.QueryInterface<ID3D11Debug>(out _debugDevice).Succeeded)
{
ID3D11InfoQueue* infoQueue;
if(nativeDevice.QueryInterface<ID3D11InfoQueue>(out infoQueue).Succeeded)
if(nativeDevice.QueryInterface<ID3D11Debug>(out debugDevice).Succeeded)
{
infoQueue.SetBreakOnSeverity(.Corruption, true);
infoQueue.SetBreakOnSeverity(.Error, true);
ID3D11InfoQueue* infoQueue;
if(nativeDevice.QueryInterface<ID3D11InfoQueue>(out infoQueue).Succeeded)
{
infoQueue.SetBreakOnSeverity(.Corruption, true);
infoQueue.SetBreakOnSeverity(.Error, true);
infoQueue.Release();
infoQueue.Release();
}
}
}
#endif
Log.EngineLogger.Trace($"D3D11 Device and Context created (Feature level: {deviceLevel})");
}
private ID3D11RenderTargetView*[MaxRTVCount] _renderTargets;
public override void SetRenderTarget(RenderTarget renderTarget, int slot = 0)
{
if(renderTarget == null)
{
_renderTargets[slot] = _swapChain.nativeBackBufferTarget;
Log.EngineLogger.Trace($"D3D11 Device and Context created (Feature level: {deviceLevel})");
}
else
{
Runtime.NotImplemented();
// We created a second GraphicsContext (for some reason?) just increment references.
nativeDevice.AddRef();
nativeContext.AddRef();
debugDevice.AddRef();
}
}
*/
private ID3D11DepthStencilView* _depthStencilTarget;
private ID3D11RenderTargetView*[MaxRTVCount] _renderTargets;
public override void SetDepthStencilTarget(DepthStencilTarget target)
{
_depthStencilTarget = target?.nativeView;
}
public override void SetRenderTarget(RenderTarget2D renderTarget, int slot, bool setDepthTarget)
{
_renderTargets[slot] = (renderTarget ?? _swapChain.BackBuffer)._nativeRenderTargetView;
if(slot == 0 && setDepthTarget)
{
SetDepthStencilTarget((renderTarget ?? _swapChain.BackBuffer).DepthStencilTarget);
}
}
public override void BindRenderTargets()
{
nativeContext.OutputMerger.SetRenderTargets(MaxRTVCount, &_renderTargets, null);
NativeContext.OutputMerger.SetRenderTargets(MaxRTVCount, &_renderTargets, _depthStencilTarget);
}
public override void ClearRenderTarget(RenderTarget renderTarget, ColorRGBA color)
public override void ClearRenderTarget(RenderTarget2D renderTarget, ColorRGBA color)
{
if(renderTarget == null)
{
nativeContext.ClearRenderTargetView(_swapChain.nativeBackBufferTarget, color);
}
else
{
Runtime.NotImplemented();
}
NativeContext.ClearRenderTargetView((renderTarget ?? _swapChain.BackBuffer)._nativeRenderTargetView, color);
}
public override void SetVertexBuffer(uint32 slot, Buffer buffer, uint32 stride, uint32 offset = 0)
{
// make stride and offset mutable so that we can take their pointers.
var stride, offset;
nativeContext.InputAssembler.SetVertexBuffers(slot, 1, &buffer.nativeBuffer, &stride, &offset);
NativeContext.InputAssembler.SetVertexBuffers(slot, 1, &buffer.nativeBuffer, &stride, &offset);
}
public override void Draw(uint32 vertexCount, uint32 startVertexIndex = 0)
{
nativeContext.Draw(vertexCount, startVertexIndex);
NativeContext.Draw(vertexCount, startVertexIndex);
}
public override void DrawIndexed(uint32 indexCount, uint32 startIndexLocation = 0, int32 vertexOffset = 0)
{
nativeContext.DrawIndexed(indexCount, startIndexLocation, vertexOffset);
NativeContext.DrawIndexed(indexCount, startIndexLocation, vertexOffset);
}
public override void SetIndexBuffer(Buffer buffer, IndexFormat indexFormat = .Index16Bit, uint32 byteOffset = 0)
{
nativeContext.InputAssembler.SetIndexBuffer(buffer.nativeBuffer, indexFormat == .Index32Bit ? .R32_UInt : .R16_UInt, byteOffset);
}
protected override void SetRasterizerStateImpl()
{
nativeContext.Rasterizer.SetState(_currentRasterizerState.nativeRasterizerState);
NativeContext.InputAssembler.SetIndexBuffer(buffer.nativeBuffer, indexFormat == .Index32Bit ? .R32_UInt : .R16_UInt, byteOffset);
}
public override void SetViewports(uint32 viewportsCount, GlitchyEngine.Renderer.Viewport* viewports)
{
nativeContext.Rasterizer.SetViewports(viewportsCount, (.)viewports);
NativeContext.Rasterizer.SetViewports(viewportsCount, (.)viewports);
}
public override void SetVertexLayout(VertexLayout vertexLayout)
{
nativeContext.InputAssembler.SetInputLayout(vertexLayout.nativeLayout);
NativeContext.InputAssembler.SetInputLayout(vertexLayout.nativeLayout);
}
public override void SetPrimitiveTopology(GlitchyEngine.Renderer.PrimitiveTopology primitiveTopology)
{
nativeContext.InputAssembler.SetPrimitiveTopology((DirectX.Common.PrimitiveTopology)primitiveTopology);
NativeContext.InputAssembler.SetPrimitiveTopology((DirectX.Common.PrimitiveTopology)primitiveTopology);
}
/**
* Binds the given shader to the corresponding shader stage.
* @param shader The shader that will be bound to the graphics context.
*/
private void BindShaderToStage<TShader>(TShader shader) where TShader : Shader
{
Debug.Profiler.ProfileRendererFunction!();
uint32 _firstTexture = D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
uint32 _textureCount = 0;
ID3D11ShaderResourceView*[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] _textures = .();
ID3D11SamplerState*[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] _samplers = .();
for(let entry in shader.Textures)
{
_textures[entry.Index] = entry.Texture?.nativeResourceView;
_samplers[entry.Index] = entry.Texture?.SamplerState?.nativeSamplerState;
if(entry.Index >= _textureCount)
_textureCount = entry.Index + 1;
if(entry.Index < _firstTexture)
_firstTexture = entry.Index;
}
if(_textureCount > 0)
{
switch(typeof(TShader))
{
// TODO: Add remaining shader stages
case typeof(PixelShader):
// TODO: bind uavs
NativeContext.PixelShader.SetShaderResources(_firstTexture, _textureCount, &_textures[_firstTexture]);
NativeContext.PixelShader.SetSamplers(_firstTexture, _textureCount, &_samplers[_firstTexture]);
case typeof(VertexShader):
NativeContext.VertexShader.SetShaderResources(_firstTexture, _textureCount, &_textures[_firstTexture]);
NativeContext.VertexShader.SetSamplers(_firstTexture, _textureCount, &_samplers[_firstTexture]);
default:
Runtime.FatalError(scope $"Shader stage \"{typeof(TShader)}\" not implemented.");
}
}
shader.Buffers.PlatformFetchNativeBuffers();
switch(typeof(TShader))
{
// TODO: Add remaining shader stages
case typeof(PixelShader):
NativeContext.PixelShader.SetConstantBuffers(0, shader.Buffers.nativeBuffers.Count, &shader.Buffers.nativeBuffers);
[IgnoreErrors]{ NativeContext.PixelShader.SetShader(((PixelShader)shader).nativeShader); }
case typeof(VertexShader):
NativeContext.VertexShader.SetConstantBuffers(0, shader.Buffers.nativeBuffers.Count, &shader.Buffers.nativeBuffers);
[IgnoreErrors]{ NativeContext.VertexShader.SetShader(((VertexShader)shader).nativeShader); }
default:
Runtime.FatalError(scope $"Shader stage \"{typeof(TShader)}\" not implemented.");
}
}
public override void SetVertexShader(VertexShader vertexShader)
{
//Todo: nativeContext.VertexShader.SetSamplers();
vertexShader.Buffers.PlatformFetchNativeBuffers();
nativeContext.VertexShader.SetConstantBuffers(0, vertexShader.Buffers.nativeBuffers.Count, &vertexShader.Buffers.nativeBuffers);
//Todo: nativeContext.VertexShader.SetShaderResources();
nativeContext.VertexShader.SetShader(vertexShader.nativeShader);
BindShaderToStage(vertexShader);
}
public override void SetPixelShader(PixelShader pixelShader)
{
//Todo: nativeContext.PixelShader.SetSamplers();
pixelShader.Buffers.PlatformFetchNativeBuffers();
nativeContext.PixelShader.SetConstantBuffers(0, pixelShader.Buffers.nativeBuffers.Count, &pixelShader.Buffers.nativeBuffers);
//Todo: nativeContext.PixelShader.SetShaderResources();
nativeContext.PixelShader.SetShader(pixelShader.nativeShader);
BindShaderToStage(pixelShader);
}
}
}
#endif
@@ -1,8 +1,12 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX.D3D11;
using DirectX.D3DCompiler;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
@@ -12,15 +16,23 @@ namespace GlitchyEngine.Renderer
public override void CompileFromSource(String code, String entryPoint, ShaderDefine[] macros = null)
{
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);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create pixel shader: Message ({(int)result}): {result}");
Debug.Profiler.ProfileResourceScope!("CreateNativePixelShader");
var result = NativeDevice.CreatePixelShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create pixel shader: Message ({(int)result}): {result}");
}
}
Reflect(nativeCode);
}
}
}
#endif
@@ -1,6 +1,10 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
@@ -48,12 +52,14 @@ namespace GlitchyEngine.Renderer
internal DirectX.D3D11.RasterizerStateDescription nativeDescription;
internal ID3D11RasterizerState* nativeRasterizerState ~ _?.Release();
public override this(GraphicsContext context, GlitchyEngine.Renderer.RasterizerStateDescription description) : this(context)
public override this(GlitchyEngine.Renderer.RasterizerStateDescription description)
{
Debug.Profiler.ProfileRendererFunction!();
_description = description;
nativeDescription = (.)_description;
var result = context.nativeDevice.CreateRasterizerState(ref nativeDescription, &nativeRasterizerState);
var result = NativeDevice.CreateRasterizerState(ref nativeDescription, &nativeRasterizerState);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create rasterizer state. Message({(int)result}): {result}");
@@ -61,3 +67,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -0,0 +1,125 @@
#if GE_GRAPHICS_DX11
using DirectX.Common;
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using DirectX.DXGI.DXGI1_2;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
using internal GlitchyEngine.Renderer;
extension RenderTarget2D
{
protected internal ID3D11Texture2D* _nativeTexture ~ _?.Release();
protected internal ID3D11ShaderResourceView* _nativeResourceView ~ _?.Release();
protected internal ID3D11RenderTargetView* _nativeRenderTargetView ~ _?.Release();
private void ReleaseAndNullify()
{
Debug.Profiler.ProfileResourceFunction!();
ReleaseAndNullify!(_nativeTexture);
ReleaseAndNullify!(_nativeResourceView);
ReleaseAndNullify!(_nativeRenderTargetView);
ReleaseRefAndNullify!(_depthStenilTarget);
}
public override void Resize(uint32 width, uint32 height)
{
Debug.Profiler.ProfileResourceFunction!();
ReleaseAndNullify();
_description.Width = width;
_description.Height = height;
ApplyChanges();
}
protected override void PlatformApplyChanges()
{
Debug.Profiler.ProfileResourceFunction!();
ReleaseAndNullify();
if(_description.IsSwapchainTarget)
{
// TODO: if the engine supports multiple windows it has to support multiple swap chains.
// kinda dirty...
var context = GraphicsContext.Get();
context.SwapChain.GetBackbuffer(out _nativeTexture);
CreateViews();
}
else
{
PlatformCreateTexture();
}
if(_description.DepthStencilFormat != .None)
{
_depthStenilTarget = new DepthStencilTarget(_description.Width, _description.Height, _description.DepthStencilFormat);
}
}
private void PlatformCreateTexture()
{
Debug.Profiler.ProfileResourceFunction!();
Texture2DDescription desc = .()
{
Width = _description.Width,
Height = _description.Height,
ArraySize = _description.ArraySize,
MipLevels = _description.MipLevels,
Format = _description.PixelFormat,
// Always bindable as ShaderResource and RenderTarget
BindFlags = .ShaderResource | .RenderTarget,
CpuAccessFlags = (.)_description.CpuAccess,
// 2D RenderTarget never has misc flags
MiscFlags = .None,
SampleDesc = .(_description.SampleCount, _description.SampleQuality),
// RenderTarget has always Default usage
Usage = .Default
};
var result = NativeDevice.CreateTexture2D(ref desc, null, &_nativeTexture);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create RenderTarget2D");
CreateViews();
}
private void CreateViews()
{
Debug.Profiler.ProfileResourceFunction!();
if(_description.IsSwapchainTarget)
{
var result = NativeDevice.CreateShaderResourceView(_nativeTexture, null, &_nativeResourceView);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create resource view");
// TODO: SRGB...
//RenderTargetViewDescription rtvDesc = .(_nativeTexture, .Texture2D, .R8G8B8A8_UNorm_SRGB);
//result = NativeDevice.CreateRenderTargetView(_nativeTexture, &rtvDesc, &_nativeRenderTargetView);
result = NativeDevice.CreateRenderTargetView(_nativeTexture, null, &_nativeRenderTargetView);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create render target view");
}
else
{
var result = NativeDevice.CreateShaderResourceView(_nativeTexture, null, &_nativeResourceView);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create resource view");
result = NativeDevice.CreateRenderTargetView(_nativeTexture, null, &_nativeRenderTargetView);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create render target view");
}
}
}
}
#endif
@@ -1,32 +1,141 @@
#if GE_GRAPHICS_DX11
using GlitchyEngine.Math;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
using internal GlitchyEngine.Renderer;
extension RendererAPI
{
private GraphicsContext _context;
private GraphicsContext _context ~ _?.ReleaseRef();
public GraphicsContext Context
{
get => _context;
set => _context = value;
set => SetReference!(_context, value);
}
public static override API Api => .D3D11;
public override void Init()
{
Debug.Profiler.ProfileFunction!();
}
public override void Clear(RenderTarget renderTarget, ColorRGBA clearColor)
private mixin RtOrBackbuffer(RenderTarget2D renderTarget)
{
_context.ClearRenderTarget(renderTarget, clearColor);
renderTarget ?? _context.SwapChain.BackBuffer
}
public override void Clear(RenderTarget2D renderTarget, ColorRGBA color)
{
Debug.Profiler.ProfileRendererFunction!();
NativeContext.ClearRenderTargetView(RtOrBackbuffer!(renderTarget)._nativeRenderTargetView, color);
}
public override void Clear(DepthStencilTarget target, ClearOptions clearOptions, float depth, uint8 stencil)
{
Debug.Profiler.ProfileRendererFunction!();
if(target == null)
return;
DirectX.D3D11.ClearFlag flags = default;
if(clearOptions.HasFlag(.Depth))
{
flags |= .Depth;
}
if(clearOptions.HasFlag(.Stencil))
{
flags |= .Stencil;
}
NativeContext.ClearDepthStencilView(target.nativeView, flags, depth, stencil);
}
public override void SetRenderTarget(RenderTarget2D renderTarget, int slot, bool setDepthBuffer)
{
Debug.Profiler.ProfileRendererFunction!();
_context.SetRenderTarget(renderTarget, slot, setDepthBuffer);
}
public override void SetDepthStencilTarget(DepthStencilTarget target)
{
Debug.Profiler.ProfileRendererFunction!();
_context.SetDepthStencilTarget(target);
}
public override void BindRenderTargets()
{
Debug.Profiler.ProfileRendererFunction!();
_context.BindRenderTargets();
}
private RasterizerState _currentRasterizerState ~ _?.ReleaseRef();
public override void SetRasterizerState(RasterizerState rasterizerState)
{
Debug.Profiler.ProfileRendererFunction!();
SetReference!(_currentRasterizerState, rasterizerState);
NativeContext.Rasterizer.SetState(_currentRasterizerState.nativeRasterizerState);
}
private BlendState _currentBlendState ~ _?.ReleaseRef();
public override void SetBlendState(BlendState blendState, ColorRGBA blendFactor)
{
Debug.Profiler.ProfileRendererFunction!();
SetReference!(_currentBlendState, blendState);
NativeContext.OutputMerger.SetBlendState(_currentBlendState.nativeBlendState, blendFactor);
}
private DepthStencilState _currentDepthStencilState ~ _?.ReleaseRef();
public override void SetDepthStencilState(DepthStencilState depthStencilState, uint8 stencilReference)
{
Debug.Profiler.ProfileRendererFunction!();
SetReference!(_currentDepthStencilState, depthStencilState);
NativeContext.OutputMerger.SetDepthStencilState(_currentDepthStencilState.nativeDepthStencilState, stencilReference);
}
public override void DrawIndexed(GeometryBinding geometry)
{
_context.DrawIndexed(geometry.IndexCount, geometry.IndexByteOffset, 0);
Debug.Profiler.ProfileRendererFunction!();
if(geometry.IsIndexed)
_context.DrawIndexed(geometry.IndexCount, geometry.IndexByteOffset, 0);
else
_context.Draw(geometry.VertexCount, 0);
}
public override void DrawIndexedInstanced(GeometryBinding geometry)
{
Debug.Profiler.ProfileRendererFunction!();
NativeContext.DrawIndexedInstanced(geometry.IndexCount, geometry.InstanceCount, geometry.IndexByteOffset, 0, 0);
}
public override void SetViewport(Viewport viewport)
{
Debug.Profiler.ProfileRendererFunction!();
_context.SetViewport(viewport);
}
}
}
#endif
@@ -1,5 +1,11 @@
using DirectX.D3D11;
#if GE_GRAPHICS_DX11
using DirectX.Common;
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace DirectX.D3D11
{
@@ -73,8 +79,6 @@ namespace DirectX.D3D11
}
}
using internal GlitchyEngine.Renderer;
namespace GlitchyEngine.Renderer
{
extension SamplerStateDescription
@@ -107,17 +111,21 @@ namespace GlitchyEngine.Renderer
protected override void PlatformCreateSamplerState()
{
Debug.Profiler.ProfileResourceFunction!();
_desc.ToNative(var nativeDesc);
HResult result = _context.nativeDevice.CreateSamplerState(ref nativeDesc, &nativeSamplerState);
HResult result = NativeDevice.CreateSamplerState(ref nativeDesc, &nativeSamplerState);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create SamplerState. Error({(int)result}): {result}");
}
public override void Bind(uint32 slot)
{
_context.nativeContext.VertexShader.SetSamplers(slot, 1, &nativeSamplerState);
_context.nativeContext.PixelShader.SetSamplers(slot, 1, &nativeSamplerState);
NativeContext.VertexShader.SetSamplers(slot, 1, &nativeSamplerState);
NativeContext.PixelShader.SetSamplers(slot, 1, &nativeSamplerState);
}
}
}
#endif
@@ -1,3 +1,5 @@
#if GE_GRAPHICS_DX11
using System;
using GlitchyEngine.Renderer;
using DirectX.Common;
@@ -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)
@@ -42,72 +46,67 @@ namespace GlitchyEngine.Renderer
var result = D3DCompiler.D3DCompile(code.CStr(), (.)code.Length, null, nativeMacros, null, entryPoint, target, compileFlags, .None, &shaderBlob, &errorBlob);
if(result.Failed)
{
StringView str = StringView((char8*)errorBlob.GetBufferPointer(), errorBlob.GetBufferSize());
StringView str = StringView((char8*)errorBlob.GetBufferPointer(), (int)errorBlob.GetBufferSize());
Log.EngineLogger.Error($"Failed to compile Shader: Error Code({(int)result}): {result} | Error Message: {str}");
}
}
protected internal void Reflect(ID3DBlob* shaderCode)
{
Debug.Profiler.ProfileResourceFunction!();
ID3D11ShaderReflection* reflection = null;
var result = D3DCompiler.D3DReflect(shaderCode.GetBufferPointer(), shaderCode.GetBufferSize(), &reflection);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to reflect shader: Message ({(int)result}): {result}");
Debug.Profiler.ProfileResourceScope!("D3DReflect");
var result = D3DCompiler.D3DReflect(shaderCode.GetBufferPointer(), shaderCode.GetBufferSize(), &reflection);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to reflect shader: Message ({(int)result}): {result}");
}
}
reflection.GetDescription(let desc);
uint32 cBufferCount = desc.ConstantBuffers;
for(uint32 i < cBufferCount)
uint32 resourceCount = desc.BoundResources;
for (uint32 i < resourceCount)
{
reflection.GetResourceBindingDescription(i, let bindDesc);
var res = reflection.GetResourceBindingDescription(i, let bindDesc);
var bufferReflection = reflection.GetConstantBufferByIndex(i);
bufferReflection.GetDescription(let bufferDesc);
// ConstantBuffer
if(bufferDesc.Type == .D3D11_CT_CBUFFER)
if (res.Failed)
{
let buffer = new ConstantBuffer(_context, bufferReflection);
_buffers.Add(bindDesc.BindPoint, buffer.Name, buffer);
buffer.ReleaseRef();
Log.EngineLogger.Error($"Error({(int)res}) {res}: Failed to get resource binding desc for resource {i}");
continue;
}
/*
bufferReflection.GetDescription(let bufferDesc);
// ConstantBuffer
if(bufferDesc.Type == .D3D11_CT_CBUFFER)
switch(bindDesc.Type)
{
GlitchyEngine.Renderer.BufferDescription cBufferDesc = .(bufferDesc.Size, .Constant, .Dynamic, .Write);
case .ConstantBuffer:
var bufferReflection = reflection.GetConstantBufferByName(bindDesc.Name);
let buffer = new Buffer(_context, cBufferDesc);
bufferReflection.GetDescription(let bufferDesc);
_buffers.Add(bindDesc.BindPoint, StringView(bufferDesc.Name), buffer);//, true
buffer.ReleaseRef();
// Buffer for default values
uint8* rawData = new:ScopedAlloc! uint8[bufferDesc.Size]*;
// Get default values
for(uint32 variable < bufferDesc.Variables)
// ConstantBuffer
if(bufferDesc.Type == .D3D11_CT_CBUFFER)
{
let variableReflection = bufferReflection.GetVariableByIndex(variable);
let buffer = new ConstantBuffer(bufferReflection);
variableReflection.GetDescription(let varDesc);
_buffers.Add(bindDesc.BindPoint, buffer.Name, buffer);
if(varDesc.DefaultValue != null)
Internal.MemCpy(rawData + varDesc.StartOffset, varDesc.DefaultValue, varDesc.Size);
buffer.ReleaseRef();
}
buffer.SetData(Span<uint8>(rawData, bufferDesc.Size));
case .Texture:
_textures.Add(scope String(bindDesc.Name), bindDesc.BindPoint, null);
case .Sampler:
// TODO: do we have to do something for samplers?
default:
Log.EngineLogger.Warning($"Unhandled shader resource type: \"{bindDesc.Type}\"");
}
*/
}
reflection.Release();
}
}
}
#endif
@@ -1,56 +1,24 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using DirectX.DXGI.DXGI1_2;
using System.Diagnostics;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
public extension SwapChain
{
private GraphicsContext _context;
private bool _changed;
private uint32 _width, _height;
internal DirectX.DXGI.IDXGIDevice* nativeDxgiDevice;
internal IDXGISwapChain1* nativeSwapChain;
internal ID3D11RenderTargetView* nativeBackBufferTarget;
public override uint32 Width
{
get => _width;
set
{
if(_width == value)
return;
_width = value;
_changed = true;
}
}
public override uint32 Height
{
get => _height;
set
{
if(_height == value)
return;
_height = value;
_changed = true;
}
}
public override GraphicsContext Context => _context;
public this(GraphicsContext context)
{
Debug.Profiler.ProfileFunction!();
_context = context;
SetResolutionFromWindow();
@@ -58,9 +26,10 @@ namespace GlitchyEngine.Renderer
public ~this()
{
Debug.Profiler.ProfileFunction!();
nativeDxgiDevice?.Release();
nativeSwapChain?.Release();
nativeBackBufferTarget?.Release();
}
void SetResolutionFromWindow()
@@ -73,11 +42,15 @@ namespace GlitchyEngine.Renderer
public override void Init()
{
Debug.Profiler.ProfileFunction!();
ApplyChanges();
}
public override void ApplyChanges()
{
Debug.Profiler.ProfileFunction!();
if(!_changed)
return;
@@ -89,6 +62,8 @@ namespace GlitchyEngine.Renderer
*/
public void UpdateSwapchain()
{
Debug.Profiler.ProfileFunction!();
Log.EngineLogger.Trace($"Updating swap chain ({_width}, {_height})");
uint32 backBufferCount = 2;
@@ -97,7 +72,8 @@ namespace GlitchyEngine.Renderer
if(nativeSwapChain != null)
{
nativeBackBufferTarget.Release();
//nativeBackBufferTarget.Release();
_backBuffer.ReleaseRef();
var resizeResult = nativeSwapChain.ResizeBuffers(backBufferCount, _width, _height, backBufferFormat, .None);
if(resizeResult.Failed)
@@ -107,7 +83,7 @@ namespace GlitchyEngine.Renderer
}
else
{
_context.nativeDevice.QueryInterface(out nativeDxgiDevice);
NativeDevice.QueryInterface(out nativeDxgiDevice);
nativeDxgiDevice.GetAdapter(let adapter);
adapter.GetParent<DirectX.DXGI.IDXGIFactory2>(let factory);
@@ -118,14 +94,14 @@ namespace GlitchyEngine.Renderer
swDesc.Height = _height;
swDesc.Format = backBufferFormat;
swDesc.SampleDescription = .(1, 0);
swDesc.BufferUsage = .RenderTargetOutput;
swDesc.BufferUsage = .RenderTargetOutput | .ShaderInput;
swDesc.BufferCount = backBufferCount;
swDesc.SwapEffect = .FlipDiscard;
SwapChainFullscreenDescription fsSwapChainDesc = .();
fsSwapChainDesc.Windowed = true;
var createResult = factory.CreateSwapChainForHwnd((.)_context.nativeDevice, _context.nativeWindowHandle, ref swDesc, &fsSwapChainDesc, null, &nativeSwapChain);
var createResult = factory.CreateSwapChainForHwnd((.)NativeDevice, _context.nativeWindowHandle, ref swDesc, &fsSwapChainDesc, null, &nativeSwapChain);
if(createResult.Failed)
{
Log.EngineLogger.Error($"Failed to create swap chain. Message({(int)createResult}):{createResult}");
@@ -134,19 +110,31 @@ namespace GlitchyEngine.Renderer
factory.Release();
}
nativeSwapChain.GetBuffer<ID3D11Texture2D>(0, let backBuffer);
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,48 +1,288 @@
#if GE_GRAPHICS_DX11
using System;
using System.IO;
using DirectX.D3D11;
using DirectX.Common;
using DirectXTK;
using GlitchyEngine.Math;
using GlitchyEngine.Platform.DX11;
using System.Collections;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
internal typealias NativeTex2DDesc = DirectX.D3D11.Texture2DDescription;
extension Texture
{
protected internal ID3D11ShaderResourceView* nativeResourceView ~ _?.Release();
/** \brief Loads the texture from the specified path.
* @param path The path of the texture to load.
* @param texture The reference to the pointer that will hold the texture.
* @returns true if the texture was loaded successfully; false otherwise.
*/
protected bool LoadDdsResourcePlatform<T>(Stream stream, ref T* texture) where T : ID3D11Resource
{
Debug.Profiler.ProfileResourceFunction!();
uint8[] ddsData = new:ScopedAlloc! uint8[stream.Length];
var result = stream.TryRead(ddsData);
if (result case .Err(let err))
{
Log.EngineLogger.Error($"Failed to read texture data from stream. Error: {err}");
}
((ID3D11Resource*)texture)?.Release();
nativeResourceView?.Release();
HResult loadResult = DDSTextureLoader.CreateDDSTextureFromMemory(NativeDevice,
ddsData.Ptr, (uint)ddsData.Count, (.)&texture, &nativeResourceView);
if(loadResult.Failed)
{
Log.EngineLogger.Error($"Failed to load texture. Error({(int)loadResult}): {loadResult}");
ReleaseAndNullify!(texture);
ReleaseAndNullify!(nativeResourceView);
return false;
}
return true;
}
}
extension Texture2DDesc
{
public NativeTex2DDesc ToNative()
{
NativeTex2DDesc desc;
desc.Width = Width;
desc.Height = Height;
desc.MipLevels = MipLevels;
desc.ArraySize = ArraySize;
desc.Format = Format;
// TODO: missing options
desc.SampleDesc = .(1, 0);
desc.Usage = (.)Usage;
desc.BindFlags = .ShaderResource;
desc.CpuAccessFlags = (.)CpuAccess;
desc.MiscFlags = .None;
return desc;
}
public static explicit operator NativeTex2DDesc(Self desc) => desc.ToNative();
}
extension Texture2D
{
internal ID3D11Texture2D* nativeTexture ~ _?.Release();
internal ID3D11ShaderResourceView* nativeView ~ _?.Release();
internal Texture2DDescription nativeDesc;
protected internal ID3D11Texture2D* nativeTexture ~ _?.Release();
protected internal NativeTex2DDesc nativeDesc;
public override uint32 Width => nativeDesc.Width;
public override uint32 Height => nativeDesc.Height;
public override uint32 ArraySize => nativeDesc.ArraySize;
public override uint32 MipLevels => nativeDesc.MipLevels;
protected override void LoadTexturePlatform()
protected override void LoadDdsPlatform(Stream stream)
{
nativeTexture?.Release();
nativeView?.Release();
Debug.Profiler.ProfileResourceFunction!();
HResult loadResult = DDSTextureLoader.CreateDDSTextureFromFile(_context.nativeDevice, _path.ToScopedNativeWChar!(),
(.)&nativeTexture, &nativeView);
LoadDdsResourcePlatform(stream, ref nativeTexture);
if(loadResult.Failed)
{
Log.EngineLogger.Error($"Failed to load texture \"{_path}\". Error({(int)loadResult}): {loadResult}");
nativeTexture?.Release();
nativeView?.Release();
// TODO: load fallback texture
}
let resType = nativeTexture.GetResourceType();
Log.EngineLogger.Assert(resType == .Texture2D, scope $"The texture \"{_path}\" is not a 2D texture (it is {resType}).");
nativeTexture.GetDescription(out nativeDesc);
}
protected override void ImplBind(uint32 slot)
protected override void CreateTexturePlatform(Texture2DDesc desc, bool isRenderTarget, void* data, uint32 linePitch)
{
_context.nativeContext.VertexShader.SetShaderResources(slot, 1, &nativeView);
_context.nativeContext.PixelShader.SetShaderResources(slot, 1, &nativeView);
Debug.Profiler.ProfileResourceFunction!();
PrepareTexturePlatform(desc, isRenderTarget);
InternalCreateTexture(data, linePitch, 0);
}
private void InternalCreateTexture(void* data, uint32 linePitch, uint32 slicePitch)
{
Debug.Profiler.ProfileResourceFunction!();
SubresourceData resData = .(data, linePitch, slicePitch);
// If data is null, set subresourceData null
SubresourceData* resDataPtr = data == null ? null : &resData;
var result = NativeDevice.CreateTexture2D(ref nativeDesc, resDataPtr, &nativeTexture);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture 2D. Error ({result.Underlying}): {result}");
result = NativeDevice.CreateShaderResourceView(nativeTexture, null, &nativeResourceView);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture view. Error ({result.Underlying}): {result}");
}
protected override void PrepareTexturePlatform(Texture2DDesc desc, bool isRenderTarget)
{
Debug.Profiler.ProfileResourceFunction!();
nativeTexture?.Release();
nativeTexture = null;
nativeResourceView?.Release();
nativeResourceView = null;
nativeDesc = (NativeTex2DDesc)desc;
if(isRenderTarget)
nativeDesc.BindFlags |= .RenderTarget;
}
// TODO: Update Texture Arrays!
protected override System.Result<void> PlatformSetData(void* data, uint32 elementSize, uint32 destX,
uint32 destY, uint32 destWidth, uint32 destHeight, uint32 arraySlice, uint32 mipLevel, GlitchyEngine.Renderer.MapType mapType)
{
Debug.Profiler.ProfileResourceFunction!();
if(nativeTexture == null)
{
if(destX == 0 && destY == 0 && destWidth == nativeDesc.Width && destHeight == nativeDesc.Height)
{
// We can pass the data while creating the buffer, so we can return here.
var rowPitch = elementSize * destWidth;
return InternalCreateTexture(data, rowPitch, rowPitch * destHeight);
}
else
{
// If we don't set the entire texture we need to create the texture with unknown content and set it manually
Log.EngineLogger.Assert(nativeDesc.Usage != .Immutable, "The entire immutable texture has to be initialized.");
InternalCreateTexture(null, 0, 0);
}
}
//Log.EngineLogger.AssertDebug();
// TODO: Debug.Assert(dstByteOffset + byteLength <= _description.Size, "The destination offset and byte length are too long for the target buffer.");
uint32 subresourceIndex = D3D11.CalcSubresource(mipLevel, arraySlice, nativeDesc.MipLevels);
switch(nativeDesc.Usage)
{
case .Default:
Box dataBox = .(destX, destY, 0, destX + destWidth, destY + destHeight, 1);
var rowPitch = elementSize * destWidth;
NativeContext.UpdateSubresource(nativeTexture, subresourceIndex, &dataBox, data, rowPitch, rowPitch * destHeight);
case .Dynamic:
Runtime.NotImplemented();
/*
Log.EngineLogger.Assert(mapType.CanWrite, "The map type has to have write access.");
// Todo: DoNotWaitFlag
MappedSubresource map = ?;
_context.nativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map);
Internal.MemCpy(((uint8*)map.Data) + dstByteOffset, data, byteLength);
_context.nativeContext.Unmap(nativeBuffer, 0);
*/
case .Immutable:
Log.EngineLogger.Error("Can't set the data of an immutable resource.");
return .Err;
default:
Log.EngineLogger.Error($"Unknown resource usage: {nativeDesc.Usage}");
return .Err;
}
return .Ok;
}
public static override void CopySubresourceRegion(Texture2D source, Texture2D destination,
ResourceBox sourceBox = default, uint32 destX = 0, uint32 destY = 0,
uint32 srcArraySlice = 0, uint32 srcMipSlice = 0, uint32 destArraySlice = 0, uint32 destMipSlice = 0)
{
Debug.Profiler.ProfileResourceFunction!();
Log.EngineLogger.AssertDebug(source != destination || srcArraySlice != destArraySlice
|| srcMipSlice != destMipSlice, "Cannot copy from and to the same sub resource.");
Log.EngineLogger.AssertDebug(source.nativeDesc.Format == destination.nativeDesc.Format,
"Source and destination resources must have the same texel-format.");
var sourceBox;
if(sourceBox.Right == 0)
{
sourceBox.Right = source.Width;
sourceBox.Bottom = source.Height;
sourceBox.Back = 1;
}
// Make sure the destination was initialized
if(destination.nativeTexture == null)
destination.InternalCreateTexture(null, 0, 0);
NativeContext.CopySubresourceRegion(destination.nativeTexture,
D3D11.CalcSubresource(destMipSlice, destArraySlice, destination.MipLevels), destX, destY, 0,
source.nativeTexture, D3D11.CalcSubresource(srcMipSlice, srcArraySlice, source.MipLevels), (.)&sourceBox);
}
public override void CopyTo(Texture2D destination)
{
Debug.Profiler.ProfileResourceFunction!();
if(nativeTexture == null)
return;
Log.EngineLogger.AssertDebug(nativeDesc.Format == destination.nativeDesc.Format,
"Source and destination resources must have the same texel-format.");
// Make sure the destination was initialized
if(destination.nativeTexture == null)
destination.InternalCreateTexture(null, 0, 0);
uint32 arraySlices = Math.Min(ArraySize, destination.ArraySize);
uint32 mipSlices = Math.Min(MipLevels, destination.MipLevels);
for(uint32 arraySlice < arraySlices)
for(uint32 mipSlice < mipSlices)
{
Box sourceBox = .(0, 0, 0, Width, Height, 1);
NativeContext.CopySubresourceRegion(destination.nativeTexture,
D3D11.CalcSubresource(mipSlice, arraySlice, destination.MipLevels), 0, 0, 0,
nativeTexture, D3D11.CalcSubresource(mipSlice, arraySlice, MipLevels), (.)&sourceBox);
}
}
}
extension TextureCube
{
protected internal ID3D11Texture2D* nativeTexture ~ _?.Release();
protected internal NativeTex2DDesc nativeDesc;
public override uint32 Width => nativeDesc.Width;
public override uint32 Height => nativeDesc.Height;
public override uint32 ArraySize => nativeDesc.ArraySize / 6;
public override uint32 MipLevels => nativeDesc.MipLevels;
protected override void LoadTexturePlatform(Stream stream)
{
Debug.Profiler.ProfileResourceFunction!();
LoadDdsResourcePlatform(stream, ref nativeTexture);
let resType = nativeTexture.GetResourceType();
Log.EngineLogger.Assert(resType == .Texture2D, scope $"The texture \"{_path}\" is not a texture cube (it is {resType}).");
nativeTexture.GetDescription(out nativeDesc);
Log.EngineLogger.Assert(nativeDesc.MiscFlags.HasFlag(.TextureCube), scope $"The texture \"{_path}\" is not a texture cube.");
// TODO: load fallback texture
}
}
}
#endif
@@ -1,9 +1,13 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX.D3D11;
using System.Diagnostics;
using DirectX.Common;
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
@@ -13,12 +17,12 @@ namespace GlitchyEngine.Renderer
public ID3DBlob* nativeShaderCode ~ _?.Release();
public this(GraphicsContext context, VertexElement[] elements, VertexShader vertexShader)
public this(VertexElement[] elements, bool ownsElements, VertexShader vertexShader)
{
nativeShaderCode = vertexShader.nativeCode..AddRef();
_context = context..AddRef();
_elements = elements;
_ownsElements = ownsElements;
CreateNativeLayout();
}
@@ -28,16 +32,18 @@ namespace GlitchyEngine.Renderer
Debug.Assert(input.Count == output.Count);
for(int i < input.Count)
output[i] = .(input[i].SemanticName, input[i].SemanticIndex, input[i].Format, input[i].InputSlot, input[i].AlignedByteOffset, (.)input[i].InputSlotClass);
output[i] = .(input[i].SemanticName, input[i].SemanticIndex, input[i].Format, input[i].InputSlot, input[i].AlignedByteOffset, (.)input[i].InputSlotClass, input[i].InstanceDataStepRate);
}
protected override void CreateNativeLayout()
{
Debug.Profiler.ProfileResourceFunction!();
var nativeElements = scope InputElementDescription[_elements.Count];
ToNativeLayout(_elements, nativeElements);
var result = _context.nativeDevice.CreateInputLayout(nativeElements.CArray(), (.)nativeElements.Count, nativeShaderCode.GetBufferPointer(), nativeShaderCode.GetBufferSize(), &nativeLayout);
var result = NativeDevice.CreateInputLayout(nativeElements.CArray(), (.)nativeElements.Count, nativeShaderCode.GetBufferPointer(), nativeShaderCode.GetBufferSize(), &nativeLayout);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create D3D11 input layout: Message({(int)result}): {result}");
@@ -45,3 +51,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -1,10 +1,13 @@
#if GE_GRAPHICS_DX11
using System;
using GlitchyEngine.Renderer;
using DirectX.Common;
using DirectX.D3D11;
using DirectX.D3DCompiler;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
@@ -14,15 +17,23 @@ namespace GlitchyEngine.Renderer
public override void CompileFromSource(String code, String entryPoint, ShaderDefine[] macros = null)
{
Debug.Profiler.ProfileResourceFunction!();
Shader.PlattformCompileShaderFromSource(code, macros, entryPoint, "vs_5_0", DefaultCompileFlags, out nativeCode);
var result = _context.nativeDevice.CreateVertexShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create vertex shader: Message ({(int)result}): {result}");
Debug.Profiler.ProfileResourceScope!("CreateNativeVertexShader");
var result = NativeDevice.CreateVertexShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create vertex shader: Message ({(int)result}): {result}");
}
}
Reflect(nativeCode);
}
}
}
#endif
@@ -1,3 +1,5 @@
#if BF_PLATFORM_WINDOWS
using System;
using GlitchyEngine.Events;
using DirectX.Windows.VirtualKeyCodes;
@@ -7,8 +9,6 @@ using static System.Windows;
namespace GlitchyEngine
{
#if GE_WINDOWS
/// Windows (WinApi) specific implementation of the Input-class
extension Input
{
@@ -20,8 +20,10 @@ namespace GlitchyEngine
public Point CursorPositionDifference;
}
static WindowsInputState* CurrentState = new WindowsInputState() ~ delete _;
static WindowsInputState* LastState = new WindowsInputState() ~ delete _;
static WindowsInputState[2] IputStates;
static WindowsInputState* CurrentState = &IputStates[0];
static WindowsInputState* LastState = &IputStates[1];
//
// Current Keyboard state
@@ -49,13 +51,13 @@ namespace GlitchyEngine
//
public override static bool WasKeyPressed(Key keycode)
{
int8 state = CurrentState.KeyStates[(int)keycode];
int8 state = LastState.KeyStates[(int)keycode];
return state < 0;
}
public override static bool WasKeyReleased(Key keycode)
{
int8 state = CurrentState.KeyStates[(int)keycode];
int8 state = LastState.KeyStates[(int)keycode];
return state >= 0;
}
@@ -148,11 +150,10 @@ namespace GlitchyEngine
//
public override static bool IsMouseButtonPressing(MouseButton button) => IsMouseButtonPressed(button) && WasMouseButtonReleased(button);
public override static bool IsMouseButtonReleasiong(MouseButton button) => IsMouseButtonReleased(button) && WasMouseButtonPressed(button);
public override static bool IsMouseButtonReleasing(MouseButton button) => IsMouseButtonReleased(button) && WasMouseButtonPressed(button);
public override static Point GetMouseMovement() => CurrentState.CursorPositionDifference;
//[CLink, CallingConvention(.Stdcall)]
//static extern int16 GetKeyState(int32 keycode);
[CLink, CallingConvention(.Stdcall)]
@@ -162,10 +163,9 @@ namespace GlitchyEngine
public override static void NewFrame()
{
// Switch last and current states
var buffer = LastState;
LastState = CurrentState;
CurrentState = buffer;
Debug.Profiler.ProfileFunction!();
Swap!(CurrentState, LastState);
// Get current keyboard state
if(DirectX.Windows.Winuser.GetKeyboardState((uint8*)&CurrentState.KeyStates) == 0)
@@ -194,5 +194,6 @@ namespace GlitchyEngine
CurrentState.CursorPositionDifference = CurrentState.CursorPosition - LastState.CursorPosition;
}
}
#endif
}
#endif
@@ -0,0 +1,48 @@
using System;
using DirectX.Windows.Winuser;
using DirectX.Common;
namespace GlitchyEngine
{
extension Key
{
// Todo: make overriding method
public void GetKeyName(String strBuffer)
{
var scanCode = MapVirtualKeyW((.)this, MAPVK_VK_TO_VSC);
char16[128] name = ?;
int result;
switch (this)
{
case .Left, .Up, .Right, .Down, .RightControl, .RightAlt,
.LeftSuper, .RightSuper, ContextMenu,
.Prior, .Next, .End, .Home, .Insert, .Delete, .Divide, .Numlock:
// set extended flag
scanCode |= 0x0100;
fallthrough;
default:
result = GetKeyNameTextW((.)(scanCode << 16), &name, name.Count);
}
if(result == 0)
{
HResult error = (.)DirectX.Windows.Kernel32.GetLastError();
Log.EngineLogger.Error($"Failed to convert keycode {(int)this}({this}) to string. WinAPI error({(int)error}): {error}");
return;
}
strBuffer.Append(Span<char16>(&name, result));
}
/// Converts the given windows specific virtual key code to a GlitchyEngine Key.
[Inline]
internal static Key FromWindowsKeyCode(int32 vKeyCode)
{
return (.)vKeyCode;
}
}
}
@@ -1,4 +1,5 @@
#if GE_WINDOWS
#if BF_PLATFORM_WINDOWS
using System;
using DirectX.Common;
using DirectX.Windows;
@@ -11,6 +12,8 @@ using GlitchyEngine.Math;
using GlitchyEngine.Renderer;
using static System.Windows;
using internal GlitchyEngine;
namespace GlitchyEngine
{
/// The windows specific Window implementation
@@ -31,6 +34,8 @@ namespace GlitchyEngine
private bool _isVSync = true;
private bool _isActive;
private GraphicsContext _graphicsContext ~ _?.ReleaseRef();
public override GraphicsContext Context => _graphicsContext;
@@ -149,10 +154,14 @@ namespace GlitchyEngine
set => _isVSync = value;
}
public override bool IsActive => _isActive;
public override void* NativeWindow => (void*)(int)_windowHandle;
public override this(WindowDescription desc)
{
Debug.Profiler.ProfileFunction!();
_minMaxInfo.MaximumTrackingSize.x = int32.MaxValue;
_minMaxInfo.MaximumTrackingSize.y = int32.MaxValue;
@@ -167,6 +176,8 @@ namespace GlitchyEngine
public ~this()
{
Debug.Profiler.ProfileFunction!();
if(!DestroyWindow(_windowHandle))
{
HResult res = (.)GetLastError();
@@ -176,8 +187,13 @@ namespace GlitchyEngine
UnregisterClass(WindowClassName.ToScopedNativeWChar!(), _instanceHandle);
}
[LinkName(.C)]
static extern HWnd GetActiveWindow();
private void Init(WindowDescription desc)
{
Debug.Profiler.ProfileFunction!();
Log.EngineLogger.Trace($"Creating window \"{desc.Title}\" ({desc.Width}, {desc.Height})...");
_instanceHandle = (.)GetModuleHandleW(null);
@@ -188,7 +204,11 @@ namespace GlitchyEngine
_windowClass.HInstance = (.)_instanceHandle;
if (desc.Icon.Ptr != null)
{
Debug.Profiler.ProfileScope!("LoadIcon");
_windowClass.Icon = LoadImageW(0, desc.Icon.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile);
}
else
_windowClass.Icon = 0;
@@ -203,8 +223,12 @@ namespace GlitchyEngine
Runtime.FatalError("Failed to register window class");
}
_windowHandle = CreateWindowExW(.None, _windowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, (.)_instanceHandle, null);
{
Debug.Profiler.ProfileScope!("CreateWindow");
_windowHandle = CreateWindowExW(.None, _windowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, (.)_instanceHandle, null);
}
_graphicsContext = new GraphicsContext(_windowHandle);
_graphicsContext.Init();
@@ -215,6 +239,9 @@ namespace GlitchyEngine
LoadWindowRectangle();
Log.EngineLogger.Trace($"Created window \"{Title}\" ({Width}, {Height})");
// Determine whether or not the window is currently active
_isActive = GetActiveWindow() == _windowHandle;
}
private bool _isResizingOrMoving;
@@ -223,6 +250,18 @@ namespace GlitchyEngine
[CLink]
static extern IntBool IsWindowUnicode(HWND whnd);
public Result<void> SetIcon(StringView filePath)
{
HICON hIcon = LoadImageW(0, filePath.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile);
if (hIcon == 0)
return .Err;
// WM_SETICON 0x0080
SendMessageW(_windowHandle, 0x0080, 1 /* ICON_BIG */, (int)hIcon);
return .Ok;
}
private static LRESULT MessageHandler(HWND hwnd, uint32 uMsg, WPARAM wParam, LPARAM lParam)
{
void* windowPtr = (void*)GetWindowLongPtrW(hwnd, GWL_USERDATA);
@@ -251,26 +290,21 @@ namespace GlitchyEngine
// Window size changed
case WM_SIZE:
{
if (wParam == (.)ResizingType.Minimized)
{
window._isMinimized = true;
}
else if (window._isMinimized)
{
window._isMinimized = false;
}
else
{
SplitHighAndLowOrder!(lParam, out window._clientRect.Width, out window._clientRect.Height);
window._isMinimized = wParam == (.)ResizingType.Minimized;
SplitHighAndLowOrder!(lParam, out window._clientRect.Width, out window._clientRect.Height);
if(window._clientRect.Width != 0 && window._clientRect.Height != 0)
{
window._graphicsContext.SwapChain.Width = (.)window._clientRect.Width;
window._graphicsContext.SwapChain.Height = (.)window._clientRect.Height;
window._graphicsContext.SwapChain.ApplyChanges();
WindowResizeEvent event = scope WindowResizeEvent(window._clientRect.Width, window._clientRect.Height, window._isResizingOrMoving);
window._eventCallback(event);
}
WindowResizeEvent event = scope WindowResizeEvent(window._clientRect.Width, window._clientRect.Height, window._isResizingOrMoving);
window._eventCallback(event);
}
// Window position changed
case WM_MOVE:
@@ -305,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);
}
@@ -443,8 +498,33 @@ namespace GlitchyEngine
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
/**
* Converts the wParam and lParam of a WM_KEYDOWN or WM_KEYUP message to the corresponding engine keycode.
*/
static Key WindowMessageToKey(WPARAM wParam, LPARAM lParam)
{
Key key = Key.FromWindowsKeyCode((.)wParam);
var scancode = (lParam & 0x00ff0000) >> 16;
bool isExtended = (lParam & 0x01000000) > 0;
switch(key)
{
case .Shift:
return Key.FromWindowsKeyCode((.)MapVirtualKeyW((.)scancode, MAPVK_VSC_TO_VK_EX));
case .Control:
return isExtended ? .RightControl : .LeftControl;
case .Alt:
return isExtended ? .RightAlt : .LeftAlt;
default:
return key;
}
}
public override void Update()
{
Debug.Profiler.ProfileFunction!();
Message message = .();
while (PeekMessageW(&message, 0, 0, 0, .Remove))
{
@@ -488,4 +568,5 @@ namespace GlitchyEngine
}
}
}
#endif
+24 -9
View File
@@ -1,5 +1,6 @@
using System;
using GlitchLog;
using GlitchyEngine.Debug;
using System.Diagnostics;
namespace GlitchyEngine
@@ -11,24 +12,38 @@ namespace GlitchyEngine
public static int Main(String[] args)
{
Log.EngineLogger.Info("Initializing Application...");
Debug.Profiler.BeginProfiling();
Stopwatch initWatch = scope Stopwatch();
Application app;
var app = CreateApplication();
{
Debug.Profiler.ProfileScope!("Initialize");
initWatch.Stop();
Log.EngineLogger.Info("Initializing Application...");
Log.EngineLogger.Info($"Application initialized ({initWatch.ElapsedMilliseconds}ms).");
Log.EngineLogger.Info("Running App...");
Stopwatch initWatch = scope Stopwatch();
app = CreateApplication();
initWatch.Stop();
Log.EngineLogger.Info($"Application initialized ({initWatch.ElapsedMilliseconds}ms).");
Log.EngineLogger.Info("Running App...");
}
app.Run();
Log.EngineLogger.Info("Uninitializing Application...");
{
Debug.Profiler.ProfileScope!("Shutdown");
delete app;
Log.EngineLogger.Info("Uninitializing Application...");
Log.EngineLogger.Info("Application uninitialized.");
delete app;
Log.EngineLogger.Info("Application uninitialized.");
}
Debug.Profiler.EndProfiling();
return 0;
}
@@ -0,0 +1,279 @@
using System;
using GlitchyEngine.Core;
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer.Animation
{
class AnimationPlayer
{
public Skeleton Skeleton ~ _.ReleaseRef();
public AnimationClip CurrentClip ~ _.ReleaseRef();
public float TimeStamp;
public SkeletonPose Pose ~ delete _;
public Matrix[] SkinningMatricies ~ delete _;
public Matrix3x3[] InvTransSkinningMatricies ~ delete _;
public this(Skeleton skeleton, AnimationClip clip)
{
Skeleton = skeleton;
CurrentClip = clip;
Pose = new SkeletonPose(Skeleton);
SkinningMatricies = new Matrix[Skeleton.Joints.Count];
InvTransSkinningMatricies = new Matrix3x3[Skeleton.Joints.Count];
}
public void Update(GameTime gameTime)
{
TimeStamp += (float)gameTime.FrameTime.TotalSeconds;
if(CurrentClip.IsLooping && CurrentClip.Duration != 0)
{
TimeStamp %= CurrentClip.Duration;
}
for(int i < Skeleton.Joints.Count)
{
ref JointPose localPose = ref Pose.LocalPose[i];
localPose = CurrentClip.JointAnimations[i].GetCurrentPose(TimeStamp);
Matrix jointToParent =
Matrix.Translation(localPose.Translation) *
Matrix.RotationQuaternion(localPose.Rotation) *
Matrix.Scaling(localPose.Scale);
uint8 parentIndex = Skeleton.Joints[i].ParentID;
ref Matrix globalPose = ref Pose.GlobalPose[i];
if(parentIndex == uint8.MaxValue)
{
globalPose = jointToParent;
}
else
{
globalPose = Pose.GlobalPose[parentIndex] * jointToParent;
}
SkinningMatricies[i] = globalPose * Skeleton.Joints[i].InverseBindPose;
InvTransSkinningMatricies[i] = ((Matrix3x3)SkinningMatricies[i]).Inverse().Transpose();
}
}
}
class AnimationClip : RefCounter
{
private Skeleton _skeleton ~ _.ReleaseRef();
public JointAnimation[] JointAnimations ~ delete _;
public bool IsLooping;
public float Duration;
public Skeleton Skeleton => _skeleton;
[AllowAppend]
public this(Skeleton skeleton)
{
var jointAnimations = new JointAnimation[skeleton.Joints.Count];
Log.EngineLogger.AssertDebug(skeleton != null);
_skeleton = skeleton..AddRef();
JointAnimations = jointAnimations;
}
// TODO: check
public ~this()
{
for(var jointAnimation in JointAnimations)
{
//jointAnimation.Dispose();
delete jointAnimation;
}
}
}
public enum InterpolationMode
{
/**
* No keyframe interpolation.
* The keyframe with the greates timestamp that is smaller than the current time will be used.
*/
Step,
/**
* Linear interpolation between the two keyframes that are closest to the current time.
*/
Linear,
/**
* Cubic spline interpolation between the two keyframes that are closest to the current time.
*/
CubicSpline
}
class JointAnimationChannel<T> where T : struct // : IDisposable
{
public float[] TimeStamps;
public T[] Values;
public InterpolationMode InterpolationMode = .Step;
public float Duration => TimeStamps[TimeStamps.Count - 1];
public this(int samples, InterpolationMode interpolationMode)
{
TimeStamps = new float[samples];
Values = new T[samples];
InterpolationMode = interpolationMode;
}
public ~this()
{
delete TimeStamps;
delete Values;
}
public (T, T, float) GetSample(float currentTime)
{
let (previousIndex, previousTime) = FindPreviousTimeStamp(currentTime);
T previousSample = Values[previousIndex];
if(InterpolationMode == .Step)
{
return (previousSample, default(T), 0f);
}
else
{
int nextIndex = previousIndex + 1;
if(nextIndex >= Values.Count)
nextIndex = Values.Count - 1;
float nextTime = TimeStamps[nextIndex];
T nextSample = Values[nextIndex];
float interpolationValue = (currentTime - previousTime) / (nextTime - previousTime);
return (previousSample, nextSample, interpolationValue);
}
}
public (int Index, float timeStamp) FindPreviousTimeStamp(float currentTime)
{
// TODO use binary search to speed things up?
int index = TimeStamps.Count - 1;
for(int i < TimeStamps.Count)
{
if(TimeStamps[i] > currentTime)
{
index = i - 1;
break;
}
}
if(index == -1)
index = 0;
// Don't return -1 but return first frame
return (index, TimeStamps[index]);
//return array.Count - 1;
}
}
class JointAnimation// : IDisposable
{
public JointAnimationChannel<Vector3> TranslationChannel ~ delete _;
public JointAnimationChannel<Quaternion> RotationChannel ~ delete _;
public JointAnimationChannel<Vector3> ScaleChannel ~ delete _;
public float Duration
{
get
{
return Math.Max(Math.Max(
TranslationChannel?.Duration ?? 0, RotationChannel?.Duration ?? 0), ScaleChannel?.Duration ?? 0);
}
}
/*
public void Dispose()
{
delete TranslationChannel;
delete RotationChannel;
delete ScaleChannel;
}
*/
public JointPose GetCurrentPose(float timeStamp)
{
JointPose result;
if(TranslationChannel != null)
{
let (previous, next, interpolationValue) = TranslationChannel.GetSample(timeStamp);
switch(TranslationChannel.InterpolationMode)
{
case .Step:
result.Translation = previous;
case .Linear:
result.Translation = Vector3.Lerp(previous, next, interpolationValue);
default:
result.Rotation = ?;
Runtime.NotImplemented();
}
}
else
{
result.Translation = .Zero;
}
if(RotationChannel != null)
{
let (previous, next, interpolationValue) = RotationChannel.GetSample(timeStamp);
switch(RotationChannel.InterpolationMode)
{
case .Step:
result.Rotation = previous;
case .Linear:
result.Rotation = Quaternion.Slerp(previous, next, interpolationValue);
default:
result.Rotation = ?;
Runtime.NotImplemented();
}
}
else
{
result.Rotation = .Identity;
}
if(ScaleChannel != null)
{
let (previous, next, interpolationValue) = ScaleChannel.GetSample(timeStamp);
//switch(ScaleChannel.InterpolationMode)
switch(InterpolationMode.Step)
{
case .Step:
result.Scale = previous;
case .Linear:
result.Scale = Vector3.Lerp(previous, next, interpolationValue);
default:
result.Rotation = ?;
Runtime.NotImplemented();
}
}
else
{
result.Scale = .One;
}
return result;
}
}
}
@@ -0,0 +1,20 @@
using GlitchyEngine.Math;
using System;
namespace GlitchyEngine.Renderer.Animation
{
public struct Joint
{
/// Converts vertices from model space to joint space
public Matrix InverseBindPose;
public String Name;
public uint8 ParentID;
}
public struct JointPose
{
public Quaternion Rotation;
public Vector3 Translation;
public Vector3 Scale;
}
}
@@ -0,0 +1,51 @@
using GlitchyEngine.Core;
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer.Animation
{
public class Skeleton : RefCounter
{
public Joint[] Joints ~ delete _;
public Joint? GetParent(Joint joint)
{
if(joint.ParentID == uint8.MaxValue)
return null;
return Joints[joint.ParentID];
}
public ~this()
{
for(var joint in Joints)
{
delete joint.Name;
}
}
}
public class SkeletonPose
{
private Skeleton _skeleton ~ _.ReleaseRef();
public JointPose[] LocalPose ~ delete _;
public Matrix[] GlobalPose ~ delete _;
public Matrix[] SkinningMatricies ~ delete _;
public Matrix3x3[] InvTransSkinningMatricies ~ delete _;
public Skeleton Skeleton => _skeleton;
public this(Skeleton skeleton)
{
_skeleton = skeleton..AddRef();
int jointCount = _skeleton.Joints.Count;
LocalPose = new JointPose[jointCount];
GlobalPose = new Matrix[jointCount];
SkinningMatricies = new Matrix[jointCount];
InvTransSkinningMatricies = new Matrix3x3[jointCount];
}
}
}
+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.
+98 -49
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>()
{
EnsureTypeMatch(1, 1, .Float);
switch(typeof(T))
{
case typeof(bool):
EnsureTypeMatch(1, 1, .Bool);
*(float*)firstByte = value;
case typeof(float):
EnsureTypeMatch(1, 1, .Float);
case typeof(Vector2):
EnsureTypeMatch(1, 2, .Float);
case typeof(Vector3):
EnsureTypeMatch(1, 3, .Float);
case typeof(Vector4):
EnsureTypeMatch(1, 4, .Float);
case typeof(int32):
EnsureTypeMatch(1, 1, .Int);
case typeof(Int2):
EnsureTypeMatch(1, 2, .Int);
case typeof(Int3):
EnsureTypeMatch(1, 3, .Int);
case typeof(Int4):
EnsureTypeMatch(1, 4, .Int);
case typeof(Matrix4x3):
EnsureTypeMatch(4, 3, .Float);
case typeof(Matrix3x3):
EnsureTypeMatch(3, 3, .Float);
case typeof(Matrix):
EnsureTypeMatch(4, 4, .Float);
case typeof(ColorRGB):
EnsureTypeMatch(1, 3, .Float);
case typeof(ColorRGBA):
EnsureTypeMatch(1, 4, .Float);
case typeof(Color):
EnsureTypeMatch(1, 4, .Float);
}
}
public void SetData(Vector2 value)
[Inline]
private void SetData<T>(T value)
{
EnsureTypeMatch(1, 2, .Float);
EnsureTypeMatch<T>();
*(Vector2*)firstByte = value;
*(T*)firstByte = value;
}
public void SetData(Vector3 value)
[Inline]
private T GetData<T>()
{
EnsureTypeMatch(1, 3, .Float);
*(Vector3*)firstByte = value;
return *(T*)firstByte;
}
public void SetData(Vector4 value)
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(1, 4, .Float);
EnsureTypeMatch<Matrix4x3>();
*(Vector4*)firstByte = value;
}
// TODO: assert length
public void SetData(Matrix4x3 value)
{
// I think this is right
EnsureTypeMatch(4, 3, .Float);
*(Matrix4x3*)firstByte = value;
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
}
}
+19 -1
View File
@@ -3,7 +3,25 @@ using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer
{
public abstract class Camera
struct Camera
{
protected Matrix _projection;
public Matrix Projection => _projection;
protected this()
{
_projection = .Identity;
}
public this(Matrix projection)
{
_projection = projection;
}
}
public abstract class OldCamera
{
protected Matrix _view;
protected Matrix _transform;
@@ -0,0 +1,99 @@
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer
{
public struct OldCameraComponent
{
public enum Projection
{
/**
* A perspective projection with near and far plane.
*/
Perspective,
/**
* A perspective projection with near and far plane,
* except that the closer a pixel is to the far plane the smaller its depth-value gets.
*/
PerspectiveReversed,
/**
* A perspective projection with a near plane and a far plane at infinite distance.
*/
PerspectiveInfinite,
/**
* A perspective projection with a near plane and a far plane at infinite distance,
* except that the closer a pixel is to the far plane the smaller its depth-value gets.
*/
PerspectiveReversedInfinite,
/**
* An orthographic projection.
*/
Orthographic
}
float _nearPlane;
float _farPlane;
Projection _projectionType;
float _fovY;
float _aspect;
public float NearPlane
{
get => _nearPlane;
set mut => _nearPlane = value;
}
public float FarPlane
{
get => _farPlane;
set mut => _farPlane = value;
}
public Projection ProjectionType
{
get => _projectionType;
set mut => _projectionType = value;
}
public float FovY
{
get => _fovY;
set mut => _fovY = value;
}
public float FovX
{
get => _fovY * _aspect;
set mut => _fovY = value / _aspect;
}
public float Aspect
{
get => _aspect;
set mut => _aspect = value;
}
public Matrix Projection => CalculateProjection();
public Matrix CalculateProjection()
{
Log.EngineLogger.Assert(_nearPlane < _farPlane, "The near plane must be smaller than the far plane.");
switch(_projectionType)
{
case .Perspective:
return Matrix.PerspectiveProjection(_fovY, _aspect, _nearPlane, _farPlane);
case .PerspectiveReversed:
return Matrix.ReversedPerspectiveProjection(_fovY, _aspect, _nearPlane, _farPlane);
case .PerspectiveInfinite:
return Matrix.InfinitePerspectiveProjection(_fovY, _aspect, _nearPlane);
case .PerspectiveReversedInfinite:
return Matrix.ReversedInfinitePerspectiveProjection(_fovY, _aspect, _nearPlane);
default:
Log.EngineLogger.Assert(false, "Unknown projection type.");
}
return .Identity;
}
}
}
+44
View File
@@ -0,0 +1,44 @@
using GlitchyEngine.Math;
using System;
namespace GlitchyEngine.Renderer
{
struct ColorHSV
{
public float H;
public float S;
public float V;
public this(float h, float s, float v)
{
H = h;
S = s;
V = v;
}
public static explicit operator ColorRGB(ColorHSV hsv)
{
float c = hsv.V * hsv.S;
float x = c * (1.0f - Math.Abs((hsv.H / 60.0f) % 2.0f - 1.0f));
float m = hsv.V - c;
ColorRGB rgb_ = ?;
if (hsv.H < 60.0f)
rgb_ = ColorRGB(c, x, 0);
else if (hsv.H < 120.0f)
rgb_ = ColorRGB(x, c, 0);
else if (hsv.H < 180.0f)
rgb_ = ColorRGB(0, c, x);
else if (hsv.H < 240.0f)
rgb_ = ColorRGB(0, x, c);
else if (hsv.H < 300.0f)
rgb_ = ColorRGB(x, 0, c);
else if (hsv.H < 360.0f)
rgb_ = ColorRGB(c, 0, x);
return .(rgb_.Red + m, rgb_.Green + m, rgb_.Blue + m);
}
}
}

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