65 Commits
Author SHA1 Message Date
Simon Lübeß 499ca0311c No CoreCLR... for now 2023-07-24 14:15:59 +02:00
Simon Lübeß 62c85e9e1f Start CoreClr from Beef and basic interaction 2023-07-21 22:23:31 +02:00
Simon Lübeß 8296f1c5cd Added NetHostBeef + Start of central preparation scripts 2023-07-21 19:37:50 +02:00
Simon Lübeß 457b43352a Debugging, but it doesn't work for some reason 2023-07-21 16:16:24 +02:00
Simon Lübeß cc7fb7311f Added single step for simulation and play mode
+ Added ManualStepTime to GameTime
2023-07-20 23:21:21 +02:00
Simon Lübeß 5f27b8001a Automatic script assembly reloading 2023-07-20 22:11:11 +02:00
Simon Lübeß 0cbb8a4966 Fixed C# -> Beef Type mapping 2023-07-20 17:53:32 +02:00
Simon Lübeß 79adc70f01 Gitignores and missing updates to scripts 2023-07-20 17:51:06 +02:00
Simon Lübeß 78ba016b03 Log Exceptions in thrown by Script OnUpdate-Function 2023-07-20 17:48:50 +02:00
Simon Lübeß 3e55a1614a Shader like vectors for C# Scripting 2023-07-20 17:48:28 +02:00
Simon Lübeß f0de874572 Start of generate vectors for C# 2023-07-19 18:43:26 +02:00
Simon Lübeß 6e02a04a4f Changed ScriptCore to DotNet Standard 2.0 2023-07-19 15:45:36 +02:00
Simon Lübeß cd1562f372 Basic Script reloading
+ ScriptComponent now saves script name instead of direct ScriptClass reference
+ Added ClearDictionaryAndDeleteKeys helper
+ Moved ScriptFieldType to separate file
+ _ScriptFieldMap now uses String as Key
2023-07-18 22:06:34 +02:00
Simon Lübeß e8505f6bdb Scripting: FindEntityWithName and get script instance
+ Some C# UUID stuff
+ Added some Mono functions
+ ScriptEngine:
  + ID -> ScriptInstance map in ScriptEngine
  + GetManagedInstance

+ Fixed Scene not visible after loading a new scene
  + Renamed OnViewportResize to SetViewportSize for Scene and SceneRenderer
2023-07-17 20:27:18 +02:00
Simon Lübeß 66d66d3308 Serialize script fields in "array" + more forgiving deserialization 2023-07-17 04:04:01 +02:00
Simon Lübeß 88b393157a Make sure we always initialize with a scene 2023-07-17 04:00:30 +02:00
Simon Lübeß 28155ec92c Allow opening a scene on launch
+ don't resize on load/new scene
2023-07-17 02:19:49 +02:00
Simon Lübeß 1641bafd06 Don't instantiate scripts for Editor + ScriptComponent Serialzation + Don't crash when scene loading failed 2023-07-17 01:27:58 +02:00
Simon Lübeß 76bff3a6c1 Texture importer now supports mip maps 2023-07-06 17:06:59 +02:00
Simon Lübeß a507eeb1cd Kind of fixed a memory leak 2023-07-01 12:49:17 +02:00
Simon Lübeß 77c45db122 Replaced IntX by intX, Basic AssetViewer (can view Textures, kind of) 2023-07-01 00:15:01 +02:00
Simon Lübeß e12fcaacff Added Math min/max for float 2023-06-28 22:03:43 +02:00
Simon Lübeß b31d43820a Support emissive materials 2023-06-28 19:33:39 +02:00
Simon Lübeß 334c2c0366 Search in Content Browser (and added ForEach in TreeNode) 2023-06-28 19:27:52 +02:00
Simon Lübeß bb520b43ec Added support for Cube Textures, Improved dds import (and textures in general), Texture refactoring and DX11 DeviceContext synchronization 2023-06-25 17:58:16 +02:00
Simon Lübeß 46693666aa Fixed current directories context menu not opening
i.e. when right clicking the background of the current directory
2023-06-25 03:20:35 +02:00
Simon Lübeß 9e1f648aa0 Transitioned from VectorX to floatX
And some trying to update Rigidbody when transform changes
2023-06-25 02:47:33 +02:00
Simon Lübeß 5f5da2ed35 Moved vector type files 2023-06-25 01:11:06 +02:00
Simon Lübeß fa2277cd99 Start of shader like vectors + added 16 bit float 2023-06-24 23:45:56 +02:00
Simon Lübeß 54936d5744 Fixed crash when viewport size is negative 2023-06-24 13:07:07 +02:00
Simon Lübeß 04d69717a3 Automatically pull and build mono once + .gitignore 2023-06-18 21:03:59 +02:00
Simon Lübeß 3becc1e25b fixed Settings-File not being created and start of Recent-Projects-Menu 2023-06-18 16:05:03 +02:00
Simon Lübeß e8e6207bc1 Merge branch 'scripting' of github.com:aharabada/glitchy-engine-beef into scripting
# Conflicts:
#	GlitchyEngine/src/Scripting/ScriptClass.bf
#	GlitchyEngine/src/Scripting/ScriptEngine.bf
#	GlitchyEngine/src/Scripting/ScriptInstance.bf
#	GlitchyEngine/src/World/Scene.bf
#	GlitchyEngineHelper/src/Mono/Mono.bf
2023-06-18 14:33:28 +02:00
Simon Lübeß f4c5bf9c2d Added binding for mono_field_get_value_object 2023-06-18 14:24:26 +02:00
Simon Lübeß f4607a6210 Script default values after init, Fix memory leak, Copy Script values on Play 2023-05-31 14:38:07 +02:00
Simon Lübeß 6a59deb9e2 Weiß ich nicht, ist Monate her... 2023-05-30 16:54:57 +02:00
Simon Lübeß ac1817fe33 Weiß ich nicht, ist Monate her... 2023-05-30 16:52:27 +02:00
Simon Lübeß 5855b13873 Wer weiß, was ich alles so gemacht hatte...
- Fixed crash caused by early ScriptEngine shutdown
2023-05-30 16:50:22 +02:00
Simon Lübeß ef5e4ba64f Fixed ScriptClass leaking 2023-03-30 15:13:21 +02:00
Simon Lübeß 4fd9a924a0 Added basic component access in Scripts 2023-03-29 16:32:46 +02:00
Simon Lübeß e60d74fccd Basic entity scripting 2023-03-29 13:33:34 +02:00
Simon Lübeß 5e5134a607 Added Mono for C# scripting 2023-03-26 13:11:44 +02:00
Simon Lübeß fd6826785a Updated submodules again? 2023-03-25 13:24:50 +01:00
Simon Lübeß 43bcf01299 Merge branch 'asset_handle' into main 2023-03-25 13:20:40 +01:00
Simon Lübeß 81ae5f1135 Updated submodules 2023-03-25 13:19:59 +01:00
Simon Lübeß 0ee2f5fa9e Merge branch 'asset_handle' into main 2023-03-25 13:15:56 +01:00
Simon Lübeß b76d4cd558 Merge remote-tracking branch 'origin/asset_handle' into asset_handle 2023-03-25 13:04:52 +01:00
Simon Lübeß 5893cfb74a Refactor 2023-03-25 13:04:02 +01:00
Simon Lübeß ed952155b6 Refactor 2023-03-25 00:41:34 +01:00
Simon Lübeß d54cdf43ee Simulation mode and pause/resume 2023-03-24 23:50:46 +01:00
Simon Lübeß f16fbc3984 Separated window for game and editor 2023-03-24 22:48:55 +01:00
Simon Lübeß 184480243f Collider rendering 2023-03-24 19:13:32 +01:00
Simon Lübeß fc1875ed96 Added Line batch rendering 2023-03-24 12:54:46 +01:00
Simon Lübeß 5733166b74 Fixed uint render targets always being cleared to zero 2023-03-19 12:49:12 +01:00
Simon Lübeß 2ba4b68e09 Split up sprite and circle renderer component 2023-03-19 12:19:22 +01:00
Simon Lübeß edcec02e94 Fixed AssetHandle serialization
- Serialize MeshComponents
- Fixed handling renamed files
2023-03-19 01:04:32 +01:00
Simon Lübeß c164aeecaa Copy scene when entering play mode 2023-03-18 23:30:57 +01:00
Simon Lübeß 793aa40975 Add up button und split scrolling in content browser
- Add Parent to TreeNode
- Start of Rename handling
2023-03-18 19:59:56 +01:00
Simon Lübeß 50714976e9 Start of async asset loading
- Refactored content manager and asset loaders
- Asset placeholder
- Error asset
2023-03-18 18:59:30 +01:00
Simon Lübeß 7b6662cbc4 Fixed Textures in Materials 2023-03-16 23:32:02 +01:00
Simon Lübeß cfa046d2ea Refactored EditorContentManager 2023-03-16 23:31:19 +01:00
Simon Lübeß b22f73a156 Handles barely work now, mostly no crashes 2023-03-16 17:57:23 +01:00
Simon Lübeß c3fb9bbd5d More asset handles 2023-03-13 20:35:36 +01:00
Simon Lübeß c22d122921 Start of asset handles 2023-03-13 20:35:30 +01:00
Simon Lübeß ad14c9f6ed AssetHandle Code snippets for design reference 2023-03-13 15:56:31 +01:00
245 changed files with 24606 additions and 4779 deletions
+3
View File
@@ -5,3 +5,6 @@ build/
recovery/ recovery/
vendor/directx vendor/directx
imgui.ini imgui.ini
GlitchyEditor/resources/scripts/ScriptCore.dll
GlitchyEditor/resources/scripts/ScriptCore.pdb
GlitchyEditor/settings.bon
+3
View File
@@ -31,3 +31,6 @@
[submodule "GlitchyEngine/vendor/Beef.Linq"] [submodule "GlitchyEngine/vendor/Beef.Linq"]
path = GlitchyEngine/vendor/Beef.Linq path = GlitchyEngine/vendor/Beef.Linq
url = https://github.com/aharabada/Beef.Linq.git url = https://github.com/aharabada/Beef.Linq.git
[submodule "GlitchyEngine/vendor/NetHostBeef"]
path = GlitchyEngine/vendor/NetHostBeef
url = https://github.com/aharabada/NetHostBeef.git
+3 -2
View File
@@ -1,6 +1,7 @@
FileVersion = 1 FileVersion = 1
Projects = {Sandbox = {Path = "Sandbox"}, GlitchyEngine = {Path = "GlitchyEngine"}, GlitchLog = {Path = "GlitchLog"}, DirectX = {Path = "GlitchyEngine/vendor/directx/DirectX"}, LodePng = {Path = "vendor/lodepng-beef/lodepng-beef"}, FreeType = {Path = "GlitchyEngine/vendor/freetype"}, cgltf-beef = {Path = "GlitchyEngine/vendor/gltf/cgltf-beef"}, GlitchyEditor = {Path = "GlitchyEditor"}, msdfgen-beef = {Path = "GlitchyEngine/vendor/msdfgen/msdfgen-beef"}, ImGui = {Path = "GlitchyEngine/vendor/imgui/ImGui"}, ImGuiImplDX11 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplDX11"}, ImGuiImplWin32 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplWin32"}, ImGuizmo = {Path = "GlitchyEngine/vendor/imgui/ImGuizmo"}, GlitchyEngineHelper = {Path = "GlitchyEngineHelper"}, bon = {Path = "GlitchyEngine/vendor/bon"}, box2d-beef = {Path = "GlitchyEngine/vendor/box2D"}, "Beef.Linq" = {Path = "GlitchyEngine/vendor/Beef.Linq/src"}} Projects = {Sandbox = {Path = "Sandbox"}, GlitchyEngine = {Path = "GlitchyEngine"}, GlitchLog = {Path = "GlitchLog"}, DirectX = {Path = "GlitchyEngine/vendor/directx/DirectX"}, LodePng = {Path = "vendor/lodepng-beef/lodepng-beef"}, FreeType = {Path = "GlitchyEngine/vendor/freetype"}, cgltf-beef = {Path = "GlitchyEngine/vendor/gltf/cgltf-beef"}, GlitchyEditor = {Path = "GlitchyEditor"}, msdfgen-beef = {Path = "GlitchyEngine/vendor/msdfgen/msdfgen-beef"}, ImGui = {Path = "GlitchyEngine/vendor/imgui/ImGui"}, ImGuiImplDX11 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplDX11"}, ImGuiImplWin32 = {Path = "GlitchyEngine/vendor/imgui/ImGuiImplWin32"}, ImGuizmo = {Path = "GlitchyEngine/vendor/imgui/ImGuizmo"}, GlitchyEngineHelper = {Path = "GlitchyEngineHelper"}, bon = {Path = "GlitchyEngine/vendor/bon"}, box2d-beef = {Path = "GlitchyEngine/vendor/box2D"}, "Beef.Linq" = {Path = "GlitchyEngine/vendor/Beef.Linq/src"}, ScriptCore = {Path = "ScriptCore"}, NetHostBeef = {Path = "GlitchyEngine/vendor/NetHostBeef/NetHostBeef"}}
WorkspaceFolders = {GlitchyEngine = ["GlitchyEngine", "GlitchLog", "GlitchyEngineHelper"], "GlitchyEngine/Dependencies" = ["cgltf-beef", "DirectX", "FreeType", "ImGui", "ImGuiImplDX11", "ImGuiImplWin32", "ImGuizmo", "LodePng", "msdfgen-beef", "bon", "box2d-beef", "Beef.Linq"]} Unlocked = ["corlib"]
WorkspaceFolders = {GlitchyEngine = ["GlitchyEngine", "GlitchLog", "GlitchyEngineHelper", "ScriptCore"], "GlitchyEngine/Dependencies" = ["cgltf-beef", "DirectX", "FreeType", "ImGui", "ImGuiImplDX11", "ImGuiImplWin32", "ImGuizmo", "LodePng", "msdfgen-beef", "bon", "box2d-beef", "Beef.Linq", "NetHostBeef"]}
[Workspace] [Workspace]
StartupProject = "GlitchyEditor" StartupProject = "GlitchyEditor"
@@ -0,0 +1,92 @@
///
/// Loading and usage of an asset.
///
// Shows the usage of the Content Manager.
// An asset is loaded with Content.LoadAsset(AssetName)
// Loading an asset returns an AssetHandle.
// Usually you want to hold on to the AssetHandle.
// If you need the actual asset you can use Content.GetAsset<AssetType>(AssetHandle) to retrieve the actual asset.
/*
* Only hold on to AssetHandle.
* Do NOT hold actual Assets! The Asset might get invalidated
* (e.g. reloading after the source-file changed).
*/
AssetHandle _lineEffect;
void Start()
{
/*
* Loading an asset returns a handle to that asset.
*/
_lineEffect = Content.LoadAsset("Shaders\\LineEffect.hlsl");
}
void Update()
{
// Use case 1:
{
// Retrieve the actual asset. Note that this Asset is only guaranteed to persist
// for the current frame.
Effect fx = Content.GetAsset<Effect>(_lineEffect);
// Note: Assets are ref counted, however since they are not inteded to be held GetAsset
// does NOT increment the reference counter so you usually should NOT decrement it.
// The exception is when you manually hold on to an asset (see Example below)
// Use asset
fx.Variables["ViewProjection"].SetData(viewProjection * transform);
fx.Variables["Color"].SetData(color);
fx.ApplyChanges();
fx.Bind();
}
// Use case 2:
{
// Retrieve the actual asset.
Effect fx = _lineEffect.Get<Effect>();
// ...
// Same as case 1
}
}
///
/// Holding on to an actual asset.
///
// Shows the usage of the Content Manager when you hold a reference to the actual Asset instead of the handle.
// This should usually not be done. Eventhough it is technically fine, it has the implication, that if the
// content manager decides to reload the asset the changes will not be reflected in the held reference
// (which they would if you used GetAsset before every usage.)
// Also the content manager can't unload the asset because the reference counter wouldn't be zero.
// Though there currently is no way for the content manager to unload an asset unless it is specifically told to do so.
/*
* Field to hold the asset.
*/
Effect _lineEffect;
void Start()
{
// Load the Asset.
AssetHandle handle = Content.LoadAsset("Shaders\\LineEffect.hlsl");
// Get the actual asset.
_lineEffect = Content.GetAsset<Effect>(handle); // Alternative: _lineEffect = handle.Get<Effect>();
// Increment the reference counter so that the asset doesn't get unloaded.
_lineEffect.AddRef();
}
void Destroy()
{
// Make sure to release the asset once you don't need it anymore. Otherwise it will leak.
_lineEffect.Release();
}
void Update()
{
_lineEffect.Variables["ViewProjection"].SetData(viewProjection * transform);
_lineEffect.Variables["Color"].SetData(color);
_lineEffect.ApplyChanges();
_lineEffect.Bind();
}
@@ -0,0 +1,44 @@
///
/// This shows the usage of a smart/typed Asset Handle.
///
// In the basic usage-patterns the user has two options:
// Either he/she holds a handle and has to query for the actual asset every time
// or he/she hold the actual asset and loses reloading capabilities.
// The idea of the auto asset is to provide a way that can provide both.
AssetHandle<Effect> _lineEffect;
void Start()
{
/*
* Loading an asset returns a handle to that asset.
* The asset handle is implicitly converted to a smart AssetHandle.
*/
_lineEffect = Content.LoadAsset("Shaders\\LineEffect.hlsl");
}
void Update()
{
// Use case 1 (Convenience)
{
_lineEffect.Variables["ViewProjection"].SetData(viewProjection * transform);
_lineEffect.Variables["Color"].SetData(color);
_lineEffect.ApplyChanges();
_lineEffect.Bind();
}
// Use case 2 (Technically more efficient)
{
Effect fx = _lineEffect.Get();
fx.Variables["ViewProjection"].SetData(viewProjection * transform);
fx.Variables["Color"].SetData(color);
fx.ApplyChanges();
fx.Bind();
}
}
//
// Auto Assets use comp time to expose all the fields, Methods and properties of the actual asset.
// They will automatically check if the asset for the handle changed and update accordingly.
// This means it is more convenient to use than both basic patterns and has all benefits of both.
//
+3
View File
@@ -0,0 +1,3 @@
# Design Code Examples
This directory (and its subdirectories) contains non functional code snippets showing the intended usage of certain systems of the engine.
+3
View File
@@ -5,3 +5,6 @@ Dependencies = {corlib = "*", GlitchLog = "*", GlitchyEngine = "*"}
Name = "GlitchyEditor" Name = "GlitchyEditor"
TargetType = "BeefGUIApplication" TargetType = "BeefGUIApplication"
StartupObject = "GlitchyEngine.Program" StartupObject = "GlitchyEngine.Program"
[Configs.Debug.Win64]
DebugCommandArguments = "\"content\\Scenes\\physics2D.scene\""
@@ -0,0 +1,398 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GlitchyEngine;
using GlitchyEngine.Math;
using static GlitchyEngine.Math.Math;
namespace Sandbox;
public class Camera : Entity
{
public float DistanceFromPlayer = 5.0f;
public float DontFollowRadius = 3.0f;
void OnCreate()
{
Log.Info("Camera Create");
}
public void OnUpdate(float deltaTime)
{
Log.Info($"Camera Update {deltaTime}");
//Entity player = FindEntityWithName("Player");
//if (player != null)
//{
// float2 playerPosition = player.Transform.Translation.XY;
// float2 cameraPosition = Transform.Translation.XY;
// float2 distanceVector = playerPosition - cameraPosition;
// float distance = length(distanceVector);
// float2 neededMovement = float2.Zero;
// if (distance > DontFollowRadius)
// {
// neededMovement = distanceVector - (distanceVector / distance) * DontFollowRadius;
// }
// Transform.Translation = new float3(cameraPosition + neededMovement, DistanceFromPlayer);
//}
//else
//{
// Log.Error("Player not found!");
//}
}
}
@@ -0,0 +1,126 @@
using System;
using GlitchyEngine;
using GlitchyEngine.Editor;
using GlitchyEngine.Math;
using static GlitchyEngine.Math.Math;
namespace Sandbox
{
public enum MyEnum
{
Yes,
No,
Maybe
}
public struct MyStruct
{
public float TheFloat;
public float2 TheVector;
public MyEnum TheEnum;
}
class MyTestEntity : Entity
{
//[ShowInEditor]
RigidBody2D _rigidBody;
public bool Bo;
public byte By;
public ushort Us;
public uint Ui;
public ulong Ul;
public sbyte Sb;
public short Sh;
public int In;
public long Lo;
public float Fl;
public double Do;
public float2 V2;
public float3 V3;
public float4 V4;
public Entity TheEntity;
public float JumpForce = 2000;
[ShowInEditor] float MoveForce = 1000;
[ShowInEditor] private int MyNumber = 1337;
[ShowInEditor] public double MyDouble = 1000.0f;
public MyStruct AStruct;
public Camera Camera;
/// <summary>
/// Called after the script component was created. (The entity might not be fully created yet)
/// </summary>
void OnCreate()
{
Log.Info($"Create! {UUID}");
Log.Info($"Jump Force: {JumpForce}");
//_rigidBody ??= GetComponent<RigidBody2D>() ?? AddComponent<RigidBody2D>();
//Camera = FindEntityWithName("Camera").As<Camera>();
//if (Camera == null)
//{
// Log.Error("Camera not found.");
//}
}
/// <summary>
/// Called every frame.
/// </summary>
/// <param name="deltaTime"></param>
void OnUpdate(float deltaTime)
{
Log.Info($"On update: {deltaTime}");
//float2 force = float2.Zero;
//if (Input.IsKeyPressed(Key.A))
//{
// force.X -= MoveForce * deltaTime;
//}
//if (Input.IsKeyPressed(Key.D))
//{
// force.X += MoveForce * deltaTime;
//}
//if (Input.IsKeyPressed(Key.Q))
// Camera.DistanceFromPlayer -= deltaTime;
//if (Input.IsKeyPressed(Key.E))
// Camera.DistanceFromPlayer += deltaTime;
//if (Input.IsKeyPressing(Key.Space))
//{
// force.Y += JumpForce;
//}
//_rigidBody.ApplyForceToCenter(force);
//if (Input.IsMouseButtonReleasing(MouseButton.MiddleButton))
//{
// Log.Info($"Ouha! {MyNumber}");
// Physics2D.Gravity *= new float2(1, -1);
//}
}
/// <summary>
/// Called before the component is being destroyed.
/// </summary>
void OnDestroy()
{
Log.Trace("Destroy");
}
}
}
@@ -0,0 +1,11 @@
{
"profiles": {
"Start Editor": {
"commandName": "Executable",
"executablePath": "D:\\Development\\Projects\\Beef\\GlitchyEngine\\build\\Debug_Win64\\GlitchyEditor\\GlitchyEditor.exe",
"commandLineArgs": "\"content\\Scenes\\physics2D.scene\"",
"workingDirectory": "D:\\Development\\Projects\\Beef\\GlitchyEngine\\GlitchyEditor",
"nativeDebugging": true
}
}
}
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7A70819C-5317-402A-8553-95C5A001E370}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Sandbox</RootNamespace>
<AssemblyName>Sandbox</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>default</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Camera.cs" />
<Compile Include="MyTestEntity.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\ScriptCore\ScriptCore.csproj">
<Project>{6222c226-fc78-498c-80ba-5cf10ac9123c}</Project>
<Name>ScriptCore</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<LangVersion>latest</LangVersion>
<OutputPath>bin</OutputPath>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<EnableDynamicLoading>true</EnableDynamicLoading>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\ScriptCore\ScriptCore.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,37 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33205.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sandbox", "Sandbox.csproj", "{7A70819C-5317-402A-8553-95C5A001E370}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptCore", "..\..\..\..\ScriptCore\ScriptCore.csproj", "{6222C226-FC78-498C-80BA-5CF10AC9123C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptCoreGenerator", "..\..\..\..\ScriptCoreGenerator\ScriptCoreGenerator.csproj", "{5D662996-1E1E-4170-A08A-B26CDB63A0DE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7A70819C-5317-402A-8553-95C5A001E370}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7A70819C-5317-402A-8553-95C5A001E370}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A70819C-5317-402A-8553-95C5A001E370}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A70819C-5317-402A-8553-95C5A001E370}.Release|Any CPU.Build.0 = Release|Any CPU
{6222C226-FC78-498C-80BA-5CF10AC9123C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6222C226-FC78-498C-80BA-5CF10AC9123C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6222C226-FC78-498C-80BA-5CF10AC9123C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6222C226-FC78-498C-80BA-5CF10AC9123C}.Release|Any CPU.Build.0 = Release|Any CPU
{5D662996-1E1E-4170-A08A-B26CDB63A0DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5D662996-1E1E-4170-A08A-B26CDB63A0DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D662996-1E1E-4170-A08A-B26CDB63A0DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D662996-1E1E-4170-A08A-B26CDB63A0DE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B5D43D8D-D80B-41AA-BE40-1DC507D87148}
EndGlobalSection
EndGlobal
@@ -0,0 +1,29 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using GlitchyEngine;
namespace Sandbox;
public static class TestClass
{
[UnmanagedCallersOnly]
public static void TestFunction()
{
Console.WriteLine("Hello from the second Assembly!");
}
public static void InternalTest()
{
Console.WriteLine("Ja, ich existiere in der Tat");
Log.Info("Yeah!");
//Console.WriteLine("Und nun bin ich anders");
}
public static int Test(IntPtr arg, int sizeofArg)
{
Console.WriteLine($"YUPPYPLASDPASDOIUZASD");
return 7;
}
}
+91 -21
View File
@@ -30,10 +30,10 @@
} }
}, },
MeshComponent = { MeshComponent = {
Mesh = "Models\\sphere.glb" Mesh = "Models/sphere.glb"
}, },
MeshRendererComponent = { MeshRendererComponent = {
Material = "Textures\\TestMaterial.mat" Material = "Textures/TestMaterial.mat"
} }
}, },
{ {
@@ -48,20 +48,20 @@
Z = -4 Z = -4
}, },
Rotation = { Rotation = {
X = 0.614328, X = 0.614328027,
Y = 0.239776, Y = 0.239776045,
Z = 0.031567, Z = 0.0315669999,
W = 0.751074 W = 0.751073837
}, },
Scale = { Scale = {
X = 0.999999, X = 0.999998868,
Y = 1, Y = 1,
Z = 1.000001 Z = 1.00000095
}, },
EditorEulerRotation = { EditorEulerRotation = {
X = 1.308998, X = 1.30899811,
Y = 0.349066, Y = 0.349065989,
Z = 0.349066 Z = 0.349065989
} }
}, },
LightComponent = { LightComponent = {
@@ -69,8 +69,8 @@
Illuminance = 10, Illuminance = 10,
Color = { Color = {
R = 1, R = 1,
G = 0.991772, G = 0.991771996,
B = 0.740862 B = 0.74086225
} }
} }
}, },
@@ -103,10 +103,10 @@
} }
}, },
MeshComponent = { MeshComponent = {
Mesh = "Models\\plane.glb" Mesh = "Models/plane.glb"
}, },
MeshRendererComponent = { MeshRendererComponent = {
Material = "Textures\\TestMaterial.mat" Material = "Textures/TestMaterial.mat"
} }
}, },
{ {
@@ -121,10 +121,10 @@
Z = -5 Z = -5
}, },
Rotation = { Rotation = {
X = 0.21644, X = 0.216440007,
Y = 0, Y = 0,
Z = 0, Z = 0,
W = 0.976296 W = 0.976296008
}, },
Scale = { Scale = {
X = 1, X = 1,
@@ -132,7 +132,7 @@
Z = 1 Z = 1
}, },
EditorEulerRotation = { EditorEulerRotation = {
X = 0.436332, X = 0.436332017,
Y = 0, Y = 0,
Z = 0 Z = 0
} }
@@ -140,15 +140,85 @@
CameraComponent = { CameraComponent = {
Primary = true, Primary = true,
ProjectionType = .InfinitePerspective, ProjectionType = .InfinitePerspective,
PerspectiveFovY = 1.047198, PerspectiveFovY = 1.30899692,
PerspectiveNearPlane = 0.1, PerspectiveNearPlane = 0.100000001,
PerspectiveFarPlane = 10000, PerspectiveFarPlane = 10000,
OrthographicHeight = 10, OrthographicHeight = 10,
OrthographicNearPlane = 0, OrthographicNearPlane = 0,
OrthographicFarPlane = 10, OrthographicFarPlane = 10,
AspectRatio = 2.156692, AspectRatio = 2.19888878,
FixedAspectRatio = false FixedAspectRatio = false
} }
},
{
Id = 420718992779301759,
NameComponent = {
Name = "Entity"
},
TransformComponent = {
Position = {
X = 3,
Y = 0.501076996,
Z = -0.669378757
},
Rotation = {
X = 0.18301262,
Y = -0.683013558,
Z = 0.683012128,
W = 0.183013007
},
Scale = {
X = 1,
Y = 1,
Z = 1
},
EditorEulerRotation = {
X = 1.57079446,
Y = -2.61799312,
Z = 0
}
},
MeshComponent = {
Mesh = "Models/plane.glb"
},
MeshRendererComponent = {
Material = ""
}
},
{
Id = 8080288871271083510,
NameComponent = {
Name = "Rocket"
},
TransformComponent = {
Position = {
X = 2.37353587,
Y = 0.629615188,
Z = -1.34831977
},
Rotation = {
X = 0.270598024,
Y = -0.65328145,
Z = 0.65328151,
W = 0.270598054
},
Scale = {
X = 0.999999881,
Y = 0.999999821,
Z = 0.999999702
},
EditorEulerRotation = {
X = 1.57079637,
Y = -2.3561945,
Z = 8.94069743e-08
}
},
MeshComponent = {
Mesh = "Models/plane.glb"
},
MeshRendererComponent = {
Material = "Textures/RocketMaterial.mat"
}
} }
] ]
} }
+265 -40
View File
@@ -6,15 +6,14 @@
NameComponent = { NameComponent = {
Name = "Quad" Name = "Quad"
}, },
SpriterRendererComponent = { SpriteRendererComponent = {
Color = { Color = {
R = 0, R = 0,
G = 0.551178, G = 0.55117774,
B = 0.328787, B = 0.32878688,
A = 1 A = 1
}, },
IsCircle = false, Sprite = "Textures/TestMat/rustediron2_albedo.png",
Sprite = "\\Textures\\TestMat\\rustediron2_albedo.png",
UvTransform = { UvTransform = {
X = 0, X = 0,
Y = 0, Y = 0,
@@ -24,7 +23,7 @@
}, },
TransformComponent = { TransformComponent = {
Position = { Position = {
X = 0, X = -0.72789681,
Y = 2, Y = 2,
Z = 0 Z = 0
}, },
@@ -69,15 +68,14 @@
NameComponent = { NameComponent = {
Name = "Floor" Name = "Floor"
}, },
SpriterRendererComponent = { SpriteRendererComponent = {
Color = { Color = {
R = 1, R = 1,
G = 0.941886, G = 0.941886961,
B = 0.401485, B = 0.401484549,
A = 1 A = 1
}, },
IsCircle = false, Sprite = "",
Sprite = null,
UvTransform = { UvTransform = {
X = 0, X = 0,
Y = 0, Y = 0,
@@ -158,30 +156,36 @@
CameraComponent = { CameraComponent = {
Primary = true, Primary = true,
ProjectionType = .InfinitePerspective, ProjectionType = .InfinitePerspective,
PerspectiveFovY = 1.308997, PerspectiveFovY = 1.30899692,
PerspectiveNearPlane = 0.1, PerspectiveNearPlane = 0.100000001,
PerspectiveFarPlane = 10000, PerspectiveFarPlane = 10000,
OrthographicHeight = 10, OrthographicHeight = 10,
OrthographicNearPlane = 0, OrthographicNearPlane = 0,
OrthographicFarPlane = 10, OrthographicFarPlane = 10,
AspectRatio = 2.969697, AspectRatio = 0.884925187,
FixedAspectRatio = false FixedAspectRatio = false
} },
ScriptComponent = {
ScriptClass = "Sandbox.Camera",
Fields = [
DistanceFromPlayer = (Float)-7,
DontFollowRadius = (Float)3
] }
}, },
{ {
Id = 15710354273720487680, Id = 15710354273720487680,
NameComponent = { NameComponent = {
Name = "Circle" Name = "Player"
}, },
SpriterRendererComponent = { CircleRendererComponent = {
Color = { Color = {
R = 1, R = 1,
G = 1, G = 0,
B = 1, B = 0,
A = 1 A = 1
}, },
IsCircle = true, InnerRadius = 0.300000012,
Sprite = "\\Textures\\rocket.png", Sprite = "",
UvTransform = { UvTransform = {
X = 0, X = 0,
Y = 0, Y = 0,
@@ -191,8 +195,8 @@
}, },
TransformComponent = { TransformComponent = {
Position = { Position = {
X = -0.121203, X = -0.121203184,
Y = 0.66016, Y = 0.660160959,
Z = 0 Z = 0
}, },
Rotation = { Rotation = {
@@ -226,7 +230,42 @@
Friction = 0.5, Friction = 0.5,
Restitution = 0, Restitution = 0,
RestitutionThreshold = 0.5 RestitutionThreshold = 0.5
} },
ScriptComponent = {
ScriptClass = "Sandbox.MyTestEntity",
Fields = [
Bo = (Bool)false,
By = (Byte)0,
Us = (UShort)57,
Ui = (UInt)54,
Ul = (ULong)53,
Sb = (SByte)-128,
Sh = (Short)95,
In = (Int)-149,
Lo = (Long)82,
Fl = (Float)-98.122963,
Do = (Double)118,
V2 = (float2){
X = 11,
Y = 12
},
V3 = (float3){
X = 13,
Y = 14,
Z = 15
},
V4 = (float4){
X = 14,
Y = 17,
Z = 18,
W = 19
},
TheEntity = (Entity)0,
JumpForce = (Float)200,
MoveForce = (Float)148,
MyNumber = (Int)22,
MyDouble = (Double)23
] }
}, },
{ {
Id = 935640822766280888, Id = 935640822766280888,
@@ -235,34 +274,34 @@
}, },
TransformComponent = { TransformComponent = {
Position = { Position = {
X = 3.883276, X = 3.88327599,
Y = 0, Y = 0,
Z = -0.808795 Z = -0.808795214
}, },
Rotation = { Rotation = {
X = 0.497987, X = 0.497987002,
Y = -0.103421, Y = -0.103420995,
Z = 0.15538, Z = 0.155380026,
W = 0.846859 W = 0.846859217
}, },
Scale = { Scale = {
X = 0.999998, X = 0.999997795,
Y = 1, Y = 1,
Z = 1 Z = 1
}, },
EditorEulerRotation = { EditorEulerRotation = {
X = 1.090894, X = 1.09089541,
Y = -0.340794, Y = -0.340793997,
Z = 0.160858 Z = 0.160857871
} }
}, },
LightComponent = { LightComponent = {
LightType = .Directional, LightType = .Directional,
Illuminance = 12.9, Illuminance = 12.8999996,
Color = { Color = {
R = 0.985467, R = 0.985467017,
G = 1, G = 1,
B = 0.569979 B = 0.569978058
} }
} }
}, },
@@ -275,7 +314,7 @@
Position = { Position = {
X = 0, X = 0,
Y = 0, Y = 0,
Z = -1.523338 Z = 2.56930423
}, },
Rotation = { Rotation = {
X = 0, X = 0,
@@ -295,10 +334,196 @@
} }
}, },
MeshComponent = { MeshComponent = {
Mesh = "\\Models\\sphere.glb" Mesh = "Models/sphere.glb"
}, },
MeshRendererComponent = { MeshRendererComponent = {
Material = "\\Textures\\TestMaterial.mat" Material = "Textures/TestMaterial.mat"
}
},
{
Id = 2241063100006479259,
NameComponent = {
Name = "Floor 2"
},
SpriteRendererComponent = {
Color = {
R = 0.425658971,
G = 0.855207562,
B = 0.0558161102,
A = 1
},
Sprite = "",
UvTransform = {
X = 0,
Y = 0,
Z = 1,
W = 1
}
},
TransformComponent = {
Position = {
X = 6.74610233,
Y = -2.03225899,
Z = 0
},
Rotation = {
X = 0,
Y = 0,
Z = -0.331792623,
W = 0.943352342
},
Scale = {
X = 5.29557419,
Y = 1,
Z = 1
},
EditorEulerRotation = {
X = 0,
Y = 0,
Z = -0.676406503
}
},
Rigidbody2D = {
BodyType = .Static,
FixedRotation = false
},
BoxCollider2D = {
Offset = {
X = 0,
Y = 0
},
Size = {
X = 0.5,
Y = 0.5
},
Density = 1,
Friction = 0.5,
Restitution = 0,
RestitutionThreshold = 0.5
}
},
{
Id = 5126705881069187728,
NameComponent = {
Name = "Floor 3"
},
SpriteRendererComponent = {
Color = {
R = 0.666116953,
G = 0.0600316115,
B = 1,
A = 1
},
Sprite = "",
UvTransform = {
X = 0,
Y = 0,
Z = 1,
W = 1
}
},
TransformComponent = {
Position = {
X = 15.2321301,
Y = -1.98709249,
Z = 0
},
Rotation = {
X = 0,
Y = 0,
Z = 0.134986058,
W = 0.990847468
},
Scale = {
X = 13.9764566,
Y = 1,
Z = 1
},
EditorEulerRotation = {
X = 0,
Y = 0,
Z = 0.270798802
}
},
Rigidbody2D = {
BodyType = .Static,
FixedRotation = false
},
BoxCollider2D = {
Offset = {
X = 0,
Y = 0
},
Size = {
X = 0.5,
Y = 0.5
},
Density = 1,
Friction = 0.5,
Restitution = 0.800000012,
RestitutionThreshold = 0.5
}
},
{
Id = 6861260438693901700,
NameComponent = {
Name = "Floor 4"
},
SpriteRendererComponent = {
Color = {
R = 0.176544324,
G = 0.666713238,
B = 0.687030852,
A = 1
},
Sprite = "",
UvTransform = {
X = 0,
Y = 0,
Z = 1,
W = 1
}
},
TransformComponent = {
Position = {
X = -6.27842855,
Y = 2.62550211,
Z = 0
},
Rotation = {
X = 0,
Y = 0,
Z = 0.187087834,
W = 0.982343197
},
Scale = {
X = 0.999999523,
Y = 6.49634314,
Z = 1
},
EditorEulerRotation = {
X = 0,
Y = 0,
Z = 0.376393586
}
},
Rigidbody2D = {
BodyType = .Static,
FixedRotation = false
},
BoxCollider2D = {
Offset = {
X = 0,
Y = 0
},
Size = {
X = 0.5,
Y = 0.5
},
Density = 1,
Friction = 0.5,
Restitution = 0.899999976,
RestitutionThreshold = 0.5
} }
} }
] ]
@@ -0,0 +1,59 @@
#pragma EngineBuffer[ Name = "SceneConstants"; Binding = "Scene" ]
cbuffer SceneConstants : register(b0)
{
float4x4 ViewProjection;
}
#pragma EngineBuffer[ Name = "ObjectConstants"; Binding = "Object" ]
cbuffer ObjectConstants : register(b1)
{
float4x4 Transform;
/**
* \brief Inverted and transposed transform matrix.
* \remarks This matrix is used in order to correctly transform normal vectors.
*/
float4x3 Transform_InvT;
#ifdef OutputEntityId
uint EntityId;
#endif
}
/**
* \brief Pixel Shader output for lit surfaces.
*/
struct PS_OUT_Lit
{
/** RGB: Albedo A: Transparency */
float4 Albedo : SV_TARGET0;
/** RG: TextureNormal.XY | BA: GeoNrm.XY */
float4 Normal : SV_TARGET1;
/** R: GeoNrm.Z | GBA: GeoTan.XYZ */
float4 Tangent : SV_TARGET2;
/** RGB: world position XYZ | A: 1.0 */
float4 Position : SV_TARGET3;
/** RGB: emissive light and color | A: unused */
float4 Emissive : SV_TARGET4;
/** R: Metallicity | G: Roughness | B: Ambient | A: Unused */
float4 Material : SV_TARGET5;
#ifdef OutputEntityId
/** Needed for editor picking. Simply pass through the EntityId. */
uint EntityId : SV_TARGET6;
#endif
};
/**
* \brief Pixel Shader output for unlit surfaces.
*/
struct PS_OUT_Unlit
{
/** RGB: Color A: Transparency */
float4 Color : SV_TARGET0;
/** RGB: emissive light and color | A: unused */
float4 Emissive : SV_TARGET1;
/** RGB: world position XYZ | A: 1.0 */
float4 Position : SV_TARGET2;
#ifdef OutputEntityId
uint EntityId : SV_TARGET5;
#endif
};
@@ -0,0 +1,4 @@
{
AssetLoader = "EffectAssetLoader",
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */
}
@@ -0,0 +1,111 @@
Texture2DArray Texture : register(t0);
SamplerState Sampler : register(s0);
Texture2DArray<int4> IntTexture : register(t1);
SamplerState IntSampler : register(s1);
Texture2DArray<uint4> UIntTexture : register(t2);
SamplerState UIntSampler : register(s2);
#define SWIZZLE_NONE 0
#define SWIZZLE_R 1
#define SWIZZLE_G 2
#define SWIZZLE_B 3
#define SWIZZLE_A 4
cbuffer Constants
{
float ColorOffset = 0.5;
float AlphaOffset = 0.0;
float ColorScale = 0.5;
float AlphaScale = 1.0;
float MipLevel = 0.0;
float ArraySlice = 0.0;
// 0: Float | 1: Int | 2: Uint
int Mode = 0;
int4 Swizzle = int4(SWIZZLE_R, SWIZZLE_G, SWIZZLE_B, SWIZZLE_A);
float2 Texels;
float4x4 WorldViewProjection;
}
struct VS_Input
{
float2 Position : POSITION;
float2 Texcoord : TEXCOORD0;
};
struct PS_Input
{
float4 Position : SV_Position;
float2 Texcoord : TEXCOORD0;
};
PS_Input VS(VS_Input input)
{
PS_Input output;
output.Position = mul(WorldViewProjection, float4(input.Position, 0.0f, 1.0f));
output.Texcoord = input.Texcoord;
return output;
}
void SwizzleColor(int swizzleMode, float4 colorToSwizzle, inout float output)
{
switch (swizzleMode)
{
case SWIZZLE_R:
output = colorToSwizzle.r;
break;
case SWIZZLE_G:
output = colorToSwizzle.g;
break;
case SWIZZLE_B:
output = colorToSwizzle.b;
break;
case SWIZZLE_A:
output = colorToSwizzle.a;
break;
default:
break;
}
}
float4 PS(PS_Input input) : SV_Target0
{
float4 outputColor;
if (Mode == 0)
{
//float4 color = Texture.SampleLevel(Sampler, float3(input.Texcoord, ArraySlice), MipLevel);
// Quasi next neighbor
float4 color = Texture.Load(int4(input.Texcoord * Texels, ArraySlice, MipLevel));
outputColor = float4(ColorOffset.xxx, AlphaOffset) + color * float4(ColorScale.xxx, AlphaScale);
}
else if (Mode == 1)
{
int4 color = IntTexture.Load(int4(input.Texcoord * Texels, ArraySlice, MipLevel));
outputColor = float4(ColorOffset.xxx, AlphaOffset) + color * float4(ColorScale.xxx, AlphaScale);
}
else if (Mode == 2)
{
uint4 color = UIntTexture.Load(int4(input.Texcoord * Texels, ArraySlice, MipLevel));
outputColor = float4(ColorOffset.xxx, AlphaOffset) + color * float4(ColorScale.xxx, AlphaScale);
}
float4 swizzledOutput = float4(0, 0, 0, 1);
SwizzleColor(Swizzle.r, outputColor, swizzledOutput.r);
SwizzleColor(Swizzle.g, outputColor, swizzledOutput.g);
SwizzleColor(Swizzle.b, outputColor, swizzledOutput.b);
SwizzleColor(Swizzle.a, outputColor, swizzledOutput.a);
return swizzledOutput;
}
#pragma Effect[VS=VS; PS=PS]
@@ -0,0 +1,4 @@
{
AssetLoader = "EffectAssetLoader",
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */
}
@@ -0,0 +1,61 @@
#define EDITOR
cbuffer Constants : register(b0)
{
float4x4 ViewProjection;
};
struct VS_Input
{
float4 Position : POSITION;
float4 Color : COLOR;
#ifdef EDITOR
uint EntityId : ENTITYID;
#endif
};
typedef struct PS_Input
{
float4 Position : SV_Position;
float4 Color : COLOR;
#ifdef EDITOR
nointerpolation uint EntityId : ENTITYID;
#endif
} VS_Output;
VS_Output VS(VS_Input input)
{
VS_Output output;
output.Position = mul(ViewProjection, input.Position);
output.Color = input.Color;
#ifdef EDITOR
output.EntityId = input.EntityId;
#endif
return output;
}
struct PS_Output
{
float4 Color : SV_TARGET0;
#ifdef EDITOR
uint EntityId : SV_TARGET1;
#endif
};
PS_Output PS(PS_Input input)
{
PS_Output output;
output.Color = input.Color;
#ifdef EDITOR
output.EntityId = input.EntityId;
#endif
return output;
}
#pragma Effect[VS=VS; PS=PS]
@@ -0,0 +1,4 @@
{
AssetLoader = "EffectAssetLoader",
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */
}
+37 -44
View File
@@ -2,6 +2,8 @@
#include "ShaderHelpers.hlsl" #include "ShaderHelpers.hlsl"
#include "GlitchyEngine.hlsl"
Texture2D AlbedoTexture : register(t0); Texture2D AlbedoTexture : register(t0);
SamplerState AlbedoSampler : register(s0); SamplerState AlbedoSampler : register(s0);
@@ -14,28 +16,11 @@ SamplerState MetallicSampler : register(s2);
Texture2D<float> RoughnessTexture : register(t3); Texture2D<float> RoughnessTexture : register(t3);
SamplerState RoughnessSampler : register(s3); SamplerState RoughnessSampler : register(s3);
// Texture2D<float> AmbientTexture : register(t4); Texture2D<float3> EmissiveTexture : register(t4);
// SamplerState AmbientSampler : register(s4); SamplerState EmissiveSampler : register(s4);
#pragma EngineBuffer[ Name = "SceneConstants"; Binding = "Scene" ] // Texture2D<float> AmbientTexture : register(t5);
cbuffer SceneConstants : register(b0) // SamplerState AmbientSampler : register(s5);
{
float4x4 ViewProjection;
}
#pragma EngineBuffer[ Name = "ObjectConstants"; Binding = "Object" ]
cbuffer ObjectConstants : register(b1)
{
float4x4 Transform;
/**
* \brief Inverted and transposed transform matrix.
* \remarks This matrix is used in order to correctly transform normal vectors.
*/
float4x3 Transform_InvT;
#ifdef OutputEntityId
uint EntityId;
#endif
}
cbuffer MaterialConstants : register(b2) cbuffer MaterialConstants : register(b2)
{ {
@@ -44,9 +29,12 @@ cbuffer MaterialConstants : register(b2)
#pragma EditorVariable[ Name = "NormalScaling"; Preview = "Normal Scaling" ] #pragma EditorVariable[ Name = "NormalScaling"; Preview = "Normal Scaling" ]
float2 NormalScaling = float2(1.0, 1.0); float2 NormalScaling = float2(1.0, 1.0);
#pragma EditorVariable[ Name = "MetallicFactor"; Preview = "Metallic Factor"; Min = 0.0f; Max = 1.0f ] #pragma EditorVariable[ Name = "MetallicFactor"; Preview = "Metallic Factor"; Min = 0.0f; Max = 1.0f ]
float MetallicFactor = 1.0; float MetallicFactor = 0.0;
#pragma EditorVariable[ Name = "RoughnessFactor"; Preview = "Rougness Factor"; Min = 0.0f; Max = 1.0f ] #pragma EditorVariable[ Name = "RoughnessFactor"; Preview = "Rougness Factor"; Min = 0.0f; Max = 1.0f ]
float RoughnessFactor = 1.0; float RoughnessFactor = 0.6;
#pragma EditorVariable[ Name = "EmissiveColor"; Preview = "Emissive Factor"; Type="ColorHDR" ]
float3 EmissiveColor = float3(0.0, 0.0, 0.0);
// # pra gma Editor Variable[ Name = "AmbientFactor"; Preview = "Ambient Factor" ]
// float AmbientFactor = 1.0; // float AmbientFactor = 1.0;
} }
@@ -59,7 +47,7 @@ struct VS_IN
float2 TexCoord : TEXCOORD; float2 TexCoord : TEXCOORD;
}; };
struct PS_IN typedef struct PS_IN
{ {
float4 Position : SV_POSITION; float4 Position : SV_POSITION;
float3 WorldPosition : WORLDPOSITION; float3 WorldPosition : WORLDPOSITION;
@@ -70,11 +58,11 @@ struct PS_IN
#ifdef OutputEntityId #ifdef OutputEntityId
nointerpolation uint EntityId : ENTITYID; nointerpolation uint EntityId : ENTITYID;
#endif #endif
}; } VS_OUT;
PS_IN VS(VS_IN input) VS_OUT VS(VS_IN input)
{ {
PS_IN output; VS_OUT output;
float4 worldPosition = mul(Transform, float4(input.Position, 1)); float4 worldPosition = mul(Transform, float4(input.Position, 1));
@@ -92,22 +80,22 @@ PS_IN VS(VS_IN input)
return output; return output;
} }
struct PS_OUT //struct PS_OUT
{ //{
float4 Albedo : SV_TARGET0; // float4 Albedo : SV_TARGET0;
// RG: TextureNormal.XY BA: GeoNrm.XY // // RG: TextureNormal.XY BA: GeoNrm.XY
float4 Normal : SV_TARGET1; // float4 Normal : SV_TARGET1;
// R: GeoNrm.Z GBA: GeoTan.XYZ // // R: GeoNrm.Z GBA: GeoTan.XYZ
float4 Tangent : SV_TARGET2; // float4 Tangent : SV_TARGET2;
float4 Position : SV_TARGET3; // float4 Position : SV_TARGET3;
// R: Metallicity G: Roughness B: Ambient // // R: Metallicity G: Roughness B: Ambient
float4 Material : SV_TARGET4; // float4 Material : SV_TARGET4;
#ifdef OutputEntityId //#ifdef OutputEntityId
uint EntityId : SV_TARGET5; // uint EntityId : SV_TARGET5;
#endif //#endif
}; //};
PS_OUT PS(PS_IN input) PS_OUT_Lit PS(PS_IN input)
{ {
// Build tangent space // Build tangent space
float3 normal = normalize(input.Normal); float3 normal = normalize(input.Normal);
@@ -124,6 +112,9 @@ PS_OUT PS(PS_IN input)
float texMetallic = MetallicTexture.Sample(MetallicSampler, input.TexCoord); float texMetallic = MetallicTexture.Sample(MetallicSampler, input.TexCoord);
float texRoughness = RoughnessTexture.Sample(RoughnessSampler, input.TexCoord); float texRoughness = RoughnessTexture.Sample(RoughnessSampler, input.TexCoord);
float3 texEmissive = EmissiveTexture.Sample(EmissiveSampler, input.TexCoord);
//float3 objectNormal = mul(tangentTransform, texNormal); //float3 objectNormal = mul(tangentTransform, texNormal);
//float3 worldNormal = mul(objectNormal, (float3x3)Transform); //float3 worldNormal = mul(objectNormal, (float3x3)Transform);
@@ -131,6 +122,7 @@ PS_OUT PS(PS_IN input)
float3 finalNormal = ScaleNormal(texNormal, NormalScaling); float3 finalNormal = ScaleNormal(texNormal, NormalScaling);
float finalMetallic = texMetallic * MetallicFactor; float finalMetallic = texMetallic * MetallicFactor;
float finalRoughness = texRoughness * RoughnessFactor; float finalRoughness = texRoughness * RoughnessFactor;
float3 finalEmissive = texEmissive * EmissiveColor;
/////////////TODO: REMOVEME /////////////TODO: REMOVEME
@@ -141,12 +133,13 @@ PS_OUT PS(PS_IN input)
/////////////TODO: END_REMOVEME /////////////TODO: END_REMOVEME
PS_OUT output; PS_OUT_Lit output;
output.Albedo = finalAlbedo; output.Albedo = finalAlbedo;
//output.Normal = float4(objectNormal, 1.0); //output.Normal = float4(objectNormal, 1.0);
output.Normal = float4(finalNormal.xy, normal.xy); output.Normal = float4(finalNormal.xy, normal.xy);
output.Tangent = float4(normal.z, tangent.xyz); output.Tangent = float4(normal.z, tangent.xyz);
output.Position = float4(input.WorldPosition, 1.0); output.Position = float4(input.WorldPosition, 1.0);
output.Emissive = float4(finalEmissive, 0.0);
output.Material = float4(finalMetallic, finalRoughness, 1.0, 0); output.Material = float4(finalMetallic, finalRoughness, 1.0, 0);
#ifdef OutputEntityId #ifdef OutputEntityId
@@ -15,7 +15,8 @@ Texture2D GBuffer_Albedo : register(t0);
Texture2D GBuffer_Normal : register(t1); Texture2D GBuffer_Normal : register(t1);
Texture2D GBuffer_Tangent : register(t2); Texture2D GBuffer_Tangent : register(t2);
Texture2D GBuffer_Position : register(t3); Texture2D GBuffer_Position : register(t3);
Texture2D GBuffer_Material : register(t4); Texture2D GBuffer_Emissive : register(t4);
Texture2D GBuffer_Material : register(t5);
cbuffer Constants cbuffer Constants
{ {
@@ -58,7 +59,8 @@ float4 PS(PS_IN input) : SV_TARGET
float4 rawAlbedo = GBuffer_Albedo.Sample(Sampler, input.TexCoord); float4 rawAlbedo = GBuffer_Albedo.Sample(Sampler, input.TexCoord);
float4 rawNormal = GBuffer_Normal.Sample(Sampler, input.TexCoord); float4 rawNormal = GBuffer_Normal.Sample(Sampler, input.TexCoord);
float4 rawTangent = GBuffer_Tangent.Sample(Sampler, input.TexCoord); float4 rawTangent = GBuffer_Tangent.Sample(Sampler, input.TexCoord);
float4 rawPosition = GBuffer_Position.Sample(Sampler, input.TexCoord); float4 rawPosition = GBuffer_Position.Sample(Sampler, input.TexCoord);
float4 rawEmissive = GBuffer_Emissive.Sample(Sampler, input.TexCoord);
float4 rawMaterial = GBuffer_Material.Sample(Sampler, input.TexCoord); float4 rawMaterial = GBuffer_Material.Sample(Sampler, input.TexCoord);
// Extract data from GBuffer // Extract data from GBuffer
@@ -108,7 +110,7 @@ float4 PS(PS_IN input) : SV_TARGET
float3 luminanceColor = LightColor * Illuminance; float3 luminanceColor = LightColor * Illuminance;
float3 final = (kd * diffuse + specular) * luminanceColor * n_dot_l; float3 final = (kd * diffuse + specular) * luminanceColor * n_dot_l + rawEmissive;
#if OUTPUT == Inspect_NormalDistribution #if OUTPUT == Inspect_NormalDistribution
final = max(final - 10000000, nrmDist.xxx); final = max(final - 10000000, nrmDist.xxx);
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,31 @@
{
Effect = "Shaders/myEffect.hlsl",
Textures = [
"AlbedoTexture": "Textures/rocket.png",
"NormalTexture": "Textures/TestMat/rustediron2_normal.png",
"MetallicTexture": "Textures/TestMat/rustediron2_metallic.png",
"RoughnessTexture": "Textures/TestMat/rustediron2_roughness.png"
],
Variables = [
"AlbedoColor": .ColorRGBA{
Value = {
R = 1,
G = 1,
B = 1,
A = 1
}
},
"NormalScaling": .Float2{
Value = {
X = 1,
Y = 1
}
},
"MetallicFactor": .Float{
Value = 0
},
"RoughnessFactor": .Float{
Value = 1
}
]
}
@@ -0,0 +1,4 @@
{
AssetLoader = "MaterialAssetLoader",
Config = (GlitchyEditor.Assets.ModelAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.ModelAssetLoaderConfig. Add [BonTarget] or force it */
}
@@ -7,16 +7,20 @@
MinFilter = .Anisotropic, MinFilter = .Anisotropic,
MagFilter = .Anisotropic, MagFilter = .Anisotropic,
MipFilter = .Anisotropic, MipFilter = .Anisotropic,
FilterMode = .Default,
ComparisonFunction = .Never, ComparisonFunction = .Never,
AddressModeU = .Wrap, AddressModeU = .Wrap,
AddressModeV = .Border, AddressModeV = .Wrap,
AddressModeW = .Clamp, AddressModeW = .Wrap,
MipMaxLOD = 160, MipLODBias = 0,
MipMinLOD = 0,
MipMaxLOD = 3,
MaxAnisotropy = 16, MaxAnisotropy = 16,
BorderColor = { BorderColor = {
R = 0.756863, R = 1,
G = 0.2, G = 1,
A = 0.956863 B = 1,
A = 1
} }
} }
} }
@@ -2,15 +2,15 @@
AssetLoader = "EditorTextureAssetLoader", AssetLoader = "EditorTextureAssetLoader",
Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){ Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){
_samplerStateDescription = { _samplerStateDescription = {
MinFilter = .Linear, MinFilter = .Anisotropic,
MagFilter = .Linear, MagFilter = .Anisotropic,
MipFilter = .Linear, MipFilter = .Anisotropic,
ComparisonFunction = .Never, ComparisonFunction = .Never,
AddressModeU = .Clamp, AddressModeU = .Wrap,
AddressModeV = .Clamp, AddressModeV = .Wrap,
AddressModeW = .Clamp, AddressModeW = .Clamp,
MipMinLOD = -340282346638528859811704183484516925440, MipMinLOD = -Infinity,
MipMaxLOD = 340282346638528859811704183484516925440, MipMaxLOD = Infinity,
MaxAnisotropy = 1, MaxAnisotropy = 1,
BorderColor = { BorderColor = {
R = 1, R = 1,
@@ -1,16 +1,20 @@
{ {
AssetLoader = "EditorTextureAssetLoader", AssetLoader = "EditorTextureAssetLoader",
Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){ Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){
_generateMipMaps = false,
_isSrgb = false,
_samplerStateDescription = { _samplerStateDescription = {
MinFilter = .Linear, MinFilter = .Linear,
MagFilter = .Linear, MagFilter = .Linear,
MipFilter = .Linear, MipFilter = .Linear,
FilterMode = .Default,
ComparisonFunction = .Never, ComparisonFunction = .Never,
AddressModeU = .Clamp, AddressModeU = .Wrap,
AddressModeV = .Clamp, AddressModeV = .Wrap,
AddressModeW = .Clamp, AddressModeW = .Clamp,
MipMinLOD = -340282346638528859811704183484516925440, MipLODBias = 2.5999999,
MipMaxLOD = 340282346638528859811704183484516925440, MipMinLOD = -Infinity,
MipMaxLOD = Infinity,
MaxAnisotropy = 1, MaxAnisotropy = 1,
BorderColor = { BorderColor = {
R = 1, R = 1,
@@ -1,17 +1,20 @@
{ {
AssetLoader = "EditorTextureAssetLoader", AssetLoader = "EditorTextureAssetLoader",
Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){ Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){
_generateMipMaps = false,
_isSrgb = true, _isSrgb = true,
_samplerStateDescription = { _samplerStateDescription = {
MinFilter = .Linear, MinFilter = .Linear,
MagFilter = .Linear, MagFilter = .Linear,
MipFilter = .Linear, MipFilter = .Linear,
FilterMode = .Default,
ComparisonFunction = .Never, ComparisonFunction = .Never,
AddressModeU = .Clamp, AddressModeU = .Wrap,
AddressModeV = .Clamp, AddressModeV = .Wrap,
AddressModeW = .Clamp, AddressModeW = .Clamp,
MipMinLOD = -340282346638528859811704183484516925440, MipLODBias = 0,
MipMaxLOD = 340282346638528859811704183484516925440, MipMinLOD = -3.40282347e+38,
MipMaxLOD = 3.40282347e+38,
MaxAnisotropy = 1, MaxAnisotropy = 1,
BorderColor = { BorderColor = {
R = 1, R = 1,
@@ -1,7 +1,11 @@
{ {
Effect = "content/Shaders/myEffect.hlsl", Effect = "Shaders/myEffect.hlsl",
Textures = [ Textures = [
"AlbedoTexture": "Textures/TestMat/rustediron2_albedo.png" "AlbedoTexture": "Textures/TestMat/rustediron2_albedo.png",
"NormalTexture": "Textures/TestMat/rustediron2_normal.png",
"MetallicTexture": "Textures/TestMat/rustediron2_metallic.png",
"RoughnessTexture": "Textures/TestMat/rustediron2_roughness.png",
"EmissiveTexture": "Textures/rocket.png"
], ],
Variables = [ Variables = [
"AlbedoColor": .ColorRGBA{ "AlbedoColor": .ColorRGBA{
@@ -23,6 +27,13 @@
}, },
"RoughnessFactor": .Float{ "RoughnessFactor": .Float{
Value = 1 Value = 1
},
"EmissiveColor": .ColorRGB{
Value = {
R = 1,
G = 0,
B = 0
}
} }
] ]
} }
@@ -4,13 +4,14 @@
_isSrgb = true, _isSrgb = true,
_samplerStateDescription = { _samplerStateDescription = {
MinFilter = .Linear, MinFilter = .Linear,
MagFilter = .Linear,
MipFilter = .Linear, MipFilter = .Linear,
ComparisonFunction = .Never, ComparisonFunction = .Never,
AddressModeU = .Clamp, AddressModeU = .Clamp,
AddressModeV = .Clamp, AddressModeV = .Clamp,
AddressModeW = .Clamp, AddressModeW = .Clamp,
MipMinLOD = -340282346638528859811704183484516925440, MipMinLOD = -3.40282347e+38,
MipMaxLOD = 340282346638528859811704183484516925440, MipMaxLOD = 3.40282347e+38,
MaxAnisotropy = 1, MaxAnisotropy = 1,
BorderColor = { BorderColor = {
R = 1, R = 1,
Binary file not shown.
@@ -0,0 +1,24 @@
{
AssetLoader = "EditorTextureAssetLoader",
Config = (GlitchyEditor.Assets.EditorTextureAssetLoaderConfig){
_isSrgb = true,
_samplerStateDescription = {
MinFilter = .Linear,
MagFilter = .Linear,
MipFilter = .Linear,
ComparisonFunction = .Never,
AddressModeU = .Clamp,
AddressModeV = .Clamp,
AddressModeW = .Clamp,
MipMinLOD = -Infinity,
MipMaxLOD = Infinity,
MaxAnisotropy = 1,
BorderColor = {
R = 1,
G = 1,
B = 1,
A = 1
}
}
}
}
+1
View File
@@ -0,0 +1 @@
4.5/
+1
View File
@@ -0,0 +1 @@
Install mono and copy the folder `lib/mono/4.5` here!
@@ -0,0 +1,23 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v7.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v7.0": {
"ScriptCore/1.0.0": {
"runtime": {
"ScriptCore.dll": {}
}
}
}
},
"libraries": {
"ScriptCore/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}
@@ -0,0 +1,9 @@
{
"runtimeOptions": {
"additionalProbingPaths": [
"C:\\Users\\Simon\\.dotnet\\store\\|arch|\\|tfm|",
"C:\\Users\\Simon\\.nuget\\packages",
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
]
}
}
@@ -0,0 +1,10 @@
{
"runtimeOptions": {
"tfm": "net7.0",
"rollForward": "LatestMinor",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "7.0.0"
}
}
}
+2 -2
View File
@@ -31,7 +31,7 @@ class AssetFile
private bool _isDirectory; private bool _isDirectory;
private Object _loadedAsset; private Asset _loadedAsset;
public bool IsDirectory => _isDirectory; public bool IsDirectory => _isDirectory;
@@ -42,7 +42,7 @@ class AssetFile
public AssetConfig AssetConfig => _assetConfig; public AssetConfig AssetConfig => _assetConfig;
public Object LoadedAsset => _loadedAsset; public Asset LoadedAsset => _loadedAsset;
[AllowAppend] [AllowAppend]
public this(EditorContentManager contentManager, StringView identifier, StringView path, bool isDirectory) public this(EditorContentManager contentManager, StringView identifier, StringView path, bool isDirectory)
+86 -3
View File
@@ -34,6 +34,24 @@ public class SubAsset
public Texture2D PreviewImage ~ _?.ReleaseRef(); public Texture2D PreviewImage ~ _?.ReleaseRef();
} }
public static class AssetIdentifier
{
public const char8 DirectorySeparatorChar = '/';
public static void Fixup(String assetIdentifier)
{
const String DotSeperator = $".{DirectorySeparatorChar}";
const String SeperatorDot = $"{DirectorySeparatorChar}.";
assetIdentifier.Replace('\\', DirectorySeparatorChar);
assetIdentifier.Replace(DotSeperator, "");
assetIdentifier.Replace(SeperatorDot, "");
if (assetIdentifier.StartsWith(DirectorySeparatorChar))
assetIdentifier.Remove(0, 1);
}
}
class AssetHierarchy class AssetHierarchy
{ {
FileSystemWatcher fsw ~ { FileSystemWatcher fsw ~ {
@@ -108,7 +126,8 @@ class AssetHierarchy
fsw.OnRenamed.Add(new (oldName, newName) => { fsw.OnRenamed.Add(new (oldName, newName) => {
Log.EngineLogger.Trace($"File renamed (From \"{oldName}\" to \"{newName}\")"); Log.EngineLogger.Trace($"File renamed (From \"{oldName}\" to \"{newName}\")");
_fileSystemDirty = true; //_fileSystemDirty = true;
FileRenamed(oldName, newName);
/*String contentFilePath = scope String(); /*String contentFilePath = scope String();
Path.InternalCombine(contentFilePath, ContentDirectory, oldName); Path.InternalCombine(contentFilePath, ContentDirectory, oldName);
@@ -121,7 +140,7 @@ class AssetHierarchy
fsw.StartRaisingEvents(); fsw.StartRaisingEvents();
} }
/// Gets the tree node for the given filePath or null, if the file/directory doesn't exist. /// Gets the tree node for the given filePath or .Err, if the file/directory doesn't exist.
/// @param filePath the path for which to return the tree node. /// @param filePath the path for which to return the tree node.
/// @remarks Do not hold a reference to the TreeNode because it can become invalid when the file hierarchy changes. /// @remarks Do not hold a reference to the TreeNode because it can become invalid when the file hierarchy changes.
public Result<TreeNode<AssetNode>> GetNodeFromPath(StringView filePath) public Result<TreeNode<AssetNode>> GetNodeFromPath(StringView filePath)
@@ -161,6 +180,7 @@ class AssetHierarchy
_assetHierarchy = new TreeNode<AssetNode>(new AssetNode()); _assetHierarchy = new TreeNode<AssetNode>(new AssetNode());
_assetHierarchy->Path = new String(ContentDirectory); _assetHierarchy->Path = new String(ContentDirectory);
_assetHierarchy->Name = new String("Content"); _assetHierarchy->Name = new String("Content");
_assetHierarchy->IsDirectory = true;
Log.EngineLogger.Trace($"Created directory node for: \"{_assetHierarchy->Path}\""); Log.EngineLogger.Trace($"Created directory node for: \"{_assetHierarchy->Path}\"");
@@ -171,6 +191,7 @@ class AssetHierarchy
{ {
String identifier = scope .(node.Path.Length); String identifier = scope .(node.Path.Length);
Path.GetRelativePath(node.Path, _contentDirectory, identifier); Path.GetRelativePath(node.Path, _contentDirectory, identifier);
AssetIdentifier.Fixup(identifier);
node.AssetFile = new AssetFile(_contentManager, identifier, node.Path, node.IsDirectory); node.AssetFile = new AssetFile(_contentManager, identifier, node.Path, node.IsDirectory);
} }
@@ -333,11 +354,73 @@ class AssetHierarchy
return; return;
OnFileContentChanged(node.Value); OnFileContentChanged(node.Value);
}
// TODO: Handle file changes (reload asset, etc...) private void FileRenamed(StringView oldFilePath, StringView newFilePath)
{
var oldFilePath;
// Ignore Config files.
if (oldFilePath.EndsWith(AssetFile.ConfigFileExtension))
return;
String oldFileNameWithContentRoot = scope .();
Path.InternalCombine(oldFileNameWithContentRoot, _contentDirectory, oldFilePath);
String newFileNameWithContentRoot = scope .();
Path.InternalCombine(newFileNameWithContentRoot, _contentDirectory, newFilePath);
// Rename config file
{
String oldConfigFileName = scope $"{oldFileNameWithContentRoot}{AssetFile.ConfigFileExtension}";
String newConfigFileName = scope $"{newFileNameWithContentRoot}{AssetFile.ConfigFileExtension}";
if (File.Exists(oldConfigFileName) && !File.Exists(newConfigFileName))
{
if (File.Move(oldConfigFileName, newConfigFileName) case .Err(let value))
{
Log.EngineLogger.Error($"Failed to move file {oldConfigFileName} to {newConfigFileName}. Code: {value}");
}
}
}
var nodeResult = GetNodeFromPath(oldFileNameWithContentRoot);
TreeNode<AssetNode> node = null;
if (!(nodeResult case .Ok(out node)))
{
// This happens, when we create new files.
Log.EngineLogger.Trace($"Could not find node for file \"{oldFileNameWithContentRoot}\"");
return;
}
_pathToAssetNode.Remove(oldFileNameWithContentRoot);
node->Path.Set(newFileNameWithContentRoot);
_pathToAssetNode.Add(node->Path, node);
node->Name.Clear();
Path.GetFileName(newFileNameWithContentRoot, node->Name);
String oldIdentifier = scope .(node->AssetFile.[Friend]_identifier);
node->AssetFile.[Friend]_path.Set(node->Path);
node->AssetFile.[Friend]_identifier.Set(newFilePath);
AssetIdentifier.Fixup(node->AssetFile.[Friend]_identifier);
node->AssetFile.[Friend]_assetConfigPath..Set(node->Path).Append(AssetFile.ConfigFileExtension);
OnFileRenamed(node.Value, oldIdentifier);
} }
public delegate void FileContentChangedFunc(AssetNode node); public delegate void FileContentChangedFunc(AssetNode node);
public Event<FileContentChangedFunc> OnFileContentChanged ~ _.Dispose(); public Event<FileContentChangedFunc> OnFileContentChanged ~ _.Dispose();
public delegate void FileRenamedFunc(AssetNode node, StringView oldName);
public Event<FileRenamedFunc> OnFileRenamed ~ _.Dispose();
} }
+8 -47
View File
@@ -48,54 +48,15 @@ class EffectAssetLoader : IAssetLoader //, IReloadingAssetLoader
Effect effect = new Effect(file, assetIdentifier, contentManager); Effect effect = new Effect(file, assetIdentifier, contentManager);
return effect; return effect;
}
/*StreamReader reader = scope .(file); public Asset GetPlaceholderAsset(Type assetType)
{
return default;
}
String text = scope .(); public Asset GetErrorAsset(Type assetType)
{
reader.ReadToEnd(text); return default;
MaterialFile materialFile = scope .();
var result = Bon.Deserialize<MaterialFile>(ref materialFile, text);
if (result case .Err)
{
Log.EngineLogger.Error("Failed to load material.");
return null;
// TODO: return error material
}
Effect fx = new Effect(materialFile.Effect);
Material material = new Material(fx);
for (let (slotName, textureIdentifier) in materialFile.Textures)
{
Texture texture = contentManager.LoadAsset(textureIdentifier) as Texture;
if (texture == null)
{
Log.EngineLogger.Error("Failed to load texture.");
// TODO: LoadAsset should return an error texture.
}
material.SetTexture(slotName, texture);
}
fx.ReleaseRef();
/*for (let (slotName, textureIdentifier) in materialFile.Variables)
{
if (texture == null)
{
Log.EngineLogger.Error("Failed to load texture.");
// TODO: LoadAsset should return an error texture.
}
material.SetVariable(slotName, );
}*/
return material; //ModelLoader.LoadMesh(file, subAsset.Value, 0);*/
} }
} }
@@ -0,0 +1,748 @@
using System.IO;
using System;
using GlitchyEngine;
using System.Collections;
namespace GlitchyEditor.Assets.Importers;
// Based on https://github.com/Diron-P/DDSReader/blob/main/dds_loader.h
public struct LoadedSurface
{
public Span<uint8> Data;
public uint32 Pitch;
public uint32 SlicePitch;
public int ArrayIndex;
public int CubeFace;
public int MipLevel;
}
public struct LoadedTextureInfo
{
public enum Dimension
{
Unknown,
Texture1D,
Texture2D,
Texture3D
}
public uint8[] PixelData = null;
public DirectX.DXGI.Format PixelFormat = .Unknown;
public int MipMapCount = 0;
public int ArraySize = 0;
public Dimension Dimension = .Unknown;
public bool IsCubeMap;
public int Width;
public int Height;
public int Depth;
}
static class DdsImporter
{
private const uint32 MagicWord = 0x20534444; // 'DDS
public enum LoadError
{
Unknown,
WrongMagicWord,
StreamReadFailed,
InvalidArraySize,
WrongDimensions,
IncompleteCubemap
}
private enum HeaderFlags : uint32
{
/// Required in every .dds file.
DDSD_CAPS = 0x1,
/// Required in every .dds file.
DDSD_HEIGHT = 0x2,
/// Required in every .dds file.
DDSD_WIDTH = 0x4,
/// Required in every .dds file.
DDSD_PITCH = 0x8,
/// Required in every .dds file.
DDSD_PIXELFORMAT = 0x1000,
/// Required in a mipmapped texture.
DDSD_MIPMAPCOUNT = 0x20000,
/// Required when pitch is provided for a compressed texture.
DDSD_LINEARSIZE = 0x80000,
/// Required in a depth texture.
DDSD_DEPTH = 0x800000
}
private enum Caps1 : uint32
{
/// Should be set, when the file contains multiple surfaces (e.g. Cubemaps or Mipmaps).
Complex = 0x8,
/// Should be set, if the file contains mipmaps.
MipMap = 0x400000,
/// Should always be set.
Texture = 0x1000
}
[AllowDuplicates]
private enum Caps2 : uint32
{
Cubemap = 0x200,
Cubemap_PositiveX = 0x400,
Cubemap_NegativeX = 0x800,
Cubemap_X = Cubemap_PositiveX | Cubemap_NegativeX,
Cubemap_PositiveY = 0x1000,
Cubemap_NegativeY = 0x2000,
Cubemap_Y = Cubemap_PositiveY | Cubemap_NegativeY,
Cubemap_PositiveZ = 0x4000,
Cubemap_NegativeZ = 0x8000,
Cubemap_Z = Cubemap_PositiveZ | Cubemap_NegativeZ,
/// This is the flag that should be set for Cubemaps in DX10+
Cubemap_AllFaces = Cubemap | Cubemap_X | Cubemap_Y | Cubemap_Z,
Volume = 0x200000,
}
[CRepr]
private struct DdsHeader
{
/// Size of this structure. This member must be set to 124.
private uint32 Size;
/// Flags to indicate which members contain valid data.
public HeaderFlags Flags;
/// Surface height (in pixels).
public uint32 Height;
/// Surface width (in pixels).
public uint32 Width;
public uint32 PitchOrLinearSize;
/// Depth of a volume texture (in pixels), otherwise unused.
public uint32 Depth;
/// Number of mipmap levels, otherwise unused.
public uint32 MipMapCount;
/// Unused.
private uint32[11] Reserved1;
/// The pixel format.
public DdsPixelFormat PixelFormat;
public Caps1 Caps;
public Caps2 Caps2;
/// Unused.
private uint32 dwCaps3;
/// Unused.
private uint32 dwCaps4;
/// Unused.
private uint32 dwReserved2;
}
private enum SurfaceType : uint32
{
/// Texture contains alpha data; dwRGBAlphaBitMask contains valid data.
AlphaPixels = 0x1,
/// Used in some older DDS files for alpha channel only uncompressed data (dwRGBBitCount contains the alpha channel bitcount; dwABitMask contains valid data)
Alpha = 0x2,
/// Texture contains compressed RGB data; dwFourCC contains valid data.
FourCC = 0x4,
/// Texture contains uncompressed RGB data; dwRGBBitCount and the RGB masks (dwRBitMask, dwGBitMask, dwBBitMask) contain valid data.
RGB = 0x40,
RGBA = RGB | AlphaPixels,
/// Used in some older DDS files for YUV uncompressed data (dwRGBBitCount contains the YUV bit count;
/// dwRBitMask contains the Y mask, dwGBitMask contains the U mask, dwBBitMask contains the V mask)
YUV = 0x200,
/// Used in some older DDS files for single channel color uncompressed data (dwRGBBitCount contains the luminance channel bit count;
/// dwRBitMask contains the channel mask). Can be combined with DDPF_ALPHAPIXELS for a two channel DDS file.
Luminance = 0x20000
}
private static uint32 MakeFourCC(char8 c0, char8 c1, char8 c2, char8 c3)
{
return ((uint32)c3 << 24 | (uint32) c2 << 16 | (uint32) c1 << 8 | (uint32) c0);
}
/*private enum FourCC : uint32
{
DXT1 = MakeFourCC('D', 'X', 'T', '1'),
DXT2 = MakeFourCC('D', 'X', 'T', '2'),
DXT3 = MakeFourCC('D', 'X', 'T', '3'),
DXT4 = MakeFourCC('D', 'X', 'T', '4'),
DXT5 = MakeFourCC('D', 'X', 'T', '5'),
/// indicates the prescense of the DDS_HEADER_DXT10 extended header
DX10 = MakeFourCC('D', 'X', '1', '0'),
}*/
[CRepr]
private struct DdsPixelFormat
{
/// Structure size; set to 32 (bytes).
private uint32 Size;
/// Values which indicate what type of data is in the surface.
public SurfaceType SurfaceType;
public uint32 FourCC;
/// Number of bits in an RGB (possibly including alpha) format. Valid when dwFlags includes DDPF_RGB, DDPF_LUMINANCE, or DDPF_YUV.
public uint32 RGBBitCount;
/// Red (or luminance or Y) mask for reading color data. For instance, given the A8R8G8B8 format, the red mask would be 0x00ff0000.
public uint32 RBitMask;
/// Green (or U) mask for reading color data. For instance, given the A8R8G8B8 format, the green mask would be 0x0000ff00.
public uint32 GBitMask;
/// Blue (or V) mask for reading color data. For instance, given the A8R8G8B8 format, the blue mask would be 0x000000ff.
public uint32 BBitMask;
/// Alpha mask for reading alpha data. dwFlags must include DDPF_ALPHAPIXELS or DDPF_ALPHA. For instance, given the A8R8G8B8 format, the alpha mask would be 0xff000000.
public uint32 ABitMask;
}
public enum ResourceDimensions : uint32
{
Texture1D = 2,
Texture2D = 3,
Texture3D = 4
}
public enum MiscFlags : uint32
{
TextureCube = 0x4,
}
public enum AlphaMode : uint32
{
Unknown = 0x0,
Straight = 0x1,
Premultiplied = 0x2,
Opaque = 0x3,
/// Any alpha channel content is being used as a 4th channel and is not intended to represent transparency (straight or premultiplied).
Custom = 0x4
}
/// DDS header extension to handle resource arrays, DXGI pixel formats that don't map to the legacy Microsoft DirectDraw pixel format structures, and additional metadata.
[CRepr]
private struct DdsDxt10Header
{
public DirectX.DXGI.Format PixelFormat;
public ResourceDimensions ResourceDimension;
public MiscFlags MiscFlags;
/// Number of elements in the array. For a 2D cubemap this is the number of cubes, so the file contains ArraySize * 6 2D textures.
/// For a 3D Texture this must be 1.
public uint32 ArraySize;
public AlphaMode AlphaMode;
}
private static Result<void, LoadError> DecodeHeader(Stream data, out DdsHeader header, out DdsDxt10Header? extendedHeader)
{
header = ?;
extendedHeader = null;
// Check the magic word
Result<uint32> magicWord = data.Read<uint32>();
if (magicWord case .Err)
return .Err(.StreamReadFailed);
if (magicWord != MagicWord)
return .Err(.WrongMagicWord);
// Load the header
Result<DdsHeader> headerResult = data.Read<DdsHeader>();
if (headerResult case .Err)
return .Err(.StreamReadFailed);
header = headerResult;
// If available, load the extended header
if (header.PixelFormat.SurfaceType.HasFlag(.FourCC) && header.PixelFormat.FourCC == MakeFourCC('D', 'X', '1', '0'))
{
Result<DdsDxt10Header> extendedHeaderResult = data.Read<DdsDxt10Header>();
if (headerResult case .Err)
return .Err(.StreamReadFailed);
extendedHeader = extendedHeaderResult;
}
return .Ok;
}
private static bool IsBitMask(DdsPixelFormat ddspf, uint32 rBitMask, uint32 gBitMask, uint32 bBitMask, uint32 aBitMask)
{
return ddspf.RBitMask == rBitMask && ddspf.GBitMask == gBitMask && ddspf.BBitMask == bBitMask && ddspf.ABitMask == aBitMask;
}
private static DirectX.DXGI.Format GetFormat(in DdsHeader header, in DdsDxt10Header? extendedheader, bool isSrgb)
{
if (extendedheader != null)
return extendedheader.Value.PixelFormat;
DdsPixelFormat pixelFormat = header.PixelFormat;
// Currently supports only basic dxgi formats.
//if (pixelFormat.SurfaceType.HasFlag(.RGBA))
if (pixelFormat.SurfaceType.HasFlag(.RGB))
{
switch (pixelFormat.RGBBitCount)
{
case 32:
if (IsBitMask(pixelFormat, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000))
return isSrgb ? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm;
if (IsBitMask(pixelFormat, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000))
return isSrgb ? .B8G8R8A8_UNorm_SRGB : .B8G8R8A8_UNorm;
if (IsBitMask(pixelFormat, 0x00FF0000, 0x0000FF00, 0x000000FF, 0))
return isSrgb ? .B8G8R8X8_UNorm_SRGB : .B8G8R8X8_UNorm;
if (IsBitMask(pixelFormat, 0x000003FF, 0x000FFc00, 0x3FF00000, 0))
return .R10G10B10A2_UNorm;
if (IsBitMask(pixelFormat, 0x3FF00000, 0x000FFc00, 0x000003FF, 0))
return .R10G10B10A2_UNorm;
if (IsBitMask(pixelFormat, 0x0000FFFF, 0xFFFF0000, 0, 0))
return .R16G16_UNorm;
if (IsBitMask(pixelFormat, 0xFFFFFFFF, 0, 0, 0))
return .R32_Float;
case 24:
// NO 24 bit formats
return .Unknown;
case 16:
if (IsBitMask(pixelFormat, 0x7C00, 0x03E0, 0x001F, 0x8000))
return .B5G5R5A1_UNorm;
if (IsBitMask(pixelFormat, 0xF800, 0x07E0, 0x001F, 0))
return .B5G6R5_UNorm;
if (IsBitMask(pixelFormat, 0x0F00, 0x00F0, 0x000F, 0xF000))
return .B4G4R4A4_UNORM;
if (IsBitMask(pixelFormat, 0x00FF, 0, 0, 0xFF00))
return .R8G8_UNorm;
if (IsBitMask(pixelFormat, 0xFFFF, 0, 0, 0))
return .R16_UNorm;
case 8:
if (IsBitMask(pixelFormat, 0xFF, 0, 0, 0))
return .R8_UNorm;
default:
return .Unknown;
}
}
else if (pixelFormat.SurfaceType.HasFlag(.Luminance))
{
switch (pixelFormat.RGBBitCount)
{
case 16:
if (IsBitMask(pixelFormat, 0xFFFF, 0, 0, 0))
return .R16_UNorm;
if (IsBitMask(pixelFormat, 0x00FF, 0, 0, 0xFF00))
return .R8G8_UNorm;
case 8:
if (IsBitMask(pixelFormat, 0xFF, 0, 0, 0))
return .R8_UNorm;
if (IsBitMask(pixelFormat, 0x00FF, 0, 0, 0xFF00))
return .R8G8_UNorm; // Some DDS writers assume the bitcount should be 8 instead of 16
default:
return .Unknown;
}
}
else if (pixelFormat.SurfaceType.HasFlag(.Luminance))
{
if (pixelFormat.RGBBitCount == 8)
{
return .A8_UNorm;
}
}
else if (pixelFormat.SurfaceType.HasFlag(.FourCC))
{
switch (pixelFormat.FourCC)
{
case MakeFourCC('D', 'X', 'T', '1'):
return isSrgb ? .BC1_UNorm_SRGB : .BC1_UNorm;
case MakeFourCC('D', 'X', 'T', '3'):
return isSrgb ? .BC2_UNorm_SRGB : .BC2_UNorm;
case MakeFourCC('D', 'X', 'T', '5'):
return isSrgb ? .BC3_UNorm_SRGB : .BC3_UNorm;
// Legacy compression formats.
case MakeFourCC('D', 'X', 'T', '2'):
return isSrgb ? .BC2_UNorm_SRGB : .BC2_UNorm;
case MakeFourCC('D', 'X', 'T', '4'):
return isSrgb ? .BC3_UNorm_SRGB : .BC3_UNorm;
case MakeFourCC('B', 'C', '4', 'U'), MakeFourCC('A', 'T', 'I', '1'):
return .BC4_UNorm;
case MakeFourCC('B', 'C', '4', 'S'):
return .BC4_SNorm;
case MakeFourCC('B', 'C', '5', 'U'), MakeFourCC('A', 'T', 'I', '2'):
return .BC5_UNorm;
case MakeFourCC('B', 'C', '5', 'S'):
return .BC5_SNorm;
case MakeFourCC('R', 'G', 'B', 'G'):
return .R8G8_B8G8_UNorm;
case MakeFourCC('G', 'R', 'G', 'B'):
return .G8R8_G8B8_UNorm;
case MakeFourCC('Y', 'U', 'Y', '2'):
return .YUY2;
case 36:
return .R16G16B16A16_UNorm;
case 111:
return .R16_Float;
case 112:
return .R16G16_Float;
case 113:
return .R16G16B16A16_Float;
case 114:
return .R32_Float;
case 115:
return .R32G32_Float;
case 116:
return .R32G32B32A32_Float;
default:
return .Unknown;
}
}
return .Unknown;
}
public static void GetPitch(uint32 width, uint32 height, DirectX.DXGI.Format format, out uint32 rowBytes, out uint32 numRows, out uint64 numBytes)
{
rowBytes = 0;
numRows = 0;
numBytes = 0;
bool bc = false;
bool packed = false;
bool planar = false;
uint32 bpe = 0;
switch (format)
{
case .BC1_Typeless, .BC1_UNorm, .BC1_UNorm_SRGB,
.BC4_Typeless, .BC4_UNorm, .BC4_SNorm:
bc = true;
bpe = 8;
break;
case .BC2_Typeless, .BC2_UNorm, .BC2_UNorm_SRGB,
.BC3_Typeless, .BC3_UNorm, .BC3_UNorm_SRGB,
.BC5_Typeless, .BC5_UNorm, .BC5_SNorm,
.BC6H_Typeless, .BC6H_UF16, .BC6H_SF16,
.BC7_Typeless, .BC7_UNorm, .BC7_UNorm_SRGB:
bc = true;
bpe = 16;
break;
case .R8G8_B8G8_UNorm, .G8R8_G8B8_UNorm, .YUY2:
packed = true;
bpe = 4;
break;
case .Y210, .Y216:
packed = true;
bpe = 8;
break;
case .NV12, .P208, .OPAQUE_420:
planar = true;
bpe = 2;
break;
case .P010, .P016:
planar = true;
bpe = 4;
break;
/*
case .D16_UNORM_S8_UINT:
case .R16_UNORM_X8_TYPELESS:
case .X16_TYPELESS_G8_UINT:
planar = true;
bpe = 4;
break;
*/
default:
break;
}
if (bc)
{
uint32 numBlocksWide = 0;
if (width > 0)
{
numBlocksWide = Math.Max(1, (width + 3u) / 4u);
}
uint32 numBlocksHigh = 0;
if (height > 0)
{
numBlocksHigh = Math.Max(1, (height + 3u) / 4u);
}
rowBytes = numBlocksWide * bpe;
numRows = numBlocksHigh;
numBytes = rowBytes * numBlocksHigh;
}
else if (packed)
{
rowBytes = (((uint32)width + 1u) >> 1) * bpe;
numRows = height;
numBytes = rowBytes * height;
}
else if (format == .NV11)
{
rowBytes = ((width + 3u) >> 2) * 4u;
numRows = height * 2u; // Direct3D makes this simplifying assumption, although it is larger than the 4:1:1 data
numBytes = rowBytes * numRows;
}
else if (planar)
{
rowBytes = ((width + 1u) >> 1) * bpe;
numBytes = (rowBytes * height) + ((rowBytes * height + 1u) >> 1);
numRows = height + ((height + 1u) >> 1);
}
else
{
uint32 bpp = format.BitsPerPixel();// BitsPerPixel(format);
if (bpp == 0)
return;
rowBytes = (width * bpp + 7u) / 8u; // round up to nearest byte
numRows = height;
numBytes = rowBytes * height;
}
//#if defined(_M_IX86) || defined(_M_ARM) || defined(_M_HYBRID_X86_ARM64)
/*static_assert(sizeof(size_t) == 4, "Not a 32-bit platform!");
if (numBytes > UINT32_MAX || rowBytes > UINT32_MAX || numRows > UINT32_MAX)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);*/
/*#else
static_assert(sizeof(size_t) == 8, "Not a 64-bit platform!");
#endif*/
/*if (outNumBytes)
{
*outNumBytes = static_cast<size_t>(numBytes);
}
if (outRowBytes)
{
*outRowBytes = static_cast<size_t>(rowBytes);
}
if (outNumRows)
{
*outNumRows = static_cast<size_t>(numRows);
}*/
}
public static Result<void, LoadError> LoadDds(Stream data, bool isSrgb, List<LoadedSurface> surfaces, out LoadedTextureInfo textureInfo)
{
// TODO: Care about alpha mode
textureInfo = .();
var headerResult = DecodeHeader(data, let header, let extendedHeader);
if (headerResult case .Err)
return headerResult;
int surfaceCount = 1;
if (header.Flags.HasFlag(.DDSD_MIPMAPCOUNT) || header.MipMapCount != 0)
{
if (header.MipMapCount == 0)
Log.EngineLogger.Warning("Mipmap count is zero, even though the flags say it shouldn't.");
if (!header.Flags.HasFlag(.DDSD_MIPMAPCOUNT))
Log.EngineLogger.Warning("Mipmap count is not zero, even though the mipmap count flag isn't set.");
textureInfo.MipMapCount = header.MipMapCount;
surfaceCount *= textureInfo.MipMapCount;
}
else
{
textureInfo.MipMapCount = 1;
}
if (extendedHeader != null)
{
textureInfo.ArraySize = extendedHeader.Value.ArraySize;
surfaceCount *= textureInfo.ArraySize;
if (textureInfo.ArraySize == 0)
return .Err(.InvalidArraySize);
}
else
{
textureInfo.ArraySize = 1;
}
if (header.Caps2.HasFlag(.Cubemap))
{
if (!header.Caps2.HasFlag(.Cubemap_AllFaces))
Log.EngineLogger.Warning("Texture might only contains partial cubemap, this is not allowed. (Defined Cubemap-Flag, but not all cubemap faces)");
surfaceCount *= 6;
textureInfo.IsCubeMap = true;
}
textureInfo.PixelFormat = GetFormat(header, extendedHeader, isSrgb);
textureInfo.Width = header.Width;
textureInfo.Height = header.Height;
textureInfo.Depth = header.Depth;
switch (extendedHeader?.ResourceDimension)
{
case .Texture1D:
textureInfo.Dimension = .Texture1D;
if (header.Flags.HasFlag(.DDSD_HEIGHT) && header.Height != 1)
return .Err(.WrongDimensions);
textureInfo.Height = 1;
textureInfo.Depth = 1;
case .Texture2D:
textureInfo.Dimension = .Texture2D;
textureInfo.Depth = 1;
case .Texture3D:
textureInfo.Dimension = .Texture3D;
case null:
if (header.Flags.HasFlag(.DDSD_DEPTH))
textureInfo.Dimension = .Texture3D;
else
{
textureInfo.Dimension = .Texture2D;
textureInfo.Depth = 1;
}
}
// Make sure we have enough space in the list (avoid allocations later)
surfaces.Reserve(surfaceCount);
uint32 scanLineSize = 0;
uint32 slicePitch = 0;
uint64 numBytes = 0;
uint32 blockSize = 0; // Block size in bytes.
switch (textureInfo.PixelFormat)
{
case .BC1_UNorm, .BC1_UNorm_SRGB, .BC1_Typeless,
.BC4_UNorm, .BC4_SNorm, .BC4_Typeless:
blockSize = 8;
case .BC2_UNorm, .BC2_UNorm_SRGB, .BC2_Typeless,
.BC3_UNorm, .BC3_UNorm_SRGB, .BC3_Typeless,
.BC5_UNorm, .BC5_SNorm, .BC5_Typeless,
.BC7_UNorm, .BC7_UNorm_SRGB, .BC7_Typeless:
blockSize = 16;
default:
}
uint32 width = 0;
uint32 height = 0;
uint32 depth = 0;
uint32 index = 0;
uint offset = 0;
for (uint32 arrayIndex = 0; arrayIndex < textureInfo.ArraySize; arrayIndex++)
{
for (uint32 cubeFace = 0; cubeFace < (textureInfo.IsCubeMap ? 6 : 1); cubeFace++)
{
width = header.Width;
height = header.Height;
depth = header.Depth;
for (uint32 mipLevel = 0; mipLevel < textureInfo.MipMapCount; ++mipLevel)
{
/*// This will recalculate te pitch of the main image as well.
if (header.Flags.HasFlag(.DDSD_LINEARSIZE))
{
// compressed textures
scanLineSize = Math.Max(1, ((width + 3u) / 4u)) * blockSize;
uint32 numScanLines = Math.Max(1, ((height + 3u) / 4u));
numBytes = scanLineSize * numScanLines;
}
else if (header.Flags.HasFlag(.DDSD_PITCH))
{
// uncompressed data
scanLineSize = (width * header.PixelFormat.RGBBitCount + 7u) / 8u;
numBytes = scanLineSize * height;
}
else
{
scanLineSize = header.PitchOrLinearSize;
numBytes = scanLineSize * height;
}*/
GetPitch(width, height, textureInfo.PixelFormat, out scanLineSize, let numRows, out numBytes);
LoadedSurface surface = .();
// We don't know how large data-array is, so we don't have one yet.
// Only save the offset, we will adjust the pointer later.
surface.Data = Span<uint8>((uint8*)(void*)offset, (int)numBytes);
surface.Pitch = scanLineSize;
//surface.SlicePitch = slicePitch;
surface.SlicePitch = scanLineSize * numRows;
surface.ArrayIndex = arrayIndex;
surface.CubeFace = cubeFace;
surface.MipLevel = mipLevel;
surfaces.Add(surface);
++index;
width >>= 1;
height >>= 1;
depth >>= 1;
if (width == 0)
{
width = 1;
}
if (height == 0)
{
height = 1;
}
offset += numBytes;
}
}
}
textureInfo.PixelData = new uint8[offset];
Result<int> pixelDataResult = data.TryRead(textureInfo.PixelData);
if (pixelDataResult case .Err)
{
delete textureInfo.PixelData;
return .Err(.StreamReadFailed);
}
for (ref LoadedSurface surface in ref surfaces)
{
// Data pointer so far only has the offset inside the array.
// Move the pointer, so that it is inside the array.
surface.Data.Ptr += (uint)(void*)textureInfo.PixelData.Ptr;
}
return .Ok;
}
}
+116 -111
View File
@@ -29,7 +29,7 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
mixin DropAssetTarget<T>() where T : Asset mixin DropAssetTarget<T>() where T : Asset
{ {
Asset asset = null; AssetHandle handle = .Invalid;
if (ImGui.BeginDragDropTarget()) if (ImGui.BeginDragDropTarget())
{ {
@@ -39,13 +39,13 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
{ {
StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize); StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize);
asset = Content.LoadAsset<Asset>(fullpath); handle = Content.LoadAsset(fullpath);
} }
ImGui.EndDragDropTarget(); ImGui.EndDragDropTarget();
} }
asset handle
} }
public this(AssetFile asset) : base(asset) public this(AssetFile asset) : base(asset)
@@ -84,11 +84,10 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
{ {
StringView path = .((char8*)payload.Data, (int)payload.DataSize); StringView path = .((char8*)payload.Data, (int)payload.DataSize);
using (Texture2D newTexture = Content.LoadAsset<Texture2D>(path))//new Texture2D(path, true)) AssetHandle<Texture2D> newTexture = Content.LoadAsset(path);
{
newTexture.SamplerState = SamplerStateManager.AnisotropicWrap; //newTexture.Get().SamplerState = SamplerStateManager.AnisotropicWrap;
material.SetTexture(texture.key, newTexture); material.SetTexture(texture.key, newTexture.Cast<Texture>());
}
} }
ImGui.EndDragDropTarget(); ImGui.EndDragDropTarget();
@@ -98,10 +97,10 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
private void ShowVariables(Material material, Effect effect) private void ShowVariables(Material material, Effect effect)
{ {
for (let (name, arguments) in effect.[Friend]_variableDescriptions) for (let (name, arguments) in effect.[Friend]_variableDescriptions)
{ {
let variable = effect.Variables[name]; if (!effect.Variables.TryGetVariable(name, let variable))
continue;
bool hasPreviewName = TryGetValue(arguments, "Preview", var previewName); bool hasPreviewName = TryGetValue(arguments, "Preview", var previewName);
@@ -109,35 +108,69 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
bool hasPreviewType = TryGetValue(arguments, "Type", var previewType); bool hasPreviewType = TryGetValue(arguments, "Type", var previewType);
if (hasPreviewType && previewType.Get<String>() == "Color") if (hasPreviewType && (previewType.Get<String>() == "Color" || previewType.Get<String>() == "ColorHDR"))
{ {
Log.EngineLogger.AssertDebug(variable.Type == .Float && variable.Rows == 1); if (previewType.Get<String>().Equals("Color", .InvariantCultureIgnoreCase))
if (variable.Columns == 3)
{ {
material.GetVariable<Vector3>(variable.Name, var value); Log.EngineLogger.AssertDebug(variable.Type == .Float && variable.Rows == 1);
value = (Vector3)ColorRGB.LinearToSRGB((ColorRGB)value); if (variable.Columns == 3)
if (ImGui.ColorEdit3(displayName.Ptr, *(float[3]*)&value))
{ {
value = (Vector3)ColorRGB.SRgbToLinear((ColorRGB)value); material.GetVariable<float3>(variable.Name, var value);
material.SetVariable(variable.Name, value);
value = (float3)ColorRGB.LinearToSRGB((ColorRGB)value);
if (ImGui.ColorEdit3(displayName.Ptr, *(float[3]*)&value))
{
value = (float3)ColorRGB.SRgbToLinear((ColorRGB)value);
material.SetVariable(variable.Name, value);
}
}
else if (variable.Columns == 4)
{
material.GetVariable<float4>(variable.Name, var value);
value = (float4)ColorRGBA.LinearToSRGB((ColorRGBA)value);
if (ImGui.ColorEdit4(displayName.Ptr, *(float[4]*)&value))
{
value = (float4)ColorRGBA.SRgbToLinear((ColorRGBA)value);
material.SetVariable(variable.Name, value);
}
} }
} }
else if (variable.Columns == 4) else if (previewType.Get<String>().Equals("ColorHDR", .InvariantCultureIgnoreCase))
{ {
material.GetVariable<Vector4>(variable.Name, var value); Log.EngineLogger.AssertDebug(variable.Type == .Float && variable.Rows == 1);
value = (Vector4)ColorRGBA.LinearToSRGB((ColorRGBA)value);
if (ImGui.ColorEdit4(displayName.Ptr, *(float[4]*)&value)) if (variable.Columns == 3)
{ {
value = (Vector4)ColorRGBA.SRgbToLinear((ColorRGBA)value); material.GetVariable<float3>(variable.Name, var value);
material.SetVariable(variable.Name, value);
value = (float3)ColorRGB.LinearToSRGB((ColorRGB)value);
if (ImGui.ColorEdit3(displayName.Ptr, *(float[3]*)&value, .HDR | .Float))
{
value = (float3)ColorRGB.SRgbToLinear((ColorRGB)value);
material.SetVariable(variable.Name, value);
}
}
else if (variable.Columns == 4)
{
material.GetVariable<float4>(variable.Name, var value);
value = (float4)ColorRGBA.LinearToSRGB((ColorRGBA)value);
if (ImGui.ColorEdit4(displayName.Ptr, *(float[4]*)&value, .HDR | .Float))
{
value = (float4)ColorRGBA.SRgbToLinear((ColorRGBA)value);
material.SetVariable(variable.Name, value);
}
} }
} }
} }
//ImGui.ColorEdit3("", null, .HDR | .Float)
else if (variable.Type == .Float && variable.Rows == 1) else if (variable.Type == .Float && variable.Rows == 1)
{ {
bool hasMin = TryGetValue(arguments, "Min", var min); bool hasMin = TryGetValue(arguments, "Min", var min);
@@ -156,28 +189,28 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
if (ImGui.EditVector<1>(displayName, ref *(float[1]*)&value, .(), 0.1f, 100.0f, minV, maxV)) if (ImGui.EditVector<1>(displayName, ref *(float[1]*)&value, .(), 0.1f, 100.0f, minV, maxV))
material.SetVariable(variable.Name, value); material.SetVariable(variable.Name, value);
case 2: case 2:
material.GetVariable<Vector2>(variable.Name, var value); material.GetVariable<float2>(variable.Name, var value);
Vector2 minV = hasMin ? min.Get<Vector2>() : .(float.MinValue); float2 minV = hasMin ? min.Get<float2>() : (float2)float.MinValue;
Vector2 maxV = hasMax ? max.Get<Vector2>() : .(float.MaxValue); float2 maxV = hasMax ? max.Get<float2>() : (float2)float.MaxValue;
if (ImGui.EditVector2(displayName, ref value, .Zero, 0.1f, 100.0f, minV, maxV)) if (ImGui.Editfloat2(displayName, ref value, .Zero, 0.1f, 100.0f, minV, maxV))
material.SetVariable(variable.Name, value); material.SetVariable(variable.Name, value);
case 3: case 3:
material.GetVariable<Vector3>(variable.Name, var value); material.GetVariable<float3>(variable.Name, var value);
Vector3 minV = hasMin ? min.Get<Vector3>() : .(float.MinValue); float3 minV = hasMin ? min.Get<float3>() : (float3)float.MinValue;
Vector3 maxV = hasMax ? max.Get<Vector3>() : .(float.MaxValue); float3 maxV = hasMax ? max.Get<float3>() : (float3)float.MaxValue;
if (ImGui.EditVector3(displayName, ref value, .Zero, 0.1f, 100.0f, minV, maxV)) if (ImGui.Editfloat3(displayName, ref value, .Zero, 0.1f, 100.0f, minV, maxV))
material.SetVariable(variable.Name, value); material.SetVariable(variable.Name, value);
case 4: case 4:
material.GetVariable<Vector4>(variable.Name, var value); material.GetVariable<float4>(variable.Name, var value);
Vector4 minV = hasMin ? min.Get<Vector4>() : .(float.MinValue); float4 minV = hasMin ? min.Get<float4>() : (float4)float.MinValue;
Vector4 maxV = hasMax ? max.Get<Vector4>() : .(float.MaxValue); float4 maxV = hasMax ? max.Get<float4>() : (float4)float.MaxValue;
if (ImGui.EditVector4(displayName, ref value, .Zero, 0.1f, 100.0f, minV, maxV)) if (ImGui.Editfloat4(displayName, ref value, .Zero, 0.1f, 100.0f, minV, maxV))
material.SetVariable(variable.Name, value); material.SetVariable(variable.Name, value);
} }
} }
@@ -200,14 +233,22 @@ class MaterialAssetLoaderConfig : AssetLoaderConfig
[BonTarget] [BonTarget]
public enum VariableValue public enum VariableValue
{ {
case bool(bool Value);
case bool2(bool2 Value);
case bool3(bool3 Value);
case bool4(bool4 Value);
case int(int Value);
case int2(int2 Value);
case int3(int3 Value);
case int4(int4 Value);
case uint(uint Value);
case uint2(uint2 Value);
case uint3(uint3 Value);
case uint4(uint4 Value);
case Float(float Value); case Float(float Value);
case Float2(Vector2 Value); case Float2(float2 Value);
case Float3(Vector3 Value); case Float3(float3 Value);
case Float4(Vector4 Value); case Float4(float4 Value);
case Int(int Value);
case Int2(Int2 Value);
case Int3(Int3 Value);
case Int4(Int4 Value);
case ColorRGB(ColorRGB Value); case ColorRGB(ColorRGB Value);
case ColorRGBA(ColorRGBA Value); case ColorRGBA(ColorRGBA Value);
case None; case None;
@@ -341,28 +382,24 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
Log.EngineLogger.Error("Failed to load material."); Log.EngineLogger.Error("Failed to load material.");
Debug.SafeBreak(); Debug.SafeBreak();
return null; return null;
// TODO: return error material
} }
Effect fx = new Effect(materialFile.Effect); Effect fx = Content.GetAsset<Effect>(contentManager.LoadAsset(materialFile.Effect, true), contentManager);
Material material = new Material(fx); Material material = new Material(fx);
for (let (slotName, textureIdentifier) in materialFile.Textures) for (let (slotName, textureIdentifier) in materialFile.Textures)
{ {
using (Texture texture = contentManager.LoadAsset(textureIdentifier) as Texture)
AssetHandle<Texture> texture = contentManager.LoadAsset(textureIdentifier);
if (texture.IsInvalid)
{ {
if (texture == null) Log.EngineLogger.Error($"Failed to load texture \"{textureIdentifier}\".");
{
Log.EngineLogger.Error($"Failed to load texture \"{textureIdentifier}\".");
// TODO: LoadAsset should return an error texture.
}
material.SetTexture(slotName, texture);
} }
}
fx.ReleaseRef(); material.SetTexture(slotName, texture);
}
for (let (slotName, variableValue) in materialFile.Variables) for (let (slotName, variableValue) in materialFile.Variables)
{ {
@@ -382,7 +419,7 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
material.SetVariable(slotName, value); material.SetVariable(slotName, value);
case .None: case .None:
default: default:
Log.EngineLogger.Error("Errorre"); Log.EngineLogger.Error($"Unknown variable type of variable {slotName}: {variableValue}");
} }
@@ -407,12 +444,11 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
materialFile.Textures = new .(); materialFile.Textures = new .();
materialFile.Variables = new .(); materialFile.Variables = new .();
//material.SetTexture(); for (let (slotName, texture) in material.[Friend]_textures)
for (let (slotName, textureViewBinding) in material.[Friend]_textures)
{ {
//materialFile.Textures.Add(slotName, textureViewBinding.) Texture textureAsset = texture.Handle.Get();
materialFile.Textures.Add(new String(slotName), new String(textureAsset?.Identifier ?? ""));
} }
Effect effect = material.Effect; Effect effect = material.Effect;
@@ -423,7 +459,6 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
for (let (name, arguments) in effect.[Friend]_variableDescriptions) for (let (name, arguments) in effect.[Friend]_variableDescriptions)
{ {
VariableValue variableValue = .None; VariableValue variableValue = .None;
//Object variantValue = null;
let variable = effect.Variables[name]; let variable = effect.Variables[name];
@@ -449,7 +484,6 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
value = ColorRGBA.LinearToSRGB((ColorRGBA)value); value = ColorRGBA.LinearToSRGB((ColorRGBA)value);
variableValue = .ColorRGBA(value); variableValue = .ColorRGBA(value);
//variantValue = new box value;
} }
} }
else if (variable.Type == .Float && variable.Rows == 1) else if (variable.Type == .Float && variable.Rows == 1)
@@ -460,60 +494,18 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
material.GetVariable<float>(variable.Name, let value); material.GetVariable<float>(variable.Name, let value);
variableValue = .Float(value); variableValue = .Float(value);
case 2: case 2:
material.GetVariable<Vector2>(variable.Name, let value); material.GetVariable<float2>(variable.Name, let value);
variableValue = .Float2(value); variableValue = .Float2(value);
case 3: case 3:
material.GetVariable<Vector3>(variable.Name, let value); material.GetVariable<float3>(variable.Name, let value);
variableValue = .Float3(value); variableValue = .Float3(value);
case 4: case 4:
material.GetVariable<Vector4>(variable.Name, let value); material.GetVariable<float4>(variable.Name, let value);
variableValue = .Float4(value); variableValue = .Float4(value);
} }
/*bool hasMin = TryGetValue(arguments, "Min", var min);
bool hasMax = TryGetValue(arguments, "Max", var max);
for (int r < variable.Rows)
{
switch (variable.Columns)
{
case 1:
material.GetVariable<float>(variable.Name, var value);
float[1] minV = hasMin ? min.Get<float[1]>() : .(float.MinValue);
float[1] maxV = hasMax ? max.Get<float[1]>() : .(float.MaxValue);
if (ImGui.EditVector<1>(displayName, ref *(float[1]*)&value, .(), 0.1f, 100.0f, minV, maxV))
material.SetVariable(variable.Name, value);
case 2:
material.GetVariable<Vector2>(variable.Name, var value);
Vector2 minV = hasMin ? min.Get<Vector2>() : .(float.MinValue);
Vector2 maxV = hasMax ? max.Get<Vector2>() : .(float.MaxValue);
if (ImGui.EditVector2(displayName, ref value, .Zero, 0.1f, 100.0f, minV, maxV))
material.SetVariable(variable.Name, value);
case 3:
material.GetVariable<Vector3>(variable.Name, var value);
Vector3 minV = hasMin ? min.Get<Vector3>() : .(float.MinValue);
Vector3 maxV = hasMax ? max.Get<Vector3>() : .(float.MaxValue);
if (ImGui.EditVector3(displayName, ref value, .Zero, 0.1f, 100.0f, minV, maxV))
material.SetVariable(variable.Name, value);
case 4:
material.GetVariable<Vector4>(variable.Name, var value);
Vector4 minV = hasMin ? min.Get<Vector4>() : .(float.MinValue);
Vector4 maxV = hasMax ? max.Get<Vector4>() : .(float.MaxValue);
if (ImGui.EditVector4(displayName, ref value, .Zero, 0.1f, 100.0f, minV, maxV))
material.SetVariable(variable.Name, value);
}
}*/
} }
materialFile.Variables.Add(name, variableValue); materialFile.Variables.Add(new String(name), variableValue);
} }
String text = scope .(); String text = scope .();
@@ -527,4 +519,17 @@ class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
return .Ok; return .Ok;
} }
Material _placeholder;
Material _error;
public Asset GetPlaceholderAsset(Type assetType)
{
return default;
}
public Asset GetErrorAsset(Type assetType)
{
return default;
}
} }
+12 -2
View File
@@ -44,8 +44,18 @@ class ModelAssetLoader : IAssetLoader //, IReloadingAssetLoader
public Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager) public Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager)
{ {
Log.EngineLogger.Assert(subAsset != null); //Log.EngineLogger.Assert(subAsset != null);
return ModelLoader.LoadMesh(file, subAsset.Value, 0); return ModelLoader.LoadMesh(file, subAsset ?? assetIdentifier, 0);
}
public Asset GetPlaceholderAsset(Type assetType)
{
return default;
}
public Asset GetErrorAsset(Type assetType)
{
return default;
} }
} }
+307 -65
View File
@@ -8,6 +8,8 @@ using GlitchyEngine.Renderer;
using GlitchyEngine.Math; using GlitchyEngine.Math;
using DirectXTK; using DirectXTK;
using ImGui; using ImGui;
using GlitchyEditor.Assets.Importers;
using System.Diagnostics;
namespace GlitchyEditor.Assets; namespace GlitchyEditor.Assets;
@@ -164,7 +166,10 @@ class EditorTextureAssetLoaderConfig : AssetLoaderConfig
} }
} }
class EditorTextureAssetLoader : IAssetLoader, IReloadingAssetLoader using internal GlitchyEngine.Renderer;
using internal GlitchyEngine.Platform.DX11;
class EditorTextureAssetLoader : IAssetLoader//, IReloadingAssetLoader
{ {
private static readonly List<StringView> _fileExtensions = new .(){".png", ".dds"} ~ delete _; // ".jpg", ".bmp" private static readonly List<StringView> _fileExtensions = new .(){".png", ".dds"} ~ delete _; // ".jpg", ".bmp"
@@ -230,81 +235,170 @@ class EditorTextureAssetLoader : IAssetLoader, IReloadingAssetLoader
return .Unknown; return .Unknown;
} }
/** \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 static bool LoadDdsResourcePlatform(Stream stream)
{
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}");
}
DirectX.D3D11.ID3D11Texture2D* texture = null;
DirectX.D3D11.ID3D11ShaderResourceView* view = null;
DirectX.Common.HResult loadResult = DDSTextureLoader.CreateDDSTextureFromMemory(GlitchyEngine.Platform.DX11.NativeDevice,
ddsData.Ptr, (uint)ddsData.Count, (.)&texture, &view);
view.Release();
return true;
}
private static Texture LoadTexture(Stream data, EditorTextureAssetLoaderConfig config) private static Texture LoadTexture(Stream data, EditorTextureAssetLoaderConfig config)
{ {
Debug.Profiler.ProfileResourceFunction!(); Debug.Profiler.ProfileResourceFunction!();
List<LoadedSurface> surfaces = scope .();
LoadedTextureInfo textureInfo;
Texture texture = null; // Make sure we clean up the pixel data
defer { delete textureInfo.PixelData; }
switch(GetTextureType(data)) switch(GetTextureType(data))
{ {
case .DDS: case .DDS:
texture = LoadDds(data, config);
var v = data.Position;
//LoadDdsResourcePlatform(data);
data.Position = v;
Result<void> result = LoadDds(data, config, surfaces, out textureInfo);
if (result case .Err)
return null;
case .PNG: case .PNG:
texture = LoadPng(data, config); Result<void> result = LoadPng(data, config, surfaces, out textureInfo);
if (result case .Err)
return null;
case .Unknown: case .Unknown:
Runtime.FatalError("Unknown image format."); Log.EngineLogger.Error("Unknown texture format.");
return null;
} }
Log.EngineLogger.AssertDebug(texture != null); // Make the format SRGB or non-SRGB
if (config.IsSRGB)
textureInfo.PixelFormat = textureInfo.PixelFormat.GetSRGB();
else
textureInfo.PixelFormat = textureInfo.PixelFormat.GetNonSRGB();
SetSampler(texture, config); Texture texture = CreateTexture(surfaces, textureInfo);
if (texture != null)
{
SetSampler(texture, config);
texture.[Friend]Complete = true;
}
return texture; return texture;
} }
public void ReloadAsset(AssetFile assetFile, Stream data) private static Texture CreateTexture(List<LoadedSurface> surfaces, LoadedTextureInfo textureInfo)
{ {
Texture reloadingTexture = assetFile.LoadedAsset as Texture; TextureSliceData[] slices = scope TextureSliceData[surfaces.Count];
if (reloadingTexture == null)
{
Log.EngineLogger.Error($"{nameof(Self)}: Requested reload of \"{assetFile.FilePath}\" but it's not a Texture!");
return;
}
EditorTextureAssetLoaderConfig config = assetFile.AssetConfig.Config as EditorTextureAssetLoaderConfig;
if (config == null) for (int i < slices.Count)
{ {
Log.EngineLogger.Error($"{nameof(Self)}: Config of asset \"{assetFile.FilePath}\" doesn't have the correct type!"); slices[i] = .(surfaces[i].Data.Ptr, surfaces[i].Pitch, surfaces[i].SlicePitch);
return;
} }
switch(GetTextureType(data)) //slices[0].SlicePitch = (.)(surfaces[0].Pitch * textureInfo.Height);
switch (textureInfo.Dimension)
{ {
case .DDS: //case .Texture1D:
ReloadDds(reloadingTexture as Texture2D, data, config); case .Texture2D:
case .PNG: if (textureInfo.IsCubeMap)
ReloadPng(reloadingTexture as Texture2D, data, config); {
case .Unknown: Texture2DDesc staging = .();
Runtime.FatalError("Unknown image format.");
staging.Width = (.)textureInfo.Width;
staging.Height = (.)textureInfo.Height;
staging.MipLevels = (.)textureInfo.MipMapCount;
staging.ArraySize = (.)textureInfo.ArraySize;
staging.Format = textureInfo.PixelFormat;
// TODO: allow enabling read/write
staging.CpuAccess = .None;
staging.Usage = .Default;
TextureCube cubeTexture = new TextureCube(staging);
//cubeTexture.SetData();
for (let surface in surfaces)
{
cubeTexture.SetData(surface.Data.Ptr, surface.Pitch / staging.Width, (TextureCubeFace)surface.CubeFace, (.)surface.ArrayIndex, (.)surface.MipLevel);
}
return cubeTexture;
}
else
{
Texture2DDesc staging = .();
staging.Width = (.)textureInfo.Width;
staging.Height = (.)textureInfo.Height;
staging.MipLevels = (.)textureInfo.MipMapCount;
staging.ArraySize = (.)textureInfo.ArraySize;
staging.Format = textureInfo.PixelFormat;
// TODO: allow enabling read/write
staging.CpuAccess = .None;
staging.Usage = .Immutable;
Texture2D stagingTexture = new Texture2D(staging);
stagingTexture.SetData(slices);
/*for (let surface in surfaces)
{
stagingTexture.[Friend]PlatformSetData(surface.Data.Ptr, surface.Pitch / staging.Width, 0, 0, staging.Width, staging.Height, (.)surface.ArrayIndex, (.)surface.MipLevel, .Write);
}*/
return stagingTexture;
}
default:
Runtime.NotImplemented();
} }
SetSampler(reloadingTexture, config); return null;
} }
private static void SetSampler(Texture texture, EditorTextureAssetLoaderConfig config) private static Result<void> LoadPng(Stream data, EditorTextureAssetLoaderConfig config, List<LoadedSurface> surfaces, out LoadedTextureInfo textureInfo)
{
using (SamplerState samplerState = SamplerStateManager.GetSampler(config.SamplerStateDescription))
{
texture.SamplerState = samplerState;
}
}
private static void ReloadPng(Texture2D reloadingTexture, Stream data, EditorTextureAssetLoaderConfig config)
{
Debug.Profiler.ProfileResourceFunction!();
using (Texture2D newTexture = LoadPng(data, config))
{
reloadingTexture.[Friend]SneakySwappyTexture(newTexture);
}
}
private static Texture2D LoadPng(Stream data, EditorTextureAssetLoaderConfig config)
{ {
Debug.Profiler.ProfileResourceFunction!(); Debug.Profiler.ProfileResourceFunction!();
textureInfo = .();
uint8[] pngData = new:ScopedAlloc! uint8[data.Length]; uint8[] pngData = new:ScopedAlloc! uint8[data.Length];
@@ -313,40 +407,188 @@ class EditorTextureAssetLoader : IAssetLoader, IReloadingAssetLoader
if (result case .Err(let err)) if (result case .Err(let err))
{ {
Log.EngineLogger.Error($"Failed to read data from stream. Texture: Error: {err}"); Log.EngineLogger.Error($"Failed to read data from stream. Texture: Error: {err}");
return .Err;
}
uint8* rawData = null;
defer
{
if (rawData != null)
LodePng.LodePng.Free(rawData);
} }
uint8* rawData = ?;
uint32 width = 0, height = 0; uint32 width = 0, height = 0;
uint32 errorCode = LodePng.LodePng.Decode32(&rawData, &width, &height, pngData.Ptr, (.)pngData.Count); {
Debug.Profiler.ProfileResourceScope!("LodePng.LodePng.Decode32");
uint32 errorCode = LodePng.LodePng.Decode32(&rawData, &width, &height, pngData.Ptr, (.)pngData.Count);
if (errorCode != 0)
{
Log.EngineLogger.Error($"Failed to decode PNG file {errorCode}.");
return .Err;
}
}
Log.EngineLogger.Assert(errorCode == 0, "Failed to load png File"); uint8[] pixelData = new uint8[4 * width * height];
Internal.MemCpy(pixelData.Ptr, rawData, pixelData.Count);
// TODO: load as SRGB because PNGs are usually not stored as linear LoadedSurface surface = .();
//Texture2DDesc desc = .(width, height, srgb? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm, 1, 1, .Immutable); surface.Data = Span<uint8>(pixelData);
Texture2DDesc desc = .(width, height, config.IsSRGB ? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm, 1, 1, .Immutable); surface.Pitch = 4 * width;
Texture2D texture = new Texture2D(desc); surface.SlicePitch = 0;
texture.SetData<Color>((.)rawData); surface.ArrayIndex = 0;
surface.MipLevel = 0;
surfaces.Add(surface);
//Texture2DDesc desc = .(width, height, config.IsSRGB ? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm, 1, 1, .Immutable);
//Texture2D texture = new Texture2D(desc);
//texture.SetData<Color>((.)rawData);
// TODO: Generate mip maps // TODO: Generate mip maps
LodePng.LodePng.Free(rawData); textureInfo.PixelData = pixelData;
textureInfo.Width = width;
textureInfo.Height = height;
textureInfo.Depth = 1;
return texture; textureInfo.ArraySize = 1;
textureInfo.MipMapCount = 1;
textureInfo.Dimension = .Texture2D;
textureInfo.IsCubeMap = false;
textureInfo.PixelFormat = config.IsSRGB ? .R8G8B8A8_UNorm_SRGB : .R8G8B8A8_UNorm;
return .Ok;
//return texture;
}
private static Result<void> LoadDds(Stream data, EditorTextureAssetLoaderConfig config, List<LoadedSurface> surfaces, out LoadedTextureInfo textureInfo)
{
var result = DdsImporter.LoadDds(data, config.IsSRGB, surfaces, out textureInfo);
if (result case .Err)
return .Err;
return .Ok;
} }
private static void ReloadDds(Texture2D reloadingTexture, Stream data, EditorTextureAssetLoaderConfig config) private static void SetSampler(Texture texture, EditorTextureAssetLoaderConfig config)
{ {
using (Texture2D newTexture = new [Friend]Texture2D(data)) using (SamplerState samplerState = SamplerStateManager.GetSampler(config.SamplerStateDescription))
{ {
reloadingTexture.[Friend]SneakySwappyTexture(newTexture); texture.SamplerState = samplerState;
} }
} }
private static Texture LoadDds(Stream data, EditorTextureAssetLoaderConfig config) private static Texture2D _placeholder2D;
{ private static TextureCube _placeholderCube;
Texture2D texture = new [Friend]Texture2D(data);
return texture; private static Texture2D _error2D;
private static TextureCube _errorCube;
public Asset GetPlaceholderAsset(Type assetType)
{
switch (assetType)
{
case typeof(TextureCube):
if (_placeholderCube == null)
{
// TODO: immutable
Texture2DDesc desc = .(1, 1, .R8G8B8A8_UNorm, 1, 1, .Default, .None);
_placeholderCube = new TextureCube(desc);
_placeholderCube.SamplerState = SamplerStateManager.PointWrap;
Color color = Color.Cyan;
_placeholderCube.SetData<Color>(&color, .PositiveX);
_placeholderCube.SetData<Color>(&color, .NegativeX);
_placeholderCube.SetData<Color>(&color, .PositiveY);
_placeholderCube.SetData<Color>(&color, .NegativeY);
_placeholderCube.SetData<Color>(&color, .PositiveZ);
_placeholderCube.SetData<Color>(&color, .NegativeZ);
Content.ManageAsset(_placeholderCube);
_placeholderCube.ReleaseRef();
_placeholderCube.[Friend]Complete = false;
}
return _placeholderCube;
case typeof(Texture2D):
fallthrough;
default:
if (_placeholder2D == null)
{
Texture2DDesc desc = .(1, 1, .R8G8B8A8_UNorm, 1, 1, .Immutable, .None);
_placeholder2D = new Texture2D(desc);
_placeholder2D.SamplerState = SamplerStateManager.PointWrap;
Color color = Color.Cyan;
_placeholder2D.SetData<Color>(&color);
Content.ManageAsset(_placeholder2D);
_placeholder2D.ReleaseRef();
_placeholder2D.[Friend]Complete = false;
}
return _placeholder2D;
}
}
public Asset GetErrorAsset(Type assetType)
{
switch (assetType)
{
case typeof(TextureCube):
if (_placeholderCube == null)
{
// TODO: immutable
Texture2DDesc desc = .(2, 2, .R8G8B8A8_UNorm, 1, 1, .Default, .None);
_errorCube = new TextureCube(desc);
_errorCube.SamplerState = SamplerStateManager.PointWrap;
Color[4] color = .(Color.HotPink, Color.Black, Color.Black, Color.HotPink);
_errorCube.SetData<Color>(&color, .PositiveX);
_errorCube.SetData<Color>(&color, .NegativeX);
_errorCube.SetData<Color>(&color, .PositiveY);
_errorCube.SetData<Color>(&color, .NegativeY);
_errorCube.SetData<Color>(&color, .PositiveZ);
_errorCube.SetData<Color>(&color, .NegativeZ);
Content.ManageAsset(_errorCube);
_errorCube.ReleaseRef();
_errorCube.[Friend]Complete = false;
}
return _placeholderCube;
case typeof(Texture2D):
fallthrough;
default:
if (_error2D == null)
{
Texture2DDesc desc = .(2, 2, .R8G8B8A8_UNorm, 1, 1, .Immutable, .None);
_error2D = new Texture2D(desc);
_error2D.SamplerState = SamplerStateManager.PointWrap;
Color[4] color = .(Color.HotPink, Color.Black, Color.Black, Color.HotPink);
_error2D.SetData<Color>(&color);
Content.ManageAsset(_error2D);
_error2D.ReleaseRef();
_placeholder2D.[Friend]Complete = true;
}
return _error2D;
}
} }
} }
@@ -0,0 +1,612 @@
using ImGui;
using System;
using GlitchyEngine.Content;
using GlitchyEngine.Renderer;
using GlitchyEngine.Math;
using GlitchyEngine;
namespace GlitchyEditor.EditWindows;
class AssetViewer : EditorWindow
{
public const String s_WindowTitle = "Asset Viewer";
public EditorContentManager _manager;
private AssetHandle _selectedAsset;
append TexturererViewerer _textureViewer = .();
public this(EditorContentManager contentManager)
{
_manager = contentManager;
}
protected override void InternalShow()
{
if(!ImGui.Begin(s_WindowTitle, &_open, .None))
{
ImGui.End();
return;
}
ImGui.PushStyleVar(.CellPadding, .(0, 0));
bool alt_pressed = ImGui.GetIO().KeyAlt;
if (ImGui.BeginTable("AssetViewerTable", 2, .BordersInnerV | .Resizable | .Reorderable | .NoPadOuterX))
{
if (alt_pressed)
{
// Header anzeigen, damit sie neu angeordnet werden können
ImGui.TableSetupColumn("Assets");
ImGui.TableSetupColumn("Viewer");
ImGui.TableHeadersRow();
}
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
if (ImGui.BeginChild("Assets"))
{
DrawAssetList();
ImGui.EndChild();
}
ImGui.TableNextColumn();
if (ImGui.BeginChild("Files"))
{
DrawAssetViewer();
ImGui.EndChild();
}
ImGui.EndTable();
}
ImGui.PopStyleVar(1);
ImGui.End();
}
void DrawAssetList()
{
for (let (handle, asset) in _manager.[Friend]_handleToAsset)
{
// TODO: gucken, was der Typ ist
String name = scope String();
if (asset.Identifier.IsWhiteSpace)
name.Append("Unnamed");
else
name.Append(asset.Identifier);
name.Append(" (");
asset.GetType().GetName(name);
name.Append(") [");
name.AppendF($"{handle.ID}]");
name.TrimStart();
ImGui.TreeNodeFlags flags = .Leaf;
if(handle == _selectedAsset)
flags |= .Selected;
if (ImGui.TreeNodeEx(name, flags, name))
{
if (ImGui.IsItemClicked(.Left))
_selectedAsset = handle;
ImGui.TreePop();
}
}
}
void DrawAssetViewer()
{
if (_selectedAsset.IsInvalid)
{
ImGui.Text("Select an asset to view it.");
return;
}
Asset asset = _selectedAsset.Get<Asset>();
/*if (!asset.Complete)
{
ImGui.Text("Loading asset...");
return;
}*/
/*switch (asset.GetType())
{
case typeof(RenderTargetGroup):
DrawRenderTargetGroupViewer((RenderTargetGroup)asset);
case typeof(Texture):
_textureViewer.ViewTexture((Texture)asset);
}*/
if (var texture = asset as Texture)
_textureViewer.ViewTexture(texture);
if (var texture = asset as RenderTargetGroup)
_textureViewer.ViewTexture(texture);
}
}
class TexturererViewerer
{
enum BackgroundMode : int32
{
White,
Black,
Pink,
Checkerboard,
CustomColor
}
enum SampleMode : int32
{
Point,
Linear
}
enum ColorChannelSwizzle : int32
{
None,
R,
G,
B,
A
}
AssetHandle<Effect> _effect;
AssetHandle<Effect> _renderTargetEffect;
float _zoom = 1.0f;
BackgroundMode _backgroundMode = .Checkerboard;
ColorRGBA _backgroundColor;
SampleMode _sampleMode = .Linear;
RenderTarget2D _target ~ _?.ReleaseRef();
// TODO: we don't need depth!
DepthStencilTarget _depth ~ _?.ReleaseRef();
public this()
{
_effect = Content.LoadAsset("Shaders\\textureViewerShader.hlsl");
_renderTargetEffect = Content.LoadAsset("Shaders\\RenderTargetGroupViewer.hlsl");
}
float2 _position;
bool _moving;
float _colorOffset = 0;
float _colorScale = 1;
float _alphaOffset = 0;
float _alphaScale = 1;
int32 _mipLevel = 0;
int32 _arraySlice = 0;
int32 _groupIndex = 0;
ColorChannelSwizzle _swizzleR = .R;
ColorChannelSwizzle _swizzleG = .G;
ColorChannelSwizzle _swizzleB = .B;
ColorChannelSwizzle _swizzleA = .A;
public void ShowSettings(float width, float height, int maxMips, int arraySize, RenderTargetGroup rtGroup)
{
char8*[] items = scope .("White", "Black", "Pink", "Checkerboard", "Custom Color");
if (ImGui.CollapsingHeader("View"))
{
ImGui.SliderFloat("Zoom", &_zoom, 0.01f, 100.0f);
float maxDimension = max(width, height);
ImGui.SliderFloat2("Position", *(float[2]*)&_position, 2 * -maxDimension * _zoom, 2 * maxDimension * _zoom);
ImGui.Separator();
ImGui.Combo("Background", (.)&_backgroundMode, items.Ptr, (.)items.Count);
if (_backgroundMode == .CustomColor)
{
ImGui.ColorPicker4("Color", ref _backgroundColor);
}
}
if (ImGui.CollapsingHeader("Sampling"))
{
items = scope .("Point", "Linear");
ImGui.Combo("Sampler", (.)&_sampleMode, items.Ptr, (.)items.Count);
ImGui.SliderInt("Mip Level", &_mipLevel, 0, (int32)maxMips);
ImGui.SliderInt("Array Slice", &_arraySlice, 0, (int32)arraySize);
ImGui.BeginDisabled(rtGroup == null);
ImGui.SliderInt("Group Target", &_groupIndex, (rtGroup?.HasDepth == true) ? -1 : 0, (int32)(rtGroup?.ColorTargetCount ?? 1) - 1);
ImGui.TextUnformatted(scope $"Target name: {(rtGroup?.GetTargetDescription(_groupIndex).DebugName ?? "???")}");
ImGui.EndDisabled();
}
if (ImGui.CollapsingHeader("Color and Transparency"))
{
ImGui.TextUnformatted("Channel Swizzle:");
if (ImGui.BeginTable("ColorAndAlpha", 4))
{
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
ImGui.PushID("Color");
ImGui.TextUnformatted("Color:");
ImGui.TableNextColumn();
ImGui.DragFloat("Offset", &_colorOffset, 0.1f);
ImGui.TableNextColumn();
ImGui.DragFloat("Scale", &_colorScale, 0.1f);
ImGui.TableNextColumn();
if (ImGui.Button("Reset"))
{
_colorOffset = 0.0f;
_colorScale = 1.0f;
}
ImGui.SameLine();
ImGui.BeginDisabled();
if (ImGui.Button("Auto"))
{
Runtime.NotImplemented();
}
ImGui.EndDisabled();
ImGui.PopID();
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
ImGui.PushID("Alpha");
ImGui.TextUnformatted("Alpha:");
ImGui.TableNextColumn();
ImGui.DragFloat("Offset", &_alphaOffset, 0.1f);
ImGui.TableNextColumn();
ImGui.DragFloat("Scale", &_alphaScale, 0.1f);
ImGui.TableNextColumn();
if (ImGui.Button("Reset"))
{
_alphaOffset = 0.0f;
_alphaScale = 1.0f;
}
ImGui.PopID();
ImGui.EndTable();
}
ImGui.Separator();
ImGui.TextUnformatted("Channel Swizzle:");
if (ImGui.BeginTable("SwizzleTable", 9))
{
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
SwizzleCombo("R", ref _swizzleR);
ImGui.TableNextColumn();
SwizzleCombo("G", ref _swizzleG);
ImGui.TableNextColumn();
SwizzleCombo("B", ref _swizzleB);
ImGui.TableNextColumn();
SwizzleCombo("A", ref _swizzleA);
ImGui.TableNextColumn();
if (ImGui.Button("Reset swizzle"))
{
_swizzleR = .R;
_swizzleG = .G;
_swizzleB = .B;
_swizzleA = .A;
}
ImGui.EndTable();
}
}
}
private void SwizzleCombo(StringView text, ref ColorChannelSwizzle swizzle)
{
ImGui.TextUnformatted(text);
ImGui.TableNextColumn();
if (ImGui.BeginCombo(scope $"##swizzle{text}", scope $"{swizzle}"))
{
if (ImGui.Selectable("R", swizzle == .R))
swizzle = .R;
if (ImGui.Selectable("G", swizzle == .G))
swizzle = .G;
if (ImGui.Selectable("B", swizzle == .B))
swizzle = .B;
if (ImGui.Selectable("A", swizzle == .A))
swizzle = .A;
if (ImGui.Selectable("None", swizzle == .None))
swizzle = .None;
ImGui.EndCombo();
}
}
public void ViewTexture(RenderTargetGroup texture)
{
ShowSettings(texture.Width, texture.Height, texture.MipLevels - 1, texture.ArraySize - 1, texture);
if (ImGui.BeginChild("imageChild"))
{
UpdateInput();
var viewportSize = ImGui.GetContentRegionAvail();
viewportSize.x = Math.Max(viewportSize.x, 1);
viewportSize.y = Math.Max(viewportSize.y, 1);
if(_target == null || viewportSize.x != _target.Width || viewportSize.y != _target.Height)
{
_target?.ReleaseRef();
_target = new RenderTarget2D(.(.R8G8B8A8_UNorm_SRGB, (.)viewportSize.x, (.)viewportSize.y));
_target.SamplerState = SamplerStateManager.PointClamp;
_depth?.ReleaseRef();
_depth = new DepthStencilTarget((.)viewportSize.x, (.)viewportSize.y, .D16_UNorm);
}
RenderBackground();
RenderTexture(texture);
ImGui.Image(_target, viewportSize);
ImGui.EndChild();
}
}
public void ViewTexture(Texture texture)
{
ShowSettings(texture.Width, texture.Height, texture.MipLevels - 1, texture.ArraySize - 1, null);
if (ImGui.BeginChild("imageChild"))
{
UpdateInput();
var viewportSize = ImGui.GetContentRegionAvail();
viewportSize.x = Math.Max(viewportSize.x, 1);
viewportSize.y = Math.Max(viewportSize.y, 1);
if(_target == null || viewportSize.x != _target.Width || viewportSize.y != _target.Height)
{
_target?.ReleaseRef();
_target = new RenderTarget2D(.(.R8G8B8A8_UNorm_SRGB, (.)viewportSize.x, (.)viewportSize.y));
_target.SamplerState = SamplerStateManager.PointClamp;
_depth?.ReleaseRef();
_depth = new DepthStencilTarget((.)viewportSize.x, (.)viewportSize.y, .D16_UNorm);
}
RenderBackground();
RenderTexture(texture);
ImGui.Image(_target, viewportSize);
ImGui.EndChild();
}
}
float lastWheel;
private void UpdateInput()
{
var windowPos = ImGui.GetWindowPos();
var mousePos = ImGui.GetIO().MousePos;
float2 mouseInWindow = .(mousePos.x - windowPos.x, mousePos.y - windowPos.y);
bool windowHovered = ImGui.IsWindowHovered();
if(windowHovered && Input.IsMouseButtonPressing(.MiddleButton))
{
_moving = true;
}
else if(Input.IsMouseButtonReleased(.MiddleButton))
{
_moving = false;
}
if(windowHovered || _moving)
{
float mouseWheel = ImGui.GetIO().MouseWheel;
float delta = mouseWheel - lastWheel;
if(delta != 0)
{
_position -= mouseInWindow;
_position /= _zoom;
_zoom *= Math.Pow(1.1f, delta);
_position *= _zoom;
_position += mouseInWindow;
}
}
if(_moving)
{
int2 movement = Input.GetMouseMovement();
_position.X += movement.X;
_position.Y += movement.Y;
}
}
OrthographicCamera _camera = new OrthographicCamera() ~ delete _;
private void RenderBackground()
{
Viewport vp = .(0, 0, _target.Width, _target.Height);
RenderCommand.SetViewport(vp);
// TODO: don't clear pink!
RenderCommand.Clear(_target, .Pink);
RenderCommand.Clear(_depth, .Depth, 1.0f, 0);
//_target.Bind();
RenderCommand.SetRenderTarget(_target);
RenderCommand.SetDepthStencilTarget(_depth);
RenderCommand.BindRenderTargets();
float2 targetSize = float2(_target.Width, _target.Height);
_camera.Left = 0;
_camera.Top = 0;
_camera.Right = targetSize.X;
_camera.Bottom = -targetSize.Y;
_camera.NearPlane = -5;
_camera.FarPlane = 5;
_camera.Update();
Renderer2D.BeginScene(_camera);
switch(_backgroundMode)
{
case .Black:
Renderer2D.DrawQuadPivotCorner(float3(0, 0, 1), targetSize, 0, .Black);
case .White:
Renderer2D.DrawQuadPivotCorner(float3(0, 0, 1), targetSize, 0, .White);
case .Pink:
Renderer2D.DrawQuadPivotCorner(float3(0, 0, 1), targetSize, 0, .HotPink);
case .CustomColor:
Renderer2D.DrawQuadPivotCorner(float3(0, 0, 1), targetSize, 0, _backgroundColor);
case .Checkerboard:
float quadSize = 50.0f;
float2 numQuads = (targetSize / 500f) * 10f;
for(float x = 0; x < numQuads.X; x++)
{
for(float y = 0; y < numQuads.Y; y++)
{
Renderer2D.DrawQuadPivotCorner(float3(x * quadSize, -y * quadSize, 1), quadSize.XX, 0, ((x + y) % 2 == 0) ? .White : .Gray);
}
}
break;
}
Renderer2D.EndScene();
}
private void RenderTexture(TextureViewBinding viewedTexture, float2 textureSize, Format format)
{
float2 mippedTextureSize = float2((int)textureSize.X >> _mipLevel, (int)textureSize.X >> _mipLevel);
// Sicherstellen, dass die Auflösung nicht 0 wird
mippedTextureSize = max(mippedTextureSize, 1);
//float2 textureSize = float2(viewedTexture.Width, viewedTexture.Height);
float2 zoomedTextureSize = textureSize * _zoom;
// TODO: Sampler state for rt group
//var sampler = viewedTexture.SamplerState;
/*switch(_sampleMode)
{
case .Point:
viewedTexture.SamplerState = SamplerStateManager.PointClamp;
case .Linear:
viewedTexture.SamplerState = SamplerStateManager.LinearClamp;
}*/
//Renderer2D.BeginScene(_camera, .SortByTexture, _effect);
// float3(_position * .(1, -1), 0)
RenderCommand.Clear(_depth, .Depth, 1.0f, 0);
Matrix matrix = .Translation(float3(_position * .(1, -1), 0)) * .Scaling(float3(zoomedTextureSize, 1));
// TODO: ViewProjection kommt nicht korrekt an?
_renderTargetEffect.Variables["WorldViewProjection"].SetData(_camera.ViewProjection * matrix);
_renderTargetEffect.Variables["ColorOffset"].SetData(_colorOffset);
_renderTargetEffect.Variables["ColorScale"].SetData(_colorScale);
_renderTargetEffect.Variables["AlphaOffset"].SetData(_alphaOffset);
_renderTargetEffect.Variables["AlphaScale"].SetData(_alphaScale);
_renderTargetEffect.Variables["Texels"].SetData(mippedTextureSize);
_renderTargetEffect.Variables["MipLevel"].SetData((float)_mipLevel);
_renderTargetEffect.Variables["Swizzle"].SetData(int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA));
if (format.IsInt())
{
// Int Texture
_renderTargetEffect.Variables["Mode"].SetData(1);
_renderTargetEffect.SetTexture("IntTexture", viewedTexture);
_renderTargetEffect.SetTexture("UIntTexture", null);
_renderTargetEffect.SetTexture("Texture", null);
}
else if (format.IsUInt())
{
// UInt Texture
_renderTargetEffect.Variables["Mode"].SetData(2);
_renderTargetEffect.SetTexture("IntTexture", null);
_renderTargetEffect.SetTexture("UIntTexture", viewedTexture);
_renderTargetEffect.SetTexture("Texture", null);
}
else
{
// Float Texture
_renderTargetEffect.Variables["Mode"].SetData(0);
_renderTargetEffect.SetTexture("IntTexture", null);
_renderTargetEffect.SetTexture("UIntTexture", null);
_renderTargetEffect.SetTexture("Texture", viewedTexture);
}
_renderTargetEffect.Variables["Swizzle"].SetData(int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA));
_renderTargetEffect.ApplyChanges();
_renderTargetEffect.Bind();
Quad.Draw();
}
private void RenderTexture(RenderTargetGroup viewedTexture)
{
var desc = _groupIndex >= 0 ? viewedTexture.[Friend]_colorTargetDescriptions[_groupIndex] : viewedTexture.[Friend]_depthTargetDescription;
RenderTexture(viewedTexture.GetViewBinding(_groupIndex), float2(viewedTexture.Width, viewedTexture.Height), desc.Format.GetShaderViewFormat());
}
private void RenderTexture(Texture viewedTexture)
{
RenderTexture(viewedTexture.GetViewBinding(), float2(viewedTexture.Width, viewedTexture.Height), viewedTexture.Format);
}
}
@@ -6,6 +6,7 @@ using System.Collections;
using GlitchyEngine.Renderer; using GlitchyEngine.Renderer;
using GlitchyEngine; using GlitchyEngine;
using GlitchyEngine.Content; using GlitchyEngine.Content;
using GlitchyEngine.Scripting;
namespace GlitchyEditor.EditWindows namespace GlitchyEditor.EditWindows
{ {
@@ -60,13 +61,15 @@ namespace GlitchyEditor.EditWindows
ShowComponentEditor<TransformComponent>("Transform", entity, => ShowTransformComponentEditor); ShowComponentEditor<TransformComponent>("Transform", entity, => ShowTransformComponentEditor);
ShowComponentEditor<CameraComponent>("Camera", entity, => ShowCameraComponentEditor, => ShowComponentContextMenu<CameraComponent>); ShowComponentEditor<CameraComponent>("Camera", entity, => ShowCameraComponentEditor, => ShowComponentContextMenu<CameraComponent>);
ShowComponentEditor<SpriterRendererComponent>("Sprite Renderer", entity, => ShowSpriteRendererComponentEditor, => ShowComponentContextMenu<SpriterRendererComponent>); ShowComponentEditor<SpriteRendererComponent>("Sprite Renderer", entity, => ShowSpriteRendererComponentEditor, => ShowComponentContextMenu<SpriteRendererComponent>);
ShowComponentEditor<CircleRendererComponent>("Circle Renderer", entity, => ShowCircleRendererComponentEditor, => ShowComponentContextMenu<CircleRendererComponent>);
ShowComponentEditor<MeshRendererComponent>("Mesh Renderer", entity, => ShowMeshRendererComponentEditor, => ShowComponentContextMenu<MeshRendererComponent>); ShowComponentEditor<MeshRendererComponent>("Mesh Renderer", entity, => ShowMeshRendererComponentEditor, => ShowComponentContextMenu<MeshRendererComponent>);
ShowComponentEditor<LightComponent>("Light", entity, => ShowLightComponentEditor, => ShowComponentContextMenu<LightComponent>); ShowComponentEditor<LightComponent>("Light", entity, => ShowLightComponentEditor, => ShowComponentContextMenu<LightComponent>);
ShowComponentEditor<MeshComponent>("Mesh", entity, => ShowMeshComponentEditor, => ShowComponentContextMenu<MeshComponent>); ShowComponentEditor<MeshComponent>("Mesh", entity, => ShowMeshComponentEditor, => ShowComponentContextMenu<MeshComponent>);
ShowComponentEditor<Rigidbody2DComponent>("Rigidbody 2D", entity, => ShowRigidBody2DComponentEditor, => ShowComponentContextMenu<Rigidbody2DComponent>); ShowComponentEditor<Rigidbody2DComponent>("Rigidbody 2D", entity, => ShowRigidBody2DComponentEditor, => ShowComponentContextMenu<Rigidbody2DComponent>);
ShowComponentEditor<BoxCollider2DComponent>("Box collider 2D", entity, => ShowBoxCollider2DComponentEditor, => ShowComponentContextMenu<BoxCollider2DComponent>); ShowComponentEditor<BoxCollider2DComponent>("Box collider 2D", entity, => ShowBoxCollider2DComponentEditor, => ShowComponentContextMenu<BoxCollider2DComponent>);
ShowComponentEditor<CircleCollider2DComponent>("Circle collider 2D", entity, => ShowCircleCollider2DComponentEditor, => ShowComponentContextMenu<CircleCollider2DComponent>); ShowComponentEditor<CircleCollider2DComponent>("Circle collider 2D", entity, => ShowCircleCollider2DComponentEditor, => ShowComponentContextMenu<CircleCollider2DComponent>);
ShowComponentEditor<ScriptComponent>("Script Component", entity, => ShowScriptComponentEditor, => ShowComponentContextMenu<ScriptComponent>);
ShowAddComponentButton(entity); ShowAddComponentButton(entity);
} }
@@ -117,18 +120,18 @@ namespace GlitchyEditor.EditWindows
private static void ShowNameComponentEditor(Entity entity) private static void ShowNameComponentEditor(Entity entity)
{ {
if (!entity.HasComponent<DebugNameComponent>()) if (!entity.HasComponent<NameComponent>())
return; return;
char8[256] nameBuffer = default; char8[256] nameBuffer = default;
DebugNameComponent* component = entity.GetComponent<DebugNameComponent>(); NameComponent* component = entity.GetComponent<NameComponent>();
String name = null; StringView name = null;
if(component != null) if(component != null)
{ {
name = component.DebugName; name = component.Name;
} }
else else
{ {
@@ -142,11 +145,10 @@ namespace GlitchyEditor.EditWindows
{ {
if(component == null) if(component == null)
{ {
component = entity.AddComponent<DebugNameComponent>(); component = entity.AddComponent<NameComponent>();
} }
component.DebugName.Clear(); component.Name = StringView(&nameBuffer);
component.DebugName.Append(&nameBuffer);
} }
} }
@@ -158,16 +160,32 @@ namespace GlitchyEditor.EditWindows
textWidth += ImGui.GetStyle().FramePadding.x * 3.0f; textWidth += ImGui.GetStyle().FramePadding.x * 3.0f;
Vector3 position = transform.Position; float3 position = transform.Position;
if (ImGui.EditVector3("Position", ref position, .Zero, 0.1f, textWidth)) if (ImGui.Editfloat3("Position", ref position, .Zero, 0.1f, textWidth))
{
transform.Position = position; transform.Position = position;
Vector3 rotationEuler = MathHelper.ToDegrees(transform.EditorRotationEuler); // if necessary reposition Rigidbody2D
if (ImGui.EditVector3("Rotation", ref rotationEuler, .Zero, 0.1f, textWidth)) if (entity.TryGetComponent<Rigidbody2DComponent>(let rigidbody2D))
{
rigidbody2D.SetPosition(position.XY);
}
}
float3 rotationEuler = MathHelper.ToDegrees(transform.EditorRotationEuler);
if (ImGui.Editfloat3("Rotation", ref rotationEuler, .Zero, 0.1f, textWidth))
{
transform.EditorRotationEuler = MathHelper.ToRadians(rotationEuler); transform.EditorRotationEuler = MathHelper.ToRadians(rotationEuler);
// if necessary reposition Rigidbody2D
if (entity.TryGetComponent<Rigidbody2DComponent>(let rigidbody2D))
{
rigidbody2D.SetAngle(rotationEuler.Z);
}
}
Vector3 scale = transform.Scale; float3 scale = transform.Scale;
if (ImGui.EditVector3("Scale", ref scale, .One, 0.1f, textWidth)) if (ImGui.Editfloat3("Scale", ref scale, .One, 0.1f, textWidth))
transform.Scale = scale; transform.Scale = scale;
} }
@@ -250,7 +268,7 @@ namespace GlitchyEditor.EditWindows
} }
} }
private static void ShowSpriteRendererComponentEditor(Entity entity, SpriterRendererComponent* spriteRendererComponent) private static void ShowSpriteRendererComponentEditor(Entity entity, SpriteRendererComponent* spriteRendererComponent)
{ {
ColorRGBA spriteColor = ColorRGBA.LinearToSRGB(spriteRendererComponent.Color); ColorRGBA spriteColor = ColorRGBA.LinearToSRGB(spriteRendererComponent.Color);
if (ImGui.ColorEdit4("Color", ref spriteColor)) if (ImGui.ColorEdit4("Color", ref spriteColor))
@@ -268,10 +286,7 @@ namespace GlitchyEditor.EditWindows
StringView path = .((char8*)payload.Data, (int)payload.DataSize); StringView path = .((char8*)payload.Data, (int)payload.DataSize);
using (Texture2D texture = Content.LoadAsset<Texture2D>(path)) spriteRendererComponent.Sprite = Content.LoadAsset(path);
{
spriteRendererComponent.Sprite = texture;
}
} }
ImGui.EndDragDropTarget(); ImGui.EndDragDropTarget();
@@ -279,8 +294,36 @@ namespace GlitchyEditor.EditWindows
ImGui.EditVector<4>("UV Transform", ref *(float[4]*)&spriteRendererComponent.UvTransform); ImGui.EditVector<4>("UV Transform", ref *(float[4]*)&spriteRendererComponent.UvTransform);
}
ImGui.Checkbox("Is Circle", &spriteRendererComponent.IsCircle); private static void ShowCircleRendererComponentEditor(Entity entity, CircleRendererComponent* circleRendererComponent)
{
ColorRGBA spriteColor = ColorRGBA.LinearToSRGB(circleRendererComponent.Color);
if (ImGui.ColorEdit4("Color", ref spriteColor))
circleRendererComponent.Color = ColorRGBA.SRgbToLinear(spriteColor);
ImGui.Button("Texture");
if (ImGui.BeginDragDropTarget())
{
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM");
if (payload != null)
{
Log.EngineLogger.Warning("");
StringView path = .((char8*)payload.Data, (int)payload.DataSize);
circleRendererComponent.Sprite = Content.LoadAsset(path);
}
ImGui.EndDragDropTarget();
}
ImGui.EditVector<4>("UV Transform", ref *(float[4]*)&circleRendererComponent.UvTransform);
ImGui.DragFloat("Inner Radius", &circleRendererComponent.InnerRadius, 0.1f, 0.0f, 1.0f);
} }
private static void ShowMeshRendererComponentEditor(Entity entity, MeshRendererComponent* meshRendererComponent) private static void ShowMeshRendererComponentEditor(Entity entity, MeshRendererComponent* meshRendererComponent)
@@ -301,10 +344,7 @@ namespace GlitchyEditor.EditWindows
{ {
StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize); StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize);
using (Material loadedMaterial = Content.LoadAsset<Material>(fullpath)) meshRendererComponent.Material = Content.LoadAsset(fullpath);
{
meshRendererComponent.Material = loadedMaterial;
}
} }
ImGui.EndDragDropTarget(); ImGui.EndDragDropTarget();
@@ -351,12 +391,12 @@ namespace GlitchyEditor.EditWindows
textWidth += ImGui.GetStyle().FramePadding.x * 3.0f; textWidth += ImGui.GetStyle().FramePadding.x * 3.0f;
Vector2 offset = boxCollider.Offset; float2 offset = boxCollider.Offset;
if (ImGui.EditVector2("Offset", ref offset, .Zero, 0.1f, textWidth)) if (ImGui.Editfloat2("Offset", ref offset, .Zero, 0.1f, textWidth))
boxCollider.Offset = offset; boxCollider.Offset = offset;
Vector2 size = boxCollider.Size; float2 size = boxCollider.Size;
if (ImGui.EditVector2("Size", ref size, .Zero, 0.1f, textWidth)) if (ImGui.Editfloat2("Size", ref size, .Zero, 0.1f, textWidth))
boxCollider.Size = size; boxCollider.Size = size;
float density = boxCollider.Density; float density = boxCollider.Density;
@@ -382,8 +422,8 @@ namespace GlitchyEditor.EditWindows
textWidth += ImGui.GetStyle().FramePadding.x * 3.0f; textWidth += ImGui.GetStyle().FramePadding.x * 3.0f;
Vector2 offset = circleCollider.Offset; float2 offset = circleCollider.Offset;
if (ImGui.EditVector2("Offset", ref offset, .Zero, 0.1f, textWidth)) if (ImGui.Editfloat2("Offset", ref offset, .Zero, 0.1f, textWidth))
circleCollider.Offset = offset; circleCollider.Offset = offset;
float radius = circleCollider.Radius; float radius = circleCollider.Radius;
@@ -407,6 +447,273 @@ namespace GlitchyEditor.EditWindows
circleCollider.RestitutionThreshold = restitutionThreshold; circleCollider.RestitutionThreshold = restitutionThreshold;
} }
private static void ShowScriptComponentEditor(Entity entity, ScriptComponent* scriptComponent)
{
static char8[64] buffer = .();
StringView search = StringView();
//char8* scriptLabel = scriptComponent.Instance?.ScriptClass.FullName.ToScopeCStr!() ?? "Select Script...";
char8* scriptLabel = scriptComponent.Instance?.ScriptClass.FullName.CStr() ?? "Select Script...";
if (ImGui.Button(scriptLabel))
ImGui.OpenPopup("SelectScript");
if (ImGui.BeginPopup("SelectScript"))
{
ImGui.InputText("##ScriptSearch", &buffer, buffer.Count);
search = StringView(&buffer);
for (let (className, scriptClass) in ScriptEngine.EntityClasses)
{
if (!search.IsWhiteSpace && !className.Contains(search, true))
continue;
if (ImGui.Selectable(className.ToScopeCStr!(),
className == scriptComponent.ScriptClassName))
{
//scriptComponent.Instance = new ScriptInstance(scriptClass);
// decrement refCount to 1 (scriptComponent.Instance increments its)
//scriptComponent.Instance.ReleaseRef();
//ScriptEngine.InitializeInstance(entity, scriptComponent);
scriptComponent.ScriptClassName = scriptClass.FullName;
//ScriptEngine.CreateScriptFieldMap(entity);
}
}
ImGui.EndPopup();
}
/*
ScriptClass scriptClass = ScriptEngine.GetScriptClass(scriptComponent.ScriptClassName);
/*T GetFieldValue<T>()
{
return scriptComponent.Instance.GetFieldValue<T>(monoField);
}
void SetFieldValue<T>(in T value)
{
scriptComponent.Instance.SetFieldValue<T>(monoField, value);
}*/
T GetFieldValue<T>(ScriptInstance scriptInstance, in ScriptField scriptField)
{
return scriptInstance.GetFieldValue<T>(scriptField);
}
void GetFieldValue<T>(ScriptInstance scriptInstance, in ScriptField scriptField, out T value)
{
value = scriptInstance.GetFieldValue<T>(scriptField);
}
void SetFieldValue<T>(ScriptInstance scriptInstance, in ScriptField scriptField, in T value)
{
scriptInstance.SetFieldValue<T>(scriptField, value);
}
if (scriptComponent.Instance?.IsInitialized == true)
{
SharpClass sharpClass = scriptComponent.Instance.ScriptClass;
ScriptInstance scriptInstance = scriptComponent.Instance;
for (let (fieldName, scriptField) in sharpClass.Fields)
{
var monoField = scriptField.[Friend]_monoField;
switch (scriptField.FieldType)
{
case .Bool:
var value = GetFieldValue<bool>(scriptInstance, scriptField);
if (ImGui.Checkbox(fieldName.ToScopeCStr!(), &value))
SetFieldValue(scriptInstance, scriptField, value);
case .SByte:
var value = GetFieldValue<int8>(scriptInstance, scriptField);
if (ImGui.DragScalar(fieldName.ToScopeCStr!(), .S8, &value))
SetFieldValue(scriptInstance, scriptField, value);
case .Short:
var value = GetFieldValue<int16>(scriptInstance, scriptField);
if (ImGui.DragScalar(fieldName.ToScopeCStr!(), .S16, &value))
SetFieldValue(scriptInstance, scriptField, value);
case .Int:
var value = GetFieldValue<int32>(scriptInstance, scriptField);
if (ImGui.DragScalar(fieldName.ToScopeCStr!(), .S32, &value))
SetFieldValue(scriptInstance, scriptField, value);
case .Long:
var value = GetFieldValue<int64>(scriptInstance, scriptField);
if (ImGui.DragScalar(fieldName.ToScopeCStr!(), .S64, &value))
SetFieldValue(scriptInstance, scriptField, value);
case .Byte:
var value = GetFieldValue<uint8>(scriptInstance, scriptField);
if (ImGui.DragScalar(fieldName.ToScopeCStr!(), .U8, &value))
SetFieldValue(scriptInstance, scriptField, value);
case .UShort:
var value = GetFieldValue<uint16>(scriptInstance, scriptField);
if (ImGui.DragScalar(fieldName.ToScopeCStr!(), .U16, &value))
SetFieldValue(scriptInstance, scriptField, value);
case .UInt:
var value = GetFieldValue<uint32>(scriptInstance, scriptField);
if (ImGui.DragScalar(fieldName.ToScopeCStr!(), .U32, &value))
SetFieldValue(scriptInstance, scriptField, value);
case .ULong:
var value = GetFieldValue<uint64>(scriptInstance, scriptField);
if (ImGui.DragScalar(fieldName.ToScopeCStr!(), .U64, &value))
SetFieldValue(scriptInstance, scriptField, value);
case .Float:
var value = GetFieldValue<float>(scriptInstance, scriptField);
if (ImGui.DragScalar(fieldName.ToScopeCStr!(), .Float, &value))
SetFieldValue(scriptInstance, scriptField, value);
case .float2:
GetFieldValue<float2>(scriptInstance, scriptField, var value);
if (ImGui.Editfloat2(fieldName, ref value))
SetFieldValue(scriptInstance, scriptField, value);
case .float3:
GetFieldValue<float3>(scriptInstance, scriptField, var value);
if (ImGui.Editfloat3(fieldName, ref value))
SetFieldValue(scriptInstance, scriptField, value);
case .float4:
GetFieldValue<float4>(scriptInstance, scriptField, var value);
if (ImGui.Editfloat4(fieldName, ref value))
SetFieldValue(scriptInstance, scriptField, value);
case .Double:
var value = GetFieldValue<double>(scriptInstance, scriptField);
if (ImGui.DragScalar(fieldName.ToScopeCStr!(), .Double, &value))
SetFieldValue(scriptInstance, scriptField, value);
case .Entity:
// TODO!
case .Enum:
// TODO!
case .Struct:
// TODO!
default:
Log.EngineLogger.Error($"Unhandled field type {scriptField.FieldType}");
}
}
}
else if (scriptClass != null)
{
let scriptFields = ScriptEngine.GetScriptFieldMap(entity);
for (var (name, field) in ref scriptFields)
{
switch (field.Type)
{
case .Bool:
var value = field.GetData<bool>();
if (ImGui.Checkbox(name.CStr(), &value))
field.SetData(value);
case .SByte:
var value = field.GetData<int8>();
if (ImGui.DragScalar(name.CStr(), .S8, &value))
field.SetData(value);
case .Short:
var value = field.GetData<int16>();
if (ImGui.DragScalar(name.CStr(), .S16, &value))
field.SetData(value);
case .Int:
var value = field.GetData<int32>();
if (ImGui.DragScalar(name.CStr(), .S32, &value))
field.SetData(value);
case .Int2:
var value = field.GetData<int2>();
if (ImGui.DragScalarN(name.CStr(), .S32, &value, 2))
field.SetData(value);
case .Int3:
var value = field.GetData<int3>();
if (ImGui.DragScalarN(name.CStr(), .S32, &value, 3))
field.SetData(value);
case .Int4:
var value = field.GetData<int4>();
if (ImGui.DragScalarN(name.CStr(), .S32, &value, 4))
field.SetData(value);
case .Long:
var value = field.GetData<int64>();
if (ImGui.DragScalar(name.CStr(), .S64, &value))
field.SetData(value);
case .Byte:
var value = field.GetData<uint8>();
if (ImGui.DragScalar(name.CStr(), .U8, &value))
field.SetData(value);
case .UShort:
var value = field.GetData<uint16>();
if (ImGui.DragScalar(name.CStr(), .U16, &value))
field.SetData(value);
case .UInt:
var value = field.GetData<uint32>();
if (ImGui.DragScalar(name.CStr(), .U32, &value))
field.SetData(value);
case .ULong:
var value = field.GetData<uint64>();
if (ImGui.DragScalar(name.CStr(), .U64, &value))
field.SetData(value);
case .Float:
var value = field.GetData<float>();
if (ImGui.DragScalar(name.CStr(), .Float, &value))
field.SetData(value);
case .float2:
var value = field.GetData<float2>();
if (ImGui.Editfloat2(name, ref value))
field.SetData(value);
case .float3:
var value = field.GetData<float3>();
if (ImGui.Editfloat3(name, ref value))
field.SetData(value);
case .float4:
var value = field.GetData<float4>();
if (ImGui.Editfloat4(name, ref value))
field.SetData(value);
case .Double:
var value = field.GetData<double>();
if (ImGui.DragScalar(name.CStr(), .Double, &value))
field.SetData(value);
case .Double2:
var value = field.GetData<double2>();
if (ImGui.DragScalarN(name.CStr(), .Double, &value, 2))
field.SetData(value);
case .Double3:
var value = field.GetData<double3>();
if (ImGui.DragScalarN(name.CStr(), .Double, &value, 3))
field.SetData(value);
case .Double4:
var value = field.GetData<double4>();
if (ImGui.DragScalarN(name.CStr(), .Double, &value, 4))
field.SetData(value);
case .Enum:
// TODO!
//case .String:
// TODO!
case .Entity:
// TODO!
case .Struct:
// TODO!
default:
Log.EngineLogger.Error($"Unhandled field type {field.Type}");
}
}
}
*/
}
private static void LabelColumn(StringView label) private static void LabelColumn(StringView label)
{ {
ImGui.TextUnformatted(label); ImGui.TextUnformatted(label);
@@ -488,10 +795,7 @@ namespace GlitchyEditor.EditWindows
StringView filePath = fullpath.Substring(0, idx); StringView filePath = fullpath.Substring(0, idx);
StringView meshName = fullpath.Substring(idx + 1);*/ StringView meshName = fullpath.Substring(idx + 1);*/
using (GeometryBinding geometry = Content.LoadAsset<GeometryBinding>(fullpath)) meshComponent.Mesh = Content.LoadAsset(fullpath);
{
meshComponent.Mesh = geometry;
}
// TODO: support multiple primitives (treat every primitive as a single mesh? or: mesh can have multiple primitives) // TODO: support multiple primitives (treat every primitive as a single mesh? or: mesh can have multiple primitives)
/*using (GeometryBinding binding = ModelLoader.LoadMesh(filePath, meshName, 0)) /*using (GeometryBinding binding = ModelLoader.LoadMesh(filePath, meshName, 0))
@@ -557,13 +861,15 @@ namespace GlitchyEditor.EditWindows
ImGui.Separator(); ImGui.Separator();
ShowComponentButton<CameraComponent>("Camera"); ShowComponentButton<CameraComponent>("Camera");
ShowComponentButton<SpriterRendererComponent>("Sprite Renderer"); ShowComponentButton<SpriteRendererComponent>("Sprite Renderer");
ShowComponentButton<CircleRendererComponent>("Circle Renderer");
ShowComponentButton<LightComponent>("Light"); ShowComponentButton<LightComponent>("Light");
ShowComponentButton<Rigidbody2DComponent>("Rigidbody 2D"); ShowComponentButton<Rigidbody2DComponent>("Rigidbody 2D");
ShowComponentButton<BoxCollider2DComponent>("Box collider 2D"); ShowComponentButton<BoxCollider2DComponent>("Box collider 2D");
ShowComponentButton<CircleCollider2DComponent>("Circle collider 2D"); ShowComponentButton<CircleCollider2DComponent>("Circle collider 2D");
ShowComponentButton<MeshComponent>("Mesh"); ShowComponentButton<MeshComponent>("Mesh");
ShowComponentButton<MeshRendererComponent>("Mesh Renderer"); ShowComponentButton<MeshRendererComponent>("Mesh Renderer");
ShowComponentButton<ScriptComponent>("C# Script");
ImGui.EndCombo(); ImGui.EndCombo();
} }
@@ -14,8 +14,7 @@ namespace GlitchyEditor.EditWindows
class ContentBrowserWindow : EditorWindow class ContentBrowserWindow : EditorWindow
{ {
// TODO: Get from project public const String s_WindowTitle = "Content Browser";
//const String ContentDirectory = "./content";
private append String _currentDirectory = .(); private append String _currentDirectory = .();
@@ -43,32 +42,76 @@ namespace GlitchyEditor.EditWindows
_currentDirectory.Set(_manager.ContentDirectory); _currentDirectory.Set(_manager.ContentDirectory);
} }
if(!ImGui.Begin("Content Browser", &_open, .None)) if(!ImGui.Begin(s_WindowTitle, &_open, .None))
{ {
ImGui.End(); ImGui.End();
return; return;
} }
// Context menu when clicking on the background. ImGui.PushStyleVar(.CellPadding, .(0, 0));
if (ImGui.BeginPopupContextWindow())
bool alt_pressed = ImGui.GetIO().KeyAlt;
if (ImGui.BeginTable("table", 2, .BordersInnerV | .Resizable | .Reorderable | .NoPadOuterX))
{ {
ShowCurrentFolderContextMenu(); if (alt_pressed)
ImGui.EndPopup(); {
// Header anzeigen, damit sie neu angeordnet werden können
ImGui.TableSetupColumn("Folder Hierarchy");
ImGui.TableSetupColumn("Files");
ImGui.TableHeadersRow();
}
ImGui.TableNextRow();
ImGui.TableSetColumnIndex(0);
if (ImGui.BeginChild("Sidebar"))
{
DrawDirectorySideBar();
ImGui.EndChild();
}
ImGui.TableNextColumn();
DrawSearchBar();
ImGui.Separator();
if (ImGui.BeginChild("Files"))
{
DrawCurrentDirectory();
// Context menu when clicking on the background.
if (ImGui.BeginPopupContextWindow())
{
ShowCurrentFolderContextMenu();
ImGui.EndPopup();
}
ImGui.EndChild();
}
ImGui.EndTable();
} }
ImGui.Columns(2); ImGui.PopStyleVar(1);
DrawDirectorySideBar();
ImGui.NextColumn();
DrawCurrentDirectory();
ImGui.Columns(1);
ImGui.End(); ImGui.End();
} }
char8[256] _filesFilter = .();
bool _searchEverywhere;
private void DrawSearchBar()
{
ImGui.TextUnformatted("Search:");
ImGui.SameLine();
ImGui.InputText("##search", &_filesFilter, _filesFilter.Count);
ImGui.SameLine();
ImGui.Checkbox("Search everywhere", &_searchEverywhere);
}
/// Renders the context menu that is shown when the user right clicks on the background of the file browser. /// Renders the context menu that is shown when the user right clicks on the background of the file browser.
private void ShowCurrentFolderContextMenu() private void ShowCurrentFolderContextMenu()
{ {
@@ -125,32 +168,74 @@ namespace GlitchyEditor.EditWindows
} }
} }
private static Vector2 DirectoryItemSize = .(110, 110); private static float2 DirectoryItemSize = .(110, 110);
const Vector2 padding = .(24, 24); const float2 padding = .(24, 24);
/// Renders the contents of _currentDirectory. /// Renders the contents of _currentDirectory.
private void DrawCurrentDirectory() private void DrawCurrentDirectory()
{ {
if (_currentDirectory.IsEmpty) List<TreeNode<AssetNode>> files = null;
return;
ImGui.Style* style = ImGui.GetStyle(); ImGui.Style* style = ImGui.GetStyle();
float window_visible_x2 = ImGui.GetWindowPos().x + ImGui.GetWindowContentRegionMax().x; float window_visible_x2 = ImGui.GetWindowPos().x + ImGui.GetWindowContentRegionMax().x;
// Get the node of the current directory. StringView searchFilter = StringView(&_filesFilter);
var currentDirectoryNode = _manager.AssetHierarchy.GetNodeFromPath(_currentDirectory);
if (currentDirectoryNode case .Err) if (_searchEverywhere && searchFilter.Length > 0)
{ {
Log.EngineLogger.Error($"No node exists for {_currentDirectory}."); files = scope:: List<TreeNode<AssetNode>>();
ImGui.TextUnformatted("Failed to display contents of directory."); _manager.AssetHierarchy.[Friend]_assetHierarchy.ForEach(scope (node) =>
return; {
if (node->Name.Contains(searchFilter, true))
{
files.Add(node);
}
});
}
else
{
if (_currentDirectory.IsEmpty)
return;
// Get the node of the current directory.
var currentDirectoryNode = _manager.AssetHierarchy.GetNodeFromPath(_currentDirectory);
if (currentDirectoryNode case .Err)
{
Log.EngineLogger.Error($"No node exists for {_currentDirectory}.");
ImGui.TextUnformatted("Failed to display contents of directory.");
return;
}
files = currentDirectoryNode->Children;
// show back button (".."-File)
if (currentDirectoryNode->Parent != null)
{
ImGui.PushID("Back");
DrawBackButton(currentDirectoryNode->Parent);
// X-Coordinate of the right side of the current entry.
float currentButtonRight = ImGui.GetItemRectMax().x;
// Expected right-Coordinate if next entry was on the same line.
float expectedButtonRight = currentButtonRight + style.ItemSpacing.x + DirectoryItemSize.X;
// If the next button won't fit on the same line we start a new line.
if (expectedButtonRight < window_visible_x2)
ImGui.SameLine();
ImGui.PopID();
}
} }
for (var entry in currentDirectoryNode->Children) for (var entry in files)
{ {
if (!entry->Name.Contains(searchFilter, true))
continue;
ImGui.PushID(entry->Name); ImGui.PushID(entry->Name);
DrawDirectoryItem(entry); DrawDirectoryItem(entry);
@@ -161,13 +246,52 @@ namespace GlitchyEditor.EditWindows
float expectedButtonRight = currentButtonRight + style.ItemSpacing.x + DirectoryItemSize.X; float expectedButtonRight = currentButtonRight + style.ItemSpacing.x + DirectoryItemSize.X;
// If we aren't the last entry and the next button won't fit on the same line we start a new line. // If we aren't the last entry and the next button won't fit on the same line we start a new line.
if (entry != currentDirectoryNode->Children.Back && expectedButtonRight < window_visible_x2) if (entry != files.Back && expectedButtonRight < window_visible_x2)
ImGui.SameLine(); ImGui.SameLine();
ImGui.PopID(); ImGui.PopID();
} }
} }
/// Renders the button for the given directory item.
private void DrawBackButton(TreeNode<AssetNode> entry)
{
ImGui.BeginChild("item", (.)DirectoryItemSize);
if (entry->Path == _selectedFile)
{
var color = ImGui.GetStyleColorVec4(.ButtonHovered);
ImGui.PushStyleColor(.Button, *color);
}
else
{
ImGui.PushStyleColor(.Button, ImGui.Vec4(0, 0, 0, 0));
}
SubTexture2D image = s_FolderTexture;
ImGui.ImageButton(image, (.)(DirectoryItemSize - padding));
ImGui.PopStyleColor();
if (ImGui.IsItemHovered() && ImGui.IsMouseClicked(.Left))
{
if (_selectedFile != entry->Path)
{
_selectedFile.Set(entry->Path);
}
}
if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(.Left))
{
EntryDoubleClicked(entry);
}
ImGui.TextUnformatted("..");
ImGui.EndChild();
}
/// Renders the button for the given directory item. /// Renders the button for the given directory item.
private void DrawDirectoryItem(TreeNode<AssetNode> entry) private void DrawDirectoryItem(TreeNode<AssetNode> entry)
{ {
@@ -9,7 +9,7 @@ using GlitchyEngine.Events;
namespace GlitchyEditor.EditWindows namespace GlitchyEditor.EditWindows
{ {
class SceneViewportWindow : EditorWindow class EditorViewportWindow : EditorWindow
{ {
public const String s_WindowTitle = "Scene"; public const String s_WindowTitle = "Scene";
@@ -27,12 +27,14 @@ namespace GlitchyEditor.EditWindows
private float _angleSnap = 45.0f; private float _angleSnap = 45.0f;
private bool _doSnap = false; private bool _doSnap = false;
private bool _visible;
public uint32 SelectedEntityId {get; private set; } public uint32 SelectedEntityId {get; private set; }
public bool SelectionChanged { get; private set; } public bool SelectionChanged { get; private set; }
public Vector2 ViewportSize => (Vector2)_oldViewportSize; public float2 ViewportSize => (float2)_oldViewportSize;
// Occurs when the viewport is resized. // Occurs when the viewport is resized.
public Event<EventHandler<Vector2>> ViewportSizeChanged ~ _.Dispose(); public Event<EventHandler<float2>> ViewportSizeChanged ~ _.Dispose();
// Occurs when an entity was clicked. // Occurs when an entity was clicked.
public Event<EventHandler<uint32>> EntityClicked ~ _.Dispose(); public Event<EventHandler<uint32>> EntityClicked ~ _.Dispose();
@@ -52,6 +54,8 @@ namespace GlitchyEditor.EditWindows
/// Gets or sets whether the editor functionality (gizmo, picking etc.) is enabled. /// Gets or sets whether the editor functionality (gizmo, picking etc.) is enabled.
public bool EditorMode { get; set; } = true public bool EditorMode { get; set; } = true
public bool Visible => _visible;
public this(Editor editor) public this(Editor editor)
{ {
_editor = editor; _editor = editor;
@@ -65,9 +69,13 @@ namespace GlitchyEditor.EditWindows
if(!ImGui.Begin(s_WindowTitle, &_open, .NoScrollbar | .MenuBar)) if(!ImGui.Begin(s_WindowTitle, &_open, .NoScrollbar | .MenuBar))
{ {
ImGui.End(); ImGui.End();
_visible = false;
return; return;
} }
_visible = true;
let viewportSize = ImGui.GetContentRegionAvail(); let viewportSize = ImGui.GetContentRegionAvail();
if(ImGui.IsWindowHovered() && Input.IsMouseButtonPressing(.RightButton)) if(ImGui.IsWindowHovered() && Input.IsMouseButtonPressing(.RightButton))
@@ -78,30 +86,27 @@ namespace GlitchyEditor.EditWindows
_hasFocus = ImGui.IsWindowFocused(); _hasFocus = ImGui.IsWindowFocused();
if (EditorMode) ShowMenuBar();
if (_editor.CurrentCamera.[Friend]BindMouse && _hasFocus)
WrapMouseInViewport();
// If we wrapped this frame we weren't hovering because the cursor has to be be out of bounds to wrap
_editor.CurrentCamera.AllowMove = _hasFocus && (ImGui.IsWindowHovered() || _wrappedCursor);
if (_hasFocus && !_editor.CurrentCamera.InUse)
{ {
ShowMenuBar(); if (Input.IsKeyPressing(.Q))
_gizmoType = .TRANSLATE;
if (_editor.CurrentCamera.[Friend]BindMouse && _hasFocus) if (Input.IsKeyPressing(.W))
WrapMouseInViewport(); _gizmoType = .ROTATE;
if (Input.IsKeyPressing(.E))
_gizmoType = .SCALE;
// If we wrapped this frame we weren't hovering because the cursor has to be be out of bounds to wrap if (Input.IsKeyPressing(.G))
_editor.CurrentCamera.AllowMove = _hasFocus && (ImGui.IsWindowHovered() || _wrappedCursor); _gizmoMode = .WORLD;
if (Input.IsKeyPressing(.L))
if (_hasFocus && !_editor.CurrentCamera.InUse) _gizmoMode = .LOCAL;
{
if (Input.IsKeyPressing(.Q))
_gizmoType = .TRANSLATE;
if (Input.IsKeyPressing(.W))
_gizmoType = .ROTATE;
if (Input.IsKeyPressing(.E))
_gizmoType = .SCALE;
if (Input.IsKeyPressing(.G))
_gizmoMode = .WORLD;
if (Input.IsKeyPressing(.L))
_gizmoMode = .LOCAL;
}
} }
if(_renderTarget != null) if(_renderTarget != null)
@@ -111,20 +116,17 @@ namespace GlitchyEditor.EditWindows
//ImGui.Image(_editor.CurrentScene.[Friend]_compositeTarget.GetViewBinding(0), viewportSize); //ImGui.Image(_editor.CurrentScene.[Friend]_compositeTarget.GetViewBinding(0), viewportSize);
} }
if (EditorMode) HandleDropTarget();
{
HandleDropTarget(); bool gizmoUsed = DrawImGuizmo(viewportSize);
bool gizmoUsed = DrawImGuizmo(viewportSize); MousePicking(viewportSize, gizmoUsed);
MousePicking(viewportSize, gizmoUsed);
}
ImGui.End(); ImGui.End();
if(_oldViewportSize != viewportSize) if(_oldViewportSize != viewportSize)
{ {
ViewportSizeChanged.Invoke(this, (Vector2)viewportSize); ViewportSizeChanged.Invoke(this, (float2)viewportSize);
_viewPortChanged = true; _viewPortChanged = true;
_oldViewportSize = viewportSize; _oldViewportSize = viewportSize;
} }
@@ -152,34 +154,36 @@ namespace GlitchyEditor.EditWindows
{ {
_wrappedCursor = false; _wrappedCursor = false;
let mousePos = (Vector2)ImGui.GetMousePos(); let mousePos = (float2)ImGui.GetMousePos();
let winPos = (Vector2)ImGui.GetWindowPos(); let winPos = (float2)ImGui.GetWindowPos();
let regionMin = winPos + (Vector2)ImGui.GetWindowContentRegionMin(); let regionMin = winPos + (float2)ImGui.GetWindowContentRegionMin();
let regionMax = winPos + (Vector2)ImGui.GetWindowContentRegionMax(); let regionMax = winPos + (float2)ImGui.GetWindowContentRegionMax();
Vector2 newMousePos = mousePos;
if (mousePos.X < regionMin.X) ImGui.DrawRect((.)regionMin, (.)regionMax, .(0, 255, 0));
float2 newMousePos = mousePos;
if (mousePos.X < regionMin.X + 1)
{ {
newMousePos.X = regionMax.X - 1; newMousePos.X = regionMax.X - 2;
} }
else if (mousePos.X > regionMax.X - 1) else if (mousePos.X > regionMax.X - 1)
{ {
newMousePos.X = regionMin.X + 1; newMousePos.X = regionMin.X + 2;
} }
if (mousePos.Y < regionMin.Y) if (mousePos.Y < regionMin.Y + 1)
{ {
newMousePos.Y = regionMax.Y - 1; newMousePos.Y = regionMax.Y - 100;
} }
else if (mousePos.Y > regionMax.Y - 1) else if (mousePos.Y > regionMax.Y - 1)
{ {
newMousePos.Y = regionMin.Y + 1; newMousePos.Y = regionMin.Y + 10;
} }
if (newMousePos != mousePos) if (any(newMousePos != mousePos))
{ {
Input.SetMousePosition((Int2)newMousePos); Input.SetMousePosition((int2)newMousePos);
// After wrapping the cursor the the other side, the camera controller must not compare the positions, // After wrapping the cursor the the other side, the camera controller must not compare the positions,
// because the delta doesn't represent the correct movement of the cursor. // because the delta doesn't represent the correct movement of the cursor.
// TODO: can be solved by using direct mouse movement instead of comparing positions // TODO: can be solved by using direct mouse movement instead of comparing positions
@@ -191,10 +195,10 @@ namespace GlitchyEditor.EditWindows
/// If the user clicks, the entity beneath the cursor will be selected. /// If the user clicks, the entity beneath the cursor will be selected.
private void MousePicking(ImGui.Vec2 viewportSize, bool gizmoUsed) private void MousePicking(ImGui.Vec2 viewportSize, bool gizmoUsed)
{ {
Vector2 relativeMouse = (Vector2)ImGui.GetMousePos() - (Vector2)ImGui.GetItemRectMin(); float2 relativeMouse = (float2)ImGui.GetMousePos() - (float2)ImGui.GetItemRectMin();
int rtWidth = _editor.CurrentScene.[Friend]_compositeTarget.Width; int rtWidth = _editor.EditorSceneRenderer.CompositeTarget.Width;
int rtHeight = _editor.CurrentScene.[Friend]_compositeTarget.Height; int rtHeight = _editor.EditorSceneRenderer.CompositeTarget.Height;
if (Input.IsMouseButtonPressing(.LeftButton) && if (Input.IsMouseButtonPressing(.LeftButton) &&
ImGui.IsWindowHovered() && !gizmoUsed && !_editor.CurrentCamera.InUse && ImGui.IsWindowHovered() && !gizmoUsed && !_editor.CurrentCamera.InUse &&
@@ -204,7 +208,7 @@ namespace GlitchyEditor.EditWindows
{ {
uint32 id = uint32.MaxValue; uint32 id = uint32.MaxValue;
_editor.CurrentScene.[Friend]_compositeTarget.GetData<uint32>(&id, 1, (.)relativeMouse.X, (.)relativeMouse.Y, 1, 1); _editor.EditorSceneRenderer.CompositeTarget.GetData<uint32>(&id, 1, (.)relativeMouse.X, (.)relativeMouse.Y, 1, 1);
SelectionChanged = true; SelectionChanged = true;
SelectedEntityId = id; SelectedEntityId = id;
@@ -312,9 +316,9 @@ namespace GlitchyEditor.EditWindows
parentView = parentTransformCmp.WorldTransform.Invert(); parentView = parentTransformCmp.WorldTransform.Invert();
} }
Vector3 snap = .(_snap); float3 snap = (float3)_snap;
if (_gizmoType.HasFlag(.ROTATE)) if (_gizmoType.HasFlag(.ROTATE))
snap = .(_angleSnap); snap = (float3)_angleSnap;
if (ImGuizmo.Manipulate((.)&view, (.)&projection, _gizmoType, _gizmoMode, (.)&worldTransform, null, _doSnap ? (.)&snap : null)) if (ImGuizmo.Manipulate((.)&view, (.)&projection, _gizmoType, _gizmoMode, (.)&worldTransform, null, _doSnap ? (.)&snap : null))
{ {
@@ -301,11 +301,11 @@ namespace GlitchyEditor.EditWindows
{ {
String name = null; String name = null;
var nameComponent = tree.Value.GetComponent<DebugNameComponent>(); var nameComponent = tree.Value.GetComponent<NameComponent>();
if(nameComponent != null) if(nameComponent != null)
{ {
name = nameComponent.DebugName; name = scope:: .(nameComponent.Name);
} }
else else
{ {
@@ -501,13 +501,13 @@ namespace GlitchyEditor.EditWindows
{ {
Entity entity = .(entityId, _scene); Entity entity = .(entityId, _scene);
String name = null; StringView name = null;
var nameComponent = entity.GetComponent<DebugNameComponent>(); var nameComponent = entity.GetComponent<NameComponent>();
if(nameComponent != null) if(nameComponent != null)
{ {
name = nameComponent.DebugName; name = nameComponent.Name;
} }
else else
{ {
@@ -0,0 +1,106 @@
using System;
using ImGui;
using GlitchyEngine.Renderer;
using GlitchyEngine.Math;
using GlitchyEngine;
using GlitchyEngine.World;
using ImGuizmo;
using GlitchyEngine.Events;
namespace GlitchyEditor.EditWindows
{
class GameViewportWindow : EditorWindow
{
public const String s_WindowTitle = "Game";
private ImGui.Vec2 _oldViewportSize = .(100, 100);
private bool _viewPortChanged;
private bool _visible;
private RenderTargetGroup _renderTarget ~ _?.ReleaseRef();
public float2 ViewportSize => (float2)_oldViewportSize;
// Occurs when the viewport is resized.
public Event<EventHandler<float2>> ViewportSizeChanged ~ _.Dispose();
// Occurs when an entity was clicked.
public Event<EventHandler<uint32>> EntityClicked ~ _.Dispose();
/// The render target that is shown in the viewport window.
public RenderTargetGroup RenderTarget
{
get => _renderTarget;
set
{
if(_renderTarget == value)
return;
SetReference!(_renderTarget, value);
}
}
public bool Visible => _visible;
public this(Editor editor)
{
_editor = editor;
}
protected override void InternalShow()
{
ImGui.PushStyleVar(.WindowPadding, ImGui.Vec2(1, 1));
defer ImGui.PopStyleVar();
if(!ImGui.Begin(s_WindowTitle, &_open, .NoScrollbar | .MenuBar))
{
ImGui.End();
_visible = false;
return;
}
_visible = true;
let viewportSize = ImGui.GetContentRegionAvail();
if(ImGui.IsWindowHovered() && Input.IsMouseButtonPressing(.RightButton))
{
let currentWindow = ImGui.GetCurrentWindow();
ImGui.FocusWindow(currentWindow);
}
_hasFocus = ImGui.IsWindowFocused();
if(_renderTarget != null)
{
ImGui.Image(_renderTarget.GetViewBinding(0), viewportSize);
}
ImGui.End();
if(_oldViewportSize != viewportSize)
{
ViewportSizeChanged.Invoke(this, (float2)viewportSize);
_viewPortChanged = true;
_oldViewportSize = viewportSize;
}
}
/// Provides the ImGui Drop target and handles dropped payload.
private void HandleDropTarget()
{
if (ImGui.BeginDragDropTarget())
{
ImGui.Payload* payload = ImGui.AcceptDragDropPayload("CONTENT_BROWSER_ITEM");
if (payload != null)
{
StringView path = .((char8*)payload.Data, (int)payload.DataSize);
_editor.RequestOpenScene(this, path);
}
ImGui.EndDragDropTarget();
}
}
}
}
@@ -10,6 +10,8 @@ namespace GlitchyEditor.EditWindows;
class PropertiesWindow : EditorWindow class PropertiesWindow : EditorWindow
{ {
public const String s_WindowTitle = "Properties";
private AssetPropertiesEditor _currentPropertiesEditor ~ delete _; private AssetPropertiesEditor _currentPropertiesEditor ~ delete _;
private bool _lockCurrentAsset; private bool _lockCurrentAsset;
@@ -18,7 +20,7 @@ class PropertiesWindow : EditorWindow
private append String _selectedFileName = .(); private append String _selectedFileName = .();
private Asset _currentAsset ~ _?.ReleaseRef(); private AssetHandle _currentAssetHandle;
public this(Editor editor) public this(Editor editor)
{ {
@@ -28,7 +30,7 @@ class PropertiesWindow : EditorWindow
protected override void InternalShow() protected override void InternalShow()
{ {
defer { ImGui.End(); } defer { ImGui.End(); }
if(!ImGui.Begin("Properties", &_open, .None)) if(!ImGui.Begin(s_WindowTitle, &_open, .None))
return; return;
// TODO: make a little button in title bar? // TODO: make a little button in title bar?
@@ -73,12 +75,13 @@ class PropertiesWindow : EditorWindow
if (assetFile == null) if (assetFile == null)
return; return;
Asset asset = _editor.ContentManager.GetAsset(null, _currentAssetHandle);
// We need the actual asset for preview and sometimes for editing // We need the actual asset for preview and sometimes for editing
if (_currentAsset?.Identifier != assetFile.Identifier) if (asset?.Identifier != assetFile.Identifier)
{ {
_currentAsset?.ReleaseRef(); _currentAssetHandle = _editor.ContentManager.LoadAsset(assetFile.Identifier);
_currentAsset = _editor.ContentManager.LoadAsset(assetFile.Identifier);
} }
// TODO: allow changing AssetLoader // TODO: allow changing AssetLoader
@@ -106,7 +109,8 @@ class PropertiesWindow : EditorWindow
if (ImGui.Button("Save Asset")) if (ImGui.Button("Save Asset"))
{ {
_editor.ContentManager.SaveAsset(_currentAsset); Asset asset = _editor.ContentManager.GetAsset(null, _currentAssetHandle);
_editor.ContentManager.SaveAsset(asset);
} }
if (!assetFile.AssetConfig.Config.Changed) if (!assetFile.AssetConfig.Config.Changed)
+17 -3
View File
@@ -17,15 +17,20 @@ namespace GlitchyEditor
private EntityHierarchyWindow _entityHierarchyWindow ~ delete _; private EntityHierarchyWindow _entityHierarchyWindow ~ delete _;
private ComponentEditWindow _componentEditWindow ~ delete _; private ComponentEditWindow _componentEditWindow ~ delete _;
private SceneViewportWindow _sceneViewportWindow~ delete _; private EditorViewportWindow _sceneViewportWindow ~ delete _;
private GameViewportWindow _gameViewportWindow ~ delete _;
private ContentBrowserWindow _contentBrowserWindow ~ delete _; private ContentBrowserWindow _contentBrowserWindow ~ delete _;
private PropertiesWindow _propertiesWindow ~ delete _; private PropertiesWindow _propertiesWindow ~ delete _;
private AssetViewer _assetViewer ~ delete _;
public Scene CurrentScene public Scene CurrentScene
{ {
get => _scene; get => _scene;
set set
{ {
if (_scene == value)
return;
_scene = value; _scene = value;
_entityHierarchyWindow.SetContext(_scene); _entityHierarchyWindow.SetContext(_scene);
} }
@@ -35,14 +40,19 @@ namespace GlitchyEditor
public EntityHierarchyWindow EntityHierarchyWindow => _entityHierarchyWindow; public EntityHierarchyWindow EntityHierarchyWindow => _entityHierarchyWindow;
public ComponentEditWindow ComponentEditWindow => _componentEditWindow; public ComponentEditWindow ComponentEditWindow => _componentEditWindow;
public SceneViewportWindow SceneViewportWindow => _sceneViewportWindow; public EditorViewportWindow SceneViewportWindow => _sceneViewportWindow;
public GameViewportWindow GameViewportWindow => _gameViewportWindow;
public ContentBrowserWindow ContentBrowserWindow => _contentBrowserWindow; public ContentBrowserWindow ContentBrowserWindow => _contentBrowserWindow;
public PropertiesWindow PropertiesWindow => _propertiesWindow; public PropertiesWindow PropertiesWindow => _propertiesWindow;
public AssetViewer AssetViewer => _assetViewer;
public EditorCamera* CurrentCamera { get; set; } public EditorCamera* CurrentCamera { get; set; }
public Event<EventHandler<StringView>> RequestOpenScene ~ _.Dispose(); public Event<EventHandler<StringView>> RequestOpenScene ~ _.Dispose();
public SceneRenderer GameSceneRenderer {get; set;}
public SceneRenderer EditorSceneRenderer {get; set;}
/// Creates a new editor for the given world /// Creates a new editor for the given world
public this(Scene scene, EditorContentManager contentManager) public this(Scene scene, EditorContentManager contentManager)
{ {
@@ -54,20 +64,24 @@ namespace GlitchyEditor
private void InitWindows() private void InitWindows()
{ {
_sceneViewportWindow = new SceneViewportWindow(this); _sceneViewportWindow = new EditorViewportWindow(this);
_gameViewportWindow = new GameViewportWindow(this);
_entityHierarchyWindow = new EntityHierarchyWindow(this, _scene); _entityHierarchyWindow = new EntityHierarchyWindow(this, _scene);
_componentEditWindow = new ComponentEditWindow(_entityHierarchyWindow); _componentEditWindow = new ComponentEditWindow(_entityHierarchyWindow);
_contentBrowserWindow = new ContentBrowserWindow((.)Application.Get().ContentManager); _contentBrowserWindow = new ContentBrowserWindow((.)Application.Get().ContentManager);
_propertiesWindow = new PropertiesWindow(this); _propertiesWindow = new PropertiesWindow(this);
_assetViewer = new AssetViewer((.)Application.Get().ContentManager);
} }
public void Update() public void Update()
{ {
_sceneViewportWindow.Show(); _sceneViewportWindow.Show();
_gameViewportWindow.Show();
_entityHierarchyWindow.Show(); _entityHierarchyWindow.Show();
_componentEditWindow.Show(); _componentEditWindow.Show();
_contentBrowserWindow.Show(); _contentBrowserWindow.Show();
_propertiesWindow.Show(); _propertiesWindow.Show();
_assetViewer.Show();
} }
} }
} }
+4 -4
View File
@@ -9,9 +9,9 @@ namespace GlitchyEditor
{ {
EditorContentManager _contentManager; EditorContentManager _contentManager;
public this() public this(String[] args)
{ {
PushLayer(new EditorLayer(_contentManager)); PushLayer(new EditorLayer(args, _contentManager));
} }
protected override IContentManager InitContentManager() protected override IContentManager InitContentManager()
@@ -39,9 +39,9 @@ namespace GlitchyEditor
} }
[Export, LinkName("CreateApplication")] [Export, LinkName("CreateApplication")]
public static Application CreateApplication() public static Application CreateApplication(String[] args)
{ {
return new EditorApp(); return new EditorApp(args);
} }
} }
} }
+356 -166
View File
@@ -8,71 +8,11 @@ using GlitchyEngine.Content;
using GlitchyEditor.Assets; using GlitchyEditor.Assets;
using GlitchyEngine; using GlitchyEngine;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using internal GlitchyEngine.Content.Asset;
namespace GlitchyEditor; namespace GlitchyEditor;
/*class PreviewImageManager
{
/// Thread that loads/generates preview images in the background
//private Thread _loaderThread;
/// Set to true to notify the loader to stop
//private bool _stopLoader;
private append Dictionary<String, Texture2D> _previewImages = .() ~ {
for (var v in _)
{
delete v.key;
v.value.ReleaseRef();
}
delete _;
};
public Texture2D GetPreviewImage(String assetName)
{
if (_previewImages.TryGetValue(assetName, let image))
{
return image..AddRef();
}
if (assetName.EndsWith(".png", .OrdinalIgnoreCase))
{
/*using (Texture2D texture = new Texture2D(assetName, true))
{
Texture2DDesc desc = .(128, 128, .R8G8B8A8_UNorm_SRGB)
{
Usage = .Immutable
};
Texture2D myActualTexture = new Texture2D(desc);
// TODO: Scale down texture (on GPU?)
}*/
Texture2D texture = new Texture2D(assetName, true);
if (texture.Width <= 128 && texture.Height <= 128)
{
}
if ( texture.MipLevels > 1)
{
}
_previewImages.Add(new String(assetName), texture);
return texture..AddRef();
}
else
{
Runtime.NotImplemented();
}
}
}*/
class EditorContentManager : IContentManager class EditorContentManager : IContentManager
{ {
private append String _contentDirectory = .(); private append String _contentDirectory = .();
@@ -81,15 +21,20 @@ class EditorContentManager : IContentManager
//private append List<String> _identifiers = .() ~ _.ClearAndDeleteItems(); //private append List<String> _identifiers = .() ~ _.ClearAndDeleteItems();
private append Dictionary<StringView, Asset> _loadedAssets = .(); // Check if all resources are unloaded private append Dictionary<StringView, AssetHandle> _identiferToHandle = .(); // TODO: Check if all resources are unloaded
private append Dictionary<AssetHandle, Asset> _handleToAsset = .();
private append AssetHierarchy _assetHierarchy = .(this); private append AssetHierarchy _assetHierarchy = .(this);
public AssetHierarchy AssetHierarchy => _assetHierarchy; public AssetHierarchy AssetHierarchy => _assetHierarchy;
private append List<AssetHandle> _reloadQueue = .();
public this() public this()
{ {
_assetHierarchy.OnFileContentChanged.Add(new => OnFileContentChanged); _assetHierarchy.OnFileContentChanged.Add(new => OnFileContentChanged);
_assetHierarchy.OnFileRenamed.Add(new => OnFileRenamed);
} }
public ~this() public ~this()
@@ -99,46 +44,24 @@ class EditorContentManager : IContentManager
private void OnFileContentChanged(AssetNode assetNode) private void OnFileContentChanged(AssetNode assetNode)
{ {
// TODO: Subassets break reloading because we can't find them when we only receive the file that changed...
// Asset isn't loaded so we don't need to reload it. // Asset isn't loaded so we don't need to reload it.
if (assetNode.AssetFile.LoadedAsset == null) if (assetNode.AssetFile.LoadedAsset == null)
return; return;
String neededAssetLoaderName = assetNode.AssetFile.AssetConfig?.AssetLoader; _reloadQueue.Add(assetNode.AssetFile.LoadedAsset.Handle);
}
if (String.IsNullOrWhiteSpace(neededAssetLoaderName))
public void OnFileRenamed(AssetNode assetNode, StringView oldIdentifier)
{
// Asset isn't loaded so we don't need to reload it.
if (assetNode.AssetFile.LoadedAsset == null)
return; return;
IAssetLoader assetLoader = null; Asset asset = assetNode.AssetFile.LoadedAsset;
String loaderNameBuffer = scope String(64); _identiferToHandle.Remove(oldIdentifier);
asset.Identifier = assetNode.AssetFile.Identifier;
for (IAssetLoader loader in _assetLoaders) _identiferToHandle.Add(asset.Identifier, asset.Handle);
{
loader.GetType().GetName(loaderNameBuffer..Clear());
if (loaderNameBuffer == neededAssetLoaderName)
{
assetLoader = loader;
break;
}
}
if (assetLoader == null)
{
Log.EngineLogger.Error($"Could not find asset loader \"{neededAssetLoaderName}\"");
return;
}
if (var assetReloader = assetLoader as IReloadingAssetLoader)
{
Stream stream = GetStream(assetNode.Path);
assetReloader.ReloadAsset(assetNode.AssetFile, stream);
delete stream;
}
} }
public void SetContentDirectory(StringView contentDirectory) public void SetContentDirectory(StringView contentDirectory)
@@ -152,9 +75,54 @@ class EditorContentManager : IContentManager
public void Update() public void Update()
{ {
SwapInLoadedAssets();
if (!_reloadQueue.IsEmpty)
{
for (AssetHandle handle in _reloadQueue)
{
ReloadAsset(handle);
}
_reloadQueue.Clear();
}
_assetHierarchy.Update(); _assetHierarchy.Update();
} }
/// Replaces placeholders with the loaded assets
private void SwapInLoadedAssets()
{
// Don't take the lock if we have nothing to do.
if (_finishedEntries.Count == 0)
return;
using (_finishedEntriesLock.Enter())
{
while (_finishedEntries.Count > 0)
{
let (placeholder, asset) = _finishedEntries[0];
delete placeholder.LoadingTask;
if (asset == null)
placeholder.PlaceholderType = .Error;
else
{
// TODO: I'm not sure whether AssetFiles are guaranteed to persist.
// Get the reference here because placeholder wont survive SwapAsset.
AssetFile file = placeholder.AssetFile;
file.[Friend]_loadedAsset = asset;
SwapAsset(placeholder, asset);
// SwapAsset increases RefCount, but this scope also holds a reference.
asset.ReleaseRef();
}
_finishedEntries.RemoveAtFast(0);
}
}
}
public IAssetLoader GetDefaultAssetLoader(StringView fileExtension) public IAssetLoader GetDefaultAssetLoader(StringView fileExtension)
{ {
if (_defaultAssetLoaders.TryGetValue(fileExtension, let value)) if (_defaultAssetLoaders.TryGetValue(fileExtension, let value))
@@ -174,9 +142,11 @@ class EditorContentManager : IContentManager
public void RegisterAssetLoader<T>() where T : new, class, IAssetLoader public void RegisterAssetLoader<T>() where T : new, class, IAssetLoader
{ {
//Log.EngineLogger.AssertDebug(!_assetLoaders.Any((l) => l.GetType() == typeof(T)), "Asset loader already registered."); // Log.EngineLogger.AssertDebug(!_assetLoaders.Any((l) => l.GetType() == typeof(T)), "Asset loader already registered.");
_assetLoaders.Add(new T()); T assetLoader = new T();
_assetLoaders.Add(assetLoader);
for (StringView ext in T.FileExtensions) for (StringView ext in T.FileExtensions)
_supportedExtensions.Add(new String(ext)); _supportedExtensions.Add(new String(ext));
@@ -237,99 +207,230 @@ class EditorContentManager : IContentManager
public bool IsLoaded(StringView identifier) public bool IsLoaded(StringView identifier)
{ {
return _loadedAssets.ContainsKey(identifier); return _identiferToHandle.ContainsKey(identifier);
} }
public Asset LoadAsset(StringView identifier) public Asset GetAsset(Type assetType, AssetHandle handle)
{ {
if (_loadedAssets.TryGetValue(identifier, let asset)) Asset asset = null;
_handleToAsset.TryGetValue(handle, out asset);
if (var placeholder = asset as PlaceholderAsset)
{ {
return asset..AddRef(); if (placeholder.PlaceholderType == .Loading)
return placeholder.AssetLoader.GetPlaceholderAsset(assetType);
else if (placeholder.PlaceholderType == .Error)
return placeholder.AssetLoader.GetErrorAsset(assetType);
} }
// Find subasset name if (assetType == null)
int poundIndex = identifier.IndexOf('#'); {
return asset;
}
else if (asset?.GetType().IsSubtypeOf(assetType) ?? false)
{
return asset;
}
else
{
// TODO: get default asset
StringView resourceName = poundIndex == -1 ? identifier : identifier.Substring(0, poundIndex); return null;
StringView? subassetName = identifier.Substring(poundIndex + 1); }
}
String filePath = scope String(resourceName.Length + _contentDirectory.Length + 2); private void ReloadAsset(AssetHandle handle)
Path.Combine(filePath, _contentDirectory, resourceName); {
Debug.Profiler.ProfileResourceFunction!();
Asset oldAsset = null;
Path.Fixup(filePath); if (!_handleToAsset.TryGetValue(handle, out oldAsset))
{
Log.EngineLogger.Error("Can't reload! No asset exists for handle.");
//filePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); return;
}
Log.EngineLogger.AssertDebug(oldAsset != null);
StringView oldIdentifier = oldAsset.Identifier;
GetResourceAndSubassetName(oldIdentifier, let resourceName, let subassetName);
String filePath = scope .();
GetResourceFilePath(resourceName, filePath);
Result<TreeNode<AssetNode>> resultNode = AssetHierarchy.GetNodeFromPath(filePath); Result<TreeNode<AssetNode>> resultNode = AssetHierarchy.GetNodeFromPath(filePath);
if (resultNode case .Err) if (resultNode case .Err)
{ {
Log.EngineLogger.Error($"Could not find asset \"{filePath}\"."); Log.EngineLogger.Error($"Could not find asset \"{filePath}\".");
return null; return;
} }
AssetFile file = resultNode->Value.AssetFile; AssetFile file = resultNode->Value.AssetFile;
IAssetLoader assetLoader = null; IAssetLoader assetLoader = GetAssetLoader(file);
String loaderTypeName = scope .(128);
for (IAssetLoader loader in _assetLoaders)
{
loader.GetType().GetName(loaderTypeName..Clear());
if (loaderTypeName == file.AssetConfig.AssetLoader)
{
assetLoader = loader;
break;
}
}
Log.EngineLogger.AssertDebug(assetLoader != null);
Stream stream = GetStream(filePath); Stream stream = GetStream(filePath);
// TODO: Add async loading!
Asset loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this); Asset loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this);
delete stream; delete stream;
if (loadedAsset == null) if (loadedAsset == null)
return null; return;
//String identifierString = new .(identifier);
//_identifiers.Add(identifierString);
//_loadedAssets[identifierString] = loadedAsset;
loadedAsset.Identifier = identifier;
ManageAsset(loadedAsset);
file.[Friend]_loadedAsset = loadedAsset; file.[Friend]_loadedAsset = loadedAsset;
return loadedAsset; SwapAsset(oldAsset, loadedAsset);
// SwapAsset increases RefCount, but this scope also holds a reference.
loadedAsset.ReleaseRef();
} }
/// Saves the asset. /// Returns the resource name and, if it exists, the subasset name.
public Result<void> SaveAsset(Asset asset) private void GetResourceAndSubassetName(StringView identifier, out StringView resourceName, out StringView? subassetName)
{ {
// Find subasset name int poundIndex = identifier.IndexOf('#');
int poundIndex = asset.Identifier.IndexOf('#');
StringView resourceName = poundIndex == -1 ? asset.Identifier : asset.Identifier.Substring(0, poundIndex); resourceName = (poundIndex != -1) ? identifier.Substring(0, poundIndex) : identifier;
StringView? subassetName = asset.Identifier.Substring(poundIndex + 1); subassetName = (poundIndex != -1) ? identifier.Substring(poundIndex + 1) : null;
}
String filePath = scope String(resourceName.Length + _contentDirectory.Length + 2); private void GetResourceFilePath(StringView resourceName, String filePath)
{
Path.Combine(filePath, _contentDirectory, resourceName); Path.Combine(filePath, _contentDirectory, resourceName);
Path.Fixup(filePath); Path.Fixup(filePath);
}
//filePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); private enum PlaceholderType
{
Loading,
Error
}
TreeNode<AssetNode> assetNode = Try!(AssetHierarchy.GetNodeFromPath(filePath)); private class PlaceholderAsset : Asset
{
public AssetFile AssetFile {get; private set;}
public Task LoadingTask {get;set;}
public IAssetLoader AssetLoader {get; private set;}
public PlaceholderType PlaceholderType {get; set;}
public this(AssetFile assetFile, IAssetLoader assetLoader, PlaceholderType placeholderType)
{
AssetFile = assetFile;
AssetLoader = assetLoader;
PlaceholderType = placeholderType;
}
}
private append Monitor _finishedEntriesLock = .();
private append List<(PlaceholderAsset placeholder, Asset newAsset)> _finishedEntries = .();
private class MissingAsset : Asset {}
public AssetHandle LoadAsset(StringView identifier, bool blocking = false)
{
Debug.Profiler.ProfileResourceFunction!();
AssetFile file = assetNode->AssetFile; // Todo: How strict should we be on paths?
String fixedIdentifier = scope String(identifier);
AssetIdentifier.Fixup(fixedIdentifier);
if (_identiferToHandle.TryGetValue(fixedIdentifier, let asset))
return asset;
GetResourceAndSubassetName(fixedIdentifier, let resourceName, let subassetName);
String filePath = scope .();
GetResourceFilePath(resourceName, filePath);
Result<TreeNode<AssetNode>> resultNode = AssetHierarchy.GetNodeFromPath(filePath);
if (resultNode case .Err)
{
Log.EngineLogger.Error($"Could not find asset \"{filePath}\".");
return .Invalid;
}
AssetFile file = resultNode->Value.AssetFile;
IAssetLoader assetLoader = GetAssetLoader(file);
// TODO: what are we supposed to do if we don't find a loader? Sure not crash...
Log.EngineLogger.AssertDebug(assetLoader != null);
Asset loadedAsset;
// TODO: Support lazy loading for all asset types
if (!(assetLoader is EditorTextureAssetLoader) || blocking)
{
Stream stream = GetStream(filePath);
loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this);
delete stream;
if (loadedAsset == null)
return .Invalid;
}
else
{
PlaceholderAsset placeholder = new PlaceholderAsset(file, assetLoader, .Loading);
String filePath2 = new String(filePath);
String newResourceName = new String(resourceName);
String newSesourceName = subassetName == null ? null : new String(subassetName.Value);
placeholder.LoadingTask = new Task(new () => {
AsyncLoadAsset(placeholder, filePath2, assetLoader, file,
newResourceName, newSesourceName);
});
ThreadPool.QueueUserWorkItem(placeholder.LoadingTask);
loadedAsset = placeholder;
}
loadedAsset.Identifier = fixedIdentifier;
AssetHandle handle = ManageAsset(loadedAsset);
// ManageAsset increases RefCount, but this scope also holds a reference.
loadedAsset.ReleaseRef();
// Add to Identifier -> Handle map
_identiferToHandle.Add(loadedAsset.Identifier, handle);
file.[Friend]_loadedAsset = loadedAsset;
return handle;
}
private void AsyncLoadAsset(PlaceholderAsset placeholder, String filePath, IAssetLoader assetLoader, AssetFile file, String resourceName, String subassetName)
{
Debug.Profiler.ProfileResourceFunction!();
Stream stream = GetStream(filePath);
Asset loadedAsset = assetLoader.LoadAsset(stream, file.AssetConfig.Config, resourceName, subassetName, this);
delete stream;
delete filePath;
delete resourceName;
delete subassetName;
using (_finishedEntriesLock.Enter())
{
_finishedEntries.Add((placeholder, loadedAsset));
}
}
/// Gets the asset loader that has to be used for the given file.
IAssetLoader GetAssetLoader(AssetFile file)
{
IAssetLoader assetLoader = null; IAssetLoader assetLoader = null;
String loaderTypeName = scope .(128); String loaderTypeName = scope .(128);
@@ -345,24 +446,67 @@ class EditorContentManager : IContentManager
} }
} }
return assetLoader;
}
public enum SaveAssetError
{
case Unknown;
case Unsavable;
case PathNotFound;
}
/// Saves the asset.
public Result<void, SaveAssetError> SaveAsset(Asset asset)
{
if (asset == null)
return .Err(.Unknown);
if (asset.Identifier.IsWhiteSpace)
return .Err(.Unsavable);
GetResourceAndSubassetName(asset.Identifier, let resourceName, let subassetName);
if (subassetName != null)
{
// TODO: should this ever be allowed? Couldn't we just save the entire asset?
// Would this ever be necessary?
Runtime.NotImplemented("Saving subassets is not currently allowed");
}
String filePath = scope String();
GetResourceFilePath(resourceName, filePath);
Result<TreeNode<AssetNode>> assetNode = AssetHierarchy.GetNodeFromPath(filePath);
if (assetNode case .Err)
return .Err(.PathNotFound);
AssetFile file = assetNode.Get()->AssetFile;
IAssetLoader assetLoader = GetAssetLoader(file);
IAssetSaver assetSaver = assetLoader as IAssetSaver; IAssetSaver assetSaver = assetLoader as IAssetSaver;
if (assetSaver == null) if (assetSaver == null)
{ {
Log.EngineLogger.Error("The asset loader can't save!"); Log.EngineLogger.Error("The asset loader can't save!");
return .Err; return .Err(.Unsavable);
} }
Stream stream = OpenStream(filePath, false, true); Stream stream = OpenStream(filePath, false);
assetSaver.EditorSaveAsset(stream, asset, file.AssetConfig.Config, resourceName, subassetName, this); assetSaver.EditorSaveAsset(stream, asset, file.AssetConfig.Config, resourceName, subassetName, this);
// Trim off the end of the file.
stream.SetLength(stream.Position);
delete stream; delete stream;
return .Ok; return .Ok;
} }
private Stream OpenStream(StringView assetIdentifier, bool openOnly, bool truncate = false) private Stream OpenStream(StringView assetIdentifier, bool openOnly)
{ {
var assetIdentifier; var assetIdentifier;
@@ -378,9 +522,6 @@ class EditorContentManager : IContentManager
FileMode fileMode = openOnly ? FileMode.Open : FileMode.OpenOrCreate; FileMode fileMode = openOnly ? FileMode.Open : FileMode.OpenOrCreate;
if (truncate)
fileMode |= .Truncate;
var result = fs.Open(assetIdentifier, fileMode, openOnly ? .Read : .ReadWrite, .ReadWrite); var result = fs.Open(assetIdentifier, fileMode, openOnly ? .Read : .ReadWrite, .ReadWrite);
if (result case .Err) if (result case .Err)
@@ -413,37 +554,86 @@ class EditorContentManager : IContentManager
return fs;*/ return fs;*/
} }
public void ManageAsset(Asset asset) public AssetHandle ManageAsset(Asset asset)
{ {
_loadedAssets.Add(asset.Identifier, asset); Log.EngineLogger.AssertDebug(asset.Handle == .Invalid, "Asset is already managed.");
Log.EngineLogger.AssertDebug(asset.ContentManager == null, "Asset is already managed.");
AssetHandle handle = .();
// Generate until we find a unique key (shouldn't happen too often)
while (handle.IsInvalid || _handleToAsset.ContainsKey(handle))
{
handle = .();
Log.EngineLogger.Trace("Handle was invalid or already taken.");
// TODO: perhaps test how often this happens.
// If this happens too often we could use a different random generator
}
//_handles.Add(asset.Identifier, handle);
_handleToAsset.Add(handle, asset);
asset.[Friend]_contentManager = this; asset.[Friend]_contentManager = this;
asset.[Friend]_handle = handle;
asset.AddRef();
return handle;
} }
public void UnmanageAsset(Asset asset) private void SwapAsset(Asset oldAsset, Asset newAsset)
{ {
_loadedAssets.Remove(asset.Identifier); newAsset.Identifier = oldAsset.Identifier;
newAsset._contentManager = this;
newAsset._handle = oldAsset.Handle;
_handleToAsset[oldAsset.Handle] = newAsset;
if (_identiferToHandle.ContainsKey(oldAsset.Identifier))
{
_identiferToHandle.Remove(oldAsset.Identifier);
_identiferToHandle.Add(newAsset.Identifier, newAsset.Handle);
}
newAsset.AddRef();
oldAsset.ReleaseRef();
}
public void UnmanageAsset(AssetHandle handle)
{
Log.EngineLogger.AssertDebug(_handleToAsset.ContainsKey(handle), "Handle doesn't correspond to an asset.");
Asset asset = _handleToAsset[handle];
if (_identiferToHandle.ContainsKey(asset.Identifier))
_identiferToHandle.Remove(asset.Identifier);
_handleToAsset.Remove(handle);
asset.[Friend]_contentManager = null; asset.[Friend]_contentManager = null;
asset.ReleaseRef();
} }
/// This will unregister all assets from this content manager. /// This will unregister all assets from this content manager.
/// Note: This will not release any assets. /// Note: This will not release any assets.
private void UnmanageAllAssets() private void UnmanageAllAssets()
{ {
for (let (_, asset) in _loadedAssets) for (let (handle, _) in _handleToAsset)
{ {
UnmanageAsset(asset); UnmanageAsset(handle);
} }
} }
public void UpdateAssetIdentifier(Asset asset, StringView oldIdentifier, StringView newIdentifier) public void AssetMoved(Asset asset, StringView oldIdentifier, StringView newIdentifier)
{ {
Runtime.NotImplemented();
if (oldIdentifier == newIdentifier) if (oldIdentifier == newIdentifier)
return; return;
Log.EngineLogger.Assert(_loadedAssets.ContainsKey(newIdentifier), "An asset with the same identifier is already managed by this content manager."); Log.EngineLogger.Assert(_identiferToHandle.ContainsKey(newIdentifier), "An asset with the same identifier is already managed by this content manager.");
// Since all we do in order to track assets is add them to a dictionary we can simply unmanage and manage it again. // Since all we do in order to track assets is add them to a dictionary we can simply unmanage and manage it again.
UnmanageAsset(asset); //UnmanageAsset(asset);
ManageAsset(asset); //ManageAsset(asset);
} }
} }
+14 -7
View File
@@ -2,12 +2,13 @@ using System;
using GlitchyEngine.Renderer; using GlitchyEngine.Renderer;
using GlitchyEngine.Math; using GlitchyEngine.Math;
using GlitchyEngine; using GlitchyEngine;
using GlitchyEngine.Content;
namespace GlitchyEditor namespace GlitchyEditor
{ {
class EditorIcons : RefCounted class EditorIcons : RefCounted
{ {
Texture2D _texture ~ _.ReleaseRef(); AssetHandle<Texture2D> _texture;
public SubTexture2D DirectionalLight ~ _.ReleaseRef(); public SubTexture2D DirectionalLight ~ _.ReleaseRef();
public SubTexture2D Camera ~ _.ReleaseRef(); public SubTexture2D Camera ~ _.ReleaseRef();
@@ -15,18 +16,21 @@ namespace GlitchyEditor
public SubTexture2D File ~ _.ReleaseRef(); public SubTexture2D File ~ _.ReleaseRef();
public SubTexture2D Play ~ _.ReleaseRef(); public SubTexture2D Play ~ _.ReleaseRef();
public SubTexture2D Stop ~ _.ReleaseRef(); public SubTexture2D Stop ~ _.ReleaseRef();
public SubTexture2D Simulate ~ _.ReleaseRef();
public SubTexture2D Pause ~ _.ReleaseRef();
public SubTexture2D SingleStep ~ _.ReleaseRef();
public SamplerState SamplerState public SamplerState SamplerState
{ {
get => _texture.SamplerState; get => _texture.Get().SamplerState;
set => _texture.SamplerState = value; set => _texture.Get().SamplerState = value;
} }
public this(String texturePath, Vector2 iconSize) public this(String texturePath, float2 iconSize)
{ {
_texture = Content.LoadAsset<Texture2D>(texturePath);//new Texture2D(texturePath); _texture = Content.LoadAsset(texturePath, null, true);
Vector2 pen = .(); float2 pen = .();
DirectionalLight = GetNextGridTexture(ref pen, iconSize); DirectionalLight = GetNextGridTexture(ref pen, iconSize);
Camera = GetNextGridTexture(ref pen, iconSize); Camera = GetNextGridTexture(ref pen, iconSize);
@@ -34,9 +38,12 @@ namespace GlitchyEditor
File = GetNextGridTexture(ref pen, iconSize); File = GetNextGridTexture(ref pen, iconSize);
Play = GetNextGridTexture(ref pen, iconSize); Play = GetNextGridTexture(ref pen, iconSize);
Stop = GetNextGridTexture(ref pen, iconSize); Stop = GetNextGridTexture(ref pen, iconSize);
Simulate = GetNextGridTexture(ref pen, iconSize);
Pause = GetNextGridTexture(ref pen, iconSize);
SingleStep = GetNextGridTexture(ref pen, iconSize);
} }
private SubTexture2D GetNextGridTexture(ref Vector2 pen, Vector2 iconSize) private SubTexture2D GetNextGridTexture(ref float2 pen, float2 iconSize)
{ {
SubTexture2D subTexture = .CreateFromGrid(_texture, pen, iconSize); SubTexture2D subTexture = .CreateFromGrid(_texture, pen, iconSize);
+487 -282
View File
@@ -13,106 +13,115 @@ using GlitchyEngine.Renderer.Animation;
using System.IO; using System.IO;
using GlitchyEngine.Core; using GlitchyEngine.Core;
using GlitchyEditor.Assets; using GlitchyEditor.Assets;
using GlitchyEngine.Scripting;
namespace GlitchyEditor namespace GlitchyEditor
{ {
class EditorLayer : Layer class EditorLayer : Layer
{ {
enum SceneState
{
Edit,
Play,
Simulate
}
RasterizerState _rasterizerState ~ _?.ReleaseRef(); RasterizerState _rasterizerState ~ _?.ReleaseRef();
RasterizerState _rasterizerStateClockWise ~ _?.ReleaseRef(); RasterizerState _rasterizerStateClockWise ~ _?.ReleaseRef();
// TODO: we shouldn't hold a reference to the context
GraphicsContext _context ~ _.ReleaseRef(); GraphicsContext _context ~ _.ReleaseRef();
BlendState _alphaBlendState ~ _.ReleaseRef(); BlendState _alphaBlendState ~ _.ReleaseRef();
BlendState _opaqueBlendState ~ _.ReleaseRef(); BlendState _opaqueBlendState ~ _.ReleaseRef();
DepthStencilState _depthStencilState ~ _.ReleaseRef(); DepthStencilState _depthStencilState ~ _.ReleaseRef();
Scene _scene ~ delete _;
String _sceneFilePath = new String() ~ delete _;
public String SceneFilePath /// Reference to the scene that is currently being played and worked on.
Scene _activeScene ~ _?.ReleaseRef();
/**
* Referece to the editor scene.
* We hold a reference to the editor scene because we need it in order
* to restore the original state once we stop the game/simulation.
* Before starting the simulation the editor scene will be copied and
* the reference in _activeScene will be replaced with the new scene.
*/
Scene _editorScene ~ _?.ReleaseRef();
SceneRenderer _gameSceneRenderer ~ delete _;
SceneRenderer _editorSceneRenderer ~ delete _;
/// Path of the current scene.
append String _sceneFilePath = .();
Editor _editor ~ delete _;
RenderTargetGroup _cameraTarget ~ _.ReleaseRef();
RenderTargetGroup _editorViewportTarget ~ _.ReleaseRef();
RenderTargetGroup _gameViewportTarget ~ _.ReleaseRef();
SettingsWindow _settingsWindow = new .() ~ delete _;
EditorCamera _camera ~ _.Dispose();
EditorIcons _editorIcons ~ _.ReleaseRef();
EditorContentManager _contentManager;
SceneState _sceneState = .Edit;
bool _isPaused = false;
bool _singleStep = false;
private float _fixedTimestep = 1.0f / 60.0f;
append List<(String Name, String Path)> _recentProjectPaths = .() ~ {
for (var item in _)
{
delete item.Name;
delete item.Path;
}
};
private GameTime _simulationGameTime = new .() ~ delete _;
/// Gets or sets the path of the current scene.
public StringView SceneFilePath
{ {
get => _sceneFilePath; get => _sceneFilePath;
set set
{ {
_sceneFilePath.Clear(); _sceneFilePath.Clear();
if (value != null) if (!value.IsWhiteSpace)
_sceneFilePath.Append(value); _sceneFilePath.Append(value);
} }
} }
Editor _editor ~ delete _; [AllowAppend]
public this(String[] args, EditorContentManager contentManager) : base("Editor")
RenderTargetGroup _cameraTarget ~ _.ReleaseRef();
RenderTargetGroup _viewportTarget ~ _.ReleaseRef();
SettingsWindow _settingsWindow = new .() ~ delete _;
EditorCamera _camera ~ _.Dispose();
/*Texture2D _editorIcons ~ _.ReleaseRef();
SubTexture2D _iconDirectionalLight ~ _.ReleaseRef();
SubTexture2D _iconCamera ~ _.ReleaseRef();*/
EditorIcons _editorIcons ~ _.ReleaseRef();
EditorContentManager _contentManager;
enum SceneState
{ {
Edit, Application.Get().Window.IsVSync = true;
Play,
// Pause,
// Simulate
}
SceneState _sceneState = .Edit;
public this(EditorContentManager contentManager) : base("Example")
{
Application.Get().Window.IsVSync = false;
//InitContentManager();
_contentManager = contentManager; _contentManager = contentManager;
InitGraphics(); InitGraphics();
_scene = new Scene(); _gameSceneRenderer = new SceneRenderer();
_editorSceneRenderer = new SceneRenderer();
_camera = EditorCamera(Vector3(3.5f, 1.25f, 2.75f), Quaternion.FromEulerAngles(MathHelper.ToRadians(40), MathHelper.ToRadians(25), 0), MathHelper.ToRadians(75), 0.1f, 1); _camera = EditorCamera(float3(-3f, 3f, -3f), Quaternion.FromEulerAngles(MathHelper.ToRadians(40), MathHelper.ToRadians(25), 0), MathHelper.ToRadians(75), 0.1f, 1);
_camera.RenderTarget = _cameraTarget; _camera.RenderTarget = _cameraTarget;
InitEditor(); InitEditor();
NewScene(); if (args.Count >= 1)
LoadSceneFile(args[0]);
// Create a new scene, if LoadSceneFile failed
if (_activeScene == null)
NewScene();
} }
/*private void InitContentManager()
{
_contentManager = new EditorContentManager();
_contentManager.RegisterAssetLoader<EditorTextureAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<EditorTextureAssetLoader>(".png", ".dds");
_contentManager.SetAssetPropertiesEditor<EditorTextureAssetLoader>(=> TextureAssetPropertiesEditor.Factory);
_contentManager.RegisterAssetLoader<ModelAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<ModelAssetLoader>(".glb", ".gltf");
_contentManager.SetAssetPropertiesEditor<ModelAssetLoader>(=> ModelAssetPropertiesEditor.Factory);
_contentManager.RegisterAssetLoader<MaterialAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<MaterialAssetLoader>(".mat");
_contentManager.SetAssetPropertiesEditor<MaterialAssetLoader>(=> MaterialAssetPropertiesEditor.Factory);
_contentManager.RegisterAssetLoader<EffectAssetLoader>();
_contentManager.SetAsDefaultAssetLoader<EffectAssetLoader>(".hlsl");
_contentManager.SetAssetPropertiesEditor<EffectAssetLoader>(=> EffectAssetPropertiesEditor.Factory);
_contentManager.SetContentDirectory("./content");
// Todo: Sketchy...
Application.Get().[Friend]_contentManager = _contentManager;
}*/
private void InitGraphics() private void InitGraphics()
{ {
_context = Application.Get().Window.Context..AddRef(); _context = Application.Get().Window.Context..AddRef();
@@ -141,7 +150,15 @@ namespace GlitchyEditor
DepthTargetDescription = .(.D24_UNorm_S8_UInt) DepthTargetDescription = .(.D24_UNorm_S8_UInt)
}); });
_viewportTarget = new RenderTargetGroup(.() _editorViewportTarget = new RenderTargetGroup(.()
{
Width = 100,
Height = 100,
ColorTargetDescriptions = TargetDescription[](
.(.R8G8B8A8_UNorm))
});
_gameViewportTarget = new RenderTargetGroup(.()
{ {
Width = 100, Width = 100,
Height = 100, Height = 100,
@@ -154,18 +171,16 @@ namespace GlitchyEditor
ContentBrowserWindow.s_FolderTexture = _editorIcons.Folder; ContentBrowserWindow.s_FolderTexture = _editorIcons.Folder;
ContentBrowserWindow.s_FileTexture = _editorIcons.File; ContentBrowserWindow.s_FileTexture = _editorIcons.File;
/*_editorIcons = new Texture2D("Textures/EditorIcons.dds");
_editorIcons.SamplerState = SamplerStateManager.AnisotropicClamp;
_iconDirectionalLight = .CreateFromGrid(_editorIcons, .(0, 0), .(64, 64));
_iconCamera = .CreateFromGrid(_editorIcons, .(1, 0), .(64, 64));*/
} }
private void InitEditor() private void InitEditor()
{ {
_editor = new Editor(_scene, _contentManager); _editor = new Editor(_editorScene, _contentManager);
_editor.SceneViewportWindow.ViewportSizeChanged.Add(new (s, e) => ViewportSizeChanged(s, e)); _editor.SceneViewportWindow.ViewportSizeChanged.Add(new (s, e) => EditorViewportSizeChanged(s, e));
_editor.GameViewportWindow.ViewportSizeChanged.Add(new (s, e) => GameViewportSizeChanged(s, e));
_editor.CurrentCamera = &_camera; _editor.CurrentCamera = &_camera;
_editor.GameSceneRenderer = _gameSceneRenderer;
_editor.EditorSceneRenderer = _editorSceneRenderer;
_editor.RequestOpenScene.Add(new (s, fileName) => { _editor.RequestOpenScene.Add(new (s, fileName) => {
LoadSceneFile(fileName); LoadSceneFile(fileName);
@@ -174,20 +189,89 @@ namespace GlitchyEditor
public override void Update(GameTime gameTime) public override void Update(GameTime gameTime)
{ {
_camera.Update(gameTime); Debug.Profiler.ProfileFunction!();
_editor.CurrentScene = _activeScene;
Scene.UpdateMode updateMode;
switch (_sceneState)
{
case .Edit:
updateMode = .Editor;
case .Play:
updateMode = .Runtime;
case .Simulate:
updateMode = .Physics;
}
if (_sceneState != .Edit && _isPaused && !_singleStep)
updateMode = .None;
/*
* If we are in edit mode we use the "official" gametime.
* However in play or simulation mode we use a simulation game time
* which is decoupled from the actual time. This allows us to manipulate
* the simulation speed without potentially messing with the rest of the editor.
*/
GameTime gt = null;
if (updateMode == .Editor)
{
gt = gameTime;
}
else
{
gt = _simulationGameTime;
if (_singleStep)
{
_singleStep = false;
_simulationGameTime.ManualStepFrame(_fixedTimestep);
}
else
{
_simulationGameTime.ManualStepFrame(gameTime.DeltaTime);
}
}
_activeScene.Update(gt, updateMode);
// Clear the swapchain-buffer // Clear the swapchain-buffer
RenderCommand.Clear(null, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0); RenderCommand.Clear(null, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0);
RenderCommand.Clear(_viewportTarget, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0);
RenderCommand.SetBlendState(_alphaBlendState); RenderCommand.SetBlendState(_alphaBlendState);
RenderCommand.SetDepthStencilState(_depthStencilState); RenderCommand.SetDepthStencilState(_depthStencilState);
if (_sceneState == .Edit) if (_editor.GameViewportWindow.Visible)
_scene.UpdateEditor(gameTime, _camera, _viewportTarget, scope => DebugDraw3D, scope => DebugDraw2D); {
else if (_sceneState == .Play) _gameSceneRenderer.Scene = _activeScene;
_scene.UpdateRuntime(gameTime, _viewportTarget);
_activeScene.SetViewportSize((uint32)_editor.GameViewportWindow.ViewportSize.X, (uint32)_editor.GameViewportWindow.ViewportSize.Y);
_gameSceneRenderer.SetViewportSize((uint32)_editor.GameViewportWindow.ViewportSize.X, (uint32)_editor.GameViewportWindow.ViewportSize.Y);
RenderCommand.Clear(_gameViewportTarget, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0);
_gameSceneRenderer.RenderRuntime(gameTime, _gameViewportTarget);
}
RenderCommand.SetBlendState(_alphaBlendState);
RenderCommand.SetDepthStencilState(_depthStencilState);
if (_editor.SceneViewportWindow.Visible)
{
_camera.Update(gameTime);
_editorSceneRenderer.Scene = _activeScene;
// Only use aspect ratio of editor-window, if game is not being rendered
if (!_editor.GameViewportWindow.Visible)
_activeScene.SetViewportSize((uint32)_editor.SceneViewportWindow.ViewportSize.X, (uint32)_editor.SceneViewportWindow.ViewportSize.Y);
_editorSceneRenderer.SetViewportSize((uint32)_editor.SceneViewportWindow.ViewportSize.X, (uint32)_editor.SceneViewportWindow.ViewportSize.Y);
RenderCommand.Clear(_editorViewportTarget, .Color | .Depth, .(0.2f, 0.2f, 0.2f), 1.0f, 0);
_editorSceneRenderer.RenderEditor(gameTime, _camera, _editorViewportTarget, scope => DebugDraw3D, scope => DebugDraw2D);
}
RenderCommand.UnbindRenderTargets(); RenderCommand.UnbindRenderTargets();
RenderCommand.SetRenderTarget(null, 0, true); RenderCommand.SetRenderTarget(null, 0, true);
@@ -216,21 +300,21 @@ namespace GlitchyEditor
Matrix Billboard(Matrix transform) Matrix Billboard(Matrix transform)
{ {
Vector3 worldPos = transform.Translation; float3 worldPos = transform.Translation;
return Matrix.Translation(worldPos) * billboard; return Matrix.Translation(worldPos) * billboard;
} }
float CalculateAlpha(Vector3 pos) float CalculateAlpha(float3 pos)
{ {
return Math.Clamp(1.5f - Vector3.Distance(_editor.CurrentCamera.Position, pos) / 50, 0, 1); return Math.Clamp(1.5f - distance(_editor.CurrentCamera.Position, pos) / 50, 0, 1);
} }
for (var (entity, transform, camera) in _scene.[Friend]_ecsWorld.Enumerate<TransformComponent, CameraComponent>()) for (var (entity, transform, camera) in _activeScene.GetEntities<TransformComponent, CameraComponent>())
{ {
if (_editor.EntityHierarchyWindow.SelectedEntities.Contains(.(entity, _scene))) if (_editor.EntityHierarchyWindow.SelectedEntities.Contains(.(entity, _activeScene)))
{ {
DebugRenderer.DrawViewFrustum(transform.WorldTransform, camera.Camera.Projection, _camera.Projection * _camera.View, .White); DebugRenderer.DrawViewFrustum(transform.WorldTransform, camera.Camera.Projection, .White);
} }
Matrix world = Billboard(transform.WorldTransform); Matrix world = Billboard(transform.WorldTransform);
@@ -240,15 +324,15 @@ namespace GlitchyEditor
//Renderer2D.DrawQuad(world, _iconCamera, .White, .(0, 0, 1, 1), entity.Index); //Renderer2D.DrawQuad(world, _iconCamera, .White, .(0, 0, 1, 1), entity.Index);
} }
for (var (entity, transform, light) in _scene.[Friend]_ecsWorld.Enumerate<TransformComponent, LightComponent>()) for (var (entity, transform, light) in _activeScene.GetEntities<TransformComponent, LightComponent>())
{ {
if (_editor.EntityHierarchyWindow.SelectedEntities.Contains(.(entity, _scene))) if (_editor.EntityHierarchyWindow.SelectedEntities.Contains(.(entity, _activeScene)))
{ {
Renderer.DrawRay(.Zero, .(0, 0, 20), ColorRGBA(light.SceneLight.Color, 1.0f), transform.WorldTransform); Renderer.DrawRay(.Zero, .(0, 0, 20), ColorRGBA(light.SceneLight.Color, 1.0f), transform.WorldTransform);
for (float angle = 0; angle < MathHelper.TwoPi; angle += MathHelper.TwoPi / 5.0f) for (float angle = 0; angle < MathHelper.TwoPi; angle += MathHelper.TwoPi / 5.0f)
{ {
Vector2 pos = MathHelper.CirclePoint(angle, 0.5f); float2 pos = MathHelper.CirclePoint(angle, 0.5f);
Renderer.DrawRay(.(pos, 0), .(pos, 20), .White, transform.WorldTransform); Renderer.DrawRay(.(pos, 0), .(pos, 20), .White, transform.WorldTransform);
} }
@@ -258,7 +342,16 @@ namespace GlitchyEditor
float alpha = CalculateAlpha(transform.WorldTransform.Translation); float alpha = CalculateAlpha(transform.WorldTransform.Translation);
Renderer2D.DrawQuad(world, _editorIcons.DirectionalLight, ColorRGBA(light.SceneLight.Color.R * alpha, light.SceneLight.Color.G * alpha, light.SceneLight.Color.B * alpha, alpha), .(0, 0, 1, 1), entity.Index); Renderer2D.DrawQuad(world, _editorIcons.DirectionalLight, ColorRGBA(light.SceneLight.Color.R * alpha, light.SceneLight.Color.G * alpha, light.SceneLight.Color.B * alpha, alpha), .(0, 0, 1, 1), entity.Index);
//Renderer2D.DrawQuad(world, _iconDirectionalLight, ColorRGBA(light.SceneLight.Color, alpha), .(0, 0, 1, 1), entity.Index); }
for (var (entity, transform, collider) in _activeScene.GetEntities<TransformComponent, BoxCollider2DComponent>())
{
Renderer2D.DrawRect(transform.WorldTransform * Matrix.Translation(collider.Offset.X, collider.Offset.Y, 0) * Matrix.Scaling(collider.Size.X * 2, collider.Size.Y * 2, 0));
}
for (var (entity, transform, collider) in _activeScene.GetEntities<TransformComponent, CircleCollider2DComponent>())
{
Renderer2D.DrawCircle(transform.WorldTransform * Matrix.Translation(collider.Offset.X, collider.Offset.Y, 0) * Matrix.Scaling(collider.Radius * 2), (Texture2D)null, ColorRGBA(0f, 1f, 0f), 0.01f);
} }
} }
@@ -278,7 +371,8 @@ namespace GlitchyEditor
private bool OnImGuiRender(ImGuiRenderEvent event) private bool OnImGuiRender(ImGuiRenderEvent event)
{ {
Input.ImGuiDebugDraw(); // TODO: make window
//Input.ImGuiDebugDraw();
//viewer.ViewTexture(Renderer.[Friend]_gBuffer.Target); //viewer.ViewTexture(Renderer.[Friend]_gBuffer.Target);
@@ -287,7 +381,8 @@ namespace GlitchyEditor
DrawMainMenuBar(); DrawMainMenuBar();
_editor.SceneViewportWindow.RenderTarget = _viewportTarget; _editor.SceneViewportWindow.RenderTarget = _editorViewportTarget;
_editor.GameViewportWindow.RenderTarget = _gameViewportTarget;
_editor.Update(); _editor.Update();
@@ -317,22 +412,129 @@ namespace GlitchyEditor
ImGui.Begin("##toolbar", null, .NoDecoration | .NoScrollbar | .NoScrollWithMouse); ImGui.Begin("##toolbar", null, .NoDecoration | .NoScrollbar | .NoScrollWithMouse);
SubTexture2D icon = _sceneState == .Edit ? _editorIcons.Play : _editorIcons.Stop; float padding = 2.0f;
float size = ImGui.GetWindowHeight() - 4.0f; float size = ImGui.GetWindowHeight() - 2 * padding;
ImGui.SameLine((ImGui.GetContentRegionMax().x / 2 - size / 2));
if (ImGui.ImageButton(icon, .(size, size), .Zero, .Ones, 0)) float centerX = ImGui.GetContentRegionMax().x / 2;
if (_sceneState == .Edit)
EditorButtons:
{ {
if (_sceneState == .Edit) // Display the buttons for edit state
{
float totalWidth = size * 3 + padding * 4;
ImGui.SameLine();
ImGui.SetCursorPosX(centerX - totalWidth / 2);
if (ImGui.ImageButton(_editorIcons.Play, .(size, size), .Zero, .Ones, 0))
OnScenePlay(); OnScenePlay();
}
else if (_sceneState == .Play) ImGui.AttachTooltip("Play the game.");
ImGui.SameLine();
ImGui.PushID(1);
if (ImGui.ImageButton(_editorIcons.Simulate, .(size, size), .Zero, .Ones, 0))
OnSceneSimulate();
ImGui.PopID();
ImGui.AttachTooltip("Enter simulation mode.\nThis only runs the physics engine.");
if (_isPaused)
{ {
OnSceneStop(); ImGui.PushStyleColor(.Button, *ImGui.GetStyleColorVec4(.ButtonActive));
defer:EditorButtons { ImGui.PopStyleColor(); }
} }
ImGui.PushID(2);
//ImGui.SameLine(penX += size + 2 * padding);
ImGui.SameLine();
if (ImGui.ImageButton(_editorIcons.Pause, .(size, size), .Zero, .Ones, 0))
_isPaused = !_isPaused;
ImGui.PopID();
ImGui.AttachTooltip("If enabled the game or simulation will be started in paused state.");
}
else
{
float totalWidth = size * 2 + padding * 2;
if (_isPaused)
{
totalWidth = size * 3 + padding * 4;
}
// Display the buttons for play/simulation state
ImGui.SameLine();
ImGui.SetCursorPosX(centerX - totalWidth / 2.0f);
SubTexture2D pauseButtonIcon = _isPaused ? _editorIcons.Play : _editorIcons.Pause;
if (ImGui.ImageButton(pauseButtonIcon, .(size, size), .Zero, .Ones, 0))
{
if (_isPaused)
OnSceneResume();
else
OnScenePause();
}
ImGui.AttachTooltip(_isPaused ? "Resume" : "Pause");
if (_isPaused)
{
ImGui.PushID(2);
ImGui.SameLine();
SubTexture2D singleStepButtonIcon = _editorIcons.SingleStep;
if (ImGui.ImageButton(singleStepButtonIcon, .(size, size), .Zero, .Ones, 0))
{
DoSingleStep();
}
if (ImGui.BeginPopupContextItem())
{
ImGui.TextUnformatted("Time step:");
ImGui.SameLine();
ImGui.DragFloat("##timestep", &_fixedTimestep, 0.001f, 0.001f, 1000.0f, "%.4fs");
float fps = 1.0f / _fixedTimestep;
ImGui.TextUnformatted("Frame rate:");
ImGui.SameLine();
if (ImGui.DragFloat("##fps", &fps, 1.0f, 0.001f, 1000.0f, "%.3f FPS"))
{
_fixedTimestep = 1.0f / fps;
}
ImGui.EndPopup();
}
ImGui.AttachTooltip("Step one Frame (Right-click to change time step)");
ImGui.PopID();
}
ImGui.SameLine();
ImGui.PushID(1);
if (ImGui.ImageButton(_editorIcons.Stop, .(size, size), .Zero, .Ones, 0))
OnSceneStop();
ImGui.PopID();
ImGui.AttachTooltip("Stop");
} }
ImGui.End(); ImGui.End();
@@ -343,194 +545,134 @@ namespace GlitchyEditor
private void OnScenePlay() private void OnScenePlay()
{ {
_sceneState = .Play;
_editor.SceneViewportWindow.EditorMode = false; _editor.SceneViewportWindow.EditorMode = false;
_sceneState = .Play;
_scene.OnRuntimeStart(); using (Scene runtimeScene = new Scene())
{
_editorScene.CopyTo(runtimeScene);
runtimeScene.OnRuntimeStart();
SetReference!(_activeScene, runtimeScene);
}
_editor.CurrentScene = _activeScene;
}
private void OnSceneSimulate()
{
_editor.SceneViewportWindow.EditorMode = false;
_sceneState = .Simulate;
using (Scene simulationScene = new Scene())
{
_editorScene.CopyTo(simulationScene);
simulationScene.OnSimulationStart();
SetReference!(_activeScene, simulationScene);
}
_editor.CurrentScene = _activeScene;
}
private void OnScenePause()
{
_isPaused = true;
}
private void OnSceneResume()
{
_isPaused = false;
}
private void DoSingleStep()
{
_singleStep = true;
} }
private void OnSceneStop() private void OnSceneStop()
{ {
_scene.OnRuntimeStop(); if (_sceneState == .Play)
_activeScene.OnRuntimeStop();
else if (_sceneState == .Simulate)
_activeScene.OnSimulationStop();
SetReference!(_activeScene, _editorScene);
_editor.SceneViewportWindow.EditorMode = true; _editor.SceneViewportWindow.EditorMode = true;
_sceneState = .Edit; _sceneState = .Edit;
}
// Just for testing _editor.CurrentScene = _activeScene;
private void TestEntitiesWithModels()
{
/*{
var lightNtt = _scene.CreateEntity("My Sexy Sun 2");
let transform = lightNtt.GetComponent<TransformComponent>();
transform.Position = .(0, 0, 0);
transform.RotationEuler = .(MathHelper.ToRadians(70), MathHelper.ToRadians(-30), 0);
let light = lightNtt.AddComponent<LightComponent>();
light.SceneLight.Illuminance = 10.0f;
light.SceneLight.Color = .(1.0f, 0.95f, 0.8f);
}
{
var lightNtt = _scene.CreateEntity("My Sexy Sun 3");
let transform = lightNtt.GetComponent<TransformComponent>();
transform.Position = .(0, 0, 0);
transform.RotationEuler = .(MathHelper.ToRadians(20), MathHelper.ToRadians(-55), 0);
let light = lightNtt.AddComponent<LightComponent>();
light.SceneLight.Illuminance = 10.0f;
light.SceneLight.Color = .(1.0f, 0.95f, 0.8f);
}
{
var cameraNtt = _scene.CreateEntity("My Camera");
let transform = cameraNtt.GetComponent<TransformComponent>();
transform.Position = .(5, 5, -5);
transform.RotationEuler = .(MathHelper.ToRadians(45), MathHelper.ToRadians(-45), 0);
let camera = cameraNtt.AddComponent<CameraComponent>();
camera.Primary = true;
camera.Camera.SetPerspective(MathHelper.ToRadians(45), 0.1f, 10.0f);
camera.RenderTarget = _cameraTarget;
}
var fxLib = Application.Get().EffectLibrary;
using (Effect myEffect = fxLib.Load("content/Shaders/myEffect.hlsl"))
/*using (Texture2D albedo = new Texture2D("Textures/White.png", true))
using (Texture2D normal = new Texture2D("Textures/White.png"))
using (Texture2D rough = new Texture2D("Textures/White.png"))
using (Texture2D metal = new Texture2D("Textures/White.png"))*/
using (Texture2D albedo = new Texture2D("Textures/TestMat/rustediron2_albedo.png", true))
using (Texture2D normal = new Texture2D("Textures/TestMat/rustediron2_normal.png"))
using (Texture2D rough = new Texture2D("Textures/TestMat/rustediron2_roughness.png"))
using (Texture2D metal = new Texture2D("Textures/TestMat/rustediron2_metallic.png"))
{
albedo.SamplerState = SamplerStateManager.AnisotropicWrap;
normal.SamplerState = SamplerStateManager.AnisotropicWrap;
rough.SamplerState = SamplerStateManager.AnisotropicWrap;
metal.SamplerState = SamplerStateManager.AnisotropicWrap;
List<AnimationClip> clips = scope .();
using (Material mat = new .(myEffect))
{
mat.SetTexture("AlbedoTexture", albedo);
mat.SetTexture("NormalTexture", normal);
mat.SetTexture("MetallicTexture", metal);
mat.SetTexture("RoughnessTexture", rough);
mat.SetVariable("AlbedoColor", ColorRGBA.White);
mat.SetVariable("NormalScaling", Vector2.One);
mat.SetVariable("MetallicFactor", 1.0f);
mat.SetVariable("RoughnessFactor", 1.0f);
// TODO: completely wrong!
EcsEntity e = ModelLoader.LoadModel("content/Models/sphere.glb", mat, _scene.[Friend]_ecsWorld, clips, "Sphere 1");
Entity entity = .(e, _scene);
var transform = entity.GetComponent<TransformComponent>();
transform.Position = .(5, 0, 5);
}
/*using (Material mat = new .(myEffect))
{
mat.SetTexture("AlbedoTexture", albedo);
mat.SetTexture("NormalTexture", normal);
mat.SetTexture("MetallicTexture", metal);
mat.SetTexture("RoughnessTexture", rough);
//mat.SetVariable("BaseColor", Vector4(1, 1, 0, 1));
//mat.SetVariable("LightDir", Vector3(0, 1, 0).Normalized());
ModelLoader.LoadModel("content/Models/sphere.glb", myEffect, mat, _scene.[Friend]_ecsWorld, clips);
}*/
ClearAndReleaseItems!(clips);
}
using (Effect myEffect = fxLib.Get("myEffect"))
using (Texture2D white = new Texture2D("Textures/White.png"))
using (Texture2D normal = new Texture2D("Textures/DefaultNormal.png"))
{
white.SamplerState = SamplerStateManager.PointClamp;
normal.SamplerState = SamplerStateManager.PointClamp;
List<AnimationClip> clips = scope .();
for (int x < 10)
for (int y < 10)
{
using (Material mat = new .(myEffect))
{
mat.SetTexture("AlbedoTexture", white);
mat.SetTexture("NormalTexture", normal);
mat.SetTexture("MetallicTexture", white);
mat.SetTexture("RoughnessTexture", white);
mat.SetVariable("AlbedoColor", Vector4(1, 0, 0, 1));
mat.SetVariable("NormalScaling", Vector2(1.0f));
mat.SetVariable("RoughnessFactor", (x + 1) / 10.0f);
mat.SetVariable("MetallicFactor", y / 9.0f);
EcsEntity e = ModelLoader.LoadModel("content/Models/sphere.glb", mat, _scene.[Friend]_ecsWorld, clips, scope $"Sphere {x} {y}"); _isPaused = false;
Entity entity = .(e, _scene); if (_activeScene != null)
var transform = entity.GetComponent<TransformComponent>(); {
transform.Position = .(x * 1.5f, y * 1.5f, 0); /*
} * Update the viewport size because if the game windows size changed in
} * "Game"-mode the updated aspect-rations will reset once we go back
* into "Editor"-mode (because Game-Mode works on a copy of the scene).
ClearAndReleaseItems!(clips); */
}*/ GameViewportSizeChanged(null, _editor.GameViewportWindow.ViewportSize);
}
} }
/// Creates a new scene. /// Creates a new scene.
private void NewScene() private void NewScene()
{ {
OnSceneStop();
SceneFilePath = null; SceneFilePath = null;
delete _scene;
_scene = new Scene();
_editor.CurrentScene = _scene;
var vpSize = _editor.SceneViewportWindow.ViewportSize;
_scene.OnViewportResize((.)vpSize.X, (.)vpSize.Y);
_camera.Position = .(-1.5f, 1.5f, -2.5f); using (Scene newScene = new Scene())
_camera.RotationEuler = .(MathHelper.ToRadians(25), MathHelper.ToRadians(35), 0);
// Create default camera
/*{
let camEntity = _scene.CreateEntity("Camera");
let transform = camEntity.Transform;
transform.Position =
}*/
/*// Create the default light source
{ {
let lightNtt = _scene.CreateEntity("Light"); _camera.Position = .(-1.5f, 1.5f, -2.5f);
let transform = lightNtt.Transform; _camera.RotationEuler = .(MathHelper.ToRadians(25), MathHelper.ToRadians(35), 0);
transform.Position = .(0, 0, 0);
transform.RotationEuler = .(MathHelper.ToRadians(45), MathHelper.ToRadians(-100), 0); // Create a default camera
{
let light = lightNtt.AddComponent<LightComponent>(); let cameraEntity = newScene.CreateEntity("Camera");
light.SceneLight.Illuminance = 10.0f; let transform = cameraEntity.Transform;
light.SceneLight.Color = .(1.0f, 0.95f, 0.8f); transform.Position = float3(0, 2, -5);
transform.RotationEuler = float3(0, MathHelper.ToRadians(25), 0);
let camera = cameraEntity.AddComponent<CameraComponent>();
camera.Primary = true;
camera.Camera.ProjectionType = .InfinitePerspective;
camera.Camera.PerspectiveFovY = MathHelper.ToRadians(75);
camera.Camera.PerspectiveNearPlane = 0.1f;
}
// Create a default light source
{
let lightEntity = newScene.CreateEntity("Light");
let transform = lightEntity.Transform;
transform.Position = .(-3, 4, -1.5f);
transform.RotationEuler = .(MathHelper.ToRadians(20), MathHelper.ToRadians(75), MathHelper.ToRadians(20));
let light = lightEntity.AddComponent<LightComponent>();
light.SceneLight.Illuminance = 10.0f;
light.SceneLight.Color = .(1.0f, 0.95f, 0.8f);
}
SetReference!(_editorScene, newScene);
_editor.CurrentScene = _editorScene;
SetReference!(_activeScene, _editorScene);
} }
TestEntitiesWithModels();*/
} }
/// Saves the scene in the file that is was loaded from or saved to last. If there is no such path (i.e. it is a new scene) the save file dialog will open. /// Saves the scene in the file that is was loaded from or saved to last. If there is no such path (i.e. it is a new scene) the save file dialog will open.
private void SaveScene() private void SaveScene()
{ {
if (String.IsNullOrWhiteSpace(SceneFilePath)) if (SceneFilePath.IsWhiteSpace)
{ {
SaveSceneAs(); SaveSceneAs();
return; return;
} }
SceneSerializer serializer = scope .(_scene); SceneSerializer serializer = scope .(_editorScene);
serializer.Serialize(SceneFilePath); serializer.Serialize(SceneFilePath);
} }
@@ -565,16 +707,44 @@ namespace GlitchyEditor
/// Loads the given scene file. /// Loads the given scene file.
private void LoadSceneFile(StringView filename) private void LoadSceneFile(StringView filename)
{ {
OnSceneStop();
SceneFilePath = scope String(filename); SceneFilePath = scope String(filename);
delete _scene; using (Scene newScene = new Scene())
_scene = new Scene(); {
_editor.CurrentScene = _scene; SceneSerializer serializer = scope .(newScene);
var vpSize = _editor.SceneViewportWindow.ViewportSize; let result = serializer.Deserialize(SceneFilePath);
_scene.OnViewportResize((.)vpSize.X, (.)vpSize.Y);
SceneSerializer serializer = scope .(_scene); // Make sure we actually loaded something!
serializer.Deserialize(SceneFilePath); if (result case .Ok)
{
SetReference!(_editorScene, newScene);
_editor.CurrentScene = _editorScene;
SetReference!(_activeScene, _editorScene);
}
else
{
Log.EngineLogger.Error("Failed to load scene file.");
}
}
}
private void DrawOpenRecentProjectMenu()
{
int i = 0;
for (let (name, path) in _recentProjectPaths)
{
i++;
if (i >= 10)
break;
if (ImGui.MenuItem(scope $"{i}: {name} ({path})"))
{
LoadSceneFile(path);
}
}
} }
private void DrawMainMenuBar() private void DrawMainMenuBar()
@@ -589,11 +759,18 @@ namespace GlitchyEditor
if (ImGui.MenuItem("Save", "Ctrl+S")) if (ImGui.MenuItem("Save", "Ctrl+S"))
SaveScene(); SaveScene();
if (ImGui.MenuItem("Save as...", "Ctrl+Shift+N")) if (ImGui.MenuItem("Save as...", "Ctrl+Shift+S"))
SaveSceneAs(); SaveSceneAs();
if (ImGui.MenuItem("Open...", "Ctrl+O")) if (ImGui.MenuItem("Open...", "Ctrl+O"))
OpenScene(); OpenScene();
if (ImGui.BeginMenu("Open Recent"))
{
DrawOpenRecentProjectMenu();
ImGui.EndMenu();
}
ImGui.Separator(); ImGui.Separator();
@@ -610,20 +787,34 @@ namespace GlitchyEditor
if(ImGui.BeginMenu("View", true)) if(ImGui.BeginMenu("View", true))
{ {
if(ImGui.MenuItem(EntityHierarchyWindow.s_WindowTitle))
{
_editor.EntityHierarchyWindow.Open = true;
}
if(ImGui.MenuItem(ComponentEditWindow.s_WindowTitle)) if(ImGui.MenuItem(ComponentEditWindow.s_WindowTitle))
{
_editor.ComponentEditWindow.Open = true; _editor.ComponentEditWindow.Open = true;
}
if(ImGui.MenuItem(SceneViewportWindow.s_WindowTitle)) if(ImGui.MenuItem(ContentBrowserWindow.s_WindowTitle))
{ _editor.ComponentEditWindow.Open = true;
if(ImGui.MenuItem(EditorViewportWindow.s_WindowTitle))
_editor.SceneViewportWindow.Open = true; _editor.SceneViewportWindow.Open = true;
}
if(ImGui.MenuItem(EntityHierarchyWindow.s_WindowTitle))
_editor.EntityHierarchyWindow.Open = true;
if(ImGui.MenuItem(GameViewportWindow.s_WindowTitle))
_editor.GameViewportWindow.Open = true;
if(ImGui.MenuItem(PropertiesWindow.s_WindowTitle))
_editor.PropertiesWindow.Open = true;
if(ImGui.MenuItem(AssetViewer.s_WindowTitle))
_editor.AssetViewer.Open = true;
ImGui.EndMenu();
}
if(ImGui.BeginMenu("Tools", true))
{
if(ImGui.MenuItem("Reload Scripts"))
ScriptEngine.ReloadAssemblies();
ImGui.EndMenu(); ImGui.EndMenu();
} }
@@ -637,19 +828,33 @@ namespace GlitchyEditor
return false; return false;
} }
private void ViewportSizeChanged(Object sender, Vector2 viewportSize) private void EditorViewportSizeChanged(Object sender, float2 viewportSize)
{ {
if(viewportSize.X <= 0 || viewportSize.Y <= 0)
return;
uint32 sizeX = (uint32)viewportSize.X; uint32 sizeX = (uint32)viewportSize.X;
uint32 sizeY = (uint32)viewportSize.Y; uint32 sizeY = (uint32)viewportSize.Y;
if(sizeX == 0 || sizeY == 0) _editorViewportTarget.Resize(sizeX, sizeY);
return;
_viewportTarget.Resize(sizeX, sizeY);
_cameraTarget.Resize(sizeX, sizeY); _cameraTarget.Resize(sizeX, sizeY);
_scene.OnViewportResize(sizeX, sizeY);
_camera.OnViewportResize(sizeX, sizeY); _camera.OnViewportResize(sizeX, sizeY);
_activeScene.SetViewportSize(sizeX, sizeY);
}
private void GameViewportSizeChanged(Object sender, float2 viewportSize)
{
if(viewportSize.X <= 0 || viewportSize.Y <= 0)
return;
uint32 sizeX = (uint32)viewportSize.X;
uint32 sizeY = (uint32)viewportSize.Y;
_gameViewportTarget.Resize(sizeX, sizeY);
_activeScene.SetViewportSize(sizeX, sizeY);
} }
private bool OnKeyPressed(KeyPressedEvent e) private bool OnKeyPressed(KeyPressedEvent e)
+12 -11
View File
@@ -64,7 +64,7 @@ namespace GlitchyEditor
_samplerPoint = SamplerStateManager.GetSampler(desc); _samplerPoint = SamplerStateManager.GetSampler(desc);
} }
Vector2 _position; float2 _position;
bool _moving; bool _moving;
@@ -105,6 +105,7 @@ namespace GlitchyEditor
{ {
_target?.ReleaseRef(); _target?.ReleaseRef();
_target = new RenderTarget2D(.(.R8G8B8A8_UNorm, (.)viewportSize.x, (.)viewportSize.y)); _target = new RenderTarget2D(.(.R8G8B8A8_UNorm, (.)viewportSize.x, (.)viewportSize.y));
_target.SamplerState = SamplerStateManager.PointClamp;
_depth?.ReleaseRef(); _depth?.ReleaseRef();
_depth = new DepthStencilTarget((.)viewportSize.x, (.)viewportSize.y, .D16_UNorm); _depth = new DepthStencilTarget((.)viewportSize.x, (.)viewportSize.y, .D16_UNorm);
} }
@@ -124,7 +125,7 @@ namespace GlitchyEditor
{ {
var windowPos = ImGui.GetWindowPos(); var windowPos = ImGui.GetWindowPos();
var mousePos = ImGui.GetIO().MousePos; var mousePos = ImGui.GetIO().MousePos;
Vector2 mouseInWindow = .(mousePos.x - windowPos.x, mousePos.y - windowPos.y); float2 mouseInWindow = .(mousePos.x - windowPos.x, mousePos.y - windowPos.y);
bool windowHovered = ImGui.IsWindowHovered(); bool windowHovered = ImGui.IsWindowHovered();
@@ -158,7 +159,7 @@ namespace GlitchyEditor
if(_moving) if(_moving)
{ {
Int2 movement = Input.GetMouseMovement(); int2 movement = Input.GetMouseMovement();
_position.X += movement.X; _position.X += movement.X;
_position.Y += movement.Y; _position.Y += movement.Y;
@@ -181,10 +182,10 @@ namespace GlitchyEditor
_context.SetDepthStencilTarget(_depth); _context.SetDepthStencilTarget(_depth);
_context.BindRenderTargets(); _context.BindRenderTargets();
Vector2 textureSize = Vector2(viewedTexture.Width, viewedTexture.Height); float2 textureSize = float2(viewedTexture.Width, viewedTexture.Height);
Vector2 zoomedTextureSize = textureSize * _zoom; float2 zoomedTextureSize = textureSize * _zoom;
Vector2 targetSize = Vector2(_target.Width, _target.Height); float2 targetSize = float2(_target.Width, _target.Height);
_effect.Variables["ColorOffset"].SetData(_colorOffset); _effect.Variables["ColorOffset"].SetData(_colorOffset);
_effect.Variables["ColorScale"].SetData(_colorScale); _effect.Variables["ColorScale"].SetData(_colorScale);
@@ -204,19 +205,19 @@ namespace GlitchyEditor
switch(_backgroundMode) switch(_backgroundMode)
{ {
case .Black: case .Black:
Renderer2D.DrawQuadPivotCorner(Vector3(0, 0, 1), targetSize, 0, .Black); Renderer2D.DrawQuadPivotCorner(float3(0, 0, 1), targetSize, 0, .Black);
case .White: case .White:
Renderer2D.DrawQuadPivotCorner(Vector3(0, 0, 1), targetSize, 0, .White); Renderer2D.DrawQuadPivotCorner(float3(0, 0, 1), targetSize, 0, .White);
case .Checkerboard: case .Checkerboard:
float quadSize = 50.0f; float quadSize = 50.0f;
Vector2 numQuads = (targetSize / 500f) * 10f; float2 numQuads = (targetSize / 500f) * 10f;
for(float x = 0; x < numQuads.X; x++) for(float x = 0; x < numQuads.X; x++)
{ {
for(float y = 0; y < numQuads.Y; y++) for(float y = 0; y < numQuads.Y; y++)
{ {
Renderer2D.DrawQuadPivotCorner(Vector3(x * quadSize, -y * quadSize, 1), quadSize.XX, 0, ((x + y) % 2 == 0) ? .White : .Gray); Renderer2D.DrawQuadPivotCorner(float3(x * quadSize, -y * quadSize, 1), quadSize.XX, 0, ((x + y) % 2 == 0) ? .White : .Gray);
} }
} }
break; break;
@@ -236,7 +237,7 @@ namespace GlitchyEditor
Renderer2D.BeginScene(_camera, .SortByTexture, _effect); Renderer2D.BeginScene(_camera, .SortByTexture, _effect);
Renderer2D.DrawQuad(Vector3(_position * .(1, -1), 0), zoomedTextureSize, 0, viewedTexture); Renderer2D.DrawQuad(float3(_position * .(1, -1), 0), zoomedTextureSize, 0, viewedTexture);
Renderer2D.EndScene(); Renderer2D.EndScene();
+22 -1
View File
@@ -1,5 +1,5 @@
FileVersion = 1 FileVersion = 1
Dependencies = {GlitchLog = "*", corlib = "*", DirectX = "*", FreeType = "*", cgltf-beef = "*", msdfgen-beef = "*", ImGui = "*", ImGuiImplDX11 = "*", ImGuiImplWin32 = "*", ImGuizmo = "*", Beefy2D = "*", LodePng = "*", GlitchyEngineHelper = "*", bon = "*", box2d-beef = "*", "Beef.Linq" = "*"} Dependencies = {GlitchLog = "*", corlib = "*", DirectX = "*", FreeType = "*", cgltf-beef = "*", msdfgen-beef = "*", ImGui = "*", ImGuiImplDX11 = "*", ImGuiImplWin32 = "*", ImGuizmo = "*", Beefy2D = "*", LodePng = "*", GlitchyEngineHelper = "*", bon = "*", box2d-beef = "*", "Beef.Linq" = "*", NetHostBeef = "*"}
[Project] [Project]
Name = "GlitchyEngine" Name = "GlitchyEngine"
@@ -25,3 +25,24 @@ PreprocessorMacros = ["TEST", "GE_WINDOWS"]
[Configs.Debug.Win64] [Configs.Debug.Win64]
PreprocessorMacros = ["DEBUG", "GE_PROFILE", "GE_PROFILE_RENDERER", "GE_PROFILE_RESOURCES", "IMGUI"] PreprocessorMacros = ["DEBUG", "GE_PROFILE", "GE_PROFILE_RENDERER", "GE_PROFILE_RESOURCES", "IMGUI"]
[[ProjectFolder.Items]]
Type = "AutoFolder"
Name = "Math"
[[ProjectFolder.Items.Items]]
Type = "IgnoreSource"
Name = "Vector2.bf"
[[ProjectFolder.Items.Items]]
Type = "IgnoreSource"
Name = "Vector3.bf"
[[ProjectFolder.Items.Items]]
Type = "IgnoreSource"
Name = "Vector4.bf"
[[ProjectFolder.Items.Items]]
Type = "IgnoreFolder"
Name = "Vectors"
AutoInclude = true
+41 -1
View File
@@ -4,6 +4,9 @@ using GlitchyEngine.ImGui;
using GlitchyEngine.Renderer; using GlitchyEngine.Renderer;
using GlitchyEngine.Debug; using GlitchyEngine.Debug;
using GlitchyEngine.Content; using GlitchyEngine.Content;
using GlitchyEngine.Scripting;
using System.Collections;
using System.Threading;
namespace GlitchyEngine namespace GlitchyEngine
{ {
@@ -27,6 +30,9 @@ namespace GlitchyEngine
private IContentManager _contentManager; private IContentManager _contentManager;
private append List<delegate void()> _jobQueue = .() ~ ClearAndDeleteItems!(_);
private append Monitor _jobQueueMutex = .();
public bool IsRunning => _running; public bool IsRunning => _running;
public Window Window => _window; public Window Window => _window;
@@ -34,9 +40,13 @@ namespace GlitchyEngine
public bool IsMinimized => _isMinimized; public bool IsMinimized => _isMinimized;
[Inline] public GameTime GameTime => _gameTime;
[Inline, Obsolete("Use Application.Instance instead.", false)]
public static Application Get() => s_Instance; public static Application Get() => s_Instance;
public static Application Instance => s_Instance;
public Settings Settings {get; private set;} = new .() ~ delete _; public Settings Settings {get; private set;} = new .() ~ delete _;
public this() public this()
@@ -66,6 +76,7 @@ namespace GlitchyEngine
RenderCommand.RendererAPI = _rendererApi; RenderCommand.RendererAPI = _rendererApi;
Renderer.Init(); Renderer.Init();
ScriptEngine.Init();
#if IMGUI #if IMGUI
_imGuiLayer = new ImGuiLayer(); _imGuiLayer = new ImGuiLayer();
@@ -88,6 +99,7 @@ namespace GlitchyEngine
Profiler.ProfileFunction!(); Profiler.ProfileFunction!();
SamplerStateManager.Uninit(); SamplerStateManager.Uninit();
Renderer.Deinit(); Renderer.Deinit();
delete _contentManager; delete _contentManager;
@@ -97,6 +109,8 @@ namespace GlitchyEngine
delete _gameTime; delete _gameTime;
delete _layerStack; delete _layerStack;
ScriptEngine.Shutdown();
} }
public void OnEvent(Event e) public void OnEvent(Event e)
@@ -150,6 +164,8 @@ namespace GlitchyEngine
} }
#endif #endif
RunJobs();
if(allowFrame && !_isMinimized) if(allowFrame && !_isMinimized)
{ {
Debug.Profiler.ProfileScope!("Update Layers"); Debug.Profiler.ProfileScope!("Update Layers");
@@ -211,5 +227,29 @@ namespace GlitchyEngine
return false; return false;
} }
/// Executes the given Job on the main thread. Takes ownership of the delegate.
public void InvokeOnMainThread(delegate void() ownJob)
{
using (_jobQueueMutex.Enter())
{
_jobQueue.Add(ownJob);
}
}
private void RunJobs()
{
Debug.Profiler.ProfileFunction!();
using (_jobQueueMutex.Enter())
{
for (let job in _jobQueue)
{
job();
}
ClearAndDeleteItems!(_jobQueue);
}
}
} }
} }
+58
View File
@@ -11,6 +11,7 @@ namespace GlitchyEngine.Collections
{ {
public T Value; public T Value;
public Self Parent;
public List<Self> Children = new .() ~ DeleteContainerAndItems!(_); public List<Self> Children = new .() ~ DeleteContainerAndItems!(_);
public this() {} public this() {}
@@ -29,6 +30,7 @@ namespace GlitchyEngine.Collections
} }
Self newChild = new .(value); Self newChild = new .(value);
newChild.Parent = this;
Children.Add(newChild); Children.Add(newChild);
@@ -54,6 +56,62 @@ namespace GlitchyEngine.Collections
{ {
return ref node.Value; return ref node.Value;
} }
public delegate void ElementFunc(TreeNode<T> currentNode);
public enum IterationMode
{
BreadthFirst,
DepthFirst
}
public void ForEach(ElementFunc func, IterationMode iterationMode = .DepthFirst)
{
if (iterationMode == .BreadthFirst)
{
ForEach_BreadthFirst(func);
}
else if (iterationMode == .DepthFirst)
{
ForEach_DepthFirst(func);
}
}
public void ForEach_BreadthFirst(ElementFunc func)
{
Queue<TreeNode<T>> nodes = scope .();
nodes.Add(this);
while (!nodes.IsEmpty)
{
TreeNode<T> node = nodes.PopFront();
func(node);
for (var child in node.Children)
{
nodes.Add(child);
}
}
}
public void ForEach_DepthFirst(ElementFunc func)
{
List<TreeNode<T>> nodes = scope .();
nodes.Add(this);
while (!nodes.IsEmpty)
{
TreeNode<T> node = nodes.PopBack();
func(node);
for (var child in node.Children)
{
nodes.Add(child);
}
}
}
} }
static static
+19 -10
View File
@@ -8,8 +8,10 @@ using System.IO;
namespace GlitchyEngine.Content; namespace GlitchyEngine.Content;
[BonTarget] [BonTarget]
class Asset : RefCounter abstract class Asset : RefCounter
{ {
internal AssetHandle _handle = .Invalid;
private append String _identifier; private append String _identifier;
internal IContentManager _contentManager; internal IContentManager _contentManager;
@@ -20,19 +22,18 @@ class Asset : RefCounter
public StringView Identifier public StringView Identifier
{ {
get => _identifier; get => _identifier;
set internal set => _identifier.Set(value);
{
_contentManager?.UpdateAssetIdentifier(this, _identifier, value);
_identifier.Set(value);
// TODO: do we need to tell the content manager, that the name changed?
}
} }
/// If true the asset is completely loaded. If false it is only partially loaded (if at all).
public bool Complete { get; internal set; }
// TODO: do we need unmanaged assets? Probably not... // TODO: do we need unmanaged assets? Probably not...
/// Gets the content manager that manages this asset; or null if this asset isn't managed. /// Gets the content manager that manages this asset; or null if this asset isn't managed.
public IContentManager ContentManager => _contentManager; public IContentManager ContentManager => _contentManager;
public AssetHandle Handle => _handle;
static this static this
{ {
gBonEnv.typeHandlers.Add(typeof(Asset), gBonEnv.typeHandlers.Add(typeof(Asset),
@@ -43,7 +44,7 @@ class Asset : RefCounter
{ {
// TODO: crash when _contentManager is deleted first... // TODO: crash when _contentManager is deleted first...
// TODO: unregister from content manager // TODO: unregister from content manager
_contentManager?.UnmanageAsset(this); //_contentManager?.UnmanageAsset(this);
} }
static void AssetSerialize(BonWriter writer, ValueView value, BonEnvironment environment, SerializeValueState state) static void AssetSerialize(BonWriter writer, ValueView value, BonEnvironment environment, SerializeValueState state)
@@ -62,7 +63,15 @@ class Asset : RefCounter
Deserialize.String!(reader, ref identifier, environment); Deserialize.String!(reader, ref identifier, environment);
Asset asset = Application.Get().ContentManager.LoadAsset(identifier); AssetHandle handle = Content.LoadAsset(identifier);
if (handle == .Invalid)
{
value.Assign<Asset>(null);
return .Ok;
}
Asset asset = Content.GetAsset<Asset>(handle);
if (asset != null) if (asset != null)
{ {
+343
View File
@@ -0,0 +1,343 @@
using Bon;
using Bon.Integrated;
using System;
using xxHash;
using System.Collections;
using System.Reflection;
using System.Diagnostics;
using GlitchyEngine.Core;
namespace GlitchyEngine.Content;
struct AssetHandle : IHashable
{
private UUID _uuid;
/// Defines an asset that is invalid.
public const AssetHandle Invalid = .(UUID(0));
public bool IsValid => this != .Invalid;
public bool IsInvalid => this == .Invalid;
public UUID ID => _uuid;
/// Create a new random AssetHandle
public this()
{
_uuid = UUID();
}
private this(UUID uuid)
{
_uuid = uuid;
}
static this
{
gBonEnv.typeHandlers.Add(typeof(AssetHandle),
((.)new => AssetSerialize, new => AssetDeserialize));
}
[Inline]
public T Get<T>(IContentManager contentManager = null) where T : Asset
{
return Content.GetAsset<T>(this, contentManager);
}
public int GetHashCode() => _uuid.GetHashCode();
static void AssetSerialize(BonWriter writer, ValueView value, BonEnvironment environment, SerializeValueState state)
{
Log.EngineLogger.Assert(value.type == typeof(AssetHandle));
AssetHandle handle = value.Get<AssetHandle>();
if (handle.IsInvalid)
writer.String("");
else
{
let identifier = handle.Get<Asset>().Identifier;
writer.String(identifier);
}
}
static Result<void> AssetDeserialize(BonReader reader, ValueView value, BonEnvironment environment, DeserializeValueState state)
{
Log.EngineLogger.Assert(value.type == typeof(AssetHandle));
String identifier = scope .();
Deserialize.String!(reader, ref identifier, environment);
AssetHandle handle = Content.LoadAsset(identifier);
value.Assign<AssetHandle>(handle);
return .Ok;
}
public override void ToString(String strBuffer)
{
_uuid.ToString(strBuffer);
}
}
struct AssetHandle<T> where T : Asset
{
private AssetHandle _handle;
private IContentManager _contentManager;
/*
* We don't increment/decrement the reference counter since we guarantee that we query for the asset every frame.
*/
private T _asset;
//private uint8 _currentFrame;
//private uint64 _actualCurrentFrame = 0;
public const Self Invalid = .();
public bool IsValid => this != .Invalid;
public bool IsInvalid => this == .Invalid;
public this(AssetHandle handle, IContentManager contentManager = null)
{
_handle = handle;
_asset = handle.Get<T>(contentManager);
_contentManager = _asset?.ContentManager;
if (_contentManager == null)
_contentManager = contentManager;
//_currentFrame = (uint8)Application.Get().GameTime.FrameCount;
}
// Creates a new invalid asset handle
private this()
{
_handle = .Invalid;
//_currentFrame = 0;
_contentManager = null;
_asset = null;
}
static this
{
gBonEnv.typeHandlers.Add(typeof(AssetHandle<T>),
((.)new => AssetSerialize, new => AssetDeserialize));
}
public static implicit operator Self(AssetHandle handle)
{
return Self(handle);
}
public static implicit operator AssetHandle(Self handle)
{
return handle._handle;
}
public static implicit operator T(ref Self handle)
{
return handle.Get();
}
public T Get(IContentManager contentManager = null)
{
return Content.GetAsset<T>(_handle, contentManager == null ? _contentManager : contentManager);
}
[Comptime, OnCompile(.TypeInit)]
static void Init()
{
for (var field in typeof(T).GetFields(BindingFlags.FlattenHierarchy))
{
if (field.IsStatic || !field.IsPublic)
continue;
String modifier = field.IsPublic ? "public" : "private";
String code = scope $"""
{modifier} {field.FieldType} {field.Name}
{{
get mut
{{
return Get().{field.Name};
}}
set mut
{{
Get().{field.Name} = value;
}}
}}
""";
Compiler.EmitTypeBody(typeof(Self), code);
}
Dictionary<StringView, (MethodInfo? Getter, MethodInfo? Setter)> properties = scope .();
for (var method in typeof(T).GetMethods(BindingFlags.FlattenHierarchy | BindingFlags.Public))
{
if (method.IsStatic || !method.IsPublic || method.IsConstructor || method.IsDestructor)
continue;
// Filter out destructors
if (method.Name == "~this")
continue;
if (method.Name.StartsWith("get__"))
{
StringView name = method.Name;
name.RemoveFromStart(5);
if (!properties.TryGetValue(name, var propertyInfo))
{
propertyInfo = default;
}
propertyInfo.Getter = method;
properties[name] = propertyInfo;
continue;
}
if (method.Name.StartsWith("set__"))
{
StringView name = method.Name;
name.RemoveFromStart(5);
if (!properties.TryGetValue(name, var propertyInfo))
{
propertyInfo = default;
}
propertyInfo.Setter = method;
properties[name] = propertyInfo;
continue;
}
String modifier = method.IsPublic ? "public" : "private";
String parameters = scope String();
String arguments = scope String();
for (int param < method.ParamCount)
{
Type paramType = method.GetParamType(param);
StringView paramName = method.GetParamName(param);
/*String buffer = scope .();
method.GetParamsDecl(buffer);*/
if (param != 0)
{
parameters.Append(", ");
arguments.Append(", ");
}
// TODO: Default value
parameters.AppendF($"{paramType} {paramName}");
/*if (!buffer.IsEmpty)
{
parameters.AppendF($"/*{buffer}*/");
}*/
arguments.AppendF($" {paramName}");
}
String code = scope $"""
{modifier} {method.ReturnType} {method.Name}({parameters}) mut
{{
return Get().{method.Name}({arguments});
}}
""";
Compiler.EmitTypeBody(typeof(Self), code);
}
for (var (propertyName, property) in properties)
{
Type propertyType = property.Getter?.ReturnType ?? property.Setter?.GetParamType(0);
String getter = scope .();
String setter = scope .();
if (property.Getter != null)
{
getter.AppendF($"""
get mut
{{
return Get().{propertyName};
}}
""");
}
if (property.Setter != null)
{
getter.AppendF($"""
set mut
{{
Get().{propertyName} = value;
}}
""");
}
String code = scope $"""
public {propertyType} {propertyName}
{{
{getter}{setter}
}}
""";
Compiler.EmitTypeBody(typeof(Self), code);
}
}
public AssetHandle<NewT> Cast<NewT>()
where NewT : Asset
where T : NewT
{
return AssetHandle<NewT>(this._handle, this._contentManager);
}
// TODO: Cast up?
public AssetHandle<NewT> Cast<NewT>() where NewT : T
{
return AssetHandle<NewT>(this._handle, this._contentManager);
}
static void AssetSerialize(BonWriter writer, ValueView value, BonEnvironment environment, SerializeValueState state)
{
Log.EngineLogger.Assert(value.type == typeof(AssetHandle<T>));
AssetHandle handle = value.Get<AssetHandle<T>>();
if (handle.IsInvalid)
writer.String("");
else
{
let identifier = handle.Get<Asset>().Identifier;
writer.String(identifier);
}
}
static Result<void> AssetDeserialize(BonReader reader, ValueView value, BonEnvironment environment, DeserializeValueState state)
{
Log.EngineLogger.Assert(value.type == typeof(AssetHandle<T>));
String identifier = scope .();
Deserialize.String!(reader, ref identifier, environment);
AssetHandle<T> handle = Content.LoadAsset(identifier);
value.Assign<AssetHandle<T>>(handle);
return .Ok;
}
}
+58 -16
View File
@@ -57,40 +57,82 @@ namespace GlitchyEngine.Content
/// @param contentManager The content manager used to load the asset. /// @param contentManager The content manager used to load the asset.
/// @returns The loaded asset. /// @returns The loaded asset.
Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager); Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager);
/// Returns the placeholder asset.
Asset GetPlaceholderAsset(Type assetType);
/// Returns the error asset.
Asset GetErrorAsset(Type assetType);
} }
static static class Content
{ {
/// Loads the specified asset with the given contentManager or the current applications content manager. /// Loads the specified asset with the given contentManager or the current applications content manager.
public static T LoadAsset<T>(StringView assetIdentifier, IContentManager contentManager = null) where T : Asset public static AssetHandle LoadAsset(StringView assetIdentifier, IContentManager contentManager = null, bool blocking = false)
{ {
var contentManager; var contentManager;
if (contentManager == null) if (contentManager == null)
contentManager = Application.Get().ContentManager; contentManager = Application.Get().ContentManager;
Asset asset = contentManager.LoadAsset(assetIdentifier); AssetHandle handle = contentManager.LoadAsset(assetIdentifier, blocking);
Log.EngineLogger.AssertDebug(asset is T); return handle;
}
/// Loads the specified asset with the given contentManager or the current applications content manager.
public static T GetAsset<T>(AssetHandle handle, IContentManager contentManager = null) where T : Asset
{
var contentManager;
if (contentManager == null)
contentManager = Application.Get().ContentManager;
Asset asset = contentManager.GetAsset(typeof(T), handle);
return (T)asset; return (T)asset;
} }
public static AssetHandle ManageAsset(Asset asset, IContentManager contentManager = null)
{
var contentManager;
if (contentManager == null)
contentManager = Application.Get().ContentManager;
return contentManager.ManageAsset(asset);
}
/*public static AssetHandle<T> ManageAsset<T>(T asset, IContentManager contentManager = null) where T : Asset
{
var contentManager;
if (contentManager == null)
contentManager = Application.Get().ContentManager;
contentManager.ManageAsset(asset);
}*/
} }
interface IContentManager interface IContentManager
{ {
/// Loads the given asset. /// Loads the Asset with the given handle and returns the handle.
Asset LoadAsset(StringView assetIdentifier); AssetHandle LoadAsset(StringView assetIdentifier, bool blocking = false);
/// Returns the asset for the given handle or null, if it isn't loaded.
Asset GetAsset(AssetHandle handle)
{
return GetAsset(null, handle);
}
/// Returns the asset for the given handle or the default asset of the given type.
Asset GetAsset(Type assetType, AssetHandle handle);
/// The content manager will manage the asset (e.g. provide it when LoadAsset is called with the assets identifier) /// The content manager will manage the asset (e.g. provide it when LoadAsset is called with the assets identifier)
void ManageAsset(Asset asset); AssetHandle ManageAsset(Asset asset);
/// The content manager will no longer manage the asset. /// The content manager will no longer manage the asset.
void UnmanageAsset(Asset asset); void UnmanageAsset(AssetHandle asset);
// TODO: Maybe calling UnmanageAsset -> ManageAsset is enough....
/// Provides a method for the asset to tell its content manager that the identifer changed.
void UpdateAssetIdentifier(Asset asset, StringView oldIdentifier, StringView newIdentifier);
/// Returns a data stream for the given asset. /// Returns a data stream for the given asset.
Stream GetStream(StringView assetIdentifier); Stream GetStream(StringView assetIdentifier);
@@ -109,22 +151,22 @@ namespace GlitchyEngine.Content
Runtime.NotImplemented(); Runtime.NotImplemented();
} }
public Asset LoadAsset(StringView assetIdentifier) public AssetHandle LoadAsset(StringView assetIdentifier, bool blocking = false)
{ {
Runtime.NotImplemented(); Runtime.NotImplemented();
} }
public void ManageAsset(Asset asset) public Asset GetAsset(Type assetType, AssetHandle handle)
{ {
Runtime.NotImplemented(); Runtime.NotImplemented();
} }
public void UnmanageAsset(Asset asset) public AssetHandle ManageAsset(Asset asset)
{ {
Runtime.NotImplemented(); Runtime.NotImplemented();
} }
public void UpdateAssetIdentifier(Asset asset, StringView oldIdentifier, StringView newIdentifier) public void UnmanageAsset(AssetHandle asset)
{ {
Runtime.NotImplemented(); Runtime.NotImplemented();
} }
+22 -172
View File
@@ -229,156 +229,6 @@ namespace GlitchyEngine.Content
return .Success; return .Success;
} }
public static EcsEntity LoadModel(String filename, Material material, EcsWorld world,
List<AnimationClip> outClips, StringView entityName = StringView())
{
CGLTF.Options options = .();
CGLTF.Data* data;
CGLTF.Result result = CGLTF.ParseFile(options, filename, out data);
Log.EngineLogger.Assert(result == .Success, "Failed to load model.");
result = CGLTF.LoadBuffers(options, data, filename);
Log.EngineLogger.Assert(result == .Success, "Failed to load buffers");
(EcsEntity entity, ?) = CreateEntity(world, entityName, .InvalidEntity);
for(var node in data.Scenes[0].Nodes)
{
NodesToEntities(data, node, entity, world, material, outClips);
}
CGLTF.Free(data);
return entity;
}
private static (EcsEntity Entity, TransformComponent* Transform) CreateEntity(EcsWorld world, StringView? name, EcsEntity parent)
{
EcsEntity entity = world.NewEntity();
var nameComponent = world.AssignComponent<DebugNameComponent>(entity);
if (name != null && name.Value.Ptr != null)
{
nameComponent.SetName(name.Value);
}
else
{
nameComponent.SetName("Unnamed Node");
}
/////TODO: !!!!!!!!REPORT!!!!!!!!!!!!!
// This works
TransformComponent cmp = .();
var childTransform = world.AssignComponent<TransformComponent>(entity, cmp);
// This trashes the stack
//var childTransform = world.AssignComponent<TransformComponent>(entity);
childTransform.Parent = parent;
return (entity, childTransform);
}
private static void NodesToEntities(CGLTF.Data* data, CGLTF.Node* node, EcsEntity parentEntity, EcsWorld world, Material material, List<AnimationClip> clips)
{
(EcsEntity entity, TransformComponent* childTransform) = CreateEntity(world, node.Name == null ? null : StringView(node.Name), parentEntity);
if(node.HasMatrix)
{
childTransform.LocalTransform = *(Matrix*)&node.Matrix;
}
else
{
if(node.HasTranslation)
childTransform.Position = *(Vector3*)&node.Translation;
else
childTransform.Position = .Zero;
if(node.HasRotation)
childTransform.Rotation = *(Quaternion*)&node.Rotation;
else
childTransform.Rotation = .Identity;
if(node.HasScale)
childTransform.Scale = *(Vector3*)&node.Scale;
else
childTransform.Scale = .(1, 1, 1);
}
// Invert the Z-Axis of the root Node to convert the coordinate system from right-handed to left-handed
if(parentEntity == .InvalidEntity)
childTransform.Scale *= .(1, 1, -1);
Skeleton skeleton = null;
if(node.Skin != null)
{
skeleton = ExtractSkeleton(node.Skin);
LoadAnimationClips(data, node.Skin, skeleton, clips);
}
if(node.Mesh != null)
{
// If we have only one primitive, add it directly to the entity
if(node.Mesh.Primitives.Length == 1)
{
var mesh = world.AssignComponent<MeshComponent>(entity);
using (var geo = PrimitiveToGeoBinding(node.Mesh.Primitives[0]))
{
mesh.Mesh = geo;
}
if(skeleton == null)
{
var meshRenderer = world.AssignComponent<MeshRendererComponent>(entity);
meshRenderer.Material = material;
}
else
{
var meshRenderer = world.AssignComponent<SkinnedMeshRendererComponent>(entity);
meshRenderer.Material = material;
meshRenderer.Skeleton = skeleton;
}
}
// otherwise one child-entity per primitive
else
{
for(var primitive in node.Mesh.Primitives)
{
EcsEntity meshEntity = world.NewEntity();
var meshParent = world.AssignComponent<ParentComponent>(meshEntity);
meshParent.Entity = entity;
var mesh = world.AssignComponent<MeshComponent>(meshEntity);
mesh.Mesh = PrimitiveToGeoBinding(primitive);
if(skeleton == null)
{
var meshRenderer = world.AssignComponent<MeshRendererComponent>(meshEntity);
meshRenderer.Material = material;
}
else
{
var meshRenderer = world.AssignComponent<SkinnedMeshRendererComponent>(meshEntity);
meshRenderer.Material = material;
meshRenderer.Skeleton = skeleton;
}
}
}
}
skeleton?.ReleaseRef();
for(var child in node.Children)
{
NodesToEntities(data, child, entity, world, material, clips);
}
}
public static GeometryBinding PrimitiveToGeoBinding(CGLTF.Primitive primitive) public static GeometryBinding PrimitiveToGeoBinding(CGLTF.Primitive primitive)
{ {
GeometryBinding binding = new GeometryBinding(); GeometryBinding binding = new GeometryBinding();
@@ -517,15 +367,15 @@ namespace GlitchyEngine.Content
Log.EngineLogger.AssertDebug(positions != null, "The model appears to have no position data?!"); Log.EngineLogger.AssertDebug(positions != null, "The model appears to have no position data?!");
Vector3[] normals = new Vector3[positions.Count]; float3[] normals = new float3[positions.Count];
if(primitive.Indices != null) if(primitive.Indices != null)
GenerateNormals(primitive.Indices, positions, normals); GenerateNormals(primitive.Indices, positions, normals);
else else
GenerateNormals(positions, normals); GenerateNormals(positions, normals);
VertexBuffer vertexBuffer = new VertexBuffer((uint32)sizeof(Vector3), (uint32)normals.Count, .Immutable); VertexBuffer vertexBuffer = new VertexBuffer((uint32)sizeof(float3), (uint32)normals.Count, .Immutable);
vertexBuffer.SetData<Vector3>(normals); vertexBuffer.SetData<float3>(normals);
bindings.Add(vertexBuffer.Binding); bindings.Add(vertexBuffer.Binding);
@@ -555,20 +405,20 @@ namespace GlitchyEngine.Content
} }
/// Generates the normals for one triangle /// Generates the normals for one triangle
static mixin GenerateTriangleNormals(int index0, int index1, int index2, CGLTF.Accessor* positions, Vector3[] normals) static mixin GenerateTriangleNormals(int index0, int index1, int index2, CGLTF.Accessor* positions, float3[] normals)
{ {
Vector3 position0 = GetEntry<Vector3>(positions, (.)index0); float3 position0 = GetEntry<float3>(positions, (.)index0);
Vector3 position1 = GetEntry<Vector3>(positions, (.)index1); float3 position1 = GetEntry<float3>(positions, (.)index1);
Vector3 position2 = GetEntry<Vector3>(positions, (.)index2); float3 position2 = GetEntry<float3>(positions, (.)index2);
ref Vector3 normal0 = ref normals[(.)index0]; ref float3 normal0 = ref normals[(.)index0];
ref Vector3 normal1 = ref normals[(.)index1]; ref float3 normal1 = ref normals[(.)index1];
ref Vector3 normal2 = ref normals[(.)index2]; ref float3 normal2 = ref normals[(.)index2];
Vector3 e0 = position1 - position0; float3 e0 = position1 - position0;
Vector3 e1 = position2 - position0; float3 e1 = position2 - position0;
Vector3 normal = Vector3.Cross(e0, e1); float3 normal = cross(e0, e1);
normal0 += normal; normal0 += normal;
normal1 += normal; normal1 += normal;
@@ -576,16 +426,16 @@ namespace GlitchyEngine.Content
} }
[Inline] [Inline]
static void NormalizeNormals(Vector3[] normals) static void NormalizeNormals(float3[] normals)
{ {
for(int i < normals.Count) for(int i < normals.Count)
{ {
normals[i].Normalize(); normalize(normals[i]);
} }
} }
/// Generates normals for the given model using indexed geometry /// Generates normals for the given model using indexed geometry
static void GenerateNormals(CGLTF.Accessor* indices, CGLTF.Accessor* positions, Vector3[] normals) static void GenerateNormals(CGLTF.Accessor* indices, CGLTF.Accessor* positions, float3[] normals)
{ {
/// Enumerate triangle-wise /// Enumerate triangle-wise
for(uint t = 0; t < indices.Count; t += 3) for(uint t = 0; t < indices.Count; t += 3)
@@ -601,7 +451,7 @@ namespace GlitchyEngine.Content
} }
/// Generates normals for the given model using nonindexed geometry /// Generates normals for the given model using nonindexed geometry
static void GenerateNormals(CGLTF.Accessor* positions, Vector3[] normals) static void GenerateNormals(CGLTF.Accessor* positions, float3[] normals)
{ {
/// Enumerate triangle-wise /// Enumerate triangle-wise
for(int i = 0; i < (.)positions.Count; i += 3) for(int i = 0; i < (.)positions.Count; i += 3)
@@ -704,7 +554,7 @@ namespace GlitchyEngine.Content
for(int i < samples) for(int i < samples)
{ {
float timeStamp = GetEntry<float>(channel.Sampler.Input, i); float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
Vector3 sample = GetEntry<Vector3>(channel.Sampler.Output, i); float3 sample = GetEntry<float3>(channel.Sampler.Output, i);
jointAnimation.TranslationChannel.TimeStamps[i] = timeStamp; jointAnimation.TranslationChannel.TimeStamps[i] = timeStamp;
jointAnimation.TranslationChannel.Values[i] = sample; jointAnimation.TranslationChannel.Values[i] = sample;
@@ -732,7 +582,7 @@ namespace GlitchyEngine.Content
for(int i < samples) for(int i < samples)
{ {
float timeStamp = GetEntry<float>(channel.Sampler.Input, i); float timeStamp = GetEntry<float>(channel.Sampler.Input, i);
Vector3 sample = GetEntry<Vector3>(channel.Sampler.Output, i); float3 sample = GetEntry<float3>(channel.Sampler.Output, i);
jointAnimation.ScaleChannel.TimeStamps[i] = timeStamp; jointAnimation.ScaleChannel.TimeStamps[i] = timeStamp;
jointAnimation.ScaleChannel.Values[i] = sample; jointAnimation.ScaleChannel.Values[i] = sample;
@@ -756,11 +606,11 @@ namespace GlitchyEngine.Content
{ {
case typeof(float): case typeof(float):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 1); CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 1);
case typeof(Vector2): case typeof(float2):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 2); CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 2);
case typeof(Vector3): case typeof(float3):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 3); CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 3);
case typeof(Vector4), typeof(Quaternion): case typeof(float4), typeof(Quaternion):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 4); CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 4);
case typeof(Matrix): case typeof(Matrix):
CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 16); CGLTF.AccessorReadFloat(accessor, (uint)index, (float*)&result, 16);
+5
View File
@@ -36,6 +36,11 @@ namespace GlitchyEngine.Core
return (int)_uuid; return (int)_uuid;
} }
public override void ToString(String strBuffer)
{
_uuid.ToString(strBuffer);
}
static void Serialize(BonWriter writer, ValueView val, BonEnvironment env, SerializeValueState state) static void Serialize(BonWriter writer, ValueView val, BonEnvironment env, SerializeValueState state)
{ {
UUID uuid = *(UUID*)val.dataPtr; UUID uuid = *(UUID*)val.dataPtr;
+12 -1
View File
@@ -48,7 +48,7 @@ namespace GlitchyEngine
let sw = append Stopwatch(startNow); let sw = append Stopwatch(startNow);
_stopwatch = sw; _stopwatch = sw;
} }
/** /**
* Starts or continues the internal timer. * Starts or continues the internal timer.
*/ */
@@ -98,5 +98,16 @@ namespace GlitchyEngine
_frameCount++; _frameCount++;
} }
/**
* Tells the timer that a frame has passed and updates the counters according to the given time step.
*/
public void ManualStepFrame(float deltaTimeSeconds)
{
_frameTime = TimeSpan.FromSeconds(deltaTimeSeconds);
_totalTime += _frameTime;
_frameCount++;
}
} }
} }
@@ -20,11 +20,10 @@ namespace GlitchyEngine.Generators
outFileName.Append(name); outFileName.Append(name);
outText.AppendF( outText.AppendF(
$""" $"""
namespace {Namespace} namespace {Namespace};
struct {name}
{{ {{
struct {name}
{{
}}
}} }}
"""); """);
} }
+57 -1
View File
@@ -1,5 +1,7 @@
using DirectX.Common; using DirectX.Common;
using GlitchyEngine.Core; using GlitchyEngine.Core;
using System.Collections;
using System;
namespace GlitchyEngine namespace GlitchyEngine
{ {
@@ -14,13 +16,22 @@ namespace GlitchyEngine
oldDest?.ReleaseRef(); oldDest?.ReleaseRef();
} }
/// Releases the reference to destination, writes newValue into it and adds a reference to newValue.
public static mixin SetReferenceVar<T>(T destination, T newValue) where T: var
{
var oldDest = destination;
destination = newValue;
destination?.AddRef();
oldDest?.Release();
}
/// Releases the reference to value and nullifies it. /// Releases the reference to value and nullifies it.
public static mixin ReleaseRefAndNullify<T>(T value) where T: RefCounter public static mixin ReleaseRefAndNullify<T>(T value) where T: RefCounter
{ {
value?.ReleaseRef(); value?.ReleaseRef();
value = null; value = null;
} }
public static mixin DeleteContainerAndReleaseItems(var container) public static mixin DeleteContainerAndReleaseItems(var container)
{ {
if (container != null) if (container != null)
@@ -37,5 +48,50 @@ namespace GlitchyEngine
value?.ReleaseRef(); value?.ReleaseRef();
container.Clear(); container.Clear();
} }
public static mixin DeleteDictionaryAndReleaseValues(var container)
{
if (container != null)
{
for (var value in container)
{
value.value?.ReleaseRef();
}
delete container;
}
}
public static mixin ClearDictionaryAndDeleteKeys(var dictionary)
{
if (dictionary != null)
{
for (var value in dictionary)
delete value.key;
delete dictionary;
}
}
public static mixin ClearDictionaryAndDeleteValues(var dictionary)
{
if (dictionary != null)
{
for (var value in dictionary)
delete value.value;
delete dictionary;
}
}
public static mixin ClearDictionaryAndDeleteKeysAndValues(var dictionary)
{
if (dictionary != null)
{
for (var value in dictionary)
{
delete value.key;
delete value.value;
}
delete dictionary;
}
}
} }
} }
+10 -10
View File
@@ -8,7 +8,7 @@ namespace GlitchyEngine.Math
{ {
extension ColorRGBA extension ColorRGBA
{ {
internal uint32 ImGuiU32 => ImGui.ImGui.ColorConvertFloat4ToU32((.)(Vector4)this); internal uint32 ImGuiU32 => ImGui.ImGui.ColorConvertFloat4ToU32((.)(float4)this);
} }
} }
@@ -20,14 +20,14 @@ namespace ImGui
{ {
extension Vec2 extension Vec2
{ {
public static explicit operator Vector2(Vec2 v) => .(v.x, v.y); public static explicit operator float2(Vec2 v) => .(v.x, v.y);
public static explicit operator Vec2(Vector2 v) => .(v.X, v.Y); public static explicit operator Vec2(float2 v) => .(v.X, v.Y);
} }
extension Vec4 extension Vec4
{ {
public static explicit operator Vector4(Vec4 v) => .(v.x, v.y, v.z, v.w); public static explicit operator float4(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); public static explicit operator Vec4(float4 v) => .(v.X, v.Y, v.Z, v.W);
} }
/*public static bool IsItemJustDeactivated() /*public static bool IsItemJustDeactivated()
@@ -75,7 +75,7 @@ namespace ImGui
if (uv0 != .Zero || uv1 != .Ones) if (uv0 != .Zero || uv1 != .Ones)
Runtime.NotImplemented(); Runtime.NotImplemented();
Vector2 v = (.)subTexture.TexCoords.XY + subTexture.TexCoords.ZW; float2 v = (.)subTexture.TexCoords.XY + subTexture.TexCoords.ZW;
Image(subTexture.Texture.GetViewBinding(), size, (.)subTexture.TexCoords.XY, (.)v, tint_col, border_col); Image(subTexture.Texture.GetViewBinding(), size, (.)subTexture.TexCoords.XY, (.)v, tint_col, border_col);
} }
@@ -92,7 +92,7 @@ namespace ImGui
if (uv0 != .Zero || uv1 != .Ones) if (uv0 != .Zero || uv1 != .Ones)
Runtime.NotImplemented(); Runtime.NotImplemented();
Vector2 v = (.)subTexture.TexCoords.XY + subTexture.TexCoords.ZW; float2 v = (.)subTexture.TexCoords.XY + subTexture.TexCoords.ZW;
return ImageButton(subTexture.Texture.GetViewBinding(), size, (.)subTexture.TexCoords.XY, (.)v, frame_padding, bg_col, tint_col); return ImageButton(subTexture.Texture.GetViewBinding(), size, (.)subTexture.TexCoords.XY, (.)v, frame_padding, bg_col, tint_col);
} }
@@ -107,19 +107,19 @@ namespace ImGui
protected internal static extern void CleanupFrame(); protected internal static extern void CleanupFrame();
/// Control to edit a vector 2 with drag functionality and reset buttons /// Control to edit a vector 2 with drag functionality and reset buttons
public static bool EditVector2(StringView label, ref Vector2 value, Vector2 resetValues = .Zero, float dragSpeed = 0.1f, float columnWidth = 100f, Vector2 minValue = .Zero, Vector2 maxValue = .Zero) public static bool Editfloat2(StringView label, ref float2 value, float2 resetValues = .Zero, float dragSpeed = 0.1f, float columnWidth = 100f, float2 minValue = .Zero, float2 maxValue = .Zero)
{ {
return EditVector<2>(label, ref *(float[2]*)&value, (float[2])resetValues, dragSpeed, columnWidth, (float[2])minValue, (float[2])maxValue); return EditVector<2>(label, ref *(float[2]*)&value, (float[2])resetValues, dragSpeed, columnWidth, (float[2])minValue, (float[2])maxValue);
} }
/// Control to edit a vector 3 with drag functionality and reset buttons /// 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, Vector3 minValue = .Zero, Vector3 maxValue = .Zero) public static bool Editfloat3(StringView label, ref float3 value, float3 resetValues = .Zero, float dragSpeed = 0.1f, float columnWidth = 100f, float3 minValue = .Zero, float3 maxValue = .Zero)
{ {
return EditVector<3>(label, ref *(float[3]*)&value, (float[3])resetValues, dragSpeed, columnWidth, (float[3])minValue, (float[3])maxValue); return EditVector<3>(label, ref *(float[3]*)&value, (float[3])resetValues, dragSpeed, columnWidth, (float[3])minValue, (float[3])maxValue);
} }
/// Control to edit a vector 4 with drag functionality and reset buttons /// Control to edit a vector 4 with drag functionality and reset buttons
public static bool EditVector4(StringView label, ref Vector4 value, Vector4 resetValues = .Zero, float dragSpeed = 0.1f, float columnWidth = 100f, Vector4 minValue = .Zero, Vector4 maxValue = .Zero) public static bool Editfloat4(StringView label, ref float4 value, float4 resetValues = .Zero, float dragSpeed = 0.1f, float columnWidth = 100f, float4 minValue = .Zero, float4 maxValue = .Zero)
{ {
return EditVector<4>(label, ref *(float[4]*)&value, (float[4])resetValues, dragSpeed, columnWidth, (float[4])minValue, (float[4])maxValue); return EditVector<4>(label, ref *(float[4]*)&value, (float[4])resetValues, dragSpeed, columnWidth, (float[4])minValue, (float[4])maxValue);
} }
+5 -3
View File
@@ -119,8 +119,6 @@ namespace GlitchyEngine.ImGui
Begin(); Begin();
ImGui.ShowDemoWindow();
{ {
Debug.Profiler.ProfileScope!("ImGuiRenderEvent"); Debug.Profiler.ProfileScope!("ImGuiRenderEvent");
@@ -142,7 +140,11 @@ namespace GlitchyEngine.ImGui
RenderCommand.BindRenderTargets(); RenderCommand.BindRenderTargets();
#if GE_GRAPHICS_DX11 #if GE_GRAPHICS_DX11
ImGuiImplDX11.RenderDrawData(ImGui.GetDrawData());
using (ContextMonitor.Enter())
{
ImGuiImplDX11.RenderDrawData(ImGui.GetDrawData());
}
#endif #endif
ImGui.CleanupFrame(); ImGui.CleanupFrame();
+10 -10
View File
@@ -28,15 +28,15 @@ namespace GlitchyEngine
public static extern bool IsKeyReleasing(Key keycode); public static extern bool IsKeyReleasing(Key keycode);
// TODO: Should Input be able to set mousepos? // TODO: Should Input be able to set mousepos?
public static extern void SetMousePosition(Int2 pos); public static extern void SetMousePosition(int2 pos);
public static extern bool IsMouseButtonPressed(MouseButton button); public static extern bool IsMouseButtonPressed(MouseButton button);
public static extern bool IsMouseButtonReleased(MouseButton button); public static extern bool IsMouseButtonReleased(MouseButton button);
public static extern bool IsMouseButtonPressing(MouseButton button); public static extern bool IsMouseButtonPressing(MouseButton button);
public static extern bool IsMouseButtonReleasing(MouseButton button); public static extern bool IsMouseButtonReleasing(MouseButton button);
public static extern Int2 GetMousePosition(); public static extern int2 GetMousePosition();
public static extern Int2 GetMouseMovement(); public static extern int2 GetMouseMovement();
public static extern Int2 GetRawMouseMovement(); public static extern int2 GetRawMouseMovement();
public static extern int32 GetMouseX(); public static extern int32 GetMouseX();
public static extern int32 GetMouseY(); public static extern int32 GetMouseY();
@@ -44,9 +44,9 @@ namespace GlitchyEngine
// public static extern bool WasMouseButtonPressing(MouseButton button); // public static extern bool WasMouseButtonPressing(MouseButton button);
public static extern bool WasMouseButtonReleased(MouseButton button); public static extern bool WasMouseButtonReleased(MouseButton button);
//public static extern bool WasMouseButtonReleasing(MouseButton button); //public static extern bool WasMouseButtonReleasing(MouseButton button);
public static extern Int2 GetLastMousePosition(); public static extern int2 GetLastMousePosition();
public static extern Int2 GetLastMouseMovement(); public static extern int2 GetLastMouseMovement();
public static extern Int2 GetLastRawMouseMovement(); public static extern int2 GetLastRawMouseMovement();
public static extern int32 GetLastMouseX(); public static extern int32 GetLastMouseX();
public static extern int32 GetLastMouseY(); public static extern int32 GetLastMouseY();
@@ -121,14 +121,14 @@ namespace GlitchyEngine
public static class Mouse public static class Mouse
{ {
private static append List<(Int2 Position, int Hash)> _lockPositions = .(); private static append List<(int2 Position, int Hash)> _lockPositions = .();
public static void LockCurrentPosition(int hash) public static void LockCurrentPosition(int hash)
{ {
LockPosition(Input.GetMousePosition(), hash); LockPosition(Input.GetMousePosition(), hash);
} }
public static void LockPosition(Int2 pos, int hash) public static void LockPosition(int2 pos, int hash)
{ {
for (var entry in _lockPositions) for (var entry in _lockPositions)
{ {
@@ -156,7 +156,7 @@ namespace GlitchyEngine
//Log.EngineLogger.AssertDebug(false, "No mouse lock position with hash found."); //Log.EngineLogger.AssertDebug(false, "No mouse lock position with hash found.");
} }
public static Int2? LockedPosition; public static int2? LockedPosition;
public static void NewFrame() public static void NewFrame()
{ {
+2 -2
View File
@@ -53,10 +53,10 @@ namespace GlitchyEngine.Math
[Inline] [Inline]
#unwarn #unwarn
public static explicit operator Vector3(ColorRGB color) => *(Vector3*)&color; public static explicit operator float3(ColorRGB color) => *(float3*)&color;
[Inline] [Inline]
#unwarn #unwarn
public static explicit operator ColorRGB(Vector3 color) => *(ColorRGB*)&color; public static explicit operator ColorRGB(float3 color) => *(ColorRGB*)&color;
} }
} }
+2 -2
View File
@@ -329,10 +329,10 @@ namespace GlitchyEngine.Math
[Inline] [Inline]
#unwarn #unwarn
public static explicit operator Vector4(ColorRGBA color) => *(Vector4*)&color; public static explicit operator float4(ColorRGBA color) => *(float4*)&color;
[Inline] [Inline]
#unwarn #unwarn
public static explicit operator ColorRGBA(Vector4 color) => *(ColorRGBA*)&color; public static explicit operator ColorRGBA(float4 color) => *(ColorRGBA*)&color;
} }
} }
+3 -3
View File
@@ -13,12 +13,12 @@ namespace System
} }
[Inline] [Inline]
public Vector2 XX => Vector2((float)this); public float2 XX => (float2)this;
[Inline] [Inline]
public Vector3 XXX => Vector3((float)this); public float3 XXX => (float3)this;
[Inline] [Inline]
public Vector4 XXXX => Vector4((float)this); public float4 XXXX => (float4)this;
} }
} }
+23 -3
View File
@@ -13,12 +13,32 @@ namespace System
} }
[Inline] [Inline]
public Int2 XX => Int2((int32)this); public int2 XX => (int32)this;
[Inline] [Inline]
public Int3 XXX => Int3((int32)this); public int3 XXX => (int32)this;
[Inline] [Inline]
public Int4 XXXX => Int4((int32)this); public int4 XXXX => (int32)this;
}
extension UInt32
{
public uint32 X
{
[Inline]
get => (uint32)this;
[Inline]
set mut => this = value;
}
[Inline]
public uint2 XX => (uint32)this;
[Inline]
public uint3 XXX => (uint32)this;
[Inline]
public uint4 XXXX => (uint32)this;
} }
} }
+831
View File
@@ -0,0 +1,831 @@
using System;
namespace GlitchyEngine.Math;
// TODO: Not all functions are available for scalars yet
static
{
/// Returns true if at least one of the components is true.
public static bool any(bool value) => value;
/// Returns true if at least one of the components is true.
public static bool any(bool2 value) => value.X || value.Y;
/// Returns true if at least one of the components is true.
public static bool any(bool3 value) => value.X || value.Y || value.Z;
/// Returns true if at least one of the components is true.
public static bool any(bool4 value) => value.X || value.Y || value.Z || value.W;
/// Returns true if all of the components are true.
public static bool all(bool value) => value;
/// Returns true if all of the components are true.
public static bool all(bool2 value) => value.X && value.Y;
/// Returns true if all of the components are true.
public static bool all(bool3 value) => value.X && value.Y && value.Z;
/// Returns true if all of the components are true.
public static bool all(bool4 value) => value.X && value.Y && value.Z && value.W;
#region abs
public static float abs(float value)
{
return Math.Abs(value);
}
public static float2 abs(float2 value)
{
return float2(Math.Abs(value.X), Math.Abs(value.Y));
}
public static float3 abs(float3 value)
{
return float3(Math.Abs(value.X), Math.Abs(value.Y), Math.Abs(value.Z));
}
public static float4 abs(float4 value)
{
return float4(Math.Abs(value.X), Math.Abs(value.Y), Math.Abs(value.Z), Math.Abs(value.W));
}
public static int abs(int value)
{
return Math.Abs(value);
}
public static int2 abs(int2 value)
{
return int2(Math.Abs(value.X), Math.Abs(value.Y));
}
public static int3 abs(int3 value)
{
return int3(Math.Abs(value.X), Math.Abs(value.Y), Math.Abs(value.Z));
}
public static int4 abs(int4 value)
{
return int4(Math.Abs(value.X), Math.Abs(value.Y), Math.Abs(value.Z), Math.Abs(value.W));
}
#endregion
#region modf / frac / trunc
// Splits the value x into fractional and integer parts, each of which has the same sign as x.
public static float modf(float x, out float integerPart)
{
return Math.[Friend]modff(x.X, out integerPart);
}
// Splits the value x into fractional and integer parts, each of which has the same sign as x.
public static float2 modf(float2 x, out float2 integerPart)
{
float2 fracPart;
fracPart.X = Math.[Friend]modff(x.X, out integerPart.X);
fracPart.Y = Math.[Friend]modff(x.Y, out integerPart.Y);
return fracPart;
}
// Splits the value x into fractional and integer parts, each of which has the same sign as x.
public static float3 modf(float3 x, out float3 integerPart)
{
float3 fracPart;
fracPart.X = Math.[Friend]modff(x.X, out integerPart.X);
fracPart.Y = Math.[Friend]modff(x.Y, out integerPart.Y);
fracPart.Z = Math.[Friend]modff(x.Z, out integerPart.Z);
return fracPart;
}
// Splits the value x into fractional and integer parts, each of which has the same sign as x.
public static float4 modf(float4 x, out float4 integerPart)
{
float4 fracPart;
fracPart.X = Math.[Friend]modff(x.X, out integerPart.X);
fracPart.Y = Math.[Friend]modff(x.Y, out integerPart.Y);
fracPart.Z = Math.[Friend]modff(x.Z, out integerPart.Z);
fracPart.W = Math.[Friend]modff(x.W, out integerPart.W);
return fracPart;
}
// Returns the fractional (or decimal) part of x; which is greater than or equal to 0 and less than 1.
public static float2 frac(float2 x) => [Inline]modf(x, let _);
// Returns the fractional (or decimal) part of x; which is greater than or equal to 0 and less than 1.
public static float3 frac(float3 x) => [Inline]modf(x, let _);
// Returns the fractional (or decimal) part of x; which is greater than or equal to 0 and less than 1.
public static float4 frac(float4 x) => [Inline]modf(x, let _);
// Truncates a floating-point value to the integer component.
public static float2 trunc(float2 x)
{
return float2(Math.Truncate(x.X), Math.Truncate(x.Y));
}
// Truncates a floating-point value to the integer component.
public static float3 trunc(float3 x)
{
return float3(Math.Truncate(x.X), Math.Truncate(x.Y), Math.Truncate(x.Z));
}
// Truncates a floating-point value to the integer component.
public static float4 trunc(float4 x)
{
return float4(Math.Truncate(x.X), Math.Truncate(x.Y), Math.Truncate(x.Z), Math.Truncate(x.W));
}
#endregion
#region infinity and nan check
/// Determines if the specified floating-point value is finite.
public static bool2 isfinite(float2 value)
{
return bool2(value.X.IsFinite, value.Y.IsFinite);
}
/// Determines if the specified floating-point value is finite.
public static bool3 isfinite(float3 value)
{
return bool3(value.X.IsFinite, value.Y.IsFinite, value.Z.IsFinite);
}
/// Determines if the specified floating-point value is finite.
public static bool4 isfinite(float4 value)
{
return bool4(value.X.IsFinite, value.Y.IsFinite, value.Z.IsFinite, value.W.IsFinite);
}
/// Determines if the specified value is infinite.
public static bool2 isinf(float2 value)
{
return bool2(value.X.IsInfinity, value.Y.IsInfinity);
}
/// Determines if the specified value is infinite.
public static bool3 isinf(float3 value)
{
return bool3(value.X.IsInfinity, value.Y.IsInfinity, value.Z.IsInfinity);
}
/// Determines if the specified value is infinite.
public static bool4 isinf(float4 value)
{
return bool4(value.X.IsInfinity, value.Y.IsInfinity, value.Z.IsInfinity, value.W.IsInfinity);
}
/// Determines if the specified value is infinite.
public static bool2 isnan(float2 value)
{
return bool2(value.X.IsNaN, value.Y.IsNaN);
}
/// Determines if the specified value is infinite.
public static bool3 isnan(float3 value)
{
return bool3(value.X.IsNaN, value.Y.IsNaN, value.Z.IsNaN);
}
/// Determines if the specified value is infinite.
public static bool4 isnan(float4 value)
{
return bool4(value.X.IsNaN, value.Y.IsNaN, value.Z.IsNaN, value.W.IsNaN);
}
#endregion
#region Sign
/// Returns the sign of x.
public static int32 sign(float x)
{
return (int32)Math.Sign(x);
}
/// Returns the sign of x.
public static int2 sign(float2 x)
{
return int2((int32)Math.Sign(x.X), (int32)Math.Sign(x.Y));
}
/// Returns the sign of x.
public static int3 sign(float3 x)
{
return int3((int32)Math.Sign(x.X), (int32)Math.Sign(x.Y), (int32)Math.Sign(x.Z));
}
/// Returns the sign of x.
public static int4 sign(float4 x)
{
return int4((int32)Math.Sign(x.X), (int32)Math.Sign(x.Y), (int32)Math.Sign(x.Z), (int32)Math.Sign(x.W));
}
/// Returns the sign of x.
public static int32 sign(int x)
{
return (int32)Math.Sign(x);
}
/// Returns the sign of x.
public static int2 sign(int2 x)
{
return int2((int32)Math.Sign(x.X), (int32)Math.Sign(x.Y));
}
/// Returns the sign of x.
public static int3 sign(int3 x)
{
return int3((int32)Math.Sign(x.X), (int32)Math.Sign(x.Y), (int32)Math.Sign(x.Z));
}
/// Returns the sign of x.
public static int4 sign(int4 x)
{
return int4((int32)Math.Sign(x.X), (int32)Math.Sign(x.Y), (int32)Math.Sign(x.Z), (int32)Math.Sign(x.W));
}
#endregion
#region ceil / floor / round
public static float ceil(float value)
{
return Math.Ceiling(value);
}
public static float2 ceil(float2 value)
{
return float2(Math.Ceiling(value.X), Math.Ceiling(value.Y));
}
public static float3 ceil(float3 value)
{
return float3(Math.Ceiling(value.X), Math.Ceiling(value.Y), Math.Ceiling(value.Z));
}
public static float4 ceil(float4 value)
{
return float4(Math.Ceiling(value.X), Math.Ceiling(value.Y), Math.Ceiling(value.Z), Math.Ceiling(value.W));
}
public static float floor(float value)
{
return Math.Floor(value);
}
public static float2 floor(float2 value)
{
return float2(Math.Floor(value.X), Math.Floor(value.Y));
}
public static float3 floor(float3 value)
{
return float3(Math.Floor(value.X), Math.Floor(value.Y), Math.Floor(value.Z));
}
public static float4 floor(float4 value)
{
return float4(Math.Floor(value.X), Math.Floor(value.Y), Math.Floor(value.Z), Math.Floor(value.W));
}
/// Rounds the specified value to the nearest integer.
public static float round(float value)
{
return Math.Round(value);
}
/// Rounds the specified value to the nearest integer.
public static float2 round(float2 value)
{
return float2(Math.Round(value.X), Math.Round(value.Y));
}
/// Rounds the specified value to the nearest integer.
public static float3 round(float3 value)
{
return float3(Math.Round(value.X), Math.Round(value.Y), Math.Round(value.Z));
}
/// Rounds the specified value to the nearest integer.
public static float4 round(float4 value)
{
return float4(Math.Round(value.X), Math.Round(value.Y), Math.Round(value.Z), Math.Round(value.W));
}
#endregion
#region min / max
public static float min(float x, float y)
{
return Math.Min(x, y);
}
public static float2 min(float2 x, float2 y)
{
return float2(Math.Min(x.X, y.X), Math.Min(x.Y, y.Y));
}
public static float3 min(float3 x, float3 y)
{
return float3(Math.Min(x.X, y.X), Math.Min(x.Y, y.Y), Math.Min(x.Z, y.Z));
}
public static float4 min(float4 x, float4 y)
{
return float4(Math.Min(x.X, y.X), Math.Min(x.Y, y.Y), Math.Min(x.Z, y.Z), Math.Min(x.W, y.W));
}
public static float max(float x, float y)
{
return Math.Max(x, y);
}
public static float2 max(float2 x, float2 y)
{
return float2(Math.Max(x.X, y.X), Math.Max(x.Y, y.Y));
}
public static float3 max(float3 x, float3 y)
{
return float3(Math.Max(x.X, y.X), Math.Max(x.Y, y.Y), Math.Max(x.Z, y.Z));
}
public static float4 max(float4 x, float4 y)
{
return float4(Math.Max(x.X, y.X), Math.Max(x.Y, y.Y), Math.Max(x.Z, y.Z), Math.Max(x.W, y.W));
}
#endregion
#region exp, pow, log
// TODO: log, log10, log2
/// Returns x raised to the power of y.
public static float pow(float x, float y)
{
return Math.Pow(x, y);
}
/// Returns x raised to the power of y.
public static float2 pow(float2 x, float2 y)
{
return float2(Math.Pow(x.X, y.X), Math.Pow(x.Y, y.Y));
}
/// Returns x raised to the power of y.
public static float3 pow(float3 x, float3 y)
{
return float3(Math.Pow(x.X, y.X), Math.Pow(x.Y, y.Y), Math.Pow(x.Z, y.Z));
}
/// Returns x raised to the power of y.
public static float4 pow(float4 x, float4 y)
{
return float4(Math.Pow(x.X, y.X), Math.Pow(x.Y, y.Y), Math.Pow(x.Z, y.Z), Math.Pow(x.W, y.W));
}
/// Returns the base-e exponential, or e^x, of the specified value.
public static float exp(float x)
{
return Math.Exp(x);
}
/// Returns the base-e exponential, or e^x, of the specified value.
public static float2 exp(float2 x)
{
return float2(Math.Exp(x.X), Math.Exp(x.Y));
}
/// Returns the base-e exponential, or e^x, of the specified value.
public static float3 exp(float3 x)
{
return float3(Math.Exp(x.X), Math.Exp(x.Y), Math.Exp(x.Z));
}
/// Returns the base-e exponential, or e^x, of the specified value.
public static float4 exp(float4 x)
{
return float4(Math.Exp(x.X), Math.Exp(x.Y), Math.Exp(x.Z), Math.Exp(x.W));
}
/// Returns the base 2 exponential, or 2^x, of the specified value.
public static float exp2(float x)
{
return Math.Pow(2, x);
}
/// Returns the base 2 exponential, or 2^x, of the specified value.
public static float2 exp2(float2 x)
{
return float2(Math.Pow(2, x.X), Math.Pow(2, x.Y));
}
/// Returns the base 2 exponential, or 2^x, of the specified value.
public static float3 exp2(float3 x)
{
return float3(Math.Pow(2, x.X), Math.Pow(2, x.Y), Math.Pow(2, x.Z));
}
/// Returns the base 2 exponential, or 2^x, of the specified value.
public static float4 exp2(float4 x)
{
return float4(Math.Pow(2, x.X), Math.Pow(2, x.Y), Math.Pow(2, x.Z), Math.Pow(2, x.W));
}
#endregion
#region Degrees / Radians
public static float toDegrees(float radians) => radians * MathHelper.RadToDeg;
public static float2 toDegrees(float2 radians) => radians * MathHelper.RadToDeg;
public static float3 toDegrees(float3 radians) => radians * MathHelper.RadToDeg;
public static float4 toDegrees(float4 radians) => radians * MathHelper.RadToDeg;
public static float toRadians(float degrees) => degrees * MathHelper.DegToRad;
public static float2 toRadians(float2 degrees) => degrees * MathHelper.DegToRad;
public static float3 toRadians(float3 degrees) => degrees * MathHelper.DegToRad;
public static float4 toRadians(float4 degrees) => degrees * MathHelper.DegToRad;
#endregion
#region Clamp
public static float clamp(float value, float min, float max)
{
return Math.Clamp(value.X, min.X, max.X);
}
public static float2 clamp(float2 value, float2 min, float2 max)
{
return float2(Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y));
}
public static float3 clamp(float3 value, float3 min, float3 max)
{
return float3(Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y), Math.Clamp(value.Z, min.Z, max.Z));
}
public static float4 clamp(float4 value, float4 min, float4 max)
{
return float4(Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y), Math.Clamp(value.Z, min.Z, max.Z), Math.Clamp(value.W, min.W, max.W));
}
public static int2 clamp(int2 value, int2 min, int2 max)
{
return int2(Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y));
}
public static int3 clamp(int3 value, int3 min, int3 max)
{
return int3(Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y), Math.Clamp(value.Z, min.Z, max.Z));
}
public static int4 clamp(int4 value, int4 min, int4 max)
{
return int4(Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y), Math.Clamp(value.Z, min.Z, max.Z), Math.Clamp(value.W, min.W, max.W));
}
#endregion
#region Lerp
/// Performs a linear interpolation.
// @param x The first vector value.
// @param y The second vector value.
// @param y A value that linearly interpolates between x and y.
public static float lerp(float x, float y, float s)
{
return x + s * (y - x);
}
/// Performs a linear interpolation.
// @param x The first vector value.
// @param y The second vector value.
// @param y A value that linearly interpolates between x and y.
public static float2 lerp(float2 x, float2 y, float s)
{
return x + s * (y - x);
}
/// Performs a linear interpolation.
// @param x The first vector value.
// @param y The second vector value.
// @param y A value that linearly interpolates between x and y.
public static float3 lerp(float3 x, float3 y, float s)
{
return x + s * (y - x);
}
/// Performs a linear interpolation.
// @param x The first vector value.
// @param y The second vector value.
// @param y A value that linearly interpolates between x and y.
public static float4 lerp(float4 x, float4 y, float s)
{
return x + s * (y - x);
}
#endregion
// mul? hlsl has like 280 overloads...
public static float normalize(float value) => 1.0f;
public static float2 normalize(float2 value) => value / length(value);
public static float3 normalize(float3 value) => value / length(value);
public static float4 normalize(float4 value) => value / length(value);
// pow
#region Reflect and Refract
/// Returns a reflection vector using an incident ray and a surface normal.
public static float2 reflect(float2 incident, float2 normal) => incident - 2 * normal * dot(incident, normal);
/// Returns a reflection vector using an incident ray and a surface normal.
public static float3 reflect(float3 incident, float3 normal) => incident - 2 * normal * dot(incident, normal);
/// Returns a reflection vector using an incident ray and a surface normal.
public static float4 reflect(float4 incident, float4 normal) => incident - 2 * normal * dot(incident, normal);
// Source: https://thebookofshaders.com/glossary/?search=refract
/// Returns a refraction vector using an entering ray, a surface normal, and a refraction index.
public static float2 refract(float2 incident, float2 normal, float refractionIndex)
{
float dot_n_i = dot(normal, incident);
float k = 1.0f - refractionIndex * refractionIndex * (1.0f - dot_n_i * dot_n_i);
if (k < 0.0f)
return 0.0f;
else
return refractionIndex * incident - (refractionIndex * dot_n_i + sqrt(k));
}
/// Returns a refraction vector using an entering ray, a surface normal, and a refraction index.
public static float3 refract(float3 incident, float3 normal, float refractionIndex)
{
float dot_n_i = dot(normal, incident);
float k = 1.0f - refractionIndex * refractionIndex * (1.0f - dot_n_i * dot_n_i);
if (k < 0.0f)
return 0.0f;
else
return refractionIndex * incident - (refractionIndex * dot_n_i + sqrt(k));
}
/// Returns a refraction vector using an entering ray, a surface normal, and a refraction index.
public static float4 refract(float4 incident, float4 normal, float refractionIndex)
{
float dot_n_i = dot(normal, incident);
float k = 1.0f - refractionIndex * refractionIndex * (1.0f - dot_n_i * dot_n_i);
if (k < 0.0f)
return 0.0f;
else
return refractionIndex * incident - (refractionIndex * dot_n_i + sqrt(k));
}
#endregion
/// Calculates the per component square root of the given value.
public static float sqrt(float value)
{
return Math.Sqrt(value);
}
/// Calculates the per component square root of the given value.
public static float2 sqrt(float2 value)
{
return float2(Math.Sqrt(value.X), Math.Sqrt(value.Y));
}
/// Calculates the per component square root of the given value.
public static float3 sqrt(float3 value)
{
return float3(Math.Sqrt(value.X), Math.Sqrt(value.Y), Math.Sqrt(value.Z));
}
/// Calculates the per component square root of the given value.
public static float4 sqrt(float4 value)
{
return float4(Math.Sqrt(value.X), Math.Sqrt(value.Y), Math.Sqrt(value.Z), Math.Sqrt(value.W));
}
// saturate (I don't think there is a faster way than simply using clamp, so simply use clamp...)
#region Step / Smoothstep
/// Compares two values, returning 0 or 1 based on which value is greater.
/// @returns 1 if the x parameter is greater than or equal to the y parameter; otherwise, 0.
public static float step(float y, float x)
{
return (x >= y) ? 1.0f : 0.0f;
}
/// Compares two values, returning 0 or 1 based on which value is greater.
/// @returns 1 if the x parameter is greater than or equal to the y parameter; otherwise, 0.
public static float2 step(float2 y, float2 x)
{
bool2 b = (x >= y);
return float2(b.X ? 1.0f : 0.0f, b.Y ? 1.0f : 0.0f);
}
/// Compares two values, returning 0 or 1 based on which value is greater.
/// @returns 1 if the x parameter is greater than or equal to the y parameter; otherwise, 0.
public static float3 step(float3 y, float3 x)
{
bool3 b = (x >= y);
return float3(b.X ? 1.0f : 0.0f, b.Y ? 1.0f : 0.0f, b.Z ? 1.0f : 0.0f);
}
/// Compares two values, returning 0 or 1 based on which value is greater.
/// @returns 1 if the x parameter is greater than or equal to the y parameter; otherwise, 0.
public static float4 step(float4 y, float4 x)
{
bool4 b = (x >= y);
return float4(b.X ? 1.0f : 0.0f, b.Y ? 1.0f : 0.0f, b.Z ? 1.0f : 0.0f, b.W ? 1.0f : 0.0f);
}
// Source: https://thebookofshaders.com/glossary/?search=smoothstep
/// Returns a smooth Hermite interpolation between 0 and 1, if x is in the range [min, max].
/// @returns Returns 0 if x is less than min; 1 if x is greater than max; otherwise, a value between 0 and 1 if x is in the range [min, max].
public static float smoothstep(float min, float max, float x)
{
float t = clamp((x - min) / (max - min), 0.0f, 1.0f);
return t * t * (3.0f - 2.0f * t);
}
/// Returns a smooth Hermite interpolation between 0 and 1, if x is in the range [min, max].
/// @returns Returns 0 if x is less than min; 1 if x is greater than max; otherwise, a value between 0 and 1 if x is in the range [min, max].
public static float2 smoothstep(float2 min, float2 max, float2 x)
{
float2 t = clamp((x - min) / (max - min), 0.0f, 1.0f);
return t * t * (3.0f - 2.0f * t);
}
/// Returns a smooth Hermite interpolation between 0 and 1, if x is in the range [min, max].
/// @returns Returns 0 if x is less than min; 1 if x is greater than max; otherwise, a value between 0 and 1 if x is in the range [min, max].
public static float3 smoothstep(float3 min, float3 max, float3 x)
{
float3 t = clamp((x - min) / (max - min), 0.0f, 1.0f);
return t * t * (3.0f - 2.0f * t);
}
/// Returns a smooth Hermite interpolation between 0 and 1, if x is in the range [min, max].
/// @returns Returns 0 if x is less than min; 1 if x is greater than max; otherwise, a value between 0 and 1 if x is in the range [min, max].
public static float4 smoothstep(float4 min, float4 max, float4 x)
{
float4 t = clamp((x - min) / (max - min), 0.0f, 1.0f);
return t * t * (3.0f - 2.0f * t);
}
#endregion
#region Reject / Project
/**
* Calculates the projection of a onto b
*/
public static float2 project(float2 a, float2 b)
{
return (b * (dot(a, b) / dot(b, b)));
}
/**
* Calculates the projection of a onto b
*/
public static float3 project(float3 a, float3 b)
{
return (b * (dot(a, b) / dot(b, b)));
}
/**
* Calculates the projection of a onto b
*/
public static float4 project(float4 a, float4 b)
{
return (b * (dot(a, b) / dot(b, b)));
}
/**
* Calculates the rejection of a from b
*/
public static float2 reject(float2 a, float2 b)
{
return (a - b * (dot(a, b) / dot(b, b)));
}
/**
* Calculates the rejection of a from b
*/
public static float3 reject(float3 a, float3 b)
{
return (a - b * (dot(a, b) / dot(b, b)));
}
/**
* Calculates the rejection of a from b
*/
public static float4 reject(float4 a, float4 b)
{
return (a - b * (dot(a, b) / dot(b, b)));
}
#endregion
#region dot
public static float dot(float2 left, float2 right)
{
return left.X * right.X + left.Y * right.Y;
}
public static float dot(float3 left, float3 right)
{
return left.X * right.X + left.Y * right.Y + left.Z * right.Z;
}
public static float dot(float4 left, float4 right)
{
return left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W;
}
public static int dot(int2 left, int2 right)
{
return left.X * right.X + left.Y * right.Y;
}
public static int dot(int3 left, int3 right)
{
return left.X * right.X + left.Y * right.Y + left.Z * right.Z;
}
public static int dot(int4 left, int4 right)
{
return left.X * right.X + left.Y * right.Y + left.Z * right.Z + left.W * right.W;
}
#endregion
#region lengthSq / length / DistanceSq / Distance
public static float lengthSq(float2 value) => dot(value, value);
public static float lengthSq(float3 value) => dot(value, value);
public static float lengthSq(float4 value) => dot(value, value);
public static int lengthSq(int2 value) => dot(value, value);
public static int lengthSq(int3 value) => dot(value, value);
public static int lengthSq(int4 value) => dot(value, value);
public static float length(float2 value) => Math.Sqrt(lengthSq(value));
public static float length(float3 value) => Math.Sqrt(lengthSq(value));
public static float length(float4 value) => Math.Sqrt(lengthSq(value));
public static float distanceSq(float2 left, float2 right) => dot(left, right);
public static float distanceSq(float3 left, float3 right) => dot(left, right);
public static float distanceSq(float4 left, float4 right) => dot(left, right);
public static float distance(float2 left, float2 right) => Math.Sqrt(distanceSq(left, right));
public static float distance(float3 left, float3 right) => Math.Sqrt(distanceSq(left, right));
public static float distance(float4 left, float4 right) => Math.Sqrt(distanceSq(left, right));
#endregion
public static float3 cross(float3 left, float3 right)
{
return float3(
left.Y * right.Z - left.Z * right.Y,
left.Z * right.X - left.X * right.Z,
left.X * right.Y - left.Y * right.X);
}
// transpose und determinante für Matrizen
// sin, cos, tan, asin, acos, atan, atan2, cosh, sinh, tanh
}
-1
View File
@@ -4,5 +4,4 @@ namespace GlitchyEngine.Math
{ {
typealias Matrix3x3 = DirectX.Math.Matrix3x3; typealias Matrix3x3 = DirectX.Math.Matrix3x3;
typealias Matrix4x3 = DirectX.Math.Matrix4x3; typealias Matrix4x3 = DirectX.Math.Matrix4x3;
typealias Matrix = DirectX.Math.Matrix;
} }
+13 -13
View File
@@ -30,19 +30,19 @@ namespace GlitchyEngine.Math
} }
// Converts the given radians to degrees // Converts the given radians to degrees
public static Vector2 ToDegrees(Vector2 radians) public static float2 ToDegrees(float2 radians)
{ {
return radians * RadToDeg; return radians * RadToDeg;
} }
// Converts the given radians to degrees // Converts the given radians to degrees
public static Vector3 ToDegrees(Vector3 radians) public static float3 ToDegrees(float3 radians)
{ {
return radians * RadToDeg; return radians * RadToDeg;
} }
// Converts the given radians to degrees // Converts the given radians to degrees
public static Vector4 ToDegrees(Vector4 radians) public static float4 ToDegrees(float4 radians)
{ {
return radians * RadToDeg; return radians * RadToDeg;
} }
@@ -54,19 +54,19 @@ namespace GlitchyEngine.Math
} }
// Converts the given degrees to radians // Converts the given degrees to radians
public static Vector2 ToRadians(Vector2 degrees) public static float2 ToRadians(float2 degrees)
{ {
return degrees * DegToRad; return degrees * DegToRad;
} }
// Converts the given degrees to radians // Converts the given degrees to radians
public static Vector3 ToRadians(Vector3 degrees) public static float3 ToRadians(float3 degrees)
{ {
return degrees * DegToRad; return degrees * DegToRad;
} }
// Converts the given degrees to radians // Converts the given degrees to radians
public static Vector4 ToRadians(Vector4 degrees) public static float4 ToRadians(float4 degrees)
{ {
return degrees * DegToRad; return degrees * DegToRad;
} }
@@ -79,24 +79,24 @@ namespace GlitchyEngine.Math
} }
/// Returns the point that lies on the unit circle at the specified angle. /// Returns the point that lies on the unit circle at the specified angle.
public static Vector2 CirclePoint(float angle, float radius = 1.0f) public static float2 CirclePoint(float angle, float radius = 1.0f)
{ {
return .(Math.Cos(angle), Math.Sin(angle)) * radius; return .(Math.Cos(angle), Math.Sin(angle)) * radius;
} }
public static Vector2 Pow(Vector2 v, float p) public static float2 Pow(float2 v, float p)
{ {
return Vector2(Math.Pow(v.X, p), Math.Pow(v.Y, p)); return float2(Math.Pow(v.X, p), Math.Pow(v.Y, p));
} }
public static Vector3 Pow(Vector3 v, float p) public static float3 Pow(float3 v, float p)
{ {
return Vector3(Math.Pow(v.X, p), Math.Pow(v.Y, p), Math.Pow(v.Z, p)); return float3(Math.Pow(v.X, p), Math.Pow(v.Y, p), Math.Pow(v.Z, p));
} }
public static Vector4 Pow(Vector4 v, float p) public static float4 Pow(float4 v, float p)
{ {
return Vector4(Math.Pow(v.X, p), Math.Pow(v.Y, p), Math.Pow(v.Z, p), Math.Pow(v.W, p)); return float4(Math.Pow(v.X, p), Math.Pow(v.Y, p), Math.Pow(v.Z, p), Math.Pow(v.W, p));
} }
} }
} }
+874
View File
@@ -0,0 +1,874 @@
using System;
namespace GlitchyEngine.Math;
/**
* Represents a 4 by 4 column-major matrix.
*/
[Union]
public struct Matrix
{
public struct Values
{
public float _11, _21, _31, _41,
_12, _22, _32, _42,
_13, _23, _33, _43,
_14, _24, _34, _44;
}
public const Matrix Zero = .();
public const Matrix Identity = .(.UnitX, .UnitY, .UnitZ, .UnitW);
public using Values V;
public float[4][4] Values;
public float4[4] Columns;
/// Creates a new zero-matrix.
public this() => this = default;
/**
* Initializes a new Matrix.
* @param value The value that will be assigned to all components.
*/
/// Creates a new matrix.
public this(float value)
{
this = ?;
_11 = _12 = _13 = _14 =
_21 = _22 = _23 = _24 =
_31 = _32 = _33 = _34 =
_41 = _42 = _43 = _44 = value;
}
/// Creates a new matrix and initializes it with the given entries.
public this(float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33)
{
Values[0][0] = m00; Values[0][1] = m10; Values[0][2] = m20; Values[0][3] = m30;
Values[1][0] = m01; Values[1][1] = m11; Values[1][2] = m21; Values[1][3] = m31;
Values[2][0] = m02; Values[2][1] = m12; Values[2][2] = m22; Values[2][3] = m32;
Values[3][0] = m03; Values[3][1] = m13; Values[3][2] = m23; Values[3][3] = m33;
}
/// Creates a new matrix and initializes it with the given column-vectors.
public this(float4 c0, float4 c1, float4 c2, float4 c3)
{
Columns[0] = c0;
Columns[1] = c1;
Columns[2] = c2;
Columns[3] = c3;
}
public ref float3 Right
{
[Inline]
get
{
#unwarn
return ref *(float3*)&Columns[0];
}
}
public ref float3 Up
{
[Inline]
get
{
#unwarn
return ref *(float3*)&Columns[1];
}
}
public ref float3 Forward
{
[Inline]
get
{
#unwarn
return ref *(float3*)&Columns[2];
}
}
public ref float3 Translation
{
[Inline]
get
{
#unwarn
return ref *(float3*)&Columns[3];
}
}
public float3 Scale
{
[Inline]
get => .(_11, _22, _33);
[Inline]
set mut
{
_11 = value.X;
_22 = value.Y;
_33 = value.Z;
}
}
public ref float this[int row, int column]
{
get
{
#unwarn
return ref *(float*)&Values[column][row];
}
[Checked]
get
{
if(column < 0 || column > 3 || row < 0 || row > 3)
Internal.ThrowIndexOutOfRange();
#unwarn
return ref *(float*)&Values[column][row];
}
}
public ref float4 this[int column]
{
get
{
#unwarn
return ref *(float4*)&Columns[column];
}
[Checked]
get
{
if(column < 0 || column > 3)
Internal.ThrowIndexOutOfRange();
#unwarn
return ref *(float4*)&Columns[column];
}
}
//
// Assignment Operators
//
// Addition
public void operator +=(Matrix value) mut
{
Columns[0] += value.Columns[0];
Columns[1] += value.Columns[1];
Columns[2] += value.Columns[2];
Columns[3] += value.Columns[3];
}
// Matrix + Scalar : Matrix + Scalar * Identity
public void operator +=(float scalar) mut
{
_11 += scalar;
_22 += scalar;
_33 += scalar;
_44 += scalar;
}
// Subtraction
public void operator -=(Matrix value) mut
{
Columns[0] -= value.Columns[0];
Columns[1] -= value.Columns[1];
Columns[2] -= value.Columns[2];
Columns[3] -= value.Columns[3];
}
// Matrix - Scalar : Matrix - Scalar * Identity
public void operator -=(float scalar) mut
{
_11 -= scalar;
_22 -= scalar;
_33 -= scalar;
_44 -= scalar;
}
// Multiplication
public void operator *=(float scalar) mut
{
Columns[0] *= scalar;
Columns[1] *= scalar;
Columns[2] *= scalar;
Columns[3] *= scalar;
}
public void operator *=(Matrix value) mut
{
this = this * value;
}
// Divide
public void operator /=(float scalar) mut
{
float inv = 1.0f / scalar;
Columns[0] *= inv;
Columns[1] *= inv;
Columns[2] *= inv;
Columns[3] *= inv;
}
//
// Operators
//
// Addition
public static Matrix operator +(Matrix left, Matrix right)
{
return .(left.Columns[0] + right.Columns[0],
left.Columns[1] + right.Columns[1],
left.Columns[2] + right.Columns[2],
left.Columns[3] + right.Columns[3]);
}
public static Matrix operator +(Matrix left, float right)
{
Matrix result = left;
result._11 += right;
result._22 += right;
result._33 += right;
result._44 += right;
return result;
}
public static Matrix operator +(float left, Matrix right)
{
Matrix result = right;
result._11 += left;
result._22 += left;
result._33 += left;
result._44 += left;
return result;
}
// Subtraction
public static Matrix operator -(Matrix left, Matrix right)
{
return .(left.Columns[0] - right.Columns[0],
left.Columns[1] - right.Columns[1],
left.Columns[2] - right.Columns[2],
left.Columns[3] - right.Columns[3]);
}
public static Matrix operator -(Matrix value, float scalar)
{
Matrix result = value;
result._11 -= scalar;
result._22 -= scalar;
result._33 -= scalar;
result._44 -= scalar;
return result;
}
public static Matrix operator -(float scalar, Matrix value)
{
Matrix result = value;
result._11 -= scalar;
result._22 -= scalar;
result._33 -= scalar;
result._44 -= scalar;
return result;
}
public static Matrix operator -(Matrix value)
{
return .(-value.Columns[0],
-value.Columns[1],
-value.Columns[2],
-value.Columns[3]);
}
// Multiplication
public static Matrix operator *(Matrix left, Matrix right)
{
#unwarn
var l = &left.V;
#unwarn
var r = &right.V;
Matrix result = ?;
result._11 = (l._11 * r._11) + (l._12 * r._21) + (l._13 * r._31) + (l._14 * r._41);
result._12 = (l._11 * r._12) + (l._12 * r._22) + (l._13 * r._32) + (l._14 * r._42);
result._13 = (l._11 * r._13) + (l._12 * r._23) + (l._13 * r._33) + (l._14 * r._43);
result._14 = (l._11 * r._14) + (l._12 * r._24) + (l._13 * r._34) + (l._14 * r._44);
result._21 = (l._21 * r._11) + (l._22 * r._21) + (l._23 * r._31) + (l._24 * r._41);
result._22 = (l._21 * r._12) + (l._22 * r._22) + (l._23 * r._32) + (l._24 * r._42);
result._23 = (l._21 * r._13) + (l._22 * r._23) + (l._23 * r._33) + (l._24 * r._43);
result._24 = (l._21 * r._14) + (l._22 * r._24) + (l._23 * r._34) + (l._24 * r._44);
result._31 = (l._31 * r._11) + (l._32 * r._21) + (l._33 * r._31) + (l._34 * r._41);
result._32 = (l._31 * r._12) + (l._32 * r._22) + (l._33 * r._32) + (l._34 * r._42);
result._33 = (l._31 * r._13) + (l._32 * r._23) + (l._33 * r._33) + (l._34 * r._43);
result._34 = (l._31 * r._14) + (l._32 * r._24) + (l._33 * r._34) + (l._34 * r._44);
result._41 = (l._41 * r._11) + (l._42 * r._21) + (l._43 * r._31) + (l._44 * r._41);
result._42 = (l._41 * r._12) + (l._42 * r._22) + (l._43 * r._32) + (l._44 * r._42);
result._43 = (l._41 * r._13) + (l._42 * r._23) + (l._43 * r._33) + (l._44 * r._43);
result._44 = (l._41 * r._14) + (l._42 * r._24) + (l._43 * r._34) + (l._44 * r._44);
return result;
}
public static Matrix operator *(Matrix value, float scalar)
{
return .(value.Columns[0] * scalar,
value.Columns[1] * scalar,
value.Columns[2] * scalar,
value.Columns[3] * scalar);
}
public static Matrix operator *(float scalar, Matrix value)
{
return .(value.Columns[0] * scalar,
value.Columns[1] * scalar,
value.Columns[2] * scalar,
value.Columns[3] * scalar);
}
/**
* Multiplies a matrix and a column-vector resulting in a column vector.
*/
public static float4 operator *(Matrix matrix, float4 columnVector)
{
#unwarn
var m = &matrix.V;
float4 result = ?;
result.X = (m._11 * columnVector.X) + (m._12 * columnVector.Y) + (m._13 * columnVector.Z) + (m._14 * columnVector.W);
result.Y = (m._21 * columnVector.X) + (m._22 * columnVector.Y) + (m._23 * columnVector.Z) + (m._24 * columnVector.W);
result.Z = (m._31 * columnVector.X) + (m._32 * columnVector.Y) + (m._33 * columnVector.Z) + (m._34 * columnVector.W);
result.W = (m._41 * columnVector.X) + (m._42 * columnVector.Y) + (m._43 * columnVector.Z) + (m._44 * columnVector.W);
return result;
}
/**
* Multiplies a row-vector and a matrix resulting in a row vector.
*/
public static float4 operator *(float4 rowVector, Matrix matrix)
{
#unwarn
var m = &matrix.V;
float4 result = ?;
result.X = (rowVector.X * m._11) + (rowVector.Y * m._21) + (rowVector.Z * m._31) + (rowVector.W * m._41);
result.Y = (rowVector.X * m._12) + (rowVector.Y * m._22) + (rowVector.Z * m._32) + (rowVector.W * m._42);
result.Z = (rowVector.X * m._13) + (rowVector.Y * m._23) + (rowVector.Z * m._33) + (rowVector.W * m._43);
result.W = (rowVector.X * m._14) + (rowVector.Y * m._24) + (rowVector.Z * m._34) + (rowVector.W * m._44);
return result;
}
// Divison
public static Matrix operator /(Matrix m, float s)
{
float f = 1 / s;
Matrix M = m;
return .(M.Columns[0] * f, M.Columns[1] * f, M.Columns[2] * f, M.Columns[3] * f);
}
public static Matrix Scaling(float scale)
{
return .(scale, 0, 0, 0,
0, scale, 0, 0,
0, 0, scale, 0,
0, 0, 0, 1);
}
public static Matrix Scaling(float scaleX, float scaleY, float scaleZ)
{
return .(scaleX, 0, 0, 0,
0, scaleY, 0, 0,
0, 0, scaleZ, 0,
0, 0, 0, 1);
}
public static Matrix Scaling(float3 scale)
{
return .(scale.X, 0, 0, 0,
0, scale.Y, 0, 0,
0, 0, scale.Z, 0,
0, 0, 0, 1);
}
public static Matrix Translation(float x, float y, float z)
{
return .(1, 0, 0, x,
0, 1, 0, y,
0, 0, 1, z,
0, 0, 0, 1);
}
public static Matrix Translation(float3 translation)
{
return .(1, 0, 0, translation.X,
0, 1, 0, translation.Y,
0, 0, 1, translation.Z,
0, 0, 0, 1);
}
public static Matrix RotationX(float rot)
{
float sin = Math.Sin(rot);
float cos = Math.Cos(rot);
return .(1, 0, 0, 0,
0, cos, -sin, 0,
0, sin, cos, 0,
0, 0, 0, 1);
}
public static Matrix RotationY(float rot)
{
float sin = Math.Sin(rot);
float cos = Math.Cos(rot);
return .(cos, 0, sin, 0,
0, 1, 0, 0,
-sin, 0, cos, 0,
0, 0, 0, 1);
}
public static Matrix RotationZ(float rot)
{
float sin = Math.Sin(rot);
float cos = Math.Cos(rot);
return .(cos, -sin, 0, 0,
sin, cos, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
}
/**
* Calculates a view Matrix that is located at specified postion and looks at the given target.
* @param position The cameras position.
* @param target The point the camera looks at.
* @param up A vector defining the up direction of the camera.
* @returns a view matrix.
*/
public static Matrix LookAt(float3 position, float3 target, float3 up)
{
float3 forward = normalize(target - position);
float3 right = normalize(cross(up, forward));
float3 newUp = normalize(cross(forward, right));
Matrix result = .Identity;
result.Forward = forward;
result.Up = up;
result.Right = right;
result.Translation.X = -dot(position, right);
result.Translation.Y = -dot(position, up);
result.Translation.Z = -dot(position, forward);
return result;
}
/// Returns the transpose of this matrix
[DisableChecks]
public Matrix Transpose()
{
return .(_11, _21, _31, _41,
_12, _22, _32, _42,
_13, _23, _33, _43,
_14, _24, _34, _44);
}
/**
Calculates the inverse of the matrix.
*/
[DisableChecks]
public float Determinant() mut
{
// From: Lengyel, Eric. Foundations of Game Engine Development, Volume 1: Mathematics (S.61). Kindle-Version.
float3 a = *(float3*)&Columns[0];
float3 b = *(float3*)&Columns[1];
float3 c = *(float3*)&Columns[2];
float3 d = *(float3*)&Columns[3];
float x = this[3, 0];
float y = this[3, 1];
float z = this[3, 2];
float w = this[3, 3];
float3 s = cross(a, b);
float3 t = cross(c, d);
float3 u = y * a - x * b;
float3 v = w * c - z * d;
return dot(s, v) + dot(t, u);
}
/**
Calculates the inverse of the matrix.
*/
public Matrix Invert()
{
// From: Lengyel, Eric. Foundations of Game Engine Development, Volume 1: Mathematics (S.61). Kindle-Version.
#unwarn
float3 a = *(float3*)&Columns[0];
#unwarn
float3 b = *(float3*)&Columns[1];
#unwarn
float3 c = *(float3*)&Columns[2];
#unwarn
float3 d = *(float3*)&Columns[3];
float x = this[3, 0];
float y = this[3, 1];
float z = this[3, 2];
float w = this[3, 3];
float3 s = cross(a, b);
float3 t = cross(c, d);
float3 u = y * a - x * b;
float3 v = w * c - z * d;
float invDet = 1.0f / (dot(s, v) + dot(t, u));
s *= invDet;
t *= invDet;
u *= invDet;
v *= invDet;
float3 r0 = cross(b, v) + t * y;
float3 r1 = cross(v, a) - t * x;
float3 r2 = cross(d, u) + s * w;
float3 r3 = cross(u, c) - s * z;
return .(r0.X, r0.Y, r0.Z, -dot(b, t),
r1.X, r1.Y, r1.Z, dot(a, t),
r2.X, r2.Y, r2.Z, -dot(d, s),
r3.X, r3.Y, r3.Z, dot(c, s));
}
/// Calculates the inverse of the matrix.
public static Matrix Invert(in Matrix matrix)
{
// From: Lengyel, Eric. Foundations of Game Engine Development, Volume 1: Mathematics (S.61). Kindle-Version.
#unwarn
float3 a = *(float3*)&matrix.Columns[0];
#unwarn
float3 b = *(float3*)&matrix.Columns[1];
#unwarn
float3 c = *(float3*)&matrix.Columns[2];
#unwarn
float3 d = *(float3*)&matrix.Columns[3];
float x = matrix[3, 0];
float y = matrix[3, 1];
float z = matrix[3, 2];
float w = matrix[3, 3];
float3 s = cross(a, b);
float3 t = cross(c, d);
float3 u = y * a - x * b;
float3 v = w * c - z * d;
float invDet = 1.0f / (dot(s, v) + dot(t, u));
s *= invDet;
t *= invDet;
u *= invDet;
v *= invDet;
float3 r0 = cross(b, v) + t * y;
float3 r1 = cross(v, a) - t * x;
float3 r2 = cross(d, u) + s * w;
float3 r3 = cross(u, c) - s * z;
return .(r0.X, r0.Y, r0.Z, -dot(b, t),
r1.X, r1.Y, r1.Z, dot(a, t),
r2.X, r2.Y, r2.Z, -dot(d, s),
r3.X, r3.Y, r3.Z, dot(c, s));
}
/**
* Creates a perspective projection matrix.
* @param fovY The vertical field of view.
* @param aspectRation The aspect ratio of the viewport.
* @param nearPlane The distance to the near plane.
* @param farPlane The distance to the far plane.
*/
public static Matrix PerspectiveProjection(float fovY, float aspectRatio, float nearPlane, float farPlane)
{
// Lengyel, Eric. Foundations of Game Engine Development, Volume 2: Rendering (Seite82). . Kindle-Version.
float g = 1.0f / Math.Tan(fovY * 0.5f);
float k = farPlane / (farPlane - nearPlane);
return .(g / aspectRatio, 0, 0, 0,
0, g, 0, 0,
0, 0, k, -nearPlane * k,
0, 0, 1, 0);
}
/**
* Creates a perspective projection matrix with reversed near- and far plane.
* (i.e. Points on near plane have z-value of 1 and points of far plane have z-value of 0)
* @param fovY The vertical field of view.
* @param aspectRation The aspect ratio of the viewport.
* @param nearPlane The distance to the near plane.
* @param farPlane The distance to the far plane.
*/
public static Matrix ReversedPerspectiveProjection(float fovY, float aspectRatio, float nearPlane, float farPlane)
{
// Lengyel, Eric. Foundations of Game Engine Development, Volume 2: Rendering (Seite86). . Kindle-Version.
float g = 1.0f / Math.Tan(fovY * 0.5f);
float k = nearPlane / (nearPlane - farPlane);
return .(g / aspectRatio, 0, 0, 0,
0, g, 0, 0,
0, 0, k, -farPlane * k,
0, 0, 1, 0);
}
/**
* Creates a perspective projection matrix with a far plane at infinity.
* @param fovY The vertical field of view.
* @param aspectRation The aspect ratio of the viewport.
* @param nearPlane The distance to the near plane.
* @param ε An offset to account for floating point round-off errors at infinity.
* Note: Use a tiny value significant compared to the floating-point value of one.
*/
public static Matrix InfinitePerspectiveProjection(float fovY, float aspectRatio, float nearPlane, float ε = 1e-6f)
{
// Lengyel, Eric. Foundations of Game Engine Development, Volume 2: Rendering (Seite83). . Kindle-Version.
float g = 1.0f / Math.Tan(fovY * 0.5f);
float f = 1 - ε;
return .(g / aspectRatio, 0, 0, 0,
0, g, 0, 0,
0, 0, f, -nearPlane * f,
0, 0, 1, 0);
}
/**
* Creates a perspective projection matrix with a far plane at infinity with reversed near- and far plane.
* (i.e. Points on near plane have z-value of 1 and points of far plane have z-value of 0)
* @param fovY The vertical field of view.
* @param aspectRation The aspect ratio of the viewport.
* @param nearPlane The distance to the near plane.
* @param ε An offset to account for floating point round-off errors at infinity.
* Note: Use a tiny value significant compared to the floating-point value of one.
*/
public static Matrix ReversedInfinitePerspectiveProjection(float fovY, float aspectRatio, float nearPlane, float ε = 1e-6f)
{
// Lengyel, Eric. Foundations of Game Engine Development, Volume 2: Rendering (Seite88). . Kindle-Version.
float g = 1.0f / Math.Tan(fovY * 0.5f);
return .(g / aspectRatio, 0, 0, 0,
0, g, 0, 0,
0, 0, ε, nearPlane * (1 - ε),
0, 0, 1, 0);
}
/**
* Creates an orthographic projection matrix with the camera centered at the near-plane.
* @param width The width of the view volume.
* @param height The height of the view volume.
* @param depth The depth of the view volume.
*/
public static Matrix OrthographicProjection(float width, float height, float depth)
{
// Lengyel, Eric. Foundations of Game Engine Development, Volume 2: Rendering (Seite91). . Kindle-Version.
return .(2.0f / width, 0, 0, 0,
0, 2.0f/height, 0, 0,
0, 0, 1.0f / depth, 0,
0, 0, 0, 1);
}
/**
* Creates an orthographic projection matrix.
* @param left The left side of the view volume.
* @param right The right side of the view volume.
* @param top The top side of the view volume.
* @param bottom The bottom side of the view volume.
* @param near The near plane of the view volume.
* @param far The far plane of the view volume.
*/
public static Matrix OrthographicProjectionOffCenter(float left, float right, float top, float bottom, float near, float far)
{
// Lengyel, Eric. Foundations of Game Engine Development, Volume 2: Rendering (Seite91). . Kindle-Version.
float w_inv = 1.0f / (right - left);
float h_inv = 1.0f / (top - bottom);
float d_inv = 1.0f / (far - near);
return .(2.0f * w_inv, 0.0f, 0.0f, -(right + left) * w_inv,
0.0f, 2.0f * h_inv, 0.0f, -(bottom + top) * h_inv,
0.0f, 0.0f, d_inv, -near * d_inv,
0.0f, 0.0f, 0.0f, 1.0f);
}
public static bool operator ==(Matrix left, Matrix right)
{
return Matrix.Equals(left, right);
}
public static bool operator !=(Matrix left, Matrix right)
{
return !Matrix.Equals(left, right);
}
public static bool Equals(Matrix left, Matrix right)
{
return left.Values == right.Values;
}
public static explicit operator Matrix3x3(Matrix value)
{
return .(value.Right, value.Up, value.Forward);
}
public static void Exponent(ref Matrix matrix, int exponent, out Matrix result)
{
if(exponent == 0)
result = .Identity;
else if(exponent == 1)
result = matrix;
else if(exponent > 1)
{
result = .Identity;
Matrix b = matrix;
var exponent;
for(;exponent > 0;)
{
if(exponent & 1 > 0)
result *= b;
exponent >>= 1;
if(exponent > 0)
b *= b;
}
}
else // Exponent < 0
{
Matrix m = matrix.Invert();
Exponent(ref m, -exponent, out result);
}
}
/**
* Orthogonalizes the matrix.
*/
public void Orthogonalize() mut
{
Columns[1] -= project(Columns[1], Columns[0]);
Columns[2] -= project(Columns[2], Columns[0]) + project(Columns[2], Columns[1]);
Columns[3] -= project(Columns[3], Columns[0]) + project(Columns[3], Columns[1]) + project(Columns[3], Columns[2]);
}
/**
* Orthonormalizes the matrix.
*/
public void Orthonormalize() mut
{
Columns[0] = normalize(Columns[0]);
Columns[1] = normalize(reject(Columns[1], Columns[0]));
Columns[2] = normalize(reject(reject(Columns[2], Columns[0]), Columns[1]));
Columns[3] = normalize(reject(reject(reject(Columns[2], Columns[0]), Columns[1]), Columns[2]));
}
public static Self RotationQuaternion(Quaternion rotation)
{
float xSq = 2 * rotation.X * rotation.X;
float ySq = 2 * rotation.Y * rotation.Y;
float zSq = 2 * rotation.Z * rotation.Z;
float xy = 2 * rotation.X * rotation.Y;
float xz = 2 * rotation.X * rotation.Z;
float xw = 2 * rotation.X * rotation.W;
float yz = 2 * rotation.Y * rotation.Z;
float yw = 2 * rotation.Y * rotation.W;
float zw = 2 * rotation.Z * rotation.W;
Self result = ?;
result._11 = 1 - ySq - zSq;
result._21 = xy + zw;
result._31 = xz - yw;
result._41 = 0;
result._12 = xy - zw;
result._22 = 1 - xSq - zSq;
result._32 = yz + xw;
result._42 = 0;
result._13 = xz + yw;
result._23 = yz - xw;
result._33 = 1 - xSq - ySq;
result._43 = 0;
result._14 = 0;
result._24 = 0;
result._34 = 0;
result._44 = 1;
return result;
}
public static void Decompose(Self matrix, out float3 position, out Quaternion rotation, out float3 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 = length(*(float3*)&matrix.Columns[0]);
scale.Y = length(*(float3*)&matrix.Columns[1]);
scale.Z = length(*(float3*)&matrix.Columns[2]);
if(MathHelper.IsZero(scale.X) || MathHelper.IsZero(scale.Y) || MathHelper.IsZero(scale.Z))
{
rotation = .Identity;
return;
}
// Remove scale from matrix (normalize the columns)
matrix.Columns[0] /= scale.X;
matrix.Columns[1] /= scale.Y;
matrix.Columns[2] /= scale.Z;
rotation = Quaternion.FromMatrix(matrix);
}
}
-89
View File
@@ -1,89 +0,0 @@
using System;
using GlitchyEngine.Math;
using GlitchyEngine;
namespace DirectX.Math
{
extension Matrix
{
typealias Vec3 = GlitchyEngine.Math.Vector3;
public static Self RotationQuaternion(Quaternion rotation)
{
float xSq = 2 * rotation.X * rotation.X;
float ySq = 2 * rotation.Y * rotation.Y;
float zSq = 2 * rotation.Z * rotation.Z;
float xy = 2 * rotation.X * rotation.Y;
float xz = 2 * rotation.X * rotation.Z;
float xw = 2 * rotation.X * rotation.W;
float yz = 2 * rotation.Y * rotation.Z;
float yw = 2 * rotation.Y * rotation.W;
float zw = 2 * rotation.Z * rotation.W;
Self result = ?;
result.V._11 = 1 - ySq - zSq;
result.V._21 = xy + zw;
result.V._31 = xz - yw;
result.V._41 = 0;
result.V._12 = xy - zw;
result.V._22 = 1 - xSq - zSq;
result.V._32 = yz + xw;
result.V._42 = 0;
result.V._13 = xz + yw;
result.V._23 = yz - xw;
result.V._33 = 1 - xSq - ySq;
result.V._43 = 0;
result.V._14 = 0;
result.V._24 = 0;
result.V._34 = 0;
result.V._44 = 1;
return result;
}
public static void Decompose(Self matrix, out Vec3 position, out Quaternion rotation, out Vec3 scale)
{
var matrix;
// Translation -> get last column
position = matrix.Translation;
// Zero translation for next step
matrix.Translation = .Zero;
// TODO: this doesn't detect mirroring
// Extract scaling from matrix
scale.X = (*(Vec3*)&matrix.Columns[0]).Magnitude();
scale.Y = (*(Vec3*)&matrix.Columns[1]).Magnitude();
scale.Z = (*(Vec3*)&matrix.Columns[2]).Magnitude();
if(MathHelper.IsZero(scale.X) || MathHelper.IsZero(scale.Y) || MathHelper.IsZero(scale.Z))
{
rotation = .Identity;
return;
}
// Remove scale from matrix (normalize the columns)
matrix.Columns[0] /= scale.X;
matrix.Columns[1] /= scale.Y;
matrix.Columns[2] /= scale.Z;
rotation = Quaternion.FromMatrix(matrix);
}
/*[Test]
static void TestQuaternionToMatrix()
{
// Rotation around Z-Axis by 90°
Quaternion quat = .(0, 0, 0.707107f, 0.707107f);
quat.Normalize();
}*/
}
}
+18 -18
View File
@@ -22,7 +22,7 @@ namespace GlitchyEngine.Math
W = w; W = w;
} }
public this(Vector3 xy, float z, float w) public this(float3 xy, float z, float w)
{ {
X = xy.X; X = xy.X;
Y = xy.Y; Y = xy.Y;
@@ -30,7 +30,7 @@ namespace GlitchyEngine.Math
W = w; W = w;
} }
public this(Vector3 xyz, float w) public this(float3 xyz, float w)
{ {
X = xyz.X; X = xyz.X;
Y = xyz.Y; Y = xyz.Y;
@@ -38,7 +38,7 @@ namespace GlitchyEngine.Math
W = w; W = w;
} }
public this(Vector4 vector) public this(float4 vector)
{ {
X = vector.X; X = vector.X;
Y = vector.Y; Y = vector.Y;
@@ -46,7 +46,7 @@ namespace GlitchyEngine.Math
W = vector.W; W = vector.W;
} }
public Vector3 Vector public float3 Vector
{ {
get => .(X, Y, Z); get => .(X, Y, Z);
set mut set mut
@@ -144,7 +144,7 @@ namespace GlitchyEngine.Math
{ {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
var m = matrix.V; var m = matrix;
Quaternion result = ?; Quaternion result = ?;
@@ -195,11 +195,11 @@ namespace GlitchyEngine.Math
{ {
Quaternion result; Quaternion result;
Vector3 v = l.Vector * r.Vector + (l.W * r.Vector) + (r.W * l.Vector); float3 v = l.Vector * r.Vector + (l.W * r.Vector) + (r.W * l.Vector);
result.X = v.X; result.X = v.X;
result.Y = v.Y; result.Y = v.Y;
result.Z = v.Z; result.Z = v.Z;
result.W = (l.W * r.W) - Vector3.Dot(l.Vector, r.Vector); result.W = (l.W * r.W) - dot(l.Vector, r.Vector);
return result; return result;
} }
@@ -220,11 +220,11 @@ namespace GlitchyEngine.Math
[Inline] [Inline]
#unwarn #unwarn
public static implicit operator Vector4(in Self value) => *(Vector4*)&value; public static implicit operator float4(in Self value) => *(float4*)&value;
[Inline] [Inline]
#unwarn #unwarn
public static implicit operator Quaternion(in Vector4 value) => *(Quaternion*)&value; public static implicit operator Quaternion(in float4 value) => *(Quaternion*)&value;
public static bool operator ==(Quaternion l, Quaternion r) public static bool operator ==(Quaternion l, Quaternion r)
{ {
@@ -236,7 +236,7 @@ namespace GlitchyEngine.Math
return l.X != r.X && l.Y != r.Y && l.Z != r.Z && l.W != r.W; return l.X != r.X && l.Y != r.Y && l.Z != r.Z && l.W != r.W;
} }
public (Vector3 Axis, float Angle) ToAxisAngle() public (float3 Axis, float Angle) ToAxisAngle()
{ {
// scalar part = cos(θ/2) // scalar part = cos(θ/2)
// So, we can extract the angle directly. // So, we can extract the angle directly.
@@ -247,12 +247,12 @@ namespace GlitchyEngine.Math
// We assume quaternion is unit length, so subtracting w^2 gives us length of just vector part (aka 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)); float length = Math.Sqrt(1.0f - (W * W));
Vector3 axis; float3 axis;
// Normalize vector part to get the axis! // Normalize vector part to get the axis!
if(length == 0) if(length == 0)
{ {
axis = Vector3.Zero; axis = float3.Zero;
} }
else else
{ {
@@ -265,9 +265,9 @@ namespace GlitchyEngine.Math
return (axis, angle); return (axis, angle);
} }
public static Quaternion FromAxisAngle(Vector3 axis, float angle) public static Quaternion FromAxisAngle(float3 axis, float angle)
{ {
float lengthSq = axis.MagnitudeSquared(); float lengthSq = lengthSq(axis);
if(lengthSq == 0) if(lengthSq == 0)
{ {
@@ -316,11 +316,11 @@ namespace GlitchyEngine.Math
return result; return result;
} }
public static Vector3 ToEulerAngles(Quaternion q) public static float3 ToEulerAngles(Quaternion q)
{ {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/ // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/
Vector3 result; float3 result;
float sqw = q.W*q.W; float sqw = q.W*q.W;
float sqx = q.X*q.X; float sqx = q.X*q.X;
@@ -354,7 +354,7 @@ namespace GlitchyEngine.Math
float y = 2.0f * (Y * Z + W * X); float y = 2.0f * (Y * Z + W * X);
float x = W * W - X * X - Y * Y + Z * Z; float x = W * W - X * X - Y * Y + Z * Z;
if (Vector2(x, y).Equals(.Zero)) //avoid atan2(0,0) - handle singularity - Matiis if (float2(x, y).Equals(.Zero)) //avoid atan2(0,0) - handle singularity - Matiis
return 2.0f * Math.Atan2(X, W); return 2.0f * Math.Atan2(X, W);
return Math.Atan2(y, x); return Math.Atan2(y, x);
@@ -370,7 +370,7 @@ namespace GlitchyEngine.Math
float y = 2.0f * (X * Y + W * Z); float y = 2.0f * (X * Y + W * Z);
float x = W * W + X * X - Y * Y - Z * Z; float x = W * W + X * X - Y * Y - Z * Z;
if (Vector2(x, y).Equals(.Zero)) //avoid atan2(0,0) - handle singularity - Matiis if (float2(x, y).Equals(.Zero)) //avoid atan2(0,0) - handle singularity - Matiis
return 0; return 0;
return Math.Atan2(y, x); return Math.Atan2(y, x);
-299
View File
@@ -1,299 +0,0 @@
using Bon;
using System;
namespace GlitchyEngine.Math
{
[BonTarget]
[SwizzleVector(2, "GlitchyEngine.Math.Vector")]
public struct Vector2
{
public const Vector2 Zero = .(0f, 0f);
public const Vector2 UnitX = .(1f, 0f);
public const Vector2 UnitY = .(0f, 1f);
public const Vector2 One = .(1f, 1f);
public const int ComponentCount = 2;
public float X, Y;
public this() => this = default;
public this(float value)
{
X = value;
Y = value;
}
public this(float x, float y)
{
X = x;
Y = y;
}
public ref float this[int index]
{
[Checked]
get mut
{
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
return ref (&X)[index];
}
[Inline]
get mut => ref (&X)[index];
}
public float this[int index]
{
[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;
}
}
/**
* Calculates the magnitude (length) of this vector.
* @remarks MagnitudeSquared might be used if only the relative length is relevant.
*/
public float Magnitude()
{
return Math.Sqrt(X * X + Y * Y);
}
/**
* Calculates the squared magnitude (length) of this vector.
*/
public float MagnitudeSquared()
{
return X * X + Y * Y;
}
//TODO: Normalization interface is bad
[Checked]
public void Normalize() mut
{
if(this == .Zero)
return;
this /= Magnitude();
}
public void Normalize() mut
{
this /= Magnitude();
}
public static Vector2 Normalize(Vector2 v)
{
return v / v.Magnitude();
}
public static float Dot(Vector2 l, Vector2 r)
{
return l.X * r.X + l.Y * r.Y;
}
/**
* Calculates the projection of a onto b
*/
public static Vector2 Project(Vector2 a, Vector2 b)
{
return (b * (Dot(a, b) / Dot(b, b)));
}
/**
* Calculates the rejection of a from 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
//
// Addition
public void operator +=(Vector2 value) mut
{
X += value.X;
Y += value.Y;
}
public void operator +=(float scalar) mut
{
X += scalar;
Y += scalar;
}
// Subtraction
public void operator -=(Vector2 value) mut
{
X -= value.X;
Y -= value.Y;
}
public void operator -=(float scalar) mut
{
X -= scalar;
Y -= scalar;
}
// Multiplication
public void operator *=(Vector2 value) mut
{
X *= value.X;
Y *= value.Y;
}
public void operator *=(float scalar) mut
{
X *= scalar;
Y *= scalar;
}
// Division
public void operator /=(float scalar) mut
{
float f = 1.0f / scalar;
X *= f;
Y *= f;
}
public void operator /=(Vector2 value) mut
{
X /= value.X;
Y /= value.Y;
}
//
// operators
//
// Addition
public static Vector2 operator +(Vector2 left, Vector2 right) => Vector2(left.X + right.X, left.Y + right.Y);
public static Vector2 operator +(Vector2 value, float scalar) => Vector2(value.X + scalar, value.Y + scalar);
public static Vector2 operator +(float scalar, Vector2 value) => Vector2(scalar + value.X, scalar + value.Y);
public static Vector2 operator +(Vector2 value) => value;
// Subtraction
public static Vector2 operator -(Vector2 left, Vector2 right) => Vector2(left.X - right.X, left.Y - right.Y);
public static Vector2 operator -(Vector2 value, float scalar) => Vector2(value.X - scalar, value.Y - scalar);
public static Vector2 operator -(float scalar, Vector2 value) => Vector2(scalar - value.X, scalar - value.Y);
public static Vector2 operator -(Vector2 value) => Vector2(-value.X, -value.Y);
// Multiplication
public static Vector2 operator *(Vector2 left, Vector2 right) => Vector2(left.X * right.X, left.Y * right.Y);
public static Vector2 operator *(Vector2 value, float scalar) => Vector2(value.X * scalar, value.Y * scalar);
public static Vector2 operator *(float scalar, Vector2 value) => Vector2(scalar * value.X, scalar * value.Y);
// Division
public static Vector2 operator /(Vector2 left, Vector2 right) => Vector2(left.X / right.X, left.Y / right.Y);
public static Vector2 operator /(Vector2 value, float scalar)
{
float inv = 1.0f / scalar;
return Vector2(value.X * inv, value.Y * inv);
}
public static Vector2 operator /(float scalar, Vector2 value) => Vector2(scalar / value.X, scalar / value.Y);
// Equality
public static bool operator ==(Vector2 left, Vector2 right) => left.X == right.X && left.Y == right.Y;
public static bool operator !=(Vector2 left, Vector2 right) => left.X != right.X || left.Y != right.Y;
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1}", X, Y);
public bool Equals(Vector2 v, float epsilon = Math.[Friend]sMachineEpsilonFloat)
{
return (Math.Abs(v.X - X) < epsilon) && (Math.Abs(v.Y - Y) < epsilon);
}
[Inline]
public static explicit operator Self(float value) => Self(value);
[Inline]
#unwarn
public static explicit operator float[2](Vector2 value) => *(float[2]*)&value;
}
}
-349
View File
@@ -1,349 +0,0 @@
using Bon;
using System;
namespace GlitchyEngine.Math
{
[BonTarget]
[SwizzleVector(3, "GlitchyEngine.Math.Vector")]
public struct Vector3
{
public const Vector3 Zero = .(0f, 0f, 0f);
public const Vector3 UnitX = .(1f, 0f, 0f);
public const Vector3 UnitY = .(0f, 1f, 0f);
public const Vector3 UnitZ = .(0f, 0f, 1f);
public const Vector3 One = .(1f, 1f, 1f);
public const Vector3 Forward = .(0f, 0f, 1f);
public const Vector3 Backward = .(0f, 0f, -1f);
public const Vector3 Left = .(-1f, 0f, 0f);
public const Vector3 Right = .(1f, 0f, 0f);
public const Vector3 Up = .(0f, 1f, 0f);
public const Vector3 Down = .(0f, -1f, 0f);
public const int ComponentCount = 3;
public float X, Y, Z;
public this() => this = default;
public this(float value)
{
X = value;
Y = value;
Z = value;
}
public this(Vector2 value, float z = 0.0f)
{
X = value.X;
Y = value.Y;
Z = z;
}
public this(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public this(Vector3 value)
{
X = value.X;
Y = value.Y;
Z = value.Z;
}
public this(Vector4 value)
{
X = value.X;
Y = value.Y;
Z = value.Z;
}
public ref float this[int index]
{
[Checked]
get mut
{
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
return ref (&X)[index];
}
[Inline]
get mut => ref (&X)[index];
}
public float this[int index]
{
get
{
switch(index)
{
case 0: return X;
case 1: return Y;
case 2: return Z;
default: Internal.ThrowIndexOutOfRange(1);
}
}
set mut
{
switch(index)
{
case 0: X = value;
case 1: Y = value;
case 2: Z = value;
default: Internal.ThrowIndexOutOfRange(1);
}
}
}
/**
* Calculates the magnitude (length) of this vector.
* @remarks If the exact magnitude isn't needed (e.g. for comparisons) consider using MagnitudeSquared which doesn't use the square root operation.
*/
public float Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z);
/**
* Calculates the squared magnitude (length) of this vector.
* @remarks This function avoids the square root operation to calculate the magnitude and is thus more suitable for comparisons where the exact magnitude isn't needed.
*/
public float MagnitudeSquared() => X * X + Y * Y + Z * Z;
/// Normalizes this vector.
[Checked]
public void Normalize() mut
{
if(this == .Zero)
return;
this /= Magnitude();
}
/// Normalizes this vector.
public void Normalize() mut
{
this /= Magnitude();
}
/// Returns a copy of this Vector with a magnitude of 1.
[Checked]
public Vector3 Normalized()
{
if(this == .Zero)
return .Zero;
return this / Magnitude();
}
/// Returns a copy of this Vector with a magnitude of 1.
public Vector3 Normalized()
{
return this / Magnitude();
}
/// Returns a copy of the given Vector with a magnitude of 1.
public static Vector3 Normalize(Vector3 v)
{
return v / v.Magnitude();
}
/// Calculates the dot product of two vectors.
public static float Dot(Vector3 l, Vector3 r) => l.X * r.X + l.Y * r.Y + l.Z * r.Z;
/// Calculates the distance between two vectors.
public static float Distance(Vector3 a, Vector3 b) => (a - b).[Inline]Magnitude();
/// Calculates the squared distance between two vectors.
public static float DistanceSquared(Vector3 a, Vector3 b) => (a - b).[Inline]MagnitudeSquared();
/// Calculates the cross product of two vectors.
public static Vector3 Cross(Vector3 l, Vector3 r)
{
return .(l.Y * r.Z - l.Z * r.Y,
l.Z * r.X - l.X * r.Z,
l.X * r.Y - l.Y * r.X);
}
/// Calculates the projection of a onto b.
public static Vector3 Project(Vector3 a, Vector3 b)
{
return (b * (Dot(a, b) / Dot(b, b)));
}
/// Calculates the rejection of a from b.
public static Vector3 Reject(Vector3 a, Vector3 b)
{
return (a - b * (Dot(a, b) / Dot(b, b)));
}
public static Vector3 Floor(Vector3 value) => .(Math.Floor(value.X), Math.Floor(value.Y), Math.Floor(value.Z));
public static Vector3 Ceiling(Vector3 value) => .(Math.Ceiling(value.X), Math.Ceiling(value.Y), Math.Ceiling(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) => .(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) => .(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y), Math.Min(a.Z, b.Z));
public static Vector3 Abs(Vector3 v) => .(Math.Abs(v.X), Math.Abs(v.Y), Math.Abs(v.Z));
public static Vector3 Clamp(Vector3 v, Vector3 min, Vector3 max)
{
return .(Math.Clamp(v.X, min.X, max.X),
Math.Clamp(v.Y, min.Y, max.Y),
Math.Clamp(v.X, min.Z, max.Z));
}
//
// Assignment operators
//
// Addition
public void operator +=(Vector3 value) mut
{
X += value.X;
Y += value.Y;
Z += value.Z;
}
public void operator +=(float scalar) mut
{
X += scalar;
Y += scalar;
Z += scalar;
}
// Subtraction
public void operator -=(Vector3 value) mut
{
X -= value.X;
Y -= value.Y;
Z -= value.Z;
}
public void operator -=(float scalar) mut
{
X -= scalar;
Y -= scalar;
Z -= scalar;
}
// Multiplication
public void operator *=(Vector3 value) mut
{
X *= value.X;
Y *= value.Y;
Z *= value.Z;
}
public void operator *=(float scalar) mut
{
X *= scalar;
Y *= scalar;
Z *= scalar;
}
// Division
public void operator /=(Vector3 value) mut
{
X /= value.X;
Y /= value.Y;
Z /= value.Z;
}
public void operator /=(float scalar) mut
{
float inv = 1.0f / scalar;
X *= inv;
Y *= inv;
Z *= inv;
}
//
// operators
//
// Addition
public static Vector3 operator +(Vector3 left, Vector3 right) => Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
public static Vector3 operator +(Vector3 value, float scalar) => Vector3(value.X + scalar, value.Y + scalar, value.Z + scalar);
public static Vector3 operator +(float scalar, Vector3 value) => Vector3(scalar + value.X, scalar + value.Y, scalar + value.Z);
public static Vector3 operator +(Vector3 value) => value;
// Subtraction
public static Vector3 operator -(Vector3 left, Vector3 right) => Vector3(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
public static Vector3 operator -(Vector3 value, float scalar) => Vector3(value.X - scalar, value.Y - scalar, value.Z - scalar);
public static Vector3 operator -(float scalar, Vector3 value) => Vector3(scalar - value.X, scalar - value.Y, scalar - value.Z);
public static Vector3 operator -(Vector3 value) => Vector3(-value.X, -value.Y, -value.Z);
// Multiplication
public static Vector3 operator *(Vector3 left, Vector3 right) => Vector3(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
public static Vector3 operator *(Vector3 value, float scalar) => Vector3(value.X * scalar, value.Y * scalar, value.Z * scalar);
public static Vector3 operator *(float scalar, Vector3 value) => Vector3(scalar * value.X, scalar * value.Y, scalar * value.Z);
// Division
public static Vector3 operator /(Vector3 left, Vector3 right) => Vector3(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
public static Vector3 operator /(Vector3 value, float scalar)
{
float inv = 1.0f / scalar;
return Vector3(value.X * inv, value.Y * inv, value.Z * inv);
}
public static Vector3 operator /(float scalar, Vector3 value) => Vector3(scalar / value.X, scalar / value.Y, scalar / value.Z);
// Modulo
public static Vector3 operator %(Vector3 left, Vector3 right) => Vector3(left.X % right.X, left.Y % right.Y, left.Z % right.Z);
public static Vector3 operator %(Vector3 value, float scalar) => Vector3(value.X % scalar, value.Y % scalar, value.Z % scalar);
public static Vector3 operator %(float scalar, Vector3 value) => Vector3(scalar % value.X, scalar % value.Y, scalar % value.Z);
// Equality
public static bool operator ==(Vector3 left, Vector3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
public static bool operator !=(Vector3 left, Vector3 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1} Z:{2}", X, Y, Z);
[Inline]
public static explicit operator Self(float value) => Self(value);
[Inline]
#unwarn
public static explicit operator float[3](Vector3 value) => *(float[3]*)&value;
}
}
-330
View File
@@ -1,330 +0,0 @@
using Bon;
using System;
namespace GlitchyEngine.Math
{
[BonTarget]
[SwizzleVector(4, "GlitchyEngine.Math.Vector")]
public struct Vector4
{
public const Vector4 Zero = .(0f, 0f, 0f, 0f);
public const Vector4 UnitX = .(1f, 0f, 0f, 0f);
public const Vector4 UnitY = .(0f, 1f, 0f, 0f);
public const Vector4 UnitZ = .(0f, 0f, 1f, 0f);
public const Vector4 UnitW = .(0f, 0f, 0f, 1f);
public const Vector4 One = .(1f, 1f, 1f, 1f);
public const int ComponentCount = 4;
public float X, Y, Z, W;
public this() => this = default;
public this(float value)
{
X = value;
Y = value;
Z = value;
W = value;
}
public this(Vector2 value, float z, float w)
{
X = value.X;
Y = value.Y;
Z = z;
W = w;
}
public this(Vector2 value1, Vector2 value2)
{
X = value1.X;
Y = value1.Y;
Z = value2.X;
W = value2.Y;
}
public this(Vector3 value, float w)
{
X = value.X;
Y = value.Y;
Z = value.Z;
W = w;
}
public this(float x, float y, float z, float w)
{
X = x;
Y = y;
Z = z;
W = w;
}
public ref float this[int index]
{
[Checked]
get mut
{
if(index < 0 || index >= ComponentCount)
Internal.ThrowIndexOutOfRange(1);
return ref (&X)[index];
}
[Inline]
get mut => ref (&X)[index];
}
public float this[int index]
{
get
{
switch(index)
{
case 0: return X;
case 1: return Y;
case 2: return Z;
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);
}
}
}
/**
* Calculates the magnitude (length) of this vector.
* @remarks MagnitudeSquared might be used if only the relative length is relevant.
*/
public float Magnitude()
{
return Math.Sqrt(X * X + Y * Y + Z * Z + W * W);
}
/**
* Calculates the squared magnitude (length) of this vector.
*/
public float MagnitudeSquared()
{
return X * X + Y * Y + Z * Z + W * W;
}
[Checked]
public void Normalize() mut
{
if(this == .Zero)
return;
this /= Magnitude();
}
public void Normalize() mut
{
this /= Magnitude();
}
public static Vector4 Normalize(Vector4 v)
{
if(v == .Zero)
return .Zero;
return v / v.Magnitude();
}
[Unchecked]
public static Vector4 Normalize(Vector4 v)
{
return v / v.Magnitude();
}
public static float Dot(Vector4 l, Vector4 r)
{
return l.X * r.X + l.Y * r.Y + l.Z * r.Z + l.W * r.W;
}
/**
* Calculates the projection of a onto b
*/
public static Vector4 Project(Vector4 a, Vector4 b)
{
return (b * (Dot(a, b) / Dot(b, b)));
}
/**
* Calculates the rejection of a from b
*/
public static Vector4 Reject(Vector4 a, Vector4 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 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
//
// Addition
public void operator +=(Vector4 value) mut
{
X += value.X;
Y += value.Y;
Z += value.Z;
W += value.W;
}
public void operator +=(float scalar) mut
{
X += scalar;
Y += scalar;
Z += scalar;
W += scalar;
}
// Subtraction
public void operator -=(Vector4 value) mut
{
X -= value.X;
Y -= value.Y;
Z -= value.Z;
W -= value.W;
}
public void operator -=(float scalar) mut
{
X -= scalar;
Y -= scalar;
Z -= scalar;
W -= scalar;
}
// Multiplication
public void operator *=(Vector4 value) mut
{
X *= value.X;
Y *= value.Y;
Z *= value.Z;
W *= value.W;
}
public void operator *=(float scalar) mut
{
X *= scalar;
Y *= scalar;
Z *= scalar;
W *= scalar;
}
// Division
public void operator /=(float scalar) mut
{
float f = 1.0f / scalar;
X *= f;
Y *= f;
Z *= f;
W *= f;
}
public void operator /=(Vector4 value) mut
{
X /= value.X;
Y /= value.Y;
Z /= value.Z;
W /= value.W;
}
//
// operators
//
// Addition
public static Vector4 operator +(Vector4 left, Vector4 right) => Vector4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
public static Vector4 operator +(Vector4 value, float scalar) => Vector4(value.X + scalar, value.Y + scalar, value.Z + scalar, value.W + scalar);
public static Vector4 operator +(float scalar, Vector4 value) => Vector4(scalar + value.X, scalar + value.Y, scalar + value.Z, scalar + value.W);
public static Vector4 operator +(Vector4 value) => value;
// Subtraction
public static Vector4 operator -(Vector4 left, Vector4 right) => Vector4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W);
public static Vector4 operator -(Vector4 value, float scalar) => Vector4(value.X - scalar, value.Y - scalar, value.Z - scalar, value.W - scalar);
public static Vector4 operator -(float scalar, Vector4 value) => Vector4(scalar - value.X, scalar - value.Y, scalar - value.Z, scalar - value.W);
public static Vector4 operator -(Vector4 value) => Vector4(-value.X, -value.Y, -value.Z, -value.W);
// Multiplication
public static Vector4 operator *(Vector4 left, Vector4 right) => Vector4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W);
public static Vector4 operator *(Vector4 value, float scalar) => Vector4(value.X * scalar, value.Y * scalar, value.Z * scalar, value.W * scalar);
public static Vector4 operator *(float scalar, Vector4 value) => Vector4(scalar * value.X, scalar * value.Y, scalar * value.Z, scalar * value.W);
// Division
public static Vector4 operator /(Vector4 left, Vector4 right) => Vector4(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W);
public static Vector4 operator /(Vector4 value, float scalar)
{
float inv = 1.0f / scalar;
return Vector4(value.X * inv, value.Y * inv, value.Z * inv, value.W * inv);
}
public static Vector4 operator /(float scalar, Vector4 value) => Vector4(scalar / value.X, scalar / value.Y, scalar / value.Z, scalar / value.W);
// Equality
public static bool operator ==(Vector4 left, Vector4 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z && left.W == right.W;
public static bool operator !=(Vector4 left, Vector4 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z || left.W != right.W;
public override void ToString(String strBuffer) => strBuffer.AppendF("X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W);
[Inline]
public static explicit operator Self(float value) => Self(value);
[Inline]
#unwarn
public static explicit operator float[4](Vector4 value) => *(float[4]*)&value;
}
}
+440
View File
@@ -0,0 +1,440 @@
using System;
namespace GlitchyEngine.Math;
[AttributeUsage(.Struct | .Class)]
struct VectorAttribute<T, ComponentCount> : Attribute, IComptimeTypeApply where ComponentCount : const int
{
public const String[4] ComponentNames = .("X", "Y", "Z", "W");
public const String[4] LowerComponentNames = .("x", "y", "z", "w");
[Comptime]
public void ApplyToType(Type type)
{
GenerateFields(type);
GenerateCasts(type);
GenerateConstructors(type);
GenerateEqualityOperators(type);
GenerateArrayAccess(type);
}
[Comptime]
private void GenerateFields(Type type)
{
String fields = scope $"public {typeof(T)} ";
for (int i < ComponentCount)
{
if (i != 0)
fields.Append(", ");
fields.Append(ComponentNames[i]);
}
fields.Append(";\n\n");
Compiler.EmitTypeBody(type, fields);
}
[Comptime]
private void GenerateCasts(Type type)
{
String constructorBody = scope String();
for (int i < ComponentCount)
{
if (i != 0)
constructorBody.Append(", ");
constructorBody.Append("value");
}
String castSingleToVector = scope $"""
public static implicit operator Self({typeof(T)} value)
{{
return Self({constructorBody});
}}
""";
Compiler.EmitTypeBody(type, castSingleToVector);
String castVectorToArray = scope $"""
[System.Inline]
#unwarn
public static explicit operator {typeof(T)}[{ComponentCount}](Self value) => *({typeof(T)}[{ComponentCount}]*)&value;
""";
Compiler.EmitTypeBody(type, castVectorToArray);
}
#region Constructors
[Comptime]
private void GenerateConstructors(Type type)
{
// Default constructor
//Compiler.EmitTypeBody(type, "public this() => this = default;\n\n");
// Single constructor
GenerateSingleConstructor(type);
if (ComponentCount == 3)
{
Generatefloat3Constructors(type);
}
else if (ComponentCount == 4)
{
Generatefloat4Constructors(type);
}
}
[Comptime]
private void GenerateSingleConstructor(Type type)
{
String parameters = scope .();
for (int i < ComponentCount)
{
if (i != 0)
parameters.Append(", ");
parameters.AppendF($"{typeof(T)} {LowerComponentNames[i]}");
}
String body = scope .();
for (int i < ComponentCount)
{
body.AppendF($"\t{ComponentNames[i]} = {LowerComponentNames[i]};\n");
}
String constructor = scope $"""
public this({parameters})
{{
{body}
}}
""";
Compiler.EmitTypeBody(type, constructor);
}
[Comptime]
private void Generatefloat3Constructors(Type type)
{
String baseName = type.GetName(.. scope String());
// Remove number from name
baseName.RemoveFromEnd(1);
String constructor1 = scope $"""
public this({baseName}2 xy, {typeof(T)} z)
{{
X = xy.X;
Y = xy.Y;
Z = z;
}}
""";
Compiler.EmitTypeBody(type, constructor1);
String constructor2 = scope $"""
public this({typeof(T)} x, {baseName}2 yz)
{{
X = x;
Y = yz.X;
Z = yz.Y;
}}
""";
Compiler.EmitTypeBody(type, constructor2);
}
[Comptime]
private void Generatefloat4Constructors(Type type)
{
String baseName = type.GetName(.. scope String());
// Remove number from name
baseName.RemoveFromEnd(1);
String constructor = scope $"""
public this({baseName}2 xy, {baseName}2 zw)
{{
X = xy.X;
Y = xy.Y;
Z = zw.X;
W = zw.Y;
}}
""";
Compiler.EmitTypeBody(type, constructor);
String constructor1 = scope $"""
public this({baseName}2 xy, {typeof(T)} z, {typeof(T)} w)
{{
X = xy.X;
Y = xy.Y;
Z = z;
W = w;
}}
""";
Compiler.EmitTypeBody(type, constructor1);
String constructor2 = scope $"""
public this({typeof(T)} x, {baseName}2 yz, {typeof(T)} w)
{{
X = x;
Y = yz.X;
Z = yz.Y;
W = w;
}}
""";
Compiler.EmitTypeBody(type, constructor2);
String constructor3 = scope $"""
public this({typeof(T)} x, {typeof(T)} y, {baseName}2 zw)
{{
X = x;
Y = y;
Z = zw.X;
W = zw.Y;
}}
""";
Compiler.EmitTypeBody(type, constructor3);
String constructor4 = scope $"""
public this({baseName}3 xyz, {typeof(T)} w)
{{
X = xyz.X;
Y = xyz.Y;
Z = xyz.Z;
W = w;
}}
""";
Compiler.EmitTypeBody(type, constructor4);
String constructor5 = scope $"""
public this({typeof(T)} x, {baseName}3 yzw)
{{
X = x;
Y = yzw.X;
Z = yzw.Y;
W = yzw.Z;
}}
""";
Compiler.EmitTypeBody(type, constructor5);
}
#endregion Constructors
[Comptime]
private void GenerateEqualityOperators(Type type)
{
GenerateComparison(type, "==");
GenerateComparison(type, "!=");
}
[Comptime]
public static void GenerateComparison(Type type, String op)
{
String boolConstructor = scope .();
for (int i < ComponentCount)
{
if (i != 0)
boolConstructor.Append(", ");
boolConstructor.AppendF($"left.{ComponentNames[i]} {op} right.{ComponentNames[i]}");
}
String typeName = type.GetName(.. scope String());
String func = scope $"""
public static bool{ComponentCount} operator{op}({typeName} left, {typeName} right)
{{
return bool{ComponentCount}({boolConstructor});
}}
""";
Compiler.EmitTypeBody(type, func);
}
[Comptime]
private static void GenerateArrayAccess(Type type)
{
String arrayAccess = scope $"""
public {typeof(T)} this[int index]
{{
get
{{
if(index < 0 || index >= {ComponentCount})
System.Internal.ThrowIndexOutOfRange(1);
#unwarn
return (&X)[index];
}}
set mut
{{
if(index < 0 || index >= {ComponentCount})
System.Internal.ThrowIndexOutOfRange(1);
#unwarn
(&X)[index] = value;
}}
}}
""";
Compiler.EmitTypeBody(type, arrayAccess);
}
}
[AttributeUsage(.Struct | .Class)]
struct ComparableVectorAttribute<T, ComponentCount> : Attribute, IComptimeTypeApply where ComponentCount : const int
{
[Comptime]
public void ApplyToType(Type type)
{
VectorAttribute<T, ComponentCount>.GenerateComparison(type, ">");
VectorAttribute<T, ComponentCount>.GenerateComparison(type, ">=");
VectorAttribute<T, ComponentCount>.GenerateComparison(type, "<");
VectorAttribute<T, ComponentCount>.GenerateComparison(type, "<=");
}
}
[AttributeUsage(.Struct | .Class)]
struct VectorMathAttribute<T, ComponentCount> : Attribute, IComptimeTypeApply where ComponentCount : const int
{
public const String[4] ComponentNames = .("X", "Y", "Z", "W");
[Comptime]
public void ApplyToType(Type type)
{
GenerateUnaryOperatorOverloads(type);
GenerateOperatorOverloads(type, "+");
GenerateOperatorOverloads(type, "-");
GenerateOperatorOverloads(type, "*");
GenerateOperatorOverloads(type, "/");
GenerateOperatorOverloads(type, "%");
}
[Comptime]
public static void GenerateUnaryOperatorOverloads(Type type)
{
String typeName = type.GetName(.. scope String());
String unaryAdd = scope $"""
public static {typeName} operator+({typeName} value) => value;
""";
Compiler.EmitTypeBody(type, unaryAdd);
String resultArguments = scope .();
for (int i < ComponentCount)
{
if (i != 0)
resultArguments.Append(", ");
resultArguments.AppendF($"-value.{ComponentNames[i]}");
}
String unaryMinus = scope $"""
public static {typeName} operator-({typeName} value)
{{
return {typeName}({resultArguments});
}}
""";
Compiler.EmitTypeBody(type, unaryMinus);
}
[Comptime]
public static void GenerateOperatorOverloads(Type type, String op)
{
String typeName = type.GetName(.. scope String());
String componentTypeName = typeof(T).GetName(.. scope String());
[Comptime]
void EmitOperatorOverload(String leftType, String rightType, String resultArguments)
{
String func = scope $"""
public static {typeName} operator{op}({leftType} left, {rightType} right)
{{
return {typeName}({resultArguments});
}}
""";
Compiler.EmitTypeBody(type, func);
}
// Vector + Vector
String resultArguments = scope .();
for (int i < ComponentCount)
{
if (i != 0)
resultArguments.Append(", ");
resultArguments.AppendF($"left.{ComponentNames[i]} {op} right.{ComponentNames[i]}");
}
EmitOperatorOverload(typeName, typeName, resultArguments);
// Vector + Scalar
resultArguments.Clear();
for (int i < ComponentCount)
{
if (i != 0)
resultArguments.Append(", ");
resultArguments.AppendF($"left.{ComponentNames[i]} {op} right");
}
EmitOperatorOverload(typeName, componentTypeName, resultArguments);
// Scalar + Vector
resultArguments.Clear();
for (int i < ComponentCount)
{
if (i != 0)
resultArguments.Append(", ");
resultArguments.AppendF($"left {op} right.{ComponentNames[i]}");
}
EmitOperatorOverload(componentTypeName, typeName, resultArguments);
}
}
-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, "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
@@ -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, "Int")]
public struct Int3 : IHashable
{
public const Int3 Zero = .(0, 0, 0);
public const Int3 UnitX = .(1, 0, 0);
public const Int3 UnitY = .(0, 1, 0);
public const Int3 UnitZ = .(0, 0, 1);
public const Int3 One = .(1, 1, 1);
public int32 X, Y, Z;
public this() => this = default;
public this(int32 value)
{
X = value;
Y = value;
Z = value;
}
public this(int value)
{
X = (.)value;
Y = (.)value;
Z = (.)value;
}
public this(int32 x, int32 y, int32 z)
{
X = x;
Y = y;
Z = z;
}
public this(Int2 xy, int32 z)
{
X = xy.X;
Y = xy.Y;
Z = z;
}
public this(int32 x, Int2 yz)
{
X = x;
Y = yz.X;
Z = yz.Y;
}
public this(int x, int y, int z)
{
X = (.)x;
Y = (.)y;
Z = (.)z;
}
public this(Int2 xy, int z)
{
X = xy.X;
Y = xy.Y;
Z = (.)z;
}
public this(int x, Int2 yz)
{
X = (.)x;
Y = yz.X;
Z = yz.Y;
}
public ref int32 this[int index]
{
[Inline]
get
{
#if DEBUG
if(index < 0 || index >= 3)
Internal.ThrowIndexOutOfRange(1);
#endif
return ref (&X)[index];
}
}
public int32 MagnitudeSquared() => X * X + Y * Y + Z * Z;
public float Magnitude() => Math.Sqrt(X * X + Y * Y + Z * Z);
public Int3 Abs() => .(Math.Abs(X), Math.Abs(Y), Math.Abs(Z));
//
// Assignment operators
//
public void operator +=(Int3 value) mut
{
X += value.X;
Y += value.Y;
Z += value.Z;
}
public void operator +=(int32 value) mut
{
X += value;
Y += value;
Z += value;
}
public void operator -=(Int3 value) mut
{
X -= value.X;
Y -= value.Y;
Z -= value.Z;
}
public void operator -=(int32 value) mut
{
X -= value;
Y -= value;
Z -= value;
}
public void operator *=(Int3 value) mut
{
X *= value.X;
Y *= value.Y;
Z *= value.Z;
}
public void operator *=(int32 value) mut
{
X *= value;
Y *= value;
Z *= value;
}
public void operator /=(Int3 value) mut
{
X /= value.X;
Y /= value.Y;
Z /= value.Z;
}
public void operator /=(int32 value) mut
{
X /= value;
Y /= value;
Z /= value;
}
// Operators
public static Int3 operator +(Int3 value) => value;
public static Int3 operator +(Int3 left, Int3 right) => .(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
public static Int3 operator +(Int3 left, int32 right) => .(left.X + right, left.Y + right, left.Z + right);
public static Int3 operator +(int32 left, Int3 right) => .(left + right.X, left + right.Y, left + right.Z);
public static Int3 operator -(Int3 value) => .(-value.X, -value.Y, -value.Z);
public static Int3 operator -(Int3 left, Int3 right) => .(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
public static Int3 operator -(Int3 left, int32 right) => .(left.X - right, left.Y - right, left.Z- right);
public static Int3 operator -(int32 left, Int3 right) => .(left - right.X, left - right.Y, left - right.Z);
public static Int3 operator *(Int3 left, Int3 right) => .(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
public static Int3 operator *(Int3 left, int32 right) => .(left.X * right, left.Y * right, left.Z * right);
public static Int3 operator *(int32 left, Int3 right) => .(left * right.X, left * right.Y, left * right.Z);
public static Int3 operator /(Int3 left, Int3 right) => .(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
public static Int3 operator /(Int3 left, int32 right) => .(left.X / right, left.Y / right, left.Z / right);
public static Int3 operator /(int32 left, Int3 right) => .(left / right.X, left / right.Y, left / right.Z);
public static Int3 operator %(Int3 left, Int3 right) => .(left.X % right.X, left.Y % right.Y, left.Z % right.Z);
public static Int3 operator %(Int3 left, int32 right) => .(left.X % right, left.Y % right, left.Z % right);
public static Int3 operator %(int32 left, Int3 right) => .(left % right.X, left % right.Y, left % right.Z);
public static bool operator ==(Int3 left, Int3 right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z;
public static bool operator ==(Int3 left, int32 right) => left.X == right && left.Y == right && left.Z == right;
public static bool operator ==(int32 left, Int3 right) => left == right.X && left == right.Y && left == right.Z;
public static bool operator !=(Int3 left, Int3 right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z;
public static bool operator !=(Int3 left, int32 right) => left.X != right || left.Y != right || left.Z != right;
public static bool operator !=(int32 left, Int3 right) => left != right.X || left != right.Y || left != right.Z;
public override void ToString(String strBuffer) => strBuffer.AppendF($"X:{X} Y:{Y} Z:{Z}");
public static explicit operator Vector3(Int3 point) => .(point.X, point.Y, point.Z);
public static explicit operator Int3(Vector3 point) => .((int32)point.X, (int32)point.Y, (int32)point.Z);
[Inline]
public static explicit operator Int2(in Int3 point) => *(Int2*)&point;
public int GetHashCode()
{
return (((X * 39) ^ Y) * 39) ^ Z;
}
}
}
-234
View File
@@ -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, "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;
}
}
}
+27
View File
@@ -0,0 +1,27 @@
using Bon;
namespace GlitchyEngine.Math;
[BonTarget]
[Vector<bool, 2>]
[SwizzleVector(2, "GlitchyEngine.Math.bool")]
struct bool2
{
}
[BonTarget]
[Vector<bool, 3>]
[SwizzleVector(3, "GlitchyEngine.Math.bool")]
struct bool3
{
}
[BonTarget]
[Vector<bool, 4>]
[SwizzleVector(4, "GlitchyEngine.Math.bool")]
struct bool4
{
}
+113
View File
@@ -0,0 +1,113 @@
using Bon;
using System;
namespace GlitchyEngine.Math;
[BonTarget]
[Vector<double, 2>]
[ComparableVector<double, 2>]
[VectorMath<double, 2>]
[SwizzleVector(2, "GlitchyEngine.Math.double")]
public struct double2
{
public const double2 Zero = .(0, 0);
public const double2 UnitX = .(1, 0);
public const double2 UnitY = .(0, 1);
public const double2 One = .(1, 1);
public static explicit operator int2(double2 value)
{
return int2((int32)value.X, (int32)value.Y);
}
public static explicit operator uint2(double2 value)
{
return uint2((uint32)value.X, (uint32)value.Y);
}
public static explicit operator half2(double2 value)
{
return half2((half)value.X, (half)value.Y);
}
public static explicit operator float2(double2 value)
{
return float2((float)value.X, (float)value.Y);
}
}
[BonTarget]
[Vector<double, 3>]
[ComparableVector<double, 3>]
[VectorMath<double, 3>]
[SwizzleVector(3, "GlitchyEngine.Math.double")]
public struct double3
{
public const double3 Zero = .(0, 0, 0);
public const double3 UnitX = .(1, 0, 0);
public const double3 UnitY = .(0, 1, 0);
public const double3 UnitZ = .(0, 0, 1);
public const double3 One = .(1, 1, 1);
public const double3 Forward = .(0, 0, 1);
public const double3 Backward = .(0, 0, -1);
public const double3 Left = .(-1, 0, 0);
public const double3 Right = .(1, 0, 0);
public const double3 Up = .(0, 1, 0);
public const double3 Down = .(0, -1, 0);
public static explicit operator int3(double3 value)
{
return int3((int32)value.X, (int32)value.Y, (int32)value.Z);
}
public static explicit operator uint3(double3 value)
{
return uint3((uint32)value.X, (uint32)value.Y, (uint32)value.Z);
}
public static explicit operator half3(double3 value)
{
return half3((half)(float)value.X, (half)(float)value.Y, (half)(float)value.Z);
}
public static explicit operator float3(double3 value)
{
return float3((float)value.X, (float)value.Y, (float)value.Z);
}
}
[BonTarget]
[Vector<double, 4>]
[ComparableVector<double, 4>]
[VectorMath<double, 4>]
[SwizzleVector(4, "GlitchyEngine.Math.double")]
public struct double4
{
public const double4 Zero = .(0, 0, 0, 0);
public const double4 UnitX = .(1, 0, 0, 0);
public const double4 UnitY = .(0, 1, 0, 0);
public const double4 UnitZ = .(0, 0, 1, 0);
public const double4 UnitW = .(0, 0, 0, 1);
public const double4 One = .(1, 1, 1, 1);
public static explicit operator int4(double4 value)
{
return int4((int32)value.X, (int32)value.Y, (int32)value.Z, (int32)value.W);
}
public static explicit operator uint4(double4 value)
{
return uint4((uint32)value.X, (uint32)value.Y, (uint32)value.Z, (uint32)value.W);
}
public static explicit operator half4(double4 value)
{
return half4((half)(float)value.X, (half)(float)value.Y, (half)(float)value.Z, (half)(float)value.W);
}
public static explicit operator float4(double4 value)
{
return float4((float)value.X, (float)value.Y, (float)value.Z, (float)value.W);
}
}
+83
View File
@@ -0,0 +1,83 @@
using Bon;
using System;
namespace GlitchyEngine.Math;
[BonTarget]
[Vector<float, 2>]
[ComparableVector<float, 2>]
[VectorMath<float, 2>]
[SwizzleVector(2, "GlitchyEngine.Math.float")]
public struct float2
{
public const float2 Zero = .(0f, 0f);
public const float2 UnitX = .(1f, 0f);
public const float2 UnitY = .(0f, 1f);
public const float2 One = .(1f, 1f);
public static implicit operator int2(float2 value)
{
return int2((int32)value.X, (int32)value.Y);
}
public static explicit operator half2(float2 value)
{
return half2((half)value.X, (half)value.Y);
}
}
[BonTarget]
[Vector<float, 3>]
[ComparableVector<float, 3>]
[VectorMath<float, 3>]
[SwizzleVector(3, "GlitchyEngine.Math.float")]
public struct float3
{
public const float3 Zero = .(0f, 0f, 0f);
public const float3 UnitX = .(1f, 0f, 0f);
public const float3 UnitY = .(0f, 1f, 0f);
public const float3 UnitZ = .(0f, 0f, 1f);
public const float3 One = .(1f, 1f, 1f);
public const float3 Forward = .(0f, 0f, 1f);
public const float3 Backward = .(0f, 0f, -1f);
public const float3 Left = .(-1f, 0f, 0f);
public const float3 Right = .(1f, 0f, 0f);
public const float3 Up = .(0f, 1f, 0f);
public const float3 Down = .(0f, -1f, 0f);
public static implicit operator int3(float3 value)
{
return int3((int32)value.X, (int32)value.Y, (int32)value.Z);
}
public static explicit operator half3(float3 value)
{
return half3((half)value.X, (half)value.Y, (half)value.Z);
}
}
[BonTarget]
[Vector<float, 4>]
[ComparableVector<float, 4>]
[VectorMath<float, 4>]
[SwizzleVector(4, "GlitchyEngine.Math.float")]
public struct float4
{
public const float4 Zero = .(0f, 0f, 0f, 0f);
public const float4 UnitX = .(1f, 0f, 0f, 0f);
public const float4 UnitY = .(0f, 1f, 0f, 0f);
public const float4 UnitZ = .(0f, 0f, 1f, 0f);
public const float4 UnitW = .(0f, 0f, 0f, 1f);
public const float4 One = .(1f, 1f, 1f, 1f);
public static implicit operator int4(float4 value)
{
return int4((int32)value.X, (int32)value.Y, (int32)value.Z, (int32)value.W);
}
public static explicit operator half4(float4 value)
{
return half4((half)value.X, (half)value.Y, (half)value.Z, (half)value.W);
}
}
+312
View File
@@ -0,0 +1,312 @@
using System;
namespace GlitchyEngine.Math;
/// Represents a 16bit floating point number. (IEEE 754 half-precision binary floating-point (binary16))
/// Note: Eventhough it is possible, it is not recommended to perform calculations on this type.
/// Most operations will simply convert the halfs to floats, perform the calculation and convert the result back to half.
struct half : IFloating, ISigned, IFormattable, IHashable, IEquatable<half>, ICanBeNaN
{
public const half MinValue = half(-65504); // Should be 0xFBFF
public const half MaxValue = half(65504); // Should be 0x7BFF
// The numbers for Inifnity, NaN and Zero need to be hardcoded as binaries,
// because the conversion intself relies on them.
public const half PositiveInfinity = half(0x7C00);
public const half NegativeInfinity = half(0xFC00);
public const half NaN = half(0x7CFF);
public const half Zero = half(0x0000);
public const half NegativeZero = half(0x8000);
private uint16 _data;
public bool IsNegative => (_data & Half_Sign_Mask) > 0;
public bool IsFinity => (_data & ~Half_Sign_Mask) < Half_Exponent_Mask;
public bool IsInfinity => (_data & ~Half_Sign_Mask) == Half_Exponent_Mask;
public bool IsPositiveInfinity => _data == PositiveInfinity._data;
public bool IsNegativeInfinity => _data == NegativeInfinity._data;
public bool IsNaN => (_data & ~Half_Sign_Mask) > Half_Exponent_Mask;
public bool IsSubnormal
{
get
{
var unsignedBits = _data & ~Half_Sign_Mask;
// Zero isn't normalized and if exponent is 0 we are unnormalized
return (unsignedBits != 0) && ((unsignedBits & Half_Exponent_Mask) == 0);
}
}
public this(float value)
{
this = FromFloat32(value);
}
private this(uint16 data)
{
_data = data;
}
public explicit static operator half(float value) => FromFloat32(value);
public explicit static operator float(half value) => ToFloat32(value);
public static half FromFloat32(float value)
{
if (value == 0.0f)
return half(0x0000);
if (value == -0.0f)
return half(0x8000);
#unwarn
uint32 singleBits = *(uint32*)&value;
uint32 sign = GetSingleSignBit(singleBits);
int32 exponent = (int32)GetSingleExponent(singleBits);
uint32 mantissa = GetSingleMantissa(singleBits);
// Shift 13 so we truncate mantissa from 23 to 10 bits.
uint32 halfMantissa = mantissa >> 13;
if (exponent == 0xFF)
{
// largest possible single exponent -> either infinity or NaN
if (mantissa == 0)
{
// Infinity
return (sign == 1) ? NegativeInfinity : PositiveInfinity;
}
else
{
// NaN -> Keeps sign and mantissa intact
uint16 halfBits = SetHalfSignBit(0, (uint16)sign);
// Set all five exponent bits to 1
halfBits = SetHalfExponent(halfBits, 0x1F);
halfBits = SetHalfMantissa(halfBits, (uint16)halfMantissa);
return half(halfBits);
}
}
// Normalized single
// excess-K decode and encode -127 is k for single, 15 is k for half
exponent = exponent - 127 + 15;
if (exponent < 0)
// The given number is too small for a half (even denormalized) -> return 0
return (sign == 1) ? NegativeZero : Zero;
if (exponent > 31)
// The given number is too large for a half -> return infinity
return (sign == 1) ? NegativeInfinity : PositiveInfinity;
// The given number fits -> convert (might become denormalized)
uint16 halfBits = SetHalfSignBit(0, (uint16)sign);
halfBits = SetHalfExponent(halfBits, (uint16)exponent);
halfBits = SetHalfMantissa(halfBits, (uint16)halfMantissa);
return half(halfBits);
}
public static float ToFloat32(half value)
{
#unwarn
uint16 halfBits = *(uint16*)&value;
if (halfBits == Zero._data)
return 0.0f;
if (halfBits == NegativeZero._data)
return -0.0f;
uint16 sign = GetHalfSignBit(halfBits);
int16 exponent = (int16)GetHalfExponent(halfBits);
uint16 mantissa = GetHalfMantissa(halfBits);
// Shift 13 so we extend mantissa from 10 to 23 bits.
uint32 singleMantissa = (uint32)mantissa << 13;
if (exponent == 0x1F)
{
// largest possible half exponent -> either infinity or NaN
if (mantissa == 0)
{
// Infinity
return (sign == 1) ? float.NegativeInfinity : float.PositiveInfinity;
}
else
{
// NaN -> Keeps sign and mantissa intact
uint32 singleBits = SetSingleSignBit(0, sign);
// Set all five exponent bits to 1
singleBits = SetSingleExponent(halfBits, 0xFF);
singleBits = SetSingleMantissa(halfBits, singleMantissa);
return *(float*)&singleBits;
}
}
// Normalized half
// excess-K decode and encode -127 is k for single, 15 is k for half
exponent = exponent - 15 + 127;
uint32 singleBits = SetSingleSignBit(0, sign);
singleBits = SetSingleExponent(singleBits, (uint32)exponent);
singleBits = SetSingleMantissa(singleBits, singleMantissa);
return *(float*)&singleBits;
}
#region Single <-> Half Helpers
const uint32 Single_Sign_Shift = 31;
const uint32 Single_Sign_Mask = 0x8000'0000; // 1 bit, offset 31
const uint32 Single_Exponent_Shift = 23;
const uint32 Single_Exponent_Mask = 0x7F80'0000; // 8 bit, offset 23
const uint32 Single_Mantissa_Mask = 0x007F'FFFF; // 23 bit, offset 0
const uint16 Half_Sign_Shift = 15;
const uint16 Half_Sign_Mask = 0x8000; // 1 bit, offset 15
const uint16 Half_Exponent_Shift = 10;
const uint16 Half_Exponent_Mask = 0x7C00; // 5 bit, offset 10
const uint16 Half_Mantissa_Mask = 0x03FF; // 10 bit, offset 0
private static uint32 GetSingleSignBit(uint32 singleData)
{
return (singleData >> Single_Sign_Shift);
}
private static uint32 GetSingleExponent(uint32 singleData)
{
return (singleData & Single_Exponent_Mask) >> Single_Exponent_Shift;
}
private static uint32 GetSingleMantissa(uint32 singleData)
{
return (singleData & Single_Mantissa_Mask);
}
private static uint32 SetSingleSignBit(uint32 singleData, uint32 signBit)
{
return ((signBit << Single_Sign_Shift) & Single_Sign_Mask) | (singleData & ~Single_Sign_Mask);
}
private static uint32 SetSingleExponent(uint32 singleData, uint32 exponent)
{
return ((exponent << Single_Exponent_Shift) & Single_Exponent_Mask) | (singleData & ~Single_Exponent_Mask);
}
private static uint32 SetSingleMantissa(uint32 singleData, uint32 mantissa)
{
return (mantissa & Single_Mantissa_Mask) | (singleData & ~Single_Mantissa_Mask);
}
private static uint16 GetHalfSignBit(uint16 HalfData)
{
return (HalfData >> Half_Sign_Shift);
}
private static uint16 GetHalfExponent(uint16 HalfData)
{
return (HalfData & Half_Exponent_Mask) >> Half_Exponent_Shift;
}
private static uint16 GetHalfMantissa(uint16 HalfData)
{
return (HalfData & Half_Mantissa_Mask);
}
private static uint16 SetHalfSignBit(uint16 HalfData, uint16 signBit)
{
return ((signBit << Half_Sign_Shift) & Half_Sign_Mask) | (HalfData & ~Half_Sign_Mask);
}
private static uint16 SetHalfExponent(uint16 HalfData, uint16 exponent)
{
return ((exponent << Half_Exponent_Shift) & Half_Exponent_Mask) | (HalfData & ~Half_Exponent_Mask);
}
private static uint16 SetHalfMantissa(uint16 HalfData, uint16 mantissa)
{
return (mantissa & Half_Mantissa_Mask) | (HalfData & ~Half_Mantissa_Mask);
}
#endregion
public int GetHashCode()
{
return _data;
}
public void ToString(String outString)
{
ToFloat32(this).ToString(outString);
}
public void ToString(String outString, String format, IFormatProvider formatProvider)
{
ToFloat32(this).ToString(outString, format, formatProvider);
}
public static half operator +(half lhs, half rhs)
{
return (half)((float)lhs + (float)rhs);
}
public static half operator +(half value) => value;
public static half operator -(half lhs, half rhs)
{
return (half)((float)lhs - (float)rhs);
}
public static half operator -(half value)
{
uint16 signBit = GetHalfSignBit(value._data);
// Invert the sign bit (~)
return half(SetHalfSignBit(value._data, ~signBit));
}
public static half operator *(half lhs, half rhs)
{
return (half)((float)lhs * (float)rhs);
}
public static half operator /(half lhs, half rhs)
{
return (half)((float)lhs / (float)rhs);
}
public static half operator %(half lhs, half rhs)
{
return (half)((float)lhs % (float)rhs);
}
public static bool operator==(half value1, half value2) => value1._data == value2._data;
public static bool operator!=(half value1, half value2) => value1._data != value2._data;
public static int operator<=>(half value1, half value2) => (float)value1 <=> (float)value2;
public bool Equals(half other)
{
return _data == other._data;
}
}
+43
View File
@@ -0,0 +1,43 @@
using Bon;
using System;
namespace GlitchyEngine.Math;
[BonTarget]
[Vector<half, 2>]
[ComparableVector<half, 2>]
[VectorMath<half, 2>]
[SwizzleVector(2, "GlitchyEngine.Math.half")]
public struct half2
{
public static explicit operator float2(half2 value)
{
return float2((float)value.X, (float)value.Y);
}
}
[BonTarget]
[Vector<half, 3>]
[ComparableVector<half, 3>]
[VectorMath<half, 3>]
[SwizzleVector(3, "GlitchyEngine.Math.half")]
public struct half3
{
public static explicit operator float3(half3 value)
{
return float3((float)value.X, (float)value.Y, (float)value.Z);
}
}
[BonTarget]
[Vector<half, 4>]
[ComparableVector<half, 4>]
[VectorMath<half, 4>]
[SwizzleVector(4, "GlitchyEngine.Math.half")]
public struct half4
{
public static explicit operator float4(half4 value)
{
return float4((float)value.X, (float)value.Y, (float)value.Z, (float)value.W);
}
}
+148
View File
@@ -0,0 +1,148 @@
using Bon;
using System;
namespace GlitchyEngine.Math;
[BonTarget]
[Vector<int32, 2>]
[ComparableVector<int32, 2>]
[VectorMath<int32, 2>]
[SwizzleVector(2, "GlitchyEngine.Math.int")]
public struct int2
{
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 static implicit operator float2(int2 value)
{
return float2(value.X, value.Y);
}
public static explicit operator uint2(int2 value)
{
return uint2((uint32)value.X, (uint32)value.Y);
}
}
[BonTarget]
[Vector<int32, 3>]
[ComparableVector<int32, 3>]
[VectorMath<int32, 3>]
[SwizzleVector(3, "GlitchyEngine.Math.int")]
public struct int3
{
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 static implicit operator float3(int3 value)
{
return float3(value.X, value.Y, value.Z);
}
public static explicit operator uint3(int3 value)
{
return uint3((uint32)value.X, (uint32)value.Y, (uint32)value.Z);
}
}
[BonTarget]
[Vector<int32, 4>]
[ComparableVector<int32, 4>]
[VectorMath<int32, 4>]
[SwizzleVector(4, "GlitchyEngine.Math.int")]
public struct int4
{
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 static implicit operator float4(int4 value)
{
return float4(value.X, value.Y, value.Z, value.W);
}
public static explicit operator uint4(int4 value)
{
return uint4((uint32)value.X, (uint32)value.Y, (uint32)value.Z, (uint32)value.W);
}
}
[BonTarget]
[Vector<uint32, 2>]
[ComparableVector<uint32, 2>]
//[VectorMath<uint32, 2>]
[SwizzleVector(2, "GlitchyEngine.Math.uint")]
public struct uint2
{
public const uint2 Zero = .(0, 0);
public const uint2 UnitX = .(1, 0);
public const uint2 UnitY = .(0, 1);
public const uint2 One = .(1, 1);
public static implicit operator float2(uint2 value)
{
return float2(value.X, value.Y);
}
public static explicit operator int2(uint2 value)
{
return int2((int32)value.X, (int32)value.Y);
}
}
[BonTarget]
[Vector<uint32, 3>]
[ComparableVector<uint32, 3>]
//[VectorMath<uint32, 3>]
[SwizzleVector(3, "GlitchyEngine.Math.uint")]
public struct uint3
{
public const uint3 Zero = .(0, 0, 0);
public const uint3 UnitX = .(1, 0, 0);
public const uint3 UnitY = .(0, 1, 0);
public const uint3 UnitZ = .(0, 0, 1);
public const uint3 One = .(1, 1, 1);
public static implicit operator float3(uint3 value)
{
return float3(value.X, value.Y, value.Z);
}
public static explicit operator int3(uint3 value)
{
return int3((int32)value.X, (int32)value.Y, (int32)value.Z);
}
}
[BonTarget]
[Vector<uint32, 4>]
[ComparableVector<uint32, 4>]
//[VectorMath<uint32, 4>]
[SwizzleVector(4, "GlitchyEngine.Math.uint")]
public struct uint4
{
public const uint4 Zero = .(0, 0, 0, 0);
public const uint4 UnitX = .(1, 0, 0, 0);
public const uint4 UnitY = .(0, 1, 0, 0);
public const uint4 UnitZ = .(0, 0, 1, 0);
public const uint4 UnitW = .(0, 0, 0, 1);
public const uint4 One = .(1, 1, 1, 1);
public static implicit operator float4(uint4 value)
{
return float4(value.X, value.Y, value.Z, value.W);
}
public static explicit operator int4(uint4 value)
{
return int4((int32)value.X, (int32)value.Y, (int32)value.Z, (int32)value.W);
}
}
@@ -14,7 +14,7 @@ namespace GlitchyEngine
private bool _rotation; private bool _rotation;
private Vector3 _cameraPosition; private float3 _cameraPosition;
private float _cameraRotation; private float _cameraRotation;
private float _cameraTranslationSpeed = 1.0f; private float _cameraTranslationSpeed = 1.0f;
private float _cameraRotationSpeed = 1.0f; private float _cameraRotationSpeed = 1.0f;
@@ -41,7 +41,7 @@ namespace GlitchyEngine
if(Application.Get().Window.IsActive) if(Application.Get().Window.IsActive)
{ {
Vector3 movement = .(); float3 movement = .();
if(Input.IsKeyPressed(Key.W)) if(Input.IsKeyPressed(Key.W))
{ {
@@ -61,8 +61,8 @@ namespace GlitchyEngine
movement.X += 1; movement.X += 1;
} }
if(movement != .Zero) if(any(movement != .Zero))
movement.Normalize(); movement = normalize(movement);
movement *= (float)(gameTime.FrameTime.TotalSeconds) * _cameraTranslationSpeed * _zoomLevel; movement *= (float)(gameTime.FrameTime.TotalSeconds) * _cameraTranslationSpeed * _zoomLevel;

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