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
253 changed files with 17753 additions and 7140 deletions
+1 -2
View File
@@ -4,5 +4,4 @@ build/
.vs/
recovery/
vendor/directx
Sandbox/imgui.ini
Sandbox/worlds
imgui.ini
+21 -6
View File
@@ -1,12 +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"}, LodePng = {Path = "vendor/lodepng-beef/lodepng-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", "GE_D3D11"]
[Configs.Release.Win32]
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
[Configs.Release.Win64]
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
ProcessorMacros = ["GE_GRAPHICS_DX11", "GE_SHADER_MATRIX_MISMATCH_IS_ERROR", "GE_SHADER_VAR_TYPE_MISMATCH_IS_ERROR", "GE_SHADER_UNUSED_VARIABLE_IS_WARNING", "GE_WINDOWS"]
[Configs.Paranoid.Win32]
PreprocessorMacros = ["DEBUG", "PARANOID", "GE_WINDOWS"]
@@ -24,8 +11,17 @@ PreprocessorMacros = ["DEBUG", "PARANOID", "GE_WINDOWS"]
[Configs.Paranoid.Win64]
PreprocessorMacros = ["DEBUG", "PARANOID", "GE_WINDOWS"]
[Configs.Release.Win32]
PreprocessorMacros = ["RELEASE", "GE_WINDOWS"]
[Configs.Release.Win64]
PreprocessorMacros = ["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"]
+118 -9
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)
{
Debug.Profiler.ProfileScope!("Loop");
if(allowFrame)
{
_gameTime.NewFrame();
}
Input.NewFrame();
#if GE_APP_SINGLESTEP
allowFrame = disableSingleFrame || Input.IsKeyPressing(Key.F10);
if(Input.IsKeyPressing(Key.F11))
{
disableSingleFrame = !disableSingleFrame;
}
#endif
if(allowFrame && !_isMinimized)
{
Debug.Profiler.ProfileScope!("Update Layers");
for(Layer layer in _layerStack)
layer.Update(_gameTime);
}
_window.Update();
if(allowFrame)
{
#if IMGUI
_imGuiLayer.ImGuiRender();
#endif
_window.Context.SwapChain.Present();
}
}
}
public void PushLayer(Layer ownLayer) => _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;
}
}
}
+81 -147
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();
{
Debug.Profiler.ProfileScope!("ImGuiRenderEvent");
var event = scope ImGuiRenderEvent();
Application.Get().OnEvent(event);
ImGui.ShowDemoWindow(&showDemo);
}
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 -9
View File
@@ -6,8 +6,6 @@ namespace GlitchyEngine
{
public static class Input
{
public static extern bool RawInput {get; set;}
public static extern bool IsKeyPressed(Key keycode);
public static extern bool IsKeyReleased(Key keycode);
public static extern bool IsKeyToggled(Key keycode);
@@ -40,15 +38,9 @@ namespace GlitchyEngine
public static extern int32 GetLastMouseY();
public static extern bool IsMouseButtonPressing(MouseButton button);
public static extern bool IsMouseButtonReleasiong(MouseButton button);
public static extern bool IsMouseButtonReleasing(MouseButton button);
public static extern Point GetMouseMovement();
/**
* Returns the raw mouse (that is the mouse movement taken direcly from the device instead of the movement processed by the OS).
* Requires RawInput to be true. If RawInput is false (or could not be initialized) this method will return the same value as GetMouseMovement();
*/
public static extern Int32_2 GetRawMouseMovement();
public static extern void NewFrame();
}
}
+1
View File
@@ -68,6 +68,7 @@ namespace GlitchyEngine
case Z = 90; // Z key
case LeftSuper = 91; // Left Windows key(Microsoft® Natural® keyboard)
case RightSuper = 92; // Right Windows key(Natural keyboard)
case ContextMenu = 93; // Context Menu key (VK_APPS)
case Numpad0 = 96; // Numeric keypad 0 key
case Numpad1 = 97; // Numeric keypad 1 key
case Numpad2 = 98; // Numeric keypad 2 key
-2
View File
@@ -29,7 +29,6 @@ namespace GlitchyEngine
public void PushLayer(Layer ownLayer)
{
_layers.Insert(_insertIndex++, ownLayer);
ownLayer.OnAttach();
}
/**
@@ -40,7 +39,6 @@ namespace GlitchyEngine
public void PushOverlay(Layer ownOverlay)
{
_layers.Add(ownOverlay);
ownOverlay.OnAttach();
}
/**
+5
View File
@@ -18,6 +18,11 @@ namespace GlitchyEngine.Math
public this(int initialCapacity = sizeof(uint))
{
var initialCapacity;
if (initialCapacity < sizeof(uint))
initialCapacity = sizeof(uint);
EnsureCapacity(initialCapacity);
}
+3 -3
View File
@@ -13,12 +13,12 @@ namespace System
}
[Inline]
public Int32_2 XX => Int32_2((int32)this);
public Int2 XX => Int2((int32)this);
[Inline]
public Int32_3 XXX => Int32_3((int32)this);
public Int3 XXX => Int3((int32)this);
[Inline]
public Int32_4 XXXX => Int32_4((int32)this);
public Int4 XXXX => Int4((int32)this);
}
}
+81
View File
@@ -0,0 +1,81 @@
using System;
namespace GlitchyEngine.Math
{
public static class MathHelper
{
/// An optimal representation of π.
public const float Pi = 3.141592654f;
/// An optimal representation of 2*π.
public const float TwoPi = 6.283185307f;
/// An optimal representation of 1/π.
public const float OneOverPi = 0.318309886f;
/// An optimal representation of 2/π.
public const float OneOverTwoPi = 0.159154943f;
/// An optimal representation of π/2.
public const float PiOverTwo = 1.570796327f;
/// An optimal representation of π/4.
public const float PiOverFour = 0.785398163f;
/// Converts radians to degrees
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);
}
*/
}
}
-16
View File
@@ -1,16 +0,0 @@
namespace GlitchyEngine.Math
{
struct Ray
{
public Vector3 Start;
public Vector3 Direction;
public this() => this = default;
public this(Vector3 start, Vector3 direction)
{
Start = start;
Direction = direction;
}
}
}
+62
View File
@@ -0,0 +1,62 @@
using System;
namespace GlitchyEngine.Math
{
public struct ResourceBox
{
/// The x position of the left hand side of the box.
public uint32 Left;
/// The y position of the top of the box.
public uint32 Top;
/// The z position of the front of the box.
public uint32 Front;
/// The x position of the right hand side of the box.
public uint32 Right;
/// The y position of the bottom of the box.
public uint32 Bottom;
/// The z position of the back of the box.
public uint32 Back;
/**
* Returns true if the box is empty.
* A box is empty if the top value is greater than or equal to the bottom value,
* or the left value is greater than or equal to the right value,
* or the front value is greater than or equal to the back value.
*/
public bool IsEmpty => Top >= Bottom || Left >= Right || Front >= Back;
/**
* Instantiates a new instance of an uninitialized Box structure.
*/
public this()
{
this = default;
}
/**
* Instantiates a new instance of a Box structure that is initialized with the dimensions of a box.
*/
public this(uint32 left, uint32 top, uint32 front, uint32 right, uint32 bottom, uint32 back)
{
Left = left;
Top = top;
Front = front;
Right = right;
Bottom = bottom;
Back = back;
}
[Inline]
public static bool operator ==(ResourceBox l, ResourceBox r)
{
return l.Left == r.Left && l.Top == r.Top && l.Front == r.Front &&
l.Right == r.Right && l.Bottom == r.Bottom && l.Back == r.Back;
}
[Inline]
public static bool operator !=(ResourceBox l, ResourceBox r)
{
return !(l == r);
}
}
}
@@ -21,9 +21,9 @@ namespace GlitchyEngine.Math
}
/**
* 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]) ||
@@ -49,9 +49,18 @@ namespace GlitchyEngine.Math
{
String swizzleName = scope String(swizzleCount);
String swizzleConstructor = scope String(swizzleCount * 3);
String setter = scope String();
String setter = scope String(128);
bool setterInvalid = invalidSetter(cmp, swizzleCount);
bool setterInvalid = IsSetterValid(cmp, swizzleCount);
if (!setterInvalid)
{
setter.Append(
"""
set mut
{
""");
}
for(int c = 0; c < swizzleCount; c++)
{
@@ -69,15 +78,20 @@ namespace GlitchyEngine.Math
}
}
//{(setterInvalid ? "[Error(\"Cannot assign multiple values to same component.\")]" : String.Empty)}
if (!setterInvalid)
{
setter.Append(
"""
}
""");
}
String swizzleString = scope $"""
public {_vectorTypeName}{swizzleCount} {swizzleName}
{{
get => .({swizzleConstructor});
set mut
{{{setter}
}}
{setter}
}}
""";
+79 -16
View File
@@ -2,7 +2,7 @@ using System;
namespace GlitchyEngine.Math
{
//[SwizzleVector(2, "Vector")]
[SwizzleVector(2, "Vector")]
public struct Vector2
{
public const Vector2 Zero = .(0f, 0f);
@@ -10,7 +10,7 @@ namespace GlitchyEngine.Math
public const Vector2 UnitY = .(0f, 1f);
public const Vector2 One = .(1f, 1f);
public const int Length = 2;
public const int ComponentCount = 2;
public float X, Y;
@@ -31,18 +31,61 @@ namespace GlitchyEngine.Math
public ref float this[int index]
{
[Checked]
get
get mut
{
if(index < 0 || index >= Length)
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
#unwarn
return ref (&X)[index];
}
[Inline]
#unwarn
get => ref (&X)[index];
get mut => ref (&X)[index];
}
public float this[int index]
{
[Checked]
get
{
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
if(index == 0)
return X;
else
return Y;
}
[Inline]
get
{
if(index == 0)
return X;
else
return Y;
}
[Checked]
set mut
{
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
if(index == 0)
X = value;
else
Y = value;
}
[Inline]
set mut
{
if(index == 0)
X = value;
else
Y = value;
}
}
/**
@@ -90,7 +133,7 @@ namespace GlitchyEngine.Math
/**
* Calculates the projection of a onto b
*/
public Vector2 Project(Vector2 a, Vector2 b)
public static Vector2 Project(Vector2 a, Vector2 b)
{
return (b * (Dot(a, b) / Dot(b, b)));
}
@@ -98,11 +141,33 @@ namespace GlitchyEngine.Math
/**
* Calculates the rejection of a from b
*/
public Vector2 Reject(Vector2 a, Vector2 b)
public static Vector2 Reject(Vector2 a, Vector2 b)
{
return (a - b * (Dot(a, b) / Dot(b, b)));
}
/**
* Interpolates linearly between two given vectors.
* @param a The first vector.
* @param b The second vector.
* @param interpolationValue The value that linearly interpolates between a and b.
* (0 means a will be returned, 1 means b will be returned.)
* @returns The resulting linear interpolation.
*/
public static Vector2 Lerp(Vector2 a, Vector2 b, float interpolationValue)
{
return a + interpolationValue * (b - a);
}
public static Vector2 Min(Vector2 a, Vector2 b)
{
return .(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y));
}
public static Vector2 Max(Vector2 a, Vector2 b)
{
return .(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
}
//
// Assignment operators
@@ -217,14 +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;
[Inline]
public static implicit operator Self(in DirectX.Math.Vector2 value) => *(Self*)&value;
[Inline]
public static explicit operator Self(float 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);
}
}
}
+55 -15
View File
@@ -2,7 +2,7 @@ using System;
namespace GlitchyEngine.Math
{
//[SwizzleVector(3, "Vector")]
[SwizzleVector(3, "Vector")]
public struct Vector3
{
public const Vector3 Zero = .(0f, 0f, 0f);
@@ -18,6 +18,8 @@ namespace GlitchyEngine.Math
public const Vector3 Up = .(0f, 1f, 0f);
public const Vector3 Down = .(0f, -1f, 0f);
public const int ComponentCount = 3;
public float X, Y, Z;
public this() => this = default;
@@ -46,18 +48,41 @@ namespace GlitchyEngine.Math
public ref float this[int index]
{
[Checked]
get
get mut
{
if(index < 0 || index > 2)
Internal.ThrowIndexOutOfRange();
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
#unwarn
return ref (&X)[index];
}
[Inline]
#unwarn
get => ref (&X)[index];
get mut => ref (&X)[index];
}
public float this[int index]
{
get
{
switch(index)
{
case 0: return X;
case 1: return Y;
case 2: return Z;
default: Internal.ThrowIndexOutOfRange(1);
}
}
set mut
{
switch(index)
{
case 0: X = value;
case 1: Y = value;
case 2: Z = value;
default: Internal.ThrowIndexOutOfRange(1);
}
}
}
/**
@@ -156,6 +181,29 @@ namespace GlitchyEngine.Math
return .(Math.Floor(value.X), Math.Floor(value.Y), Math.Floor(value.Z));
}
/**
* Interpolates linearly between two given vectors.
* @param a The first vector.
* @param b The second vector.
* @param interpolationValue The value that linearly interpolates between a and b.
* (0 means a will be returned, 1 means b will be returned.)
* @returns The resulting linear interpolation.
*/
public static Vector3 Lerp(Vector3 a, Vector3 b, float interpolationValue)
{
return a + interpolationValue * (b - a);
}
public static Vector3 Min(Vector3 a, Vector3 b)
{
return .(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Min(a.Z, b.Z));
}
public static Vector3 Max(Vector3 a, Vector3 b)
{
return .(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y), Math.Min(a.Z, b.Z));
}
//
// Assignment operators
//
@@ -286,14 +334,6 @@ namespace GlitchyEngine.Math
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1} Z:{2}", X, Y, Z);
// Todo: move to extension
[Inline]
public static implicit operator DirectX.Math.Vector3(in Self value) => *(DirectX.Math.Vector3*)&value;
[Inline]
public static implicit operator Self(in DirectX.Math.Vector3 value) => *(Self*)&value;
[Inline]
public static explicit operator Self(float value) => Self(value);
}
+58 -15
View File
@@ -2,7 +2,7 @@ using System;
namespace GlitchyEngine.Math
{
//[SwizzleVector(4, "Vector")]
[SwizzleVector(4, "Vector")]
public struct Vector4
{
public const Vector4 Zero = .(0f, 0f, 0f, 0f);
@@ -12,6 +12,8 @@ namespace GlitchyEngine.Math
public const Vector4 UnitW = .(0f, 0f, 0f, 1f);
public const Vector4 One = .(1f, 1f, 1f, 1f);
public const int ComponentCount = 4;
public float X, Y, Z, W;
public this() => this = default;
@@ -59,17 +61,43 @@ namespace GlitchyEngine.Math
public ref float this[int index]
{
[Checked]
get
get mut
{
if(index < 0 || index > 3)
Internal.ThrowIndexOutOfRange();
#unwarn
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
return ref (&X)[index];
}
[Inline]
#unwarn
get => ref (&X)[index];
get mut => ref (&X)[index];
}
public float this[int index]
{
get
{
switch(index)
{
case 0: return X;
case 1: return Y;
case 2: return Z;
case 3: return W;
default: Internal.ThrowIndexOutOfRange(1);
}
}
set mut
{
switch(index)
{
case 0: X = value;
case 1: Y = value;
case 2: Z = value;
case 3: W = value;
default: Internal.ThrowIndexOutOfRange(1);
}
}
}
/**
@@ -138,6 +166,29 @@ namespace GlitchyEngine.Math
return (a - b * (Dot(a, b) / Dot(b, b)));
}
/**
* Interpolates linearly between two given vectors.
* @param a The first vector.
* @param b The second vector.
* @param interpolationValue The value that linearly interpolates between a and b.
* (0 means a will be returned, 1 means b will be returned.)
* @returns The resulting linear interpolation.
*/
public static Vector4 Lerp(Vector4 a, Vector4 b, float interpolationValue)
{
return a + interpolationValue * (b - a);
}
public static Vector4 Min(Vector4 a, Vector4 b)
{
return .(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Min(a.Z, b.Z), Math.Min(a.W, b.W));
}
public static Vector4 Max(Vector4 a, Vector4 b)
{
return .(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y), Math.Min(a.Z, b.Z), Math.Min(a.W, b.W));
}
//
// Assignment operators
//
@@ -267,14 +318,6 @@ namespace GlitchyEngine.Math
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W);
// Todo: move to extension
[Inline]
public static implicit operator DirectX.Math.Vector4(in Self value) => *(DirectX.Math.Vector4*)&value;
[Inline]
public static implicit operator Self(in DirectX.Math.Vector4 value) => *(Self*)&value;
[Inline]
public static explicit operator Self(float value) => Self(value);
}
+162
View File
@@ -0,0 +1,162 @@
#pragma warning disable 4204
using System;
using System.Diagnostics;
namespace GlitchyEngine.Math
{
/**
* A Vector with two components of type int32.
*/
[SwizzleVector(2, "Int")]
public struct Int2 : IHashable
{
public const Int2 Zero = .(0, 0);
public const Int2 UnitX = .(1, 0);
public const Int2 UnitY = .(0, 1);
public const Int2 One = .(1, 1);
public int32 X, Y;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
}
public this(int32 x, int32 y)
{
X = x;
Y = y;
}
public this(int x, int y)
{
X = (.)x;
Y = (.)y;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 2)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y;
public float Magnitude() => Math.Sqrt(X * X + Y * Y);
public Int2 Abs() => .(Math.Abs(X), Math.Abs(Y));
//
// Assignment operators
//
public void operator +=(Int2 value) mut
{
X += value.X;
Y += value.Y;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
}
public void operator -=(Int2 value) mut
{
X -= value.X;
Y -= value.Y;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
}
public void operator *=(Int2 value) mut
{
X *= value.X;
Y *= value.Y;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
}
public void operator /=(Int2 value) mut
{
X /= value.X;
Y /= value.Y;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
}
// Operators
public static Int2 operator +(Int2 value) => value;
public static Int2 operator +(Int2 left, Int2 right) => .(left.X + right.X, left.Y + right.Y);
public static Int2 operator +(Int2 left, int32 right) => .(left.X + right, left.Y + right);
public static Int2 operator +(int32 left, Int2 right) => .(left + right.X, left + right.Y);
public static Int2 operator -(Int2 value) => .(-value.X, -value.Y);
public static Int2 operator -(Int2 left, Int2 right) => .(left.X - right.X, left.Y - right.Y);
public static Int2 operator -(Int2 left, int32 right) => .(left.X - right, left.Y - right);
public static Int2 operator -(int32 left, Int2 right) => .(left - right.X, left - right.Y);
public static Int2 operator *(Int2 left, Int2 right) => .(left.X * right.X, left.Y * right.Y);
public static Int2 operator *(Int2 left, int32 right) => .(left.X * right, left.Y * right);
public static Int2 operator *(int32 left, Int2 right) => .(left * right.X, left * right.Y);
public static Int2 operator /(Int2 left, Int2 right) => .(left.X / right.X, left.Y / right.Y);
public static Int2 operator /(Int2 left, int32 right) => .(left.X / right, left.Y / right);
public static Int2 operator /(int32 left, Int2 right) => .(left / right.X, left / right.Y);
public static Int2 operator %(Int2 left, Int2 right) => .(left.X % right.X, left.Y % right.Y);
public static Int2 operator %(Int2 left, int32 right) => .(left.X % right, left.Y % right);
public static Int2 operator %(int32 left, Int2 right) => .(left % right.X, left % right.Y);
public static bool operator ==(Int2 left, Int2 right) => left.X == right.X && left.Y == right.Y;
public static bool operator ==(Int2 left, int32 right) => left.X == right && left.Y == right;
public static bool operator ==(int32 left, Int2 right) => left == right.X && left == right.Y;
public static bool operator !=(Int2 left, Int2 right) => left.X != right.X || left.Y != right.Y;
public static bool operator !=(Int2 left, int32 right) => left.X != right || left.Y != right;
public static bool operator !=(int32 left, Int2 right) => left != right.X || left != right.Y;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y}");
public static explicit operator Vector2(Int2 point) => .(point.X, point.Y);
public static explicit operator Int2(Vector2 point) => .((int32)point.X, (int32)point.Y);
public int GetHashCode()
{
return (X * 39) ^ Y;
}
}
}
+206
View File
@@ -0,0 +1,206 @@
#pragma warning disable 4204
using System;
using System.Diagnostics;
namespace GlitchyEngine.Math
{
/**
* A Vector with three components of type int32.
*/
[SwizzleVector(3, "Int")]
public struct Int3 : IHashable
{
public const Int3 Zero = .(0, 0, 0);
public const Int3 UnitX = .(1, 0, 0);
public const Int3 UnitY = .(0, 1, 0);
public const Int3 UnitZ = .(0, 0, 1);
public const Int3 One = .(1, 1, 1);
public int32 X, Y, Z;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
Z = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
Z = (.)value;
}
public this(int32 x, int32 y, int32 z)
{
X = x;
Y = y;
Z = z;
}
public this(Int2 xy, int32 z)
{
X = xy.X;
Y = xy.Y;
Z = z;
}
public this(int32 x, Int2 yz)
{
X = x;
Y = yz.X;
Z = yz.Y;
}
public this(int x, int y, int z)
{
X = (.)x;
Y = (.)y;
Z = (.)z;
}
public this(Int2 xy, int z)
{
X = xy.X;
Y = xy.Y;
Z = (.)z;
}
public this(int x, Int2 yz)
{
X = (.)x;
Y = yz.X;
Z = yz.Y;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 3)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y + Z * Z;
public float Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z);
public Int3 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z));
//
// Assignment operators
//
public void operator +=(Int3 value) mut
{
X += value.X;
Y += value.Y;
Z += value.Z;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
Z += value;
}
public void operator -=(Int3 value) mut
{
X -= value.X;
Y -= value.Y;
Z -= value.Z;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
Z -= value;
}
public void operator *=(Int3 value) mut
{
X *= value.X;
Y *= value.Y;
Z *= value.Z;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
Z *= value;
}
public void operator /=(Int3 value) mut
{
X /= value.X;
Y /= value.Y;
Z /= value.Z;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
Z /= value;
}
// Operators
public static Int3 operator +(Int3 value) => value;
public static Int3 operator +(Int3 left, Int3 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
public static Int3 operator +(Int3 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right);
public static Int3 operator +(int32 left, Int3 right) => .(left + right.X, left + right.Y, left + right.Z);
public static Int3 operator -(Int3 value) => .(-value.X, -value.Y, -value.Z);
public static Int3 operator -(Int3 left, Int3 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
public static Int3 operator -(Int3 left, int32 right) => .(left.X - right, left.Y - right, left.Z- right);
public static Int3 operator -(int32 left, Int3 right) => .(left - right.X, left - right.Y, left - right.Z);
public static Int3 operator *(Int3 left, Int3 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
public static Int3 operator *(Int3 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right);
public static Int3 operator *(int32 left, Int3 right) => .(left * right.X, left * right.Y, left * right.Z);
public static Int3 operator /(Int3 left, Int3 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
public static Int3 operator /(Int3 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right);
public static Int3 operator /(int32 left, Int3 right) => .(left / right.X, left / right.Y, left / right.Z);
public static Int3 operator %(Int3 left, Int3 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z);
public static Int3 operator %(Int3 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right);
public static Int3 operator %(int32 left, Int3 right) => .(left % right.X, left % right.Y, left % right.Z);
public static bool operator ==(Int3 left, Int3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
public static bool operator ==(Int3 left, int32 right) => left.X == right && left.Y == right && left.Z == right;
public static bool operator ==(int32 left, Int3 right) => left == right.X && left == right.Y && left == right.Z;
public static bool operator !=(Int3 left, Int3 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
public static bool operator !=(Int3 left, int32 right) => left.X != right || left.Y != right || left.Z != right;
public static bool operator !=(int32 left, Int3 right) => left != right.X || left != right.Y || left != right.Z;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y} Z:{Z}");
public static explicit operator Vector3(Int3 point) => .(point.X, point.Y, point.Z);
public static explicit operator Int3(Vector3 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z);
[Inline]
public static explicit operator Int2(in Int3 point) => *(Int2*)&point;
public int GetHashCode()
{
return (((X * 39) ^ Y) * 39) ^ Z;
}
}
}
-162
View File
@@ -1,162 +0,0 @@
#pragma warning disable 4204
using System;
using System.Diagnostics;
namespace GlitchyEngine.Math
{
/**
* A Vector with two components of type int32.
*/
[SwizzleVector(2, "Int32_")]
public struct Int32_2 : IHashable
{
public const Int32_2 Zero = .(0, 0);
public const Int32_2 UnitX = .(1, 0);
public const Int32_2 UnitY = .(0, 1);
public const Int32_2 One = .(1, 1);
public int32 X, Y;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
}
public this(int32 x, int32 y)
{
X = x;
Y = y;
}
public this(int x, int y)
{
X = (.)x;
Y = (.)y;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 2)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y;
public float Magnitude() => Math.Sqrt(X * X + Y * Y);
public Int32_2 Abs() => .(Math.Abs(X), Math.Abs(Y));
//
// Assignment operators
//
public void operator +=(Int32_2 value) mut
{
X += value.X;
Y += value.Y;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
}
public void operator -=(Int32_2 value) mut
{
X -= value.X;
Y -= value.Y;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
}
public void operator *=(Int32_2 value) mut
{
X *= value.X;
Y *= value.Y;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
}
public void operator /=(Int32_2 value) mut
{
X /= value.X;
Y /= value.Y;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
}
// Operators
public static Int32_2 operator +(Int32_2 value) => value;
public static Int32_2 operator +(Int32_2 left, Int32_2 right) => .(left.X + right.X, left.Y + right.Y);
public static Int32_2 operator +(Int32_2 left, int32 right) => .(left.X + right, left.Y + right);
public static Int32_2 operator +(int32 left, Int32_2 right) => .(left + right.X, left + right.Y);
public static Int32_2 operator -(Int32_2 value) => .(-value.X, -value.Y);
public static Int32_2 operator -(Int32_2 left, Int32_2 right) => .(left.X - right.X, left.Y - right.Y);
public static Int32_2 operator -(Int32_2 left, int32 right) => .(left.X - right, left.Y - right);
public static Int32_2 operator -(int32 left, Int32_2 right) => .(left - right.X, left - right.Y);
public static Int32_2 operator *(Int32_2 left, Int32_2 right) => .(left.X * right.X, left.Y * right.Y);
public static Int32_2 operator *(Int32_2 left, int32 right) => .(left.X * right, left.Y * right);
public static Int32_2 operator *(int32 left, Int32_2 right) => .(left * right.X, left * right.Y);
public static Int32_2 operator /(Int32_2 left, Int32_2 right) => .(left.X / right.X, left.Y / right.Y);
public static Int32_2 operator /(Int32_2 left, int32 right) => .(left.X / right, left.Y / right);
public static Int32_2 operator /(int32 left, Int32_2 right) => .(left / right.X, left / right.Y);
public static Int32_2 operator %(Int32_2 left, Int32_2 right) => .(left.X % right.X, left.Y % right.Y);
public static Int32_2 operator %(Int32_2 left, int32 right) => .(left.X % right, left.Y % right);
public static Int32_2 operator %(int32 left, Int32_2 right) => .(left % right.X, left % right.Y);
public static bool operator ==(Int32_2 left, Int32_2 right) => left.X == right.X && left.Y == right.Y;
public static bool operator ==(Int32_2 left, int32 right) => left.X == right && left.Y == right;
public static bool operator ==(int32 left, Int32_2 right) => left == right.X && left == right.Y;
public static bool operator !=(Int32_2 left, Int32_2 right) => left.X != right.X || left.Y != right.Y;
public static bool operator !=(Int32_2 left, int32 right) => left.X != right || left.Y != right;
public static bool operator !=(int32 left, Int32_2 right) => left != right.X || left != right.Y;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y}");
public static explicit operator Vector2(Int32_2 point) => .(point.X, point.Y);
public static explicit operator Int32_2(Vector2 point) => .((int32)point.X, (int32)point.Y);
public int GetHashCode()
{
return (X * 39) ^ Y;
}
}
}
-206
View File
@@ -1,206 +0,0 @@
#pragma warning disable 4204
using System;
using System.Diagnostics;
namespace GlitchyEngine.Math
{
/**
* A Vector with three components of type int32.
*/
[SwizzleVector(3, "Int32_")]
public struct Int32_3 : IHashable
{
public const Int32_3 Zero = .(0, 0, 0);
public const Int32_3 UnitX = .(1, 0, 0);
public const Int32_3 UnitY = .(0, 1, 0);
public const Int32_3 UnitZ = .(0, 0, 1);
public const Int32_3 One = .(1, 1, 1);
public int32 X, Y, Z;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
Z = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
Z = (.)value;
}
public this(int32 x, int32 y, int32 z)
{
X = x;
Y = y;
Z = z;
}
public this(Int32_2 xy, int32 z)
{
X = xy.X;
Y = xy.Y;
Z = z;
}
public this(int32 x, Int32_2 yz)
{
X = x;
Y = yz.X;
Z = yz.Y;
}
public this(int x, int y, int z)
{
X = (.)x;
Y = (.)y;
Z = (.)z;
}
public this(Int32_2 xy, int z)
{
X = xy.X;
Y = xy.Y;
Z = (.)z;
}
public this(int x, Int32_2 yz)
{
X = (.)x;
Y = yz.X;
Z = yz.Y;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 3)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y + Z * Z;
public float Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z);
public Int32_3 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z));
//
// Assignment operators
//
public void operator +=(Int32_3 value) mut
{
X += value.X;
Y += value.Y;
Z += value.Z;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
Z += value;
}
public void operator -=(Int32_3 value) mut
{
X -= value.X;
Y -= value.Y;
Z -= value.Z;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
Z -= value;
}
public void operator *=(Int32_3 value) mut
{
X *= value.X;
Y *= value.Y;
Z *= value.Z;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
Z *= value;
}
public void operator /=(Int32_3 value) mut
{
X /= value.X;
Y /= value.Y;
Z /= value.Z;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
Z /= value;
}
// Operators
public static Int32_3 operator +(Int32_3 value) => value;
public static Int32_3 operator +(Int32_3 left, Int32_3 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
public static Int32_3 operator +(Int32_3 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right);
public static Int32_3 operator +(int32 left, Int32_3 right) => .(left + right.X, left + right.Y, left + right.Z);
public static Int32_3 operator -(Int32_3 value) => .(-value.X, -value.Y, -value.Z);
public static Int32_3 operator -(Int32_3 left, Int32_3 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
public static Int32_3 operator -(Int32_3 left, int32 right) => .(left.X - right, left.Y - right, left.Z- right);
public static Int32_3 operator -(int32 left, Int32_3 right) => .(left - right.X, left - right.Y, left - right.Z);
public static Int32_3 operator *(Int32_3 left, Int32_3 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
public static Int32_3 operator *(Int32_3 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right);
public static Int32_3 operator *(int32 left, Int32_3 right) => .(left * right.X, left * right.Y, left * right.Z);
public static Int32_3 operator /(Int32_3 left, Int32_3 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
public static Int32_3 operator /(Int32_3 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right);
public static Int32_3 operator /(int32 left, Int32_3 right) => .(left / right.X, left / right.Y, left / right.Z);
public static Int32_3 operator %(Int32_3 left, Int32_3 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z);
public static Int32_3 operator %(Int32_3 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right);
public static Int32_3 operator %(int32 left, Int32_3 right) => .(left % right.X, left % right.Y, left % right.Z);
public static bool operator ==(Int32_3 left, Int32_3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
public static bool operator ==(Int32_3 left, int32 right) => left.X == right && left.Y == right && left.Z == right;
public static bool operator ==(int32 left, Int32_3 right) => left == right.X && left == right.Y && left == right.Z;
public static bool operator !=(Int32_3 left, Int32_3 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
public static bool operator !=(Int32_3 left, int32 right) => left.X != right || left.Y != right || left.Z != right;
public static bool operator !=(int32 left, Int32_3 right) => left != right.X || left != right.Y || left != right.Z;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y} Z:{Z}");
public static explicit operator Vector3(Int32_3 point) => .(point.X, point.Y, point.Z);
public static explicit operator Int32_3(Vector3 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z);
[Inline]
public static explicit operator Int32_2(in Int32_3 point) => *(Int32_2*)&point;
public int GetHashCode()
{
return (((X * 39) ^ Y) * 39) ^ Z;
}
}
}
-234
View File
@@ -1,234 +0,0 @@
#pragma warning disable 4204
using System;
using System.Diagnostics;
namespace GlitchyEngine.Math
{
/**
* A Vector with four components of type int32.
*/
[SwizzleVector(4, "Int32_")]
public struct Int32_4 : IHashable
{
public const Int32_4 Zero = .(0, 0, 0, 0);
public const Int32_4 UnitX = .(1, 0, 0, 0);
public const Int32_4 UnitY = .(0, 1, 0, 0);
public const Int32_4 UnitZ = .(0, 0, 1, 0);
public const Int32_4 UnitW = .(0, 0, 0, 1);
public const Int32_4 One = .(1, 1, 1, 1);
public int32 X, Y, Z, W;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
Z = value;
W = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
Z = (.)value;
W = (.)value;
}
public this(int32 x, int32 y, int32 z, int32 w)
{
X = x;
Y = y;
Z = z;
W = w;
}
public this(Int32_2 xy, int32 z, int32 w)
{
X = xy.X;
Y = xy.Y;
Z = z;
W = w;
}
public this(int32 x, Int32_2 yz, int32 w)
{
X = x;
Y = yz.X;
Z = yz.Y;
W = w;
}
public this(int32 x, int32 y, Int32_2 zw)
{
X = x;
Y = y;
Z = zw.X;
W = zw.Y;
}
public this(Int32_2 xy, Int32_2 zw)
{
X = xy.X;
Y = xy.Y;
Z = zw.X;
W = zw.Y;
}
public this(Int32_3 xyz, int32 w)
{
X = xyz.X;
Y = xyz.Y;
Z = xyz.Z;
W = w;
}
public this(int32 x, Int32_3 yzw)
{
X = x;
Y = yzw.X;
Z = yzw.Y;
W = yzw.Z;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 3)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y + Z * Z + W * W;
public float Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
public Int32_4 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z), Math.Abs(W));
//
// Assignment operators
//
public void operator +=(Int32_4 value) mut
{
X += value.X;
Y += value.Y;
Z += value.Z;
W += value.W;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
Z += value;
W += value;
}
public void operator -=(Int32_4 value) mut
{
X -= value.X;
Y -= value.Y;
Z -= value.Z;
W -= value.W;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
Z -= value;
W -= value;
}
public void operator *=(Int32_4 value) mut
{
X *= value.X;
Y *= value.Y;
Z *= value.Z;
W *= value.W;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
Z *= value;
W *= value;
}
public void operator /=(Int32_4 value) mut
{
X /= value.X;
Y /= value.Y;
Z /= value.Z;
W /= value.W;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
Z /= value;
W /= value;
}
// Operators
public static Int32_4 operator +(Int32_4 value) => value;
public static Int32_4 operator +(Int32_4 left, Int32_4 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
public static Int32_4 operator +(Int32_4 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right, left.W + right);
public static Int32_4 operator +(int32 left, Int32_4 right) => .(left + right.X, left + right.Y, left + right.Z, left + right.W);
public static Int32_4 operator -(Int32_4 value) => .(-value.X, -value.Y, -value.Z, -value.W);
public static Int32_4 operator -(Int32_4 left, Int32_4 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W);
public static Int32_4 operator -(Int32_4 left, int32 right) => .(left.X - right, left.Y - right, left.Z - right, left.W - right);
public static Int32_4 operator -(int32 left, Int32_4 right) => .(left - right.X, left - right.Y, left - right.Z, left - right.W);
public static Int32_4 operator *(Int32_4 left, Int32_4 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W);
public static Int32_4 operator *(Int32_4 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right, left.W * right);
public static Int32_4 operator *(int32 left, Int32_4 right) => .(left * right.X, left * right.Y, left * right.Z, left * right.W);
public static Int32_4 operator /(Int32_4 left, Int32_4 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W);
public static Int32_4 operator /(Int32_4 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right, left.W / right);
public static Int32_4 operator /(int32 left, Int32_4 right) => .(left / right.X, left / right.Y, left / right.Z, left / right.W);
public static Int32_4 operator %(Int32_4 left, Int32_4 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z, left.W % right.W);
public static Int32_4 operator %(Int32_4 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right, left.W % right);
public static Int32_4 operator %(int32 left, Int32_4 right) => .(left % right.X, left % right.Y, left % right.Z, left % right.W);
public static bool operator ==(Int32_4 left, Int32_4 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z && left.W == right.W;
public static bool operator ==(Int32_4 left, int32 right) => left.X == right && left.Y == right && left.Z == right && left.W == right;
public static bool operator ==(int32 left, Int32_4 right) => left == right.X && left == right.Y && left == right.Z && left == right.W;
public static bool operator !=(Int32_4 left, Int32_4 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z || left.W != right.W;
public static bool operator !=(Int32_4 left, int32 right) => left.X != right || left.Y != right || left.Z != right || left.W != right;
public static bool operator !=(int32 left, Int32_4 right) => left != right.X || left != right.Y || left != right.Z || left != right.W;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y} Z:{Z} W:{W}");
public static explicit operator Vector4(Int32_4 point) => .(point.X, point.Y, point.Z, point.W);
public static explicit operator Int32_4(Vector4 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z, (int32)point.W);
[Inline]
public static explicit operator Int32_2(in Int32_4 point) => *(Int32_2*)&point;
[Inline]
public static explicit operator Int32_3(in Int32_4 point) => *(Int32_3*)&point;
public int GetHashCode()
{
return (((((X * 39) ^ Y) * 39) ^ Z) * 39) ^ W;
}
}
}
+234
View File
@@ -0,0 +1,234 @@
#pragma warning disable 4204
using System;
using System.Diagnostics;
namespace GlitchyEngine.Math
{
/**
* A Vector with four components of type int32.
*/
[SwizzleVector(4, "Int")]
public struct Int4 : IHashable
{
public const Int4 Zero = .(0, 0, 0, 0);
public const Int4 UnitX = .(1, 0, 0, 0);
public const Int4 UnitY = .(0, 1, 0, 0);
public const Int4 UnitZ = .(0, 0, 1, 0);
public const Int4 UnitW = .(0, 0, 0, 1);
public const Int4 One = .(1, 1, 1, 1);
public int32 X, Y, Z, W;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
Z = value;
W = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
Z = (.)value;
W = (.)value;
}
public this(int32 x, int32 y, int32 z, int32 w)
{
X = x;
Y = y;
Z = z;
W = w;
}
public this(Int2 xy, int32 z, int32 w)
{
X = xy.X;
Y = xy.Y;
Z = z;
W = w;
}
public this(int32 x, Int2 yz, int32 w)
{
X = x;
Y = yz.X;
Z = yz.Y;
W = w;
}
public this(int32 x, int32 y, Int2 zw)
{
X = x;
Y = y;
Z = zw.X;
W = zw.Y;
}
public this(Int2 xy, Int2 zw)
{
X = xy.X;
Y = xy.Y;
Z = zw.X;
W = zw.Y;
}
public this(Int3 xyz, int32 w)
{
X = xyz.X;
Y = xyz.Y;
Z = xyz.Z;
W = w;
}
public this(int32 x, Int3 yzw)
{
X = x;
Y = yzw.X;
Z = yzw.Y;
W = yzw.Z;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 3)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y + Z * Z + W * W;
public float Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
public Int4 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z), Math.Abs(W));
//
// Assignment operators
//
public void operator +=(Int4 value) mut
{
X += value.X;
Y += value.Y;
Z += value.Z;
W += value.W;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
Z += value;
W += value;
}
public void operator -=(Int4 value) mut
{
X -= value.X;
Y -= value.Y;
Z -= value.Z;
W -= value.W;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
Z -= value;
W -= value;
}
public void operator *=(Int4 value) mut
{
X *= value.X;
Y *= value.Y;
Z *= value.Z;
W *= value.W;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
Z *= value;
W *= value;
}
public void operator /=(Int4 value) mut
{
X /= value.X;
Y /= value.Y;
Z /= value.Z;
W /= value.W;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
Z /= value;
W /= value;
}
// Operators
public static Int4 operator +(Int4 value) => value;
public static Int4 operator +(Int4 left, Int4 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
public static Int4 operator +(Int4 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right, left.W + right);
public static Int4 operator +(int32 left, Int4 right) => .(left + right.X, left + right.Y, left + right.Z, left + right.W);
public static Int4 operator -(Int4 value) => .(-value.X, -value.Y, -value.Z, -value.W);
public static Int4 operator -(Int4 left, Int4 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W);
public static Int4 operator -(Int4 left, int32 right) => .(left.X - right, left.Y - right, left.Z - right, left.W - right);
public static Int4 operator -(int32 left, Int4 right) => .(left - right.X, left - right.Y, left - right.Z, left - right.W);
public static Int4 operator *(Int4 left, Int4 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W);
public static Int4 operator *(Int4 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right, left.W * right);
public static Int4 operator *(int32 left, Int4 right) => .(left * right.X, left * right.Y, left * right.Z, left * right.W);
public static Int4 operator /(Int4 left, Int4 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W);
public static Int4 operator /(Int4 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right, left.W / right);
public static Int4 operator /(int32 left, Int4 right) => .(left / right.X, left / right.Y, left / right.Z, left / right.W);
public static Int4 operator %(Int4 left, Int4 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z, left.W % right.W);
public static Int4 operator %(Int4 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right, left.W % right);
public static Int4 operator %(int32 left, Int4 right) => .(left % right.X, left % right.Y, left % right.Z, left % right.W);
public static bool operator ==(Int4 left, Int4 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z && left.W == right.W;
public static bool operator ==(Int4 left, int32 right) => left.X == right && left.Y == right && left.Z == right && left.W == right;
public static bool operator ==(int32 left, Int4 right) => left == right.X && left == right.Y && left == right.Z && left == right.W;
public static bool operator !=(Int4 left, Int4 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z || left.W != right.W;
public static bool operator !=(Int4 left, int32 right) => left.X != right || left.Y != right || left.Z != right || left.W != right;
public static bool operator !=(int32 left, Int4 right) => left != right.X || left != right.Y || left != right.Z || left != right.W;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y} Z:{Z} W:{W}");
public static explicit operator Vector4(Int4 point) => .(point.X, point.Y, point.Z, point.W);
public static explicit operator Int4(Vector4 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z, (int32)point.W);
[Inline]
public static explicit operator Int2(in Int4 point) => *(Int2*)&point;
[Inline]
public static explicit operator Int3(in Int4 point) => *(Int3*)&point;
public int GetHashCode()
{
return (((((X * 39) ^ Y) * 39) ^ Z) * 39) ^ W;
}
}
}
@@ -0,0 +1,134 @@
using GlitchyEngine.Renderer;
using GlitchyEngine.Events;
using GlitchyEngine.Math;
using System;
namespace GlitchyEngine
{
/// A simple controller for an orthographic camera
public class OrthographicCameraController
{
private OrthographicCamera _camera ~ delete _;
private float _aspectRatio;
private float _zoomLevel = 1.0f;
private bool _rotation;
private Vector3 _cameraPosition;
private float _cameraRotation;
private float _cameraTranslationSpeed = 1.0f;
private float _cameraRotationSpeed = 1.0f;
public OrthographicCamera Camera => _camera;
public this(float aspectRatio, bool rotation = false)
{
Debug.Profiler.ProfileFunction!();
_aspectRatio = aspectRatio;
_rotation = rotation;
_camera = new OrthographicCamera();
_camera.NearPlane = -10f;
_camera.FarPlane = 10f;
UpdateCamera();
}
public void Update(GameTime gameTime)
{
Debug.Profiler.ProfileFunction!();
if(Application.Get().Window.IsActive)
{
Vector3 movement = .();
if(Input.IsKeyPressed(Key.W))
{
movement.Y += 1;
}
if(Input.IsKeyPressed(Key.S))
{
movement.Y -= 1;
}
if(Input.IsKeyPressed(Key.A))
{
movement.X -= 1;
}
if(Input.IsKeyPressed(Key.D))
{
movement.X += 1;
}
if(movement != .Zero)
movement.Normalize();
movement *= (float)(gameTime.FrameTime.TotalSeconds) * _cameraTranslationSpeed * _zoomLevel;
_cameraPosition += movement;
if(_rotation)
{
if(Input.IsKeyPressed(Key.Q))
{
_cameraRotation += (float)(gameTime.FrameTime.TotalSeconds) * _cameraRotationSpeed;
}
if(Input.IsKeyPressed(Key.E))
{
_cameraRotation -= (float)(gameTime.FrameTime.TotalSeconds) * _cameraRotationSpeed;
}
}
UpdateCamera();
}
}
public void OnEvent(Event e)
{
Debug.Profiler.ProfileFunction!();
EventDispatcher dispatcher = EventDispatcher(e);
dispatcher.Dispatch<MouseScrolledEvent>(scope => OnMouseScrolled);
dispatcher.Dispatch<WindowResizeEvent>(scope => OnWindowResized);
}
private bool OnMouseScrolled(MouseScrolledEvent e)
{
Debug.Profiler.ProfileFunction!();
_zoomLevel -= e.YOffset * 0.25f;
_zoomLevel = Math.Max(_zoomLevel, 0.25f);
UpdateCamera();
return false;
}
private bool OnWindowResized(WindowResizeEvent e)
{
Debug.Profiler.ProfileFunction!();
_aspectRatio = (float)e.Width / (float)e.Height;
UpdateCamera();
return false;
}
private void UpdateCamera()
{
Debug.Profiler.ProfileFunction!();
_camera.Left = -_aspectRatio * _zoomLevel;
_camera.Right = _aspectRatio * _zoomLevel;
_camera.Top = _zoomLevel;
_camera.Bottom = -_zoomLevel;
_camera.Rotation = .(_camera.Rotation.X, _camera.Rotation.Y, _cameraRotation);
_camera.Position = _cameraPosition;
_camera.Update();
}
}
}
@@ -0,0 +1,186 @@
using System;
using GlitchyEngine.Events;
using GlitchyEngine.Math;
using GlitchyEngine.Renderer;
namespace GlitchyEngine
{
/// A simple controller for a perspective camera
public class PerspectiveCameraController
{
private PerspectiveCamera _camera ~ delete _;
private float _aspectRatio;
private float _fovY = Math.PI_f / 4;
private Vector3 _cameraPosition;
private Vector3 _cameraRotation;
private float _cameraTranslationSpeed = 1.0f;
private float _cameraRotationSpeedX = 0.001f;
private float _cameraRotationSpeedY = 0.001f;
public PerspectiveCamera Camera => _camera;
public float TranslationSpeed
{
get => _cameraTranslationSpeed;
set => _cameraTranslationSpeed = value;
}
public Vector2 RotationSpeed
{
get => .(_cameraRotationSpeedX, _cameraRotationSpeedY);
set
{
_cameraRotationSpeedX = value.X;
_cameraRotationSpeedY = value.Y;
}
}
public Vector3 CameraPosition
{
get => _cameraPosition;
set
{
_cameraPosition = value;
UpdateCamera();
}
}
public Vector3 CameraRotation
{
get => _cameraRotation;
set
{
_cameraRotation = value;
UpdateCamera();
}
}
public float FovY
{
get => _fovY;
set
{
_fovY = value;
UpdateCamera();
}
}
public float AspectRatio
{
get => _aspectRatio;
set
{
_aspectRatio = value;
UpdateCamera();
}
}
public this(float aspectRatio)
{
Debug.Profiler.ProfileFunction!();
_aspectRatio = aspectRatio;
_camera = new PerspectiveCamera();
_camera.ProjectionType = .Infinite;
_camera.NearPlane = 0.01f;
UpdateCamera();
}
public void Update(GameTime gameTime)
{
Debug.Profiler.ProfileFunction!();
if(Application.Get().Window.IsActive)
{
Vector3 movement = .();
if(Input.IsKeyPressed(Key.W))
{
movement.Z += 1;
}
if(Input.IsKeyPressed(Key.S))
{
movement.Z -= 1;
}
if(Input.IsKeyPressed(Key.A))
{
movement.X -= 1;
}
if(Input.IsKeyPressed(Key.D))
{
movement.X += 1;
}
if(Input.IsKeyPressed(Key.Space))
{
movement.Y += 1;
}
if(Input.IsKeyPressed(Key.Control))
{
movement.Y -= 1;
}
if(movement != .Zero)
movement.Normalize();
movement *= (float)(gameTime.FrameTime.TotalSeconds) * _cameraTranslationSpeed;
Vector4 delta = Vector4(movement, 1.0f) * _camera.View;
_cameraPosition += Vector3(delta.X, delta.Y, delta.Z);
//_cameraPosition += movement;
// Camera rotation
var mouseDelta = Input.GetMouseMovement();
float rotY = mouseDelta.X * _cameraRotationSpeedX;
float rotX = mouseDelta.Y * _cameraRotationSpeedY;
_cameraRotation.X += rotX;
_cameraRotation.Y += rotY;
UpdateCamera();
}
}
public void OnEvent(Event e)
{
Debug.Profiler.ProfileFunction!();
EventDispatcher dispatcher = EventDispatcher(e);
dispatcher.Dispatch<WindowResizeEvent>(scope => OnWindowResized);
}
private bool OnWindowResized(WindowResizeEvent e)
{
Debug.Profiler.ProfileFunction!();
_aspectRatio = (float)e.Width / (float)e.Height;
UpdateCamera();
return false;
}
private void UpdateCamera()
{
Debug.Profiler.ProfileFunction!();
_camera.AspectRatio = _aspectRatio;
_camera.FovY = _fovY;
_camera.Rotation = _cameraRotation;
_camera.Position = _cameraPosition;
_camera.Update();
}
}
}
@@ -0,0 +1,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
@@ -1,16 +1,45 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using internal GlitchyEngine.Renderer;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
extension DepthStencilFormat
{
public static explicit operator Format(Self self)
{
switch(self)
{
case .D16_UNorm:
return .D16_UNorm;
case .D24_UNorm_S8_UInt:
return .D24_UNorm_S8_UInt;
case .D32_Float:
return .D32_Float;
case .D32_Float_S8X24_UInt:
return .D32_Float_S8X24_UInt;
case .None:
Log.EngineLogger.Assert(false, "None is not a valid depth stencil format");
return .Unknown;
}
}
}
// TODO: Depth stencil target is technically just a RenderTarget
extension DepthStencilTarget
{
internal ID3D11DepthStencilView* nativeView ~ _?.Release();
protected override void PlatformCreate()
{
Debug.Profiler.ProfileResourceFunction!();
Texture2DDescription desc = .();
desc.Format = .D32_Float;
desc.Format = (.)_format;
desc.ArraySize = 1;
desc.BindFlags = .DepthStencil;
desc.Width = _width;
@@ -18,21 +47,13 @@ namespace GlitchyEngine.Renderer
desc.SampleDesc = .(1, 0);
ID3D11Texture2D* tex = ?;
_context.nativeDevice.CreateTexture2D(ref desc, null, &tex);
NativeDevice.CreateTexture2D(ref desc, null, &tex);
_context.nativeDevice.CreateDepthStencilView(tex, null, &nativeView);
NativeDevice.CreateDepthStencilView(tex, null, &nativeView);
tex.Release();
}
public override void Bind()
{
_context.SetDepthStencilTarget(this);
}
}
public override void Clear(float depthValue, uint8 stencilValue, DepthStencilClearFlag clearFlags)
{
_context.nativeContext.ClearDepthStencilView(nativeView, (.)clearFlags, depthValue, stencilValue);
}
}
}
#endif
@@ -1,3 +1,5 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX;
using DirectX.D3D11;
@@ -10,9 +12,11 @@ namespace GlitchyEngine.Renderer
{
protected override void Compile(String vsPath, String vsEntry, String psPath, String psEntry)
{
Debug.Profiler.ProfileResourceFunction!();
// Todo: macros
VertexShader = new VertexShader(_context, vsPath, vsEntry);
PixelShader = new PixelShader(_context, psPath, psEntry);
VertexShader = new VertexShader(vsPath, vsEntry);
PixelShader = new PixelShader(psPath, psEntry);
Reflect();
}
@@ -23,3 +27,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -1,10 +1,14 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
using internal GlitchyEngine.Renderer;
extension GeometryBinding
{
internal ID3D11Buffer*[DirectX.D3D11.D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT] nativeBuffers;
@@ -27,6 +31,8 @@ namespace GlitchyEngine.Renderer
protected override void PlatformSetVertexBuffer(VertexBufferBinding binding, uint32 slot)
{
Debug.Profiler.ProfileResourceFunction!();
Log.EngineLogger.Assert(slot < nativeBuffers.Count, "The buffer slot has to be in the range from 0 to 31.");
VertexBuffer vertexBuffer = binding.Buffer;
@@ -40,8 +46,8 @@ namespace GlitchyEngine.Renderer
nativeBuffers[slot] = vertexBuffer.nativeBuffer..AddRef();
bufferStrides[slot] = vertexBuffer.Binding.Stride;
bufferOffsets[slot] = vertexBuffer.Binding.Offset;
bufferStrides[slot] = binding.Stride;
bufferOffsets[slot] = binding.Offset;
}
else
{
@@ -55,12 +61,16 @@ namespace GlitchyEngine.Renderer
protected override void PlatformSetVertexLayout(VertexLayout vertexLayout)
{
Debug.Profiler.ProfileResourceFunction!();
nativeVertexLayout?.Release();
nativeVertexLayout = vertexLayout?.nativeLayout..AddRef();
}
protected override void PlatformSetIndexBuffer(IndexBuffer indexBuffer)
{
Debug.Profiler.ProfileResourceFunction!();
Log.EngineLogger.Assert(indexBuffer.nativeDescription.BindFlags.HasFlag(.IndexBuffer),
scope $"Buffer ({indexBuffer.nativeBuffer.GetDebugName(.. scope .())}) must have IndexBuffer-flag set to be bound as an index buffer.");
@@ -73,32 +83,33 @@ namespace GlitchyEngine.Renderer
}
public override void Bind(GraphicsContext context = null)
public override void Bind()
{
var context;
context ??= _context;
Debug.Profiler.ProfileRendererFunction!();
context.nativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nativeBuffers, &bufferStrides, &bufferOffsets);
context.nativeContext.InputAssembler.SetInputLayout(_vertexLayout.nativeLayout);
context.nativeContext.InputAssembler.SetPrimitiveTopology((.)_primitiveTopology);
NativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nativeBuffers, &bufferStrides, &bufferOffsets);
NativeContext.InputAssembler.SetInputLayout(_vertexLayout.nativeLayout);
NativeContext.InputAssembler.SetPrimitiveTopology((.)_primitiveTopology);
context.nativeContext.InputAssembler.SetIndexBuffer(_indexBuffer.nativeBuffer, _indexBuffer.Format == .Index32Bit ? .R32_UInt : .R16_UInt, _indexByteOffset);
if(_indexBuffer != null)
NativeContext.InputAssembler.SetIndexBuffer(_indexBuffer.nativeBuffer, _indexBuffer.Format == .Index32Bit ? .R32_UInt : .R16_UInt, _indexByteOffset);
}
public void Unbind(GraphicsContext context = null)
public override void Unbind()
{
var context;
context ??= _context;
Debug.Profiler.ProfileRendererFunction!();
ID3D11Buffer*[nativeBuffers.Count] nullBuffers = .();
uint32[nativeBuffers.Count] zeroStrides = .();
uint32[nativeBuffers.Count] zeroOffsets = .();
context.nativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nullBuffers, &zeroStrides, &zeroOffsets);
context.nativeContext.InputAssembler.SetInputLayout(null);
context.nativeContext.InputAssembler.SetPrimitiveTopology(.Undefined);
NativeContext.InputAssembler.SetVertexBuffers(0, nativeBuffers.Count, &nullBuffers, &zeroStrides, &zeroOffsets);
NativeContext.InputAssembler.SetInputLayout(null);
NativeContext.InputAssembler.SetPrimitiveTopology(.Undefined);
context.nativeContext.InputAssembler.SetIndexBuffer(null, .R16_UNorm, 0);
NativeContext.InputAssembler.SetIndexBuffer(null, .R16_UNorm, 0);
}
}
}
#endif
@@ -1,3 +1,5 @@
#if GE_GRAPHICS_DX11
using System;
using System.Diagnostics;
using DirectX.Common;
@@ -6,9 +8,12 @@ using DirectX.D3D11.SDKLayers;
using DirectX.DXGI.DXGI1_2;
using GlitchyEngine.Renderer;
using GlitchyEngine.Math;
using DirectX.D3D11.DeviceContextStages;
using internal GlitchyEngine.Renderer;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
/// DirectX 11 specific implementation of the GraphicsContext
@@ -18,10 +23,12 @@ namespace GlitchyEngine.Renderer
internal Windows.HWnd nativeWindowHandle;
internal ID3D11Device* nativeDevice;
internal ID3D11DeviceContext* nativeContext;
/// Gets whether or not the DX11 device is initialized.
//internal bool IsDx11Initialized => nativeDevice != null;
//internal ID3D11Device* nativeDevice => NativeDevice;
//internal ID3D11DeviceContext* nativeContext => NativeContext;
private ID3D11Debug* _debugDevice;
//internal ID3D11Debug* debugDevice => DebugDevice;
public override SwapChain SwapChain => _swapChain;
@@ -31,24 +38,29 @@ namespace GlitchyEngine.Renderer
public this(Windows.HWnd windowHandle)
{
Debug.Profiler.ProfileFunction!();
nativeWindowHandle = windowHandle;
_swapChain = new SwapChain(this);
s_GraphicsContext = this;
}
public ~this()
{
Debug.Profiler.ProfileFunction!();
delete _swapChain;
nativeDevice?.Release();
nativeContext?.Release();
_debugDevice.ReportLiveDeviceObjects(.Detail);
_debugDevice?.Release();
Dx11Release();
}
public override void Init()
{
InitDevice();
Debug.Profiler.ProfileFunction!();
Dx11Init();
SwapChain.Init();
}
@@ -56,11 +68,11 @@ namespace GlitchyEngine.Renderer
/**
* Initializes the Device and ImmediateContext.
*/
/*
void InitDevice()
{
nativeDevice?.Release();
nativeContext?.Release();
if(!IsDx11Initialized)
{
Log.EngineLogger.Trace("Creating D3D11 Device and Context...");
DeviceCreationFlags deviceFlags = .None;
@@ -72,10 +84,10 @@ namespace GlitchyEngine.Renderer
FeatureLevel deviceLevel = ?;
var deviceResult = D3D11.CreateDevice(null, .Hardware, 0, deviceFlags, levels, &nativeDevice, &deviceLevel, &nativeContext);
Debug.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message(0x{(int32)deviceResult}): {deviceResult}");
Log.EngineLogger.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message({(int32)deviceResult}): {deviceResult}");
#if DEBUG
if(nativeDevice.QueryInterface<ID3D11Debug>(out _debugDevice).Succeeded)
if(nativeDevice.QueryInterface<ID3D11Debug>(out debugDevice).Succeeded)
{
ID3D11InfoQueue* infoQueue;
if(nativeDevice.QueryInterface<ID3D11InfoQueue>(out infoQueue).Succeeded)
@@ -90,104 +102,149 @@ namespace GlitchyEngine.Renderer
Log.EngineLogger.Trace($"D3D11 Device and Context created (Feature level: {deviceLevel})");
}
else
{
// We created a second GraphicsContext (for some reason?) just increment references.
nativeDevice.AddRef();
nativeContext.AddRef();
debugDevice.AddRef();
}
}
*/
private ID3D11DepthStencilView* _depthStencilTarget;
private ID3D11RenderTargetView*[MaxRTVCount] _renderTargets;
internal void SetDepthStencilTarget(DepthStencilTarget target)
public override void SetDepthStencilTarget(DepthStencilTarget target)
{
_depthStencilTarget = target?.nativeView;
}
public override void SetRenderTarget(RenderTarget renderTarget, int slot = 0)
public override void SetRenderTarget(RenderTarget2D renderTarget, int slot, bool setDepthTarget)
{
if(renderTarget == null)
_renderTargets[slot] = (renderTarget ?? _swapChain.BackBuffer)._nativeRenderTargetView;
if(slot == 0 && setDepthTarget)
{
_renderTargets[slot] = _swapChain.nativeBackBufferTarget;
}
else
{
Runtime.NotImplemented();
SetDepthStencilTarget((renderTarget ?? _swapChain.BackBuffer).DepthStencilTarget);
}
}
public override void BindRenderTargets()
{
nativeContext.OutputMerger.SetRenderTargets(MaxRTVCount, &_renderTargets, _depthStencilTarget);
NativeContext.OutputMerger.SetRenderTargets(MaxRTVCount, &_renderTargets, _depthStencilTarget);
}
public override void ClearRenderTarget(RenderTarget renderTarget, ColorRGBA color)
public override void ClearRenderTarget(RenderTarget2D renderTarget, ColorRGBA color)
{
if(renderTarget == null)
{
nativeContext.ClearRenderTargetView(_swapChain.nativeBackBufferTarget, color);
}
else
{
Runtime.NotImplemented();
}
NativeContext.ClearRenderTargetView((renderTarget ?? _swapChain.BackBuffer)._nativeRenderTargetView, color);
}
public override void SetVertexBuffer(uint32 slot, Buffer buffer, uint32 stride, uint32 offset = 0)
{
// make stride and offset mutable so that we can take their pointers.
var stride, offset;
nativeContext.InputAssembler.SetVertexBuffers(slot, 1, &buffer.nativeBuffer, &stride, &offset);
NativeContext.InputAssembler.SetVertexBuffers(slot, 1, &buffer.nativeBuffer, &stride, &offset);
}
public override void Draw(uint32 vertexCount, uint32 startVertexIndex = 0)
{
nativeContext.Draw(vertexCount, startVertexIndex);
NativeContext.Draw(vertexCount, startVertexIndex);
}
public override void DrawIndexed(uint32 indexCount, uint32 startIndexLocation = 0, int32 vertexOffset = 0)
{
nativeContext.DrawIndexed(indexCount, startIndexLocation, vertexOffset);
NativeContext.DrawIndexed(indexCount, startIndexLocation, vertexOffset);
}
public override void SetIndexBuffer(Buffer buffer, IndexFormat indexFormat = .Index16Bit, uint32 byteOffset = 0)
{
nativeContext.InputAssembler.SetIndexBuffer(buffer.nativeBuffer, indexFormat == .Index32Bit ? .R32_UInt : .R16_UInt, byteOffset);
}
protected override void SetRasterizerStateImpl()
{
nativeContext.Rasterizer.SetState(_currentRasterizerState.nativeRasterizerState);
NativeContext.InputAssembler.SetIndexBuffer(buffer.nativeBuffer, indexFormat == .Index32Bit ? .R32_UInt : .R16_UInt, byteOffset);
}
public override void SetViewports(uint32 viewportsCount, GlitchyEngine.Renderer.Viewport* viewports)
{
nativeContext.Rasterizer.SetViewports(viewportsCount, (.)viewports);
NativeContext.Rasterizer.SetViewports(viewportsCount, (.)viewports);
}
public override void SetVertexLayout(VertexLayout vertexLayout)
{
nativeContext.InputAssembler.SetInputLayout(vertexLayout.nativeLayout);
NativeContext.InputAssembler.SetInputLayout(vertexLayout.nativeLayout);
}
public override void SetPrimitiveTopology(GlitchyEngine.Renderer.PrimitiveTopology primitiveTopology)
{
nativeContext.InputAssembler.SetPrimitiveTopology((DirectX.Common.PrimitiveTopology)primitiveTopology);
NativeContext.InputAssembler.SetPrimitiveTopology((DirectX.Common.PrimitiveTopology)primitiveTopology);
}
/**
* Binds the given shader to the corresponding shader stage.
* @param shader The shader that will be bound to the graphics context.
*/
private void BindShaderToStage<TShader>(TShader shader) where TShader : Shader
{
Debug.Profiler.ProfileRendererFunction!();
uint32 _firstTexture = D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
uint32 _textureCount = 0;
ID3D11ShaderResourceView*[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] _textures = .();
ID3D11SamplerState*[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] _samplers = .();
for(let entry in shader.Textures)
{
_textures[entry.Index] = entry.Texture?.nativeResourceView;
_samplers[entry.Index] = entry.Texture?.SamplerState?.nativeSamplerState;
if(entry.Index >= _textureCount)
_textureCount = entry.Index + 1;
if(entry.Index < _firstTexture)
_firstTexture = entry.Index;
}
if(_textureCount > 0)
{
switch(typeof(TShader))
{
// TODO: Add remaining shader stages
case typeof(PixelShader):
// TODO: bind uavs
NativeContext.PixelShader.SetShaderResources(_firstTexture, _textureCount, &_textures[_firstTexture]);
NativeContext.PixelShader.SetSamplers(_firstTexture, _textureCount, &_samplers[_firstTexture]);
case typeof(VertexShader):
NativeContext.VertexShader.SetShaderResources(_firstTexture, _textureCount, &_textures[_firstTexture]);
NativeContext.VertexShader.SetSamplers(_firstTexture, _textureCount, &_samplers[_firstTexture]);
default:
Runtime.FatalError(scope $"Shader stage \"{typeof(TShader)}\" not implemented.");
}
}
shader.Buffers.PlatformFetchNativeBuffers();
switch(typeof(TShader))
{
// TODO: Add remaining shader stages
case typeof(PixelShader):
NativeContext.PixelShader.SetConstantBuffers(0, shader.Buffers.nativeBuffers.Count, &shader.Buffers.nativeBuffers);
[IgnoreErrors]{ NativeContext.PixelShader.SetShader(((PixelShader)shader).nativeShader); }
case typeof(VertexShader):
NativeContext.VertexShader.SetConstantBuffers(0, shader.Buffers.nativeBuffers.Count, &shader.Buffers.nativeBuffers);
[IgnoreErrors]{ NativeContext.VertexShader.SetShader(((VertexShader)shader).nativeShader); }
default:
Runtime.FatalError(scope $"Shader stage \"{typeof(TShader)}\" not implemented.");
}
}
public override void SetVertexShader(VertexShader vertexShader)
{
//Todo: nativeContext.VertexShader.SetSamplers();
vertexShader.Buffers.PlatformFetchNativeBuffers();
nativeContext.VertexShader.SetConstantBuffers(0, vertexShader.Buffers.nativeBuffers.Count, &vertexShader.Buffers.nativeBuffers);
//Todo: nativeContext.VertexShader.SetShaderResources();
nativeContext.VertexShader.SetShader(vertexShader.nativeShader);
BindShaderToStage(vertexShader);
}
public override void SetPixelShader(PixelShader pixelShader)
{
//Todo: nativeContext.PixelShader.SetSamplers();
pixelShader.Buffers.PlatformFetchNativeBuffers();
nativeContext.PixelShader.SetConstantBuffers(0, pixelShader.Buffers.nativeBuffers.Count, &pixelShader.Buffers.nativeBuffers);
//Todo: nativeContext.PixelShader.SetShaderResources();
nativeContext.PixelShader.SetShader(pixelShader.nativeShader);
BindShaderToStage(pixelShader);
}
}
}
#endif
@@ -1,8 +1,12 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX.D3D11;
using DirectX.D3DCompiler;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
@@ -12,15 +16,23 @@ namespace GlitchyEngine.Renderer
public override void CompileFromSource(String code, String entryPoint, ShaderDefine[] macros = null)
{
Debug.Profiler.ProfileRendererFunction!();
Shader.PlattformCompileShaderFromSource(code, macros, entryPoint, "ps_5_0", DefaultCompileFlags, out nativeCode);
var result = _context.nativeDevice.CreatePixelShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
{
Debug.Profiler.ProfileResourceScope!("CreateNativePixelShader");
var result = NativeDevice.CreatePixelShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create pixel shader: Message ({(int)result}): {result}");
}
}
Reflect(nativeCode);
}
}
}
#endif
@@ -1,6 +1,10 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
@@ -48,12 +52,14 @@ namespace GlitchyEngine.Renderer
internal DirectX.D3D11.RasterizerStateDescription nativeDescription;
internal ID3D11RasterizerState* nativeRasterizerState ~ _?.Release();
public override this(GraphicsContext context, GlitchyEngine.Renderer.RasterizerStateDescription description) : this(context)
public override this(GlitchyEngine.Renderer.RasterizerStateDescription description)
{
Debug.Profiler.ProfileRendererFunction!();
_description = description;
nativeDescription = (.)_description;
var result = context.nativeDevice.CreateRasterizerState(ref nativeDescription, &nativeRasterizerState);
var result = NativeDevice.CreateRasterizerState(ref nativeDescription, &nativeRasterizerState);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create rasterizer state. Message({(int)result}): {result}");
@@ -61,3 +67,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -0,0 +1,125 @@
#if GE_GRAPHICS_DX11
using DirectX.Common;
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using DirectX.DXGI.DXGI1_2;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
using internal GlitchyEngine.Renderer;
extension RenderTarget2D
{
protected internal ID3D11Texture2D* _nativeTexture ~ _?.Release();
protected internal ID3D11ShaderResourceView* _nativeResourceView ~ _?.Release();
protected internal ID3D11RenderTargetView* _nativeRenderTargetView ~ _?.Release();
private void ReleaseAndNullify()
{
Debug.Profiler.ProfileResourceFunction!();
ReleaseAndNullify!(_nativeTexture);
ReleaseAndNullify!(_nativeResourceView);
ReleaseAndNullify!(_nativeRenderTargetView);
ReleaseRefAndNullify!(_depthStenilTarget);
}
public override void Resize(uint32 width, uint32 height)
{
Debug.Profiler.ProfileResourceFunction!();
ReleaseAndNullify();
_description.Width = width;
_description.Height = height;
ApplyChanges();
}
protected override void PlatformApplyChanges()
{
Debug.Profiler.ProfileResourceFunction!();
ReleaseAndNullify();
if(_description.IsSwapchainTarget)
{
// TODO: if the engine supports multiple windows it has to support multiple swap chains.
// kinda dirty...
var context = GraphicsContext.Get();
context.SwapChain.GetBackbuffer(out _nativeTexture);
CreateViews();
}
else
{
PlatformCreateTexture();
}
if(_description.DepthStencilFormat != .None)
{
_depthStenilTarget = new DepthStencilTarget(_description.Width, _description.Height, _description.DepthStencilFormat);
}
}
private void PlatformCreateTexture()
{
Debug.Profiler.ProfileResourceFunction!();
Texture2DDescription desc = .()
{
Width = _description.Width,
Height = _description.Height,
ArraySize = _description.ArraySize,
MipLevels = _description.MipLevels,
Format = _description.PixelFormat,
// Always bindable as ShaderResource and RenderTarget
BindFlags = .ShaderResource | .RenderTarget,
CpuAccessFlags = (.)_description.CpuAccess,
// 2D RenderTarget never has misc flags
MiscFlags = .None,
SampleDesc = .(_description.SampleCount, _description.SampleQuality),
// RenderTarget has always Default usage
Usage = .Default
};
var result = NativeDevice.CreateTexture2D(ref desc, null, &_nativeTexture);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create RenderTarget2D");
CreateViews();
}
private void CreateViews()
{
Debug.Profiler.ProfileResourceFunction!();
if(_description.IsSwapchainTarget)
{
var result = NativeDevice.CreateShaderResourceView(_nativeTexture, null, &_nativeResourceView);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create resource view");
// TODO: SRGB...
//RenderTargetViewDescription rtvDesc = .(_nativeTexture, .Texture2D, .R8G8B8A8_UNorm_SRGB);
//result = NativeDevice.CreateRenderTargetView(_nativeTexture, &rtvDesc, &_nativeRenderTargetView);
result = NativeDevice.CreateRenderTargetView(_nativeTexture, null, &_nativeRenderTargetView);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create render target view");
}
else
{
var result = NativeDevice.CreateShaderResourceView(_nativeTexture, null, &_nativeResourceView);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create resource view");
result = NativeDevice.CreateRenderTargetView(_nativeTexture, null, &_nativeRenderTargetView);
Log.EngineLogger.Assert(result.Succeeded, "Failed to create render target view");
}
}
}
}
#endif
@@ -1,39 +1,141 @@
#if GE_GRAPHICS_DX11
using GlitchyEngine.Math;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
using internal GlitchyEngine.Renderer;
extension RendererAPI
{
private GraphicsContext _context;
private GraphicsContext _context ~ _?.ReleaseRef();
public GraphicsContext Context
{
get => _context;
set => _context = value;
set => SetReference!(_context, value);
}
public static override API Api => .D3D11;
public override void Init()
{
Debug.Profiler.ProfileFunction!();
}
public override void Clear(RenderTarget renderTarget, ColorRGBA clearColor)
private mixin RtOrBackbuffer(RenderTarget2D renderTarget)
{
_context.ClearRenderTarget(renderTarget, clearColor);
renderTarget ?? _context.SwapChain.BackBuffer
}
public override void Clear(DepthStencilTarget target, float depthValue, uint8 stencilValue, DepthStencilClearFlag clearFlags)
public override void Clear(RenderTarget2D renderTarget, ColorRGBA color)
{
_context.nativeContext.ClearDepthStencilView(target.nativeView, (.)clearFlags, depthValue, stencilValue);
Debug.Profiler.ProfileRendererFunction!();
NativeContext.ClearRenderTargetView(RtOrBackbuffer!(renderTarget)._nativeRenderTargetView, color);
}
public override void Clear(DepthStencilTarget target, ClearOptions clearOptions, float depth, uint8 stencil)
{
Debug.Profiler.ProfileRendererFunction!();
if(target == null)
return;
DirectX.D3D11.ClearFlag flags = default;
if(clearOptions.HasFlag(.Depth))
{
flags |= .Depth;
}
if(clearOptions.HasFlag(.Stencil))
{
flags |= .Stencil;
}
NativeContext.ClearDepthStencilView(target.nativeView, flags, depth, stencil);
}
public override void SetRenderTarget(RenderTarget2D renderTarget, int slot, bool setDepthBuffer)
{
Debug.Profiler.ProfileRendererFunction!();
_context.SetRenderTarget(renderTarget, slot, setDepthBuffer);
}
public override void SetDepthStencilTarget(DepthStencilTarget target)
{
Debug.Profiler.ProfileRendererFunction!();
_context.SetDepthStencilTarget(target);
}
public override void BindRenderTargets()
{
Debug.Profiler.ProfileRendererFunction!();
_context.BindRenderTargets();
}
private RasterizerState _currentRasterizerState ~ _?.ReleaseRef();
public override void SetRasterizerState(RasterizerState rasterizerState)
{
Debug.Profiler.ProfileRendererFunction!();
SetReference!(_currentRasterizerState, rasterizerState);
NativeContext.Rasterizer.SetState(_currentRasterizerState.nativeRasterizerState);
}
private BlendState _currentBlendState ~ _?.ReleaseRef();
public override void SetBlendState(BlendState blendState, ColorRGBA blendFactor)
{
Debug.Profiler.ProfileRendererFunction!();
SetReference!(_currentBlendState, blendState);
NativeContext.OutputMerger.SetBlendState(_currentBlendState.nativeBlendState, blendFactor);
}
private DepthStencilState _currentDepthStencilState ~ _?.ReleaseRef();
public override void SetDepthStencilState(DepthStencilState depthStencilState, uint8 stencilReference)
{
Debug.Profiler.ProfileRendererFunction!();
SetReference!(_currentDepthStencilState, depthStencilState);
NativeContext.OutputMerger.SetDepthStencilState(_currentDepthStencilState.nativeDepthStencilState, stencilReference);
}
public override void DrawIndexed(GeometryBinding geometry)
{
Debug.Profiler.ProfileRendererFunction!();
if(geometry.IsIndexed)
_context.DrawIndexed(geometry.IndexCount, geometry.IndexByteOffset, 0);
else
_context.Draw(geometry.VertexCount, 0);
}
public override void DrawIndexedInstanced(GeometryBinding geometry)
{
Debug.Profiler.ProfileRendererFunction!();
NativeContext.DrawIndexedInstanced(geometry.IndexCount, geometry.InstanceCount, geometry.IndexByteOffset, 0, 0);
}
public override void SetViewport(Viewport viewport)
{
Debug.Profiler.ProfileRendererFunction!();
_context.SetViewport(viewport);
}
}
}
#endif
@@ -1,5 +1,11 @@
using DirectX.D3D11;
#if GE_GRAPHICS_DX11
using DirectX.Common;
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace DirectX.D3D11
{
@@ -73,8 +79,6 @@ namespace DirectX.D3D11
}
}
using internal GlitchyEngine.Renderer;
namespace GlitchyEngine.Renderer
{
extension SamplerStateDescription
@@ -107,17 +111,21 @@ namespace GlitchyEngine.Renderer
protected override void PlatformCreateSamplerState()
{
Debug.Profiler.ProfileResourceFunction!();
_desc.ToNative(var nativeDesc);
HResult result = _context.nativeDevice.CreateSamplerState(ref nativeDesc, &nativeSamplerState);
HResult result = NativeDevice.CreateSamplerState(ref nativeDesc, &nativeSamplerState);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create SamplerState. Error({(int)result}): {result}");
}
public override void Bind(uint32 slot)
{
_context.nativeContext.VertexShader.SetSamplers(slot, 1, &nativeSamplerState);
_context.nativeContext.PixelShader.SetSamplers(slot, 1, &nativeSamplerState);
NativeContext.VertexShader.SetSamplers(slot, 1, &nativeSamplerState);
NativeContext.PixelShader.SetSamplers(slot, 1, &nativeSamplerState);
}
}
}
#endif
@@ -1,3 +1,5 @@
#if GE_GRAPHICS_DX11
using System;
using GlitchyEngine.Renderer;
using DirectX.Common;
@@ -16,7 +18,7 @@ namespace GlitchyEngine.Renderer
*/
internal ID3DBlob* nativeCode ~ _?.Release();
protected const ShaderCompileFlags DefaultCompileFlags =
protected const ShaderCompileFlags DefaultCompileFlags = .EnableStrictness |
#if DEBUG
.Debug;
#else
@@ -25,6 +27,8 @@ namespace GlitchyEngine.Renderer
internal static void PlattformCompileShaderFromSource(String code, ShaderDefine[] macros, String entryPoint, String target, ShaderCompileFlags compileFlags, out ID3DBlob* shaderBlob)
{
Debug.Profiler.ProfileResourceFunction!();
ShaderMacro* nativeMacros = macros == null ? null : new:ScopedAlloc! ShaderMacro[macros.Count]*;
for(int i < macros?.Count ?? 0)
@@ -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;
{
Debug.Profiler.ProfileResourceScope!("D3DReflect");
var result = D3DCompiler.D3DReflect(shaderCode.GetBufferPointer(), shaderCode.GetBufferSize(), &reflection);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to reflect shader: Message ({(int)result}): {result}");
}
}
reflection.GetDescription(let desc);
uint32 cBufferCount = desc.ConstantBuffers;
for(uint32 i < cBufferCount)
uint32 resourceCount = desc.BoundResources;
for (uint32 i < resourceCount)
{
reflection.GetResourceBindingDescription(i, let bindDesc);
var res = reflection.GetResourceBindingDescription(i, let bindDesc);
var bufferReflection = reflection.GetConstantBufferByIndex(i);
if (res.Failed)
{
Log.EngineLogger.Error($"Error({(int)res}) {res}: Failed to get resource binding desc for resource {i}");
continue;
}
switch(bindDesc.Type)
{
case .ConstantBuffer:
var bufferReflection = reflection.GetConstantBufferByName(bindDesc.Name);
bufferReflection.GetDescription(let bufferDesc);
// ConstantBuffer
if(bufferDesc.Type == .D3D11_CT_CBUFFER)
{
let buffer = new ConstantBuffer(_context, bufferReflection);
let buffer = new ConstantBuffer(bufferReflection);
_buffers.Add(bindDesc.BindPoint, buffer.Name, buffer);
buffer.ReleaseRef();
}
/*
bufferReflection.GetDescription(let bufferDesc);
// ConstantBuffer
if(bufferDesc.Type == .D3D11_CT_CBUFFER)
{
GlitchyEngine.Renderer.BufferDescription cBufferDesc = .(bufferDesc.Size, .Constant, .Dynamic, .Write);
let buffer = new Buffer(_context, cBufferDesc);
_buffers.Add(bindDesc.BindPoint, StringView(bufferDesc.Name), buffer);//, true
buffer.ReleaseRef();
// Buffer for default values
uint8* rawData = new:ScopedAlloc! uint8[bufferDesc.Size]*;
// Get default values
for(uint32 variable < bufferDesc.Variables)
{
let variableReflection = bufferReflection.GetVariableByIndex(variable);
variableReflection.GetDescription(let varDesc);
if(varDesc.DefaultValue != null)
Internal.MemCpy(rawData + varDesc.StartOffset, varDesc.DefaultValue, varDesc.Size);
case .Texture:
_textures.Add(scope String(bindDesc.Name), bindDesc.BindPoint, null);
case .Sampler:
// TODO: do we have to do something for samplers?
default:
Log.EngineLogger.Warning($"Unhandled shader resource type: \"{bindDesc.Type}\"");
}
}
buffer.SetData(Span<uint8>(rawData, bufferDesc.Size));
}
*/
}
reflection.Release();
}
}
}
#endif
@@ -1,56 +1,24 @@
#if GE_GRAPHICS_DX11
using DirectX.D3D11;
using DirectX.DXGI.DXGI1_2;
using System.Diagnostics;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
public extension SwapChain
{
private GraphicsContext _context;
private bool _changed;
private uint32 _width, _height;
internal DirectX.DXGI.IDXGIDevice* nativeDxgiDevice;
internal IDXGISwapChain1* nativeSwapChain;
internal ID3D11RenderTargetView* nativeBackBufferTarget;
public override uint32 Width
{
get => _width;
set
{
if(_width == value)
return;
_width = value;
_changed = true;
}
}
public override uint32 Height
{
get => _height;
set
{
if(_height == value)
return;
_height = value;
_changed = true;
}
}
public override GraphicsContext Context => _context;
public this(GraphicsContext context)
{
Debug.Profiler.ProfileFunction!();
_context = context;
SetResolutionFromWindow();
@@ -58,9 +26,10 @@ namespace GlitchyEngine.Renderer
public ~this()
{
Debug.Profiler.ProfileFunction!();
nativeDxgiDevice?.Release();
nativeSwapChain?.Release();
nativeBackBufferTarget?.Release();
}
void SetResolutionFromWindow()
@@ -73,11 +42,15 @@ namespace GlitchyEngine.Renderer
public override void Init()
{
Debug.Profiler.ProfileFunction!();
ApplyChanges();
}
public override void ApplyChanges()
{
Debug.Profiler.ProfileFunction!();
if(!_changed)
return;
@@ -89,6 +62,8 @@ namespace GlitchyEngine.Renderer
*/
public void UpdateSwapchain()
{
Debug.Profiler.ProfileFunction!();
Log.EngineLogger.Trace($"Updating swap chain ({_width}, {_height})");
uint32 backBufferCount = 2;
@@ -97,7 +72,8 @@ namespace GlitchyEngine.Renderer
if(nativeSwapChain != null)
{
nativeBackBufferTarget.Release();
//nativeBackBufferTarget.Release();
_backBuffer.ReleaseRef();
var resizeResult = nativeSwapChain.ResizeBuffers(backBufferCount, _width, _height, backBufferFormat, .None);
if(resizeResult.Failed)
@@ -107,7 +83,7 @@ namespace GlitchyEngine.Renderer
}
else
{
_context.nativeDevice.QueryInterface(out nativeDxgiDevice);
NativeDevice.QueryInterface(out nativeDxgiDevice);
nativeDxgiDevice.GetAdapter(let adapter);
adapter.GetParent<DirectX.DXGI.IDXGIFactory2>(let factory);
@@ -118,14 +94,14 @@ namespace GlitchyEngine.Renderer
swDesc.Height = _height;
swDesc.Format = backBufferFormat;
swDesc.SampleDescription = .(1, 0);
swDesc.BufferUsage = .RenderTargetOutput;
swDesc.BufferUsage = .RenderTargetOutput | .ShaderInput;
swDesc.BufferCount = backBufferCount;
swDesc.SwapEffect = .FlipDiscard;
SwapChainFullscreenDescription fsSwapChainDesc = .();
fsSwapChainDesc.Windowed = true;
var createResult = factory.CreateSwapChainForHwnd((.)_context.nativeDevice, _context.nativeWindowHandle, ref swDesc, &fsSwapChainDesc, null, &nativeSwapChain);
var createResult = factory.CreateSwapChainForHwnd((.)NativeDevice, _context.nativeWindowHandle, ref swDesc, &fsSwapChainDesc, null, &nativeSwapChain);
if(createResult.Failed)
{
Log.EngineLogger.Error($"Failed to create swap chain. Message({(int)createResult}):{createResult}");
@@ -134,19 +110,31 @@ namespace GlitchyEngine.Renderer
factory.Release();
}
nativeSwapChain.GetBuffer<ID3D11Texture2D>(0, let backBuffer);
RenderTarget2DDescription desc = .(backBufferFormat, _width, _height);
desc.DepthStencilFormat = _depthStencilFormat;
desc.IsSwapchainTarget = true;
RenderTargetViewDescription rtvDesc = .(backBuffer, .Texture2D, backBufferViewFormat);
_context.nativeDevice.CreateRenderTargetView(backBuffer, &rtvDesc, &nativeBackBufferTarget);
// Todo: perhaps just update the render target
_backBuffer = new RenderTarget2D(desc);
_backBufferViewport = GlitchyEngine.Renderer.Viewport(0, 0, _width, _height, 0.0f, 1.0f);
}
backBuffer.Release();
internal void GetBackbuffer(out ID3D11Texture2D* texture)
{
Debug.Profiler.ProfileResourceFunction!();
var result = nativeSwapChain.GetBuffer<ID3D11Texture2D>(0, out texture);
Log.EngineLogger.Assert(result.Succeeded, "Failed to get backbuffer.");
}
public override void Present()
{
Debug.Profiler.ProfileFunction!();
nativeSwapChain.Present(Application.Get().Window.IsVSync ? 1 : 0, .None);
}
}
}
#endif
@@ -1,22 +1,60 @@
#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;
typealias NativeTex2DDesc = DirectX.D3D11.Texture2DDescription;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
internal typealias NativeTex2DDesc = DirectX.D3D11.Texture2DDescription;
extension Texture
{
protected internal ID3D11ShaderResourceView* nativeView ~ _?.Release();
protected internal ID3D11ShaderResourceView* nativeResourceView ~ _?.Release();
protected override void ImplBind(uint32 slot)
/** \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
{
_context.nativeContext.VertexShader.SetShaderResources(slot, 1, &nativeView);
_context.nativeContext.PixelShader.SetShaderResources(slot, 1, &nativeView);
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;
}
}
@@ -34,15 +72,16 @@ namespace GlitchyEngine.Renderer
// TODO: missing options
desc.SampleDesc = .(1, 0);
desc.Usage = .Immutable;
desc.Usage = (.)Usage;
desc.BindFlags = .ShaderResource;
desc.CpuAccessFlags = .None;
desc.CpuAccessFlags = (.)CpuAccess;
desc.MiscFlags = .None;
return desc;
}
public static implicit operator NativeTex2DDesc(Self desc) => desc.ToNative();
public static explicit operator NativeTex2DDesc(Self desc) => desc.ToNative();
}
extension Texture2D
@@ -55,23 +94,11 @@ namespace GlitchyEngine.Renderer
public override uint32 ArraySize => nativeDesc.ArraySize;
public override uint32 MipLevels => nativeDesc.MipLevels;
protected override void LoadTexturePlatform()
protected override void LoadDdsPlatform(Stream stream)
{
nativeTexture?.Release();
nativeView?.Release();
Debug.Profiler.ProfileResourceFunction!();
HResult loadResult = DDSTextureLoader.CreateDDSTextureFromFile(_context.nativeDevice, _path.ToScopedNativeWChar!(),
(.)&nativeTexture, &nativeView);
if(loadResult.Failed)
{
Log.EngineLogger.Error($"Failed to load texture \"{_path}\". Error({(int)loadResult}): {loadResult}");
nativeTexture?.Release();
nativeView?.Release();
// TODO: load fallback texture
}
LoadDdsResourcePlatform(stream, ref nativeTexture);
let resType = nativeTexture.GetResourceType();
Log.EngineLogger.Assert(resType == .Texture2D, scope $"The texture \"{_path}\" is not a 2D texture (it is {resType}).");
@@ -79,22 +106,156 @@ namespace GlitchyEngine.Renderer
nativeTexture.GetDescription(out nativeDesc);
}
protected override void CreateTexturePlatform(Texture2DDesc desc, void* data, uint32 linePitch)
protected override void CreateTexturePlatform(Texture2DDesc desc, bool isRenderTarget, void* data, uint32 linePitch)
{
nativeTexture?.Release();
nativeView?.Release();
Debug.Profiler.ProfileResourceFunction!();
nativeDesc = desc;
SubresourceData resData = .(data, linePitch, 0);
PrepareTexturePlatform(desc, isRenderTarget);
InternalCreateTexture(data, linePitch, 0);
}
var result = _context.[Friend]nativeDevice.CreateTexture2D(ref nativeDesc, &resData, &nativeTexture);
private void InternalCreateTexture(void* data, uint32 linePitch, uint32 slicePitch)
{
Debug.Profiler.ProfileResourceFunction!();
SubresourceData resData = .(data, linePitch, slicePitch);
// If data is null, set subresourceData null
SubresourceData* resDataPtr = data == null ? null : &resData;
var result = NativeDevice.CreateTexture2D(ref nativeDesc, resDataPtr, &nativeTexture);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture 2D. Error ({result.Underlying}): {result}");
result = _context.[Friend]nativeDevice.CreateShaderResourceView(nativeTexture, null, &nativeView);
result = NativeDevice.CreateShaderResourceView(nativeTexture, null, &nativeResourceView);
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create texture view. Error ({result.Underlying}): {result}");
}
protected override void PrepareTexturePlatform(Texture2DDesc desc, bool isRenderTarget)
{
Debug.Profiler.ProfileResourceFunction!();
nativeTexture?.Release();
nativeTexture = null;
nativeResourceView?.Release();
nativeResourceView = null;
nativeDesc = (NativeTex2DDesc)desc;
if(isRenderTarget)
nativeDesc.BindFlags |= .RenderTarget;
}
// TODO: Update Texture Arrays!
protected override System.Result<void> PlatformSetData(void* data, uint32 elementSize, uint32 destX,
uint32 destY, uint32 destWidth, uint32 destHeight, uint32 arraySlice, uint32 mipLevel, GlitchyEngine.Renderer.MapType mapType)
{
Debug.Profiler.ProfileResourceFunction!();
if(nativeTexture == null)
{
if(destX == 0 && destY == 0 && destWidth == nativeDesc.Width && destHeight == nativeDesc.Height)
{
// We can pass the data while creating the buffer, so we can return here.
var rowPitch = elementSize * destWidth;
return InternalCreateTexture(data, rowPitch, rowPitch * destHeight);
}
else
{
// If we don't set the entire texture we need to create the texture with unknown content and set it manually
Log.EngineLogger.Assert(nativeDesc.Usage != .Immutable, "The entire immutable texture has to be initialized.");
InternalCreateTexture(null, 0, 0);
}
}
//Log.EngineLogger.AssertDebug();
// TODO: Debug.Assert(dstByteOffset + byteLength <= _description.Size, "The destination offset and byte length are too long for the target buffer.");
uint32 subresourceIndex = D3D11.CalcSubresource(mipLevel, arraySlice, nativeDesc.MipLevels);
switch(nativeDesc.Usage)
{
case .Default:
Box dataBox = .(destX, destY, 0, destX + destWidth, destY + destHeight, 1);
var rowPitch = elementSize * destWidth;
NativeContext.UpdateSubresource(nativeTexture, subresourceIndex, &dataBox, data, rowPitch, rowPitch * destHeight);
case .Dynamic:
Runtime.NotImplemented();
/*
Log.EngineLogger.Assert(mapType.CanWrite, "The map type has to have write access.");
// Todo: DoNotWaitFlag
MappedSubresource map = ?;
_context.nativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map);
Internal.MemCpy(((uint8*)map.Data) + dstByteOffset, data, byteLength);
_context.nativeContext.Unmap(nativeBuffer, 0);
*/
case .Immutable:
Log.EngineLogger.Error("Can't set the data of an immutable resource.");
return .Err;
default:
Log.EngineLogger.Error($"Unknown resource usage: {nativeDesc.Usage}");
return .Err;
}
return .Ok;
}
public static override void CopySubresourceRegion(Texture2D source, Texture2D destination,
ResourceBox sourceBox = default, uint32 destX = 0, uint32 destY = 0,
uint32 srcArraySlice = 0, uint32 srcMipSlice = 0, uint32 destArraySlice = 0, uint32 destMipSlice = 0)
{
Debug.Profiler.ProfileResourceFunction!();
Log.EngineLogger.AssertDebug(source != destination || srcArraySlice != destArraySlice
|| srcMipSlice != destMipSlice, "Cannot copy from and to the same sub resource.");
Log.EngineLogger.AssertDebug(source.nativeDesc.Format == destination.nativeDesc.Format,
"Source and destination resources must have the same texel-format.");
var sourceBox;
if(sourceBox.Right == 0)
{
sourceBox.Right = source.Width;
sourceBox.Bottom = source.Height;
sourceBox.Back = 1;
}
// Make sure the destination was initialized
if(destination.nativeTexture == null)
destination.InternalCreateTexture(null, 0, 0);
NativeContext.CopySubresourceRegion(destination.nativeTexture,
D3D11.CalcSubresource(destMipSlice, destArraySlice, destination.MipLevels), destX, destY, 0,
source.nativeTexture, D3D11.CalcSubresource(srcMipSlice, srcArraySlice, source.MipLevels), (.)&sourceBox);
}
public override void CopyTo(Texture2D destination)
{
Debug.Profiler.ProfileResourceFunction!();
if(nativeTexture == null)
return;
Log.EngineLogger.AssertDebug(nativeDesc.Format == destination.nativeDesc.Format,
"Source and destination resources must have the same texel-format.");
// Make sure the destination was initialized
if(destination.nativeTexture == null)
destination.InternalCreateTexture(null, 0, 0);
uint32 arraySlices = Math.Min(ArraySize, destination.ArraySize);
uint32 mipSlices = Math.Min(MipLevels, destination.MipLevels);
for(uint32 arraySlice < arraySlices)
for(uint32 mipSlice < mipSlices)
{
Box sourceBox = .(0, 0, 0, Width, Height, 1);
NativeContext.CopySubresourceRegion(destination.nativeTexture,
D3D11.CalcSubresource(mipSlice, arraySlice, destination.MipLevels), 0, 0, 0,
nativeTexture, D3D11.CalcSubresource(mipSlice, arraySlice, MipLevels), (.)&sourceBox);
}
}
}
extension TextureCube
@@ -107,23 +268,11 @@ namespace GlitchyEngine.Renderer
public override uint32 ArraySize => nativeDesc.ArraySize / 6;
public override uint32 MipLevels => nativeDesc.MipLevels;
protected override void LoadTexturePlatform()
protected override void LoadTexturePlatform(Stream stream)
{
nativeTexture?.Release();
nativeView?.Release();
Debug.Profiler.ProfileResourceFunction!();
HResult loadResult = DDSTextureLoader.CreateDDSTextureFromFile(_context.nativeDevice, _path.ToScopedNativeWChar!(),
(.)&nativeTexture, &nativeView);
if(loadResult.Failed)
{
Log.EngineLogger.Error($"Failed to load texture \"{_path}\". Error({(int)loadResult}): {loadResult}");
nativeTexture?.Release();
nativeView?.Release();
// TODO: load fallback texture
}
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}).");
@@ -135,3 +284,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -1,9 +1,13 @@
#if GE_GRAPHICS_DX11
using System;
using DirectX.D3D11;
using System.Diagnostics;
using DirectX.Common;
using DirectX.D3D11;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
@@ -13,12 +17,12 @@ namespace GlitchyEngine.Renderer
public ID3DBlob* nativeShaderCode ~ _?.Release();
public this(GraphicsContext context, VertexElement[] elements, VertexShader vertexShader)
public this(VertexElement[] elements, bool ownsElements, VertexShader vertexShader)
{
nativeShaderCode = vertexShader.nativeCode..AddRef();
_context = context..AddRef();
_elements = elements;
_ownsElements = ownsElements;
CreateNativeLayout();
}
@@ -28,16 +32,18 @@ namespace GlitchyEngine.Renderer
Debug.Assert(input.Count == output.Count);
for(int i < input.Count)
output[i] = .(input[i].SemanticName, input[i].SemanticIndex, input[i].Format, input[i].InputSlot, input[i].AlignedByteOffset, (.)input[i].InputSlotClass);
output[i] = .(input[i].SemanticName, input[i].SemanticIndex, input[i].Format, input[i].InputSlot, input[i].AlignedByteOffset, (.)input[i].InputSlotClass, input[i].InstanceDataStepRate);
}
protected override void CreateNativeLayout()
{
Debug.Profiler.ProfileResourceFunction!();
var nativeElements = scope InputElementDescription[_elements.Count];
ToNativeLayout(_elements, nativeElements);
var result = _context.nativeDevice.CreateInputLayout(nativeElements.CArray(), (.)nativeElements.Count, nativeShaderCode.GetBufferPointer(), nativeShaderCode.GetBufferSize(), &nativeLayout);
var result = NativeDevice.CreateInputLayout(nativeElements.CArray(), (.)nativeElements.Count, nativeShaderCode.GetBufferPointer(), nativeShaderCode.GetBufferSize(), &nativeLayout);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create D3D11 input layout: Message({(int)result}): {result}");
@@ -45,3 +51,5 @@ namespace GlitchyEngine.Renderer
}
}
}
#endif
@@ -1,10 +1,13 @@
#if GE_GRAPHICS_DX11
using System;
using GlitchyEngine.Renderer;
using DirectX.Common;
using DirectX.D3D11;
using DirectX.D3DCompiler;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
namespace GlitchyEngine.Renderer
{
@@ -14,15 +17,23 @@ namespace GlitchyEngine.Renderer
public override void CompileFromSource(String code, String entryPoint, ShaderDefine[] macros = null)
{
Debug.Profiler.ProfileResourceFunction!();
Shader.PlattformCompileShaderFromSource(code, macros, entryPoint, "vs_5_0", DefaultCompileFlags, out nativeCode);
var result = _context.nativeDevice.CreateVertexShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
{
Debug.Profiler.ProfileResourceScope!("CreateNativeVertexShader");
var result = NativeDevice.CreateVertexShader(nativeCode.GetBufferPointer(), nativeCode.GetBufferSize(), null, &nativeShader);
if(result.Failed)
{
Log.EngineLogger.Error($"Failed to create vertex shader: Message ({(int)result}): {result}");
}
}
Reflect(nativeCode);
}
}
}
#endif
@@ -1,49 +0,0 @@
#if BF_PLATFORM_WINDOWS
using static System.Windows;
namespace GlitchyEngine.Threading
{
extension Mutex
{
internal Handle nativeHandle;
public this(bool initialyOwned = false)
{
nativeHandle = CreateMutexW(null, initialyOwned, null);
Log.EngineLogger.AssertDebug(nativeHandle != 0, scope $"Failed to create mutex. Error code: {GetLastError()}");
}
public ~this()
{
CloseHandle(nativeHandle);
}
public override LockResult Lock(uint32 timeout = InfiniteTimeout)
{
int i = WaitForSingleObject(nativeHandle, timeout);
switch(i)
{
case 0x00000000L:
return .Released;
case 0x00000080L:
return .Abandoned;
case 0x00000102L:
return .Timeout;
case 0xFFFFFFFF:
return .Failed;
default:
return .Unknown;
}
}
public bool Unlock()
{
return ReleaseMutex(nativeHandle);
}
}
}
#endif
@@ -1,55 +0,0 @@
#if BF_PLATFORM_WINDOWS
using static System.Windows;
namespace GlitchyEngine.Threading
{
extension Semaphore
{
internal Handle nativeHandle;
public override this(int32 initialCount = 1, int32 maximumCount = 1)
{
nativeHandle = (.)CreateSemaphoreW(null, initialCount, maximumCount, null);
Log.EngineLogger.AssertDebug(nativeHandle != 0, scope $"Failed to create semaphore. Error code: {GetLastError()}");
}
public ~this()
{
CloseHandle(nativeHandle);
}
public override LockResult Lock(uint32 timeout = InfiniteTimeout)
{
int i = WaitForSingleObject(nativeHandle, timeout);
switch(i)
{
case 0x00000000L:
return .Released;
case 0x00000080L:
return .Abandoned;
case 0x00000102L:
return .Timeout;
case 0xFFFFFFFF:
return .Failed;
default:
return .Unknown;
}
}
public bool Unlock(int32 releaseCount = 1)
{
return ReleaseSemaphore(nativeHandle, releaseCount, null);
}
public bool Unlock(out int32 previousCount, int32 releaseCount = 1)
{
previousCount = ?;
return ReleaseSemaphore(nativeHandle, releaseCount, &previousCount);
}
}
}
#endif
@@ -1,38 +0,0 @@
namespace System
{
extension Windows
{
public struct SecurityAttributes
{
public uint32 Length;
public SECURITY_DESCRIPTOR* SecurityDescriptor;
public IntBool InheritHandle;
}
[LinkName(.C)]
public static extern Windows.Handle CreateSemaphoreW(SecurityAttributes* semaphoreAttributes, int32 initialCount, int32 maximumCount, char16* name);
[LinkName(.C)]
public static extern uint32 WaitForSingleObject(Handle handle, uint32 timeout);
[LinkName(.C)]
public static extern uint32 WaitForMultipleObjects(uint32 count, Handle *handles, IntBool bWaitAll, uint32 timeout);
public const uint32 InfiniteTimeout = 0xFFFFFFFF;
[Inline]
public static uint32 WaitForMultipleObjects(Handle[] handles, bool waitForAll, uint32 timeout = InfiniteTimeout)
{
return WaitForMultipleObjects((.)handles.Count, handles.CArray(), waitForAll, timeout);
}
[LinkName(.C)]
public static extern IntBool ReleaseSemaphore(Handle semaphore, int32 releaseCount, int32* previousCount);
[LinkName(.C)]
public static extern Handle CreateMutexW(SecurityAttributes* mutexAttributes, IntBool initialOwner, char16* name);
[LinkName(.C)]
public static extern IntBool ReleaseMutex(Handle mutex);
}
}
@@ -1,3 +1,5 @@
#if BF_PLATFORM_WINDOWS
using System;
using GlitchyEngine.Events;
using DirectX.Windows.VirtualKeyCodes;
@@ -7,39 +9,21 @@ using static System.Windows;
namespace GlitchyEngine
{
#if GE_WINDOWS
/// Windows (WinApi) specific implementation of the Input-class
extension Input
{
internal static bool sUseRawInput = true;
public override static bool RawInput
{
get => sUseRawInput;
set
{
if(sUseRawInput == value)
return;
sUseRawInput = value;
//if(sUseRawInput)
// TODO: init raw input
}
}
/// Represents the state of the input devices on the windows platform (WinApi that is).
struct WindowsInputState
{
public int8[256] KeyStates;
public Point CursorPosition;
public Point CursorPositionDifference;
public Int32_2 RawMovement;
}
static WindowsInputState* CurrentState = new WindowsInputState() ~ delete _;
static WindowsInputState* LastState = new WindowsInputState() ~ delete _;
static WindowsInputState[2] IputStates;
static WindowsInputState* CurrentState = &IputStates[0];
static WindowsInputState* LastState = &IputStates[1];
//
// Current Keyboard state
@@ -67,13 +51,13 @@ namespace GlitchyEngine
//
public override static bool WasKeyPressed(Key keycode)
{
int8 state = CurrentState.KeyStates[(int)keycode];
int8 state = LastState.KeyStates[(int)keycode];
return state < 0;
}
public override static bool WasKeyReleased(Key keycode)
{
int8 state = CurrentState.KeyStates[(int)keycode];
int8 state = LastState.KeyStates[(int)keycode];
return state >= 0;
}
@@ -166,24 +150,10 @@ namespace GlitchyEngine
//
public override static bool IsMouseButtonPressing(MouseButton button) => IsMouseButtonPressed(button) && WasMouseButtonReleased(button);
public override static bool IsMouseButtonReleasiong(MouseButton button) => IsMouseButtonReleased(button) && WasMouseButtonPressed(button);
public override static bool IsMouseButtonReleasing(MouseButton button) => IsMouseButtonReleased(button) && WasMouseButtonPressed(button);
public override static Point GetMouseMovement() => CurrentState.CursorPositionDifference;
public override static Int32_2 GetRawMouseMovement()
{
if(sUseRawInput)
{
return CurrentState.RawMovement;
}
else
{
var v = GetMouseMovement();
return *(Int32_2*)&v;
}
}
//[CLink, CallingConvention(.Stdcall)]
//static extern int16 GetKeyState(int32 keycode);
[CLink, CallingConvention(.Stdcall)]
@@ -191,14 +161,11 @@ namespace GlitchyEngine
[CLink, CallingConvention(.Stdcall)]
static extern IntBool ScreenToClient(HWnd hWnd, ref Point p);
internal static Int32_2 rawMovement;
public override static void NewFrame()
{
// Switch last and current states
var buffer = LastState;
LastState = CurrentState;
CurrentState = buffer;
Debug.Profiler.ProfileFunction!();
Swap!(CurrentState, LastState);
// Get current keyboard state
if(DirectX.Windows.Winuser.GetKeyboardState((uint8*)&CurrentState.KeyStates) == 0)
@@ -225,9 +192,8 @@ namespace GlitchyEngine
// Calculate cursor movement
CurrentState.CursorPositionDifference = CurrentState.CursorPosition - LastState.CursorPosition;
CurrentState.RawMovement = rawMovement;
rawMovement = .Zero;
}
}
}
#endif
}
@@ -0,0 +1,48 @@
using System;
using DirectX.Windows.Winuser;
using DirectX.Common;
namespace GlitchyEngine
{
extension Key
{
// Todo: make overriding method
public void GetKeyName(String strBuffer)
{
var scanCode = MapVirtualKeyW((.)this, MAPVK_VK_TO_VSC);
char16[128] name = ?;
int result;
switch (this)
{
case .Left, .Up, .Right, .Down, .RightControl, .RightAlt,
.LeftSuper, .RightSuper, ContextMenu,
.Prior, .Next, .End, .Home, .Insert, .Delete, .Divide, .Numlock:
// set extended flag
scanCode |= 0x0100;
fallthrough;
default:
result = GetKeyNameTextW((.)(scanCode << 16), &name, name.Count);
}
if(result == 0)
{
HResult error = (.)DirectX.Windows.Kernel32.GetLastError();
Log.EngineLogger.Error($"Failed to convert keycode {(int)this}({this}) to string. WinAPI error({(int)error}): {error}");
return;
}
strBuffer.Append(Span<char16>(&name, result));
}
/// Converts the given windows specific virtual key code to a GlitchyEngine Key.
[Inline]
internal static Key FromWindowsKeyCode(int32 vKeyCode)
{
return (.)vKeyCode;
}
}
}
@@ -1,4 +1,5 @@
#if GE_WINDOWS
#if BF_PLATFORM_WINDOWS
using System;
using DirectX.Common;
using DirectX.Windows;
@@ -9,9 +10,10 @@ using GlitchyEngine.Events;
using System.Diagnostics;
using GlitchyEngine.Math;
using GlitchyEngine.Renderer;
using DirectX.Windows.Winuser.RawInput;
using static System.Windows;
using internal GlitchyEngine;
namespace GlitchyEngine
{
/// The windows specific Window implementation
@@ -32,6 +34,8 @@ namespace GlitchyEngine
private bool _isVSync = true;
private bool _isActive;
private GraphicsContext _graphicsContext ~ _?.ReleaseRef();
public override GraphicsContext Context => _graphicsContext;
@@ -150,10 +154,14 @@ namespace GlitchyEngine
set => _isVSync = value;
}
public override bool IsActive => _isActive;
public override void* NativeWindow => (void*)(int)_windowHandle;
public override this(WindowDescription desc)
{
Debug.Profiler.ProfileFunction!();
_minMaxInfo.MaximumTrackingSize.x = int32.MaxValue;
_minMaxInfo.MaximumTrackingSize.y = int32.MaxValue;
@@ -168,6 +176,8 @@ namespace GlitchyEngine
public ~this()
{
Debug.Profiler.ProfileFunction!();
if(!DestroyWindow(_windowHandle))
{
HResult res = (.)GetLastError();
@@ -177,8 +187,13 @@ namespace GlitchyEngine
UnregisterClass(WindowClassName.ToScopedNativeWChar!(), _instanceHandle);
}
[LinkName(.C)]
static extern HWnd GetActiveWindow();
private void Init(WindowDescription desc)
{
Debug.Profiler.ProfileFunction!();
Log.EngineLogger.Trace($"Creating window \"{desc.Title}\" ({desc.Width}, {desc.Height})...");
_instanceHandle = (.)GetModuleHandleW(null);
@@ -189,7 +204,11 @@ namespace GlitchyEngine
_windowClass.HInstance = (.)_instanceHandle;
if (desc.Icon.Ptr != null)
{
Debug.Profiler.ProfileScope!("LoadIcon");
_windowClass.Icon = LoadImageW(0, desc.Icon.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile);
}
else
_windowClass.Icon = 0;
@@ -204,8 +223,12 @@ namespace GlitchyEngine
Runtime.FatalError("Failed to register window class");
}
{
Debug.Profiler.ProfileScope!("CreateWindow");
_windowHandle = CreateWindowExW(.None, _windowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, (.)_instanceHandle, null);
}
_graphicsContext = new GraphicsContext(_windowHandle);
_graphicsContext.Init();
@@ -217,40 +240,8 @@ namespace GlitchyEngine
Log.EngineLogger.Trace($"Created window \"{Title}\" ({Width}, {Height})");
if(Input.RawInput)
{
InitRawInput();
}
}
[CLink, CallingConvention(.Stdcall)]
static extern IntBool RegisterRawInputDevices(RAWINPUTDEVICE* rawInputDevices, uint32 numDevices, uint32 size);
private void InitRawInput()
{
RAWINPUTDEVICE[1] Rid;
Rid[0].UsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
Rid[0].Usage = 0x02; // HID_USAGE_GENERIC_MOUSE
Rid[0].Flags = 0;//RIDEV_NOLEGACY; // adds mouse and also ignores legacy mouse messages
Rid[0].Target = 0;
/*
Rid[1].UsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
Rid[1].Usage = 0x06; // HID_USAGE_GENERIC_KEYBOARD
Rid[1].Flags = RIDEV_NOLEGACY; // adds keyboard and also ignores legacy keyboard messages
Rid[1].Target = 0;
*/
if (!RegisterRawInputDevices(&Rid, Rid.Count, sizeof(RAWINPUTDEVICE)))
{
DirectX.Common.HResult errorCode = (.)GetLastError();
Log.EngineLogger.Error($"Failed to register raw input devices. Message({(int32)errorCode}):{errorCode}");
// Disable raw input
Input.RawInput = false;
}
else
{
Log.EngineLogger.Trace($"Registered raw input devices.");
}
// Determine whether or not the window is currently active
_isActive = GetActiveWindow() == _windowHandle;
}
private bool _isResizingOrMoving;
@@ -259,6 +250,18 @@ namespace GlitchyEngine
[CLink]
static extern IntBool IsWindowUnicode(HWND whnd);
public Result<void> SetIcon(StringView filePath)
{
HICON hIcon = LoadImageW(0, filePath.ToScopedNativeWChar!(), .Icon, 0, 0, .LoadFromFile);
if (hIcon == 0)
return .Err;
// WM_SETICON 0x0080
SendMessageW(_windowHandle, 0x0080, 1 /* ICON_BIG */, (int)hIcon);
return .Ok;
}
private static LRESULT MessageHandler(HWND hwnd, uint32 uMsg, WPARAM wParam, LPARAM lParam)
{
void* windowPtr = (void*)GetWindowLongPtrW(hwnd, GWL_USERDATA);
@@ -287,27 +290,22 @@ namespace GlitchyEngine
// Window size changed
case WM_SIZE:
{
if (wParam == (.)ResizingType.Minimized)
{
window._isMinimized = true;
}
else if (window._isMinimized)
{
window._isMinimized = false;
}
else
{
window._isMinimized = wParam == (.)ResizingType.Minimized;
SplitHighAndLowOrder!(lParam, out window._clientRect.Width, out window._clientRect.Height);
if(window._clientRect.Width != 0 && window._clientRect.Height != 0)
{
window._graphicsContext.SwapChain.Width = (.)window._clientRect.Width;
window._graphicsContext.SwapChain.Height = (.)window._clientRect.Height;
window._graphicsContext.SwapChain.ApplyChanges();
}
WindowResizeEvent event = scope WindowResizeEvent(window._clientRect.Width, window._clientRect.Height, window._isResizingOrMoving);
window._eventCallback(event);
}
}
// Window position changed
case WM_MOVE:
{
@@ -341,18 +339,39 @@ namespace GlitchyEngine
window._eventCallback(moveEvent);
}
case 0x0006: //WM_ACTIVATE
{
SplitHighAndLowOrder!((int64)wParam, let lowWParam, let highWParam);
bool isActivate = lowWParam > 0;
//HWnd windowHandle = (.)lParam;
if(window._isActive != isActivate)
{
window._isActive = isActivate;
if(window._isActive)
window._eventCallback(scope WindowActivateEvent());
else
window._eventCallback(scope WindowDeactivateEvent());
}
}
////
//// Keyboard input
////
case WM_KEYDOWN:
case WM_KEYDOWN, WM_SYSKEYDOWN:
{
var event = scope KeyPressedEvent((Key)wParam, (int32)lParam & 0xFFFF);
Key key = WindowMessageToKey(wParam, lParam);
var event = scope KeyPressedEvent(key, (int32)lParam & 0xFFFF);
window._eventCallback(event);
}
case WM_KEYUP:
case WM_KEYUP, WM_SYSKEYUP:
{
var event = scope KeyReleasedEvent((Key)wParam);
Key key = WindowMessageToKey(wParam, lParam);
var event = scope KeyReleasedEvent(key);
window._eventCallback(event);
}
@@ -474,130 +493,38 @@ namespace GlitchyEngine
return 0;
}
// Raw Input
case 0x00FF: //WM_INPUT
{
uint32 dataSize = ?;
GetRawInputData((.)lParam, RID_INPUT, null, &dataSize, sizeof(RAWINPUTHEADER));
if (dataSize > 0)
{
uint8[] rawData = scope .[dataSize];
if (GetRawInputData((.)lParam, RID_INPUT, rawData.CArray(), &dataSize, sizeof(RAWINPUTHEADER)) == dataSize)
{
RAWINPUT* raw = (.)rawData.CArray();
if (raw.Header.Type == RIM_TYPEMOUSE)
{
int32 movementX = raw.Data.Mouse.lLastX;
int32 movementY = raw.Data.Mouse.lLastY;
// TODO: raw.Data.Mouse.usFlags defines whether movement is abosulte, relative, etc...
Input.[Friend]rawMovement += .(movementX, movementY);
/*
var event = scope MouseMovedEvent(movementX, movementY);
window._eventCallback(event);
// Convert button transition flags to a more manageable type
var buttonTransitions = (RawMouseButtonTransition)raw.Data.Mouse.DUMMYUNIONNAME.DUMMYSTRUCTNAME.usButtonFlags;
if(buttonTransitions.HasFlag(.LeftDown))
{
var buttonEvent = scope MouseButtonPressedEvent(.LeftButton);
window._eventCallback(buttonEvent);
}
else if(buttonTransitions.HasFlag(.LeftUp))
{
var buttonEvent = scope MouseButtonReleasedEvent(.LeftButton);
window._eventCallback(buttonEvent);
}
if(buttonTransitions.HasFlag(.RightDown))
{
var buttonEvent = scope MouseButtonPressedEvent(.RightButton);
window._eventCallback(buttonEvent);
}
else if(buttonTransitions.HasFlag(.RightUp))
{
var buttonEvent = scope MouseButtonReleasedEvent(.RightButton);
window._eventCallback(buttonEvent);
}
if(buttonTransitions.HasFlag(.MiddleDown))
{
var buttonEvent = scope MouseButtonPressedEvent(.MiddleButton);
window._eventCallback(buttonEvent);
}
else if(buttonTransitions.HasFlag(.MiddleUp))
{
var buttonEvent = scope MouseButtonReleasedEvent(.MiddleButton);
window._eventCallback(buttonEvent);
}
if(buttonTransitions.HasFlag(.XButton1Down))
{
var buttonEvent = scope MouseButtonPressedEvent(.XButton1);
window._eventCallback(buttonEvent);
}
else if(buttonTransitions.HasFlag(.XButton1Up))
{
var buttonEvent = scope MouseButtonReleasedEvent(.XButton1);
window._eventCallback(buttonEvent);
}
if(buttonTransitions.HasFlag(.XButton2Down))
{
var buttonEvent = scope MouseButtonPressedEvent(.XButton2);
window._eventCallback(buttonEvent);
}
else if(buttonTransitions.HasFlag(.XButton2Up))
{
var buttonEvent = scope MouseButtonReleasedEvent(.XButton2);
window._eventCallback(buttonEvent);
}
if(buttonTransitions.HasFlag(.MouseWheel))
{
int32 rotation = raw.Data.Mouse.DUMMYUNIONNAME.DUMMYSTRUCTNAME.usButtonData / WHEEL_DELTA;
var scrollEvent = scope MouseScrolledEvent(0, rotation);
window._eventCallback(scrollEvent);
}
if(buttonTransitions.HasFlag(.MouseHWheel))
{
int32 rotation = raw.Data.Mouse.DUMMYUNIONNAME.DUMMYSTRUCTNAME.usButtonData / WHEEL_DELTA;
var scrollEvent = scope MouseScrolledEvent(rotation, 0);
window._eventCallback(scrollEvent);
}
*/
}
}
}
}
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
enum RawMouseButtonTransition
/**
* Converts the wParam and lParam of a WM_KEYDOWN or WM_KEYUP message to the corresponding engine keycode.
*/
static Key WindowMessageToKey(WPARAM wParam, LPARAM lParam)
{
LeftDown = 0x0001,
LeftUp = 0x0002,
MiddleDown = 0x0010,
MiddleUp = 0x0020,
RightDown = 0x0004,
RightUp = 0x0008,
XButton1Down = 0x0040,
XButton1Up = 0x0080,
XButton2Down = 0x0100,
XButton2Up = 0x0200,
MouseWheel = 0x0400,
MouseHWheel = 0x0800
Key key = Key.FromWindowsKeyCode((.)wParam);
var scancode = (lParam & 0x00ff0000) >> 16;
bool isExtended = (lParam & 0x01000000) > 0;
switch(key)
{
case .Shift:
return Key.FromWindowsKeyCode((.)MapVirtualKeyW((.)scancode, MAPVK_VSC_TO_VK_EX));
case .Control:
return isExtended ? .RightControl : .LeftControl;
case .Alt:
return isExtended ? .RightAlt : .LeftAlt;
default:
return key;
}
}
public override void Update()
{
Debug.Profiler.ProfileFunction!();
Message message = .();
while (PeekMessageW(&message, 0, 0, 0, .Remove))
{
@@ -641,4 +568,5 @@ namespace GlitchyEngine
}
}
}
#endif
+16 -1
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)
{
Debug.Profiler.BeginProfiling();
Application app;
{
Debug.Profiler.ProfileScope!("Initialize");
Log.EngineLogger.Info("Initializing Application...");
Stopwatch initWatch = scope Stopwatch();
var app = CreateApplication();
app = CreateApplication();
initWatch.Stop();
Log.EngineLogger.Info($"Application initialized ({initWatch.ElapsedMilliseconds}ms).");
Log.EngineLogger.Info("Running App...");
}
app.Run();
{
Debug.Profiler.ProfileScope!("Shutdown");
Log.EngineLogger.Info("Uninitializing Application...");
delete app;
Log.EngineLogger.Info("Application uninitialized.");
}
Debug.Profiler.EndProfiling();
return 0;
}
@@ -0,0 +1,279 @@
using System;
using GlitchyEngine.Core;
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer.Animation
{
class AnimationPlayer
{
public Skeleton Skeleton ~ _.ReleaseRef();
public AnimationClip CurrentClip ~ _.ReleaseRef();
public float TimeStamp;
public SkeletonPose Pose ~ delete _;
public Matrix[] SkinningMatricies ~ delete _;
public Matrix3x3[] InvTransSkinningMatricies ~ delete _;
public this(Skeleton skeleton, AnimationClip clip)
{
Skeleton = skeleton;
CurrentClip = clip;
Pose = new SkeletonPose(Skeleton);
SkinningMatricies = new Matrix[Skeleton.Joints.Count];
InvTransSkinningMatricies = new Matrix3x3[Skeleton.Joints.Count];
}
public void Update(GameTime gameTime)
{
TimeStamp += (float)gameTime.FrameTime.TotalSeconds;
if(CurrentClip.IsLooping && CurrentClip.Duration != 0)
{
TimeStamp %= CurrentClip.Duration;
}
for(int i < Skeleton.Joints.Count)
{
ref JointPose localPose = ref Pose.LocalPose[i];
localPose = CurrentClip.JointAnimations[i].GetCurrentPose(TimeStamp);
Matrix jointToParent =
Matrix.Translation(localPose.Translation) *
Matrix.RotationQuaternion(localPose.Rotation) *
Matrix.Scaling(localPose.Scale);
uint8 parentIndex = Skeleton.Joints[i].ParentID;
ref Matrix globalPose = ref Pose.GlobalPose[i];
if(parentIndex == uint8.MaxValue)
{
globalPose = jointToParent;
}
else
{
globalPose = Pose.GlobalPose[parentIndex] * jointToParent;
}
SkinningMatricies[i] = globalPose * Skeleton.Joints[i].InverseBindPose;
InvTransSkinningMatricies[i] = ((Matrix3x3)SkinningMatricies[i]).Inverse().Transpose();
}
}
}
class AnimationClip : RefCounter
{
private Skeleton _skeleton ~ _.ReleaseRef();
public JointAnimation[] JointAnimations ~ delete _;
public bool IsLooping;
public float Duration;
public Skeleton Skeleton => _skeleton;
[AllowAppend]
public this(Skeleton skeleton)
{
var jointAnimations = new JointAnimation[skeleton.Joints.Count];
Log.EngineLogger.AssertDebug(skeleton != null);
_skeleton = skeleton..AddRef();
JointAnimations = jointAnimations;
}
// TODO: check
public ~this()
{
for(var jointAnimation in JointAnimations)
{
//jointAnimation.Dispose();
delete jointAnimation;
}
}
}
public enum InterpolationMode
{
/**
* No keyframe interpolation.
* The keyframe with the greates timestamp that is smaller than the current time will be used.
*/
Step,
/**
* Linear interpolation between the two keyframes that are closest to the current time.
*/
Linear,
/**
* Cubic spline interpolation between the two keyframes that are closest to the current time.
*/
CubicSpline
}
class JointAnimationChannel<T> where T : struct // : IDisposable
{
public float[] TimeStamps;
public T[] Values;
public InterpolationMode InterpolationMode = .Step;
public float Duration => TimeStamps[TimeStamps.Count - 1];
public this(int samples, InterpolationMode interpolationMode)
{
TimeStamps = new float[samples];
Values = new T[samples];
InterpolationMode = interpolationMode;
}
public ~this()
{
delete TimeStamps;
delete Values;
}
public (T, T, float) GetSample(float currentTime)
{
let (previousIndex, previousTime) = FindPreviousTimeStamp(currentTime);
T previousSample = Values[previousIndex];
if(InterpolationMode == .Step)
{
return (previousSample, default(T), 0f);
}
else
{
int nextIndex = previousIndex + 1;
if(nextIndex >= Values.Count)
nextIndex = Values.Count - 1;
float nextTime = TimeStamps[nextIndex];
T nextSample = Values[nextIndex];
float interpolationValue = (currentTime - previousTime) / (nextTime - previousTime);
return (previousSample, nextSample, interpolationValue);
}
}
public (int Index, float timeStamp) FindPreviousTimeStamp(float currentTime)
{
// TODO use binary search to speed things up?
int index = TimeStamps.Count - 1;
for(int i < TimeStamps.Count)
{
if(TimeStamps[i] > currentTime)
{
index = i - 1;
break;
}
}
if(index == -1)
index = 0;
// Don't return -1 but return first frame
return (index, TimeStamps[index]);
//return array.Count - 1;
}
}
class JointAnimation// : IDisposable
{
public JointAnimationChannel<Vector3> TranslationChannel ~ delete _;
public JointAnimationChannel<Quaternion> RotationChannel ~ delete _;
public JointAnimationChannel<Vector3> ScaleChannel ~ delete _;
public float Duration
{
get
{
return Math.Max(Math.Max(
TranslationChannel?.Duration ?? 0, RotationChannel?.Duration ?? 0), ScaleChannel?.Duration ?? 0);
}
}
/*
public void Dispose()
{
delete TranslationChannel;
delete RotationChannel;
delete ScaleChannel;
}
*/
public JointPose GetCurrentPose(float timeStamp)
{
JointPose result;
if(TranslationChannel != null)
{
let (previous, next, interpolationValue) = TranslationChannel.GetSample(timeStamp);
switch(TranslationChannel.InterpolationMode)
{
case .Step:
result.Translation = previous;
case .Linear:
result.Translation = Vector3.Lerp(previous, next, interpolationValue);
default:
result.Rotation = ?;
Runtime.NotImplemented();
}
}
else
{
result.Translation = .Zero;
}
if(RotationChannel != null)
{
let (previous, next, interpolationValue) = RotationChannel.GetSample(timeStamp);
switch(RotationChannel.InterpolationMode)
{
case .Step:
result.Rotation = previous;
case .Linear:
result.Rotation = Quaternion.Slerp(previous, next, interpolationValue);
default:
result.Rotation = ?;
Runtime.NotImplemented();
}
}
else
{
result.Rotation = .Identity;
}
if(ScaleChannel != null)
{
let (previous, next, interpolationValue) = ScaleChannel.GetSample(timeStamp);
//switch(ScaleChannel.InterpolationMode)
switch(InterpolationMode.Step)
{
case .Step:
result.Scale = previous;
case .Linear:
result.Scale = Vector3.Lerp(previous, next, interpolationValue);
default:
result.Rotation = ?;
Runtime.NotImplemented();
}
}
else
{
result.Scale = .One;
}
return result;
}
}
}
@@ -0,0 +1,20 @@
using GlitchyEngine.Math;
using System;
namespace GlitchyEngine.Renderer.Animation
{
public struct Joint
{
/// Converts vertices from model space to joint space
public Matrix InverseBindPose;
public String Name;
public uint8 ParentID;
}
public struct JointPose
{
public Quaternion Rotation;
public Vector3 Translation;
public Vector3 Scale;
}
}
@@ -0,0 +1,51 @@
using GlitchyEngine.Core;
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer.Animation
{
public class Skeleton : RefCounter
{
public Joint[] Joints ~ delete _;
public Joint? GetParent(Joint joint)
{
if(joint.ParentID == uint8.MaxValue)
return null;
return Joints[joint.ParentID];
}
public ~this()
{
for(var joint in Joints)
{
delete joint.Name;
}
}
}
public class SkeletonPose
{
private Skeleton _skeleton ~ _.ReleaseRef();
public JointPose[] LocalPose ~ delete _;
public Matrix[] GlobalPose ~ delete _;
public Matrix[] SkinningMatricies ~ delete _;
public Matrix3x3[] InvTransSkinningMatricies ~ delete _;
public Skeleton Skeleton => _skeleton;
public this(Skeleton skeleton)
{
_skeleton = skeleton..AddRef();
int jointCount = _skeleton.Joints.Count;
LocalPose = new JointPose[jointCount];
GlobalPose = new Matrix[jointCount];
SkinningMatricies = new Matrix[jointCount];
InvTransSkinningMatricies = new Matrix3x3[jointCount];
}
}
}
+10 -6
View File
@@ -1,5 +1,7 @@
using System;
using GlitchyEngine.Core;
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer
{
public struct BlendStateDescription
@@ -70,23 +72,25 @@ namespace GlitchyEngine.Renderer
public static readonly Self Default = .(false, false, .(.Default, .Default, .Default, .Default, .Default, .Default, .Default, .Default));
}
public class BlendState : RefCounted
public class BlendState : RefCounter
{
protected GraphicsContext _context ~ _?.ReleaseRef();
protected BlendStateDescription _desc;
public BlendStateDescription Description => _desc;
public this(GraphicsContext context, BlendStateDescription desc)
public this(BlendStateDescription desc)
{
_context = context..AddRef();
Debug.Profiler.ProfileResourceFunction!();
_desc = desc;
PlatformCreateBlendState();
}
public extern void Bind(ColorRGBA blendFactor = .White);
public ~this()
{
Debug.Profiler.ProfileResourceFunction!();
}
protected extern void PlatformCreateBlendState();
}
+5 -52
View File
@@ -1,47 +1,8 @@
using System;
using GlitchyEngine.Core;
namespace GlitchyEngine.Renderer
{
/**
* Identifies expected resource use during rendering.
* The usage directly reflects whether a resource is accessible by the CPU and/or the graphics processing unit (GPU).
*/
public enum Usage
{
/**
* A resource that requires read and write access by the GPU.
* This is likely to be the most common usage choice.
*/
Default = 0,
/**
* A resource that can only be read by the GPU. It cannot be written by the GPU, and cannot be accessed at all by the CPU.
*/
Immutable = 1,
/**
* A resource that is accessible by both the GPU (read only) and the CPU (write only).
* A dynamic resource is a good choice for a resource that will be updated by the CPU at least once per frame.
* To update a dynamic resource, use a Map method.
*/
Dynamic = 2,
/**
* A resource that supports data transfer (copy) from the GPU to the CPU.
*/
Staging = 3
}
/**
* Defines how the CPU can access a resource.
*/
public enum CPUAccessFlags
{
/// The CPU has no access to the resource.
None = 0,
/// The CPU has read access to the resource.
Read = 1,
/// The CPU has write access to the resource.
Write = 2
}
/**
* Defines how to bind a buffer to the pipeline.
*/
@@ -120,26 +81,19 @@ namespace GlitchyEngine.Renderer
}
/// Represents a buffer containing binary data on the GPU.
public class Buffer : RefCounted
public class Buffer : RefCounter
{
internal GraphicsContext _context ~ _?.ReleaseRef();
protected BufferDescription _description;
public GraphicsContext Context => _context;
public BufferDescription Description => _description;
protected this(GraphicsContext context)
{
_context = context..AddRef();
}
protected this() { }
/**
* Creates a new instance of a Buffer.
* @param description The buffer description.
*/
public this(GraphicsContext context, BufferDescription description) : this(context)
public this(BufferDescription description)
{
_description = description;
}
@@ -201,10 +155,9 @@ namespace GlitchyEngine.Renderer
/**
* Creates a new instance of a Buffer<T>
* @param context The graphics context.
* @param description Describes the Buffer. Note: description.Size is ignored, as it will always be sizeof(T).
*/
public this(GraphicsContext context, BufferDescription description) : base(context)
public this(BufferDescription description)
{
_description = description;
_description.Size = (uint32)sizeof(T);

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