mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Compare commits
154
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd2b990a8f | ||
|
|
29b6f37c2a | ||
|
|
188dc4b3b6 | ||
|
|
62ad8bee35 | ||
|
|
f8e533f28d | ||
|
|
faa3040471 | ||
|
|
05187e6b9f | ||
|
|
eaffc1a135 | ||
|
|
ec9fccee12 | ||
|
|
164384e55e | ||
|
|
9b14cdddb9 | ||
|
|
85b3fd5bc9 | ||
|
|
c56db0f069 | ||
|
|
8ccbc565ad | ||
|
|
a14d86012a | ||
|
|
8b64afee0c | ||
|
|
344aabae2b | ||
|
|
dec0a004a5 | ||
|
|
4e314d1de2 | ||
|
|
4157477882 | ||
|
|
c65fa58940 | ||
|
|
b5071e3499 | ||
|
|
2f5baa5f5e | ||
|
|
ab39386b27 | ||
|
|
3095c8f0bb | ||
|
|
e4e86bd21d | ||
|
|
8382060f8a | ||
|
|
e076eabfa1 | ||
|
|
c8fdba6121 | ||
|
|
6935e9daf9 | ||
|
|
1e1ec7aaa9 | ||
|
|
6bec99ab06 | ||
|
|
aad44dd835 | ||
|
|
28ad9298af | ||
|
|
86977213ff | ||
|
|
4c20cca585 | ||
|
|
4c855819c9 | ||
|
|
aabcd5f9b2 | ||
|
|
3fd5452146 | ||
|
|
10e2294f9c | ||
|
|
bca4281d84 | ||
|
|
28a9242023 | ||
|
|
1b8cc93834 | ||
|
|
1eeab94575 | ||
|
|
419ed7c9d2 | ||
|
|
986ffeade0 | ||
|
|
c94ffbef64 | ||
|
|
7df651103b | ||
|
|
8f51d1c626 | ||
|
|
fca126c960 | ||
|
|
4992511112 | ||
|
|
b94b2afc55 | ||
|
|
d002dfe25e | ||
|
|
b4f41fc218 | ||
|
|
ac0f4dade6 | ||
|
|
52cbf9dcf4 | ||
|
|
e57c1c876f | ||
|
|
3f83c200f2 | ||
|
|
74243cd285 | ||
|
|
bf03e99e4a | ||
|
|
d156618453 | ||
|
|
042ae83a0f | ||
|
|
cc38236184 | ||
|
|
184b92e95c | ||
|
|
41dacf72b6 | ||
|
|
c01ccf571e | ||
|
|
ed644b5b41 | ||
|
|
72e87ee9ad | ||
|
|
566d39d928 | ||
|
|
89bb4f4430 | ||
|
|
08846a0c0b | ||
|
|
c41425ad36 | ||
|
|
28311165ad | ||
|
|
271286fb7e | ||
|
|
f77b153512 | ||
|
|
f7bcff5337 | ||
|
|
0a53ff48f9 | ||
|
|
edfcfd3b20 | ||
|
|
063cbfa415 | ||
|
|
311deb5bc0 | ||
|
|
c2d330fc8f | ||
|
|
1b63c922da | ||
|
|
39a825634a | ||
|
|
ecfe838ad4 | ||
|
|
081f7b4a7e | ||
|
|
5f5192b0f4 | ||
|
|
3e9c9e8202 | ||
|
|
1e614eb944 | ||
|
|
1c97a5e9c4 | ||
|
|
4b91116b51 | ||
|
|
941cc8b767 | ||
|
|
88f6acb336 | ||
|
|
736c8ce205 | ||
|
|
5cffe34620 | ||
|
|
f3b4fbd628 | ||
|
|
e3c7057dce | ||
|
|
b00119b41e | ||
|
|
9dc6b585bd | ||
|
|
1b46ff779a | ||
|
|
c931451f10 | ||
|
|
300a2f1a8a | ||
|
|
e9644018ff | ||
|
|
173942114f | ||
|
|
39c9109919 | ||
|
|
2cc8889ac1 | ||
|
|
5fb8401a1a | ||
|
|
6ed5f6d943 | ||
|
|
95ccb597d6 | ||
|
|
05671649b4 | ||
|
|
50bcfa2caf | ||
|
|
108e24eb38 | ||
|
|
bfd1754d14 | ||
|
|
6af2e49387 | ||
|
|
68745f0515 | ||
|
|
60482059a4 | ||
|
|
9b6bb2b8d8 | ||
|
|
07faafd28d | ||
|
|
2c0b000afc | ||
|
|
9d0fb99e3d | ||
|
|
400045b005 | ||
|
|
571bf98be2 | ||
|
|
c34f585bbf | ||
|
|
5e50d75eb9 | ||
|
|
77a7a04637 | ||
|
|
6d50208700 | ||
|
|
10e8b3c282 | ||
|
|
b5088329f5 | ||
|
|
824c19c0b2 | ||
|
|
0236130df1 | ||
|
|
28121d1b42 | ||
|
|
bb52f4c068 | ||
|
|
49c3e93e08 | ||
|
|
25e1c53a08 | ||
|
|
24295731f3 | ||
|
|
aaed4199d3 | ||
|
|
e5437c6ff6 | ||
|
|
688ebad278 | ||
|
|
83b12efcff | ||
|
|
7a306cac69 | ||
|
|
06b981e88a | ||
|
|
76087d6ba4 | ||
|
|
acfb052137 | ||
|
|
2f0505241f | ||
|
|
577cca5886 | ||
|
|
cf1f2a9a7b | ||
|
|
bcf75bf8cb | ||
|
|
ad73abd4ea | ||
|
|
b98de9835d | ||
|
|
d788853dd3 | ||
|
|
d8b3b137dd | ||
|
|
ff1c6db222 | ||
|
|
73a6cc0854 | ||
|
|
66e62f42ec | ||
|
|
7b7a3dc645 |
@@ -12,3 +12,4 @@ GlitchyEditor/MonoDebugger.log
|
||||
GlitchyEditor/Resources/Scripts/*
|
||||
|
||||
.cache
|
||||
generated
|
||||
|
||||
+6
-3
@@ -22,9 +22,6 @@
|
||||
[submodule "GlitchyEngine/vendor/bon"]
|
||||
path = GlitchyEngine/vendor/bon
|
||||
url = https://github.com/EinScott/bon.git
|
||||
[submodule "GlitchyEngineHelper/vendor/DirectXTK"]
|
||||
path = GlitchyEngineHelper/vendor/DirectXTK
|
||||
url = https://github.com/microsoft/DirectXTK.git
|
||||
[submodule "GlitchyEngine/vendor/box2D"]
|
||||
path = GlitchyEngine/vendor/box2D
|
||||
url = https://github.com/aharabada/box2d-beef.git
|
||||
@@ -34,3 +31,9 @@
|
||||
[submodule "vendor/ImGui.NET"]
|
||||
path = vendor/ImGui.NET
|
||||
url = https://github.com/aharabada/ImGui.NET.git
|
||||
[submodule "GlitchyEngine/vendor/NetHostBeef"]
|
||||
path = GlitchyEngine/vendor/NetHostBeef
|
||||
url = https://github.com/aharabada/NetHostBeef.git
|
||||
[submodule "GlitchyEngine/vendor/Xml-Beef"]
|
||||
path = GlitchyEngine/vendor/Xml-Beef
|
||||
url = https://github.com/aharabada/Xml-Beef.git
|
||||
|
||||
+29
-2
@@ -1,9 +1,36 @@
|
||||
FileVersion = 1
|
||||
Projects = {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"}}
|
||||
WorkspaceFolders = {GlitchyEngine = ["GlitchyEngine", "GlitchLog", "GlitchyEngineHelper", "ScriptCore"], "GlitchyEngine/Dependencies" = ["cgltf-beef", "DirectX", "FreeType", "ImGui", "ImGuiImplDX11", "ImGuiImplWin32", "ImGuizmo", "LodePng", "msdfgen-beef", "bon", "box2d-beef", "Beef.Linq"]}
|
||||
Locked = ["ImGui"]
|
||||
|
||||
[Workspace]
|
||||
StartupProject = "GlitchyEditor"
|
||||
|
||||
[Projects]
|
||||
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"}
|
||||
"GlitchyEngine.Test" = {Path = "GlitchyEngine.Test"}
|
||||
Xml-Beef = {Path = "GlitchyEngine/vendor/Xml-Beef"}
|
||||
"GlitchyEngine.Benchmark" = {Path = "GlitchyEngine.Benchmark"}
|
||||
|
||||
[WorkspaceFolders]
|
||||
GlitchyEngine = ["GlitchyEngine", "GlitchLog", "GlitchyEngineHelper", "ScriptCore", "GlitchyEngine.Test", "GlitchyEngine.Benchmark"]
|
||||
"GlitchyEngine/Dependencies" = ["cgltf-beef", "DirectX", "FreeType", "ImGui", "ImGuiImplDX11", "ImGuiImplWin32", "ImGuizmo", "LodePng", "msdfgen-beef", "bon", "box2d-beef", "Beef.Linq", "NetHostBeef", "Xml-Beef"]
|
||||
Beef = ["Beefy2D", "corlib"]
|
||||
|
||||
[Configs.Debug.Win64]
|
||||
AllocStackTraceDepth = 12
|
||||
|
||||
@@ -1,18 +1,33 @@
|
||||
FileVersion = 1
|
||||
Dependencies = {corlib = "*", GlitchLog = "*", GlitchyEngine = "*"}
|
||||
|
||||
[Project]
|
||||
Name = "GlitchyEditor"
|
||||
TargetType = "BeefGUIApplication"
|
||||
StartupObject = "GlitchyEngine.Program"
|
||||
ProcessorMacros = ["GE_EDITOR_IMGUI_DEMO"]
|
||||
ProcessorMacros = ["GE_EDITOR_IMGUI_DEMO", "GE_GRAPHICS_DX11"]
|
||||
|
||||
[Dependencies]
|
||||
corlib = "*"
|
||||
GlitchLog = "*"
|
||||
GlitchyEngine = "*"
|
||||
Xml-Beef = "*"
|
||||
ImGuiImplDX11 = "*"
|
||||
ImGuiImplWin32 = "*"
|
||||
ImGuizmo = "*"
|
||||
|
||||
[Platform.Windows]
|
||||
IconFile = "$(ProjectDir)/Resources/Textures/GlitchyEngineIcon.ico"
|
||||
|
||||
[Configs.Debug.Win64]
|
||||
PostBuildCmds = ["CopyFilesIfNewer(\"$(WorkspaceDir)/bin/vswhere.exe\", \"$(TargetDir)\")"]
|
||||
DebugCommandArguments = "\"D:\\Development\\Git\\SingleStateToOrbit\""
|
||||
PreprocessorMacros = ["DEBUG", "IMGUI"]
|
||||
|
||||
[Configs.Release.Win64]
|
||||
TargetDirectory = "$(BuildDir)/dist"
|
||||
PostBuildCmds = ["CopyFilesIfNewer(\"$(WorkspaceDir)/bin/vswhere.exe\", \"$(TargetDir)\")"]
|
||||
DebugCommandArguments = "\"D:\\Development\\Git\\SingleStateToOrbit\""
|
||||
PreprocessorMacros = ["RELEASE", "IMGUI"]
|
||||
|
||||
[Configs.Paranoid.Win64]
|
||||
PostBuildCmds = ["CopyFilesIfNewer(\"$(WorkspaceDir)/bin/vswhere.exe\", \"$(TargetDir)\")"]
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 9434890118063137951
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
Effect = 7757902626263730438,
|
||||
Textures = [],
|
||||
Constants = []
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "MaterialImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 14657247797693923776
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
Effect = 2172704887168568810,
|
||||
Textures = [],
|
||||
Constants = []
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "MaterialImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 15110879072477854834
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 12886429392536704724
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunManager" selected=".NET Launch Settings Profile.ScriptCoreGenerator: Debug Code Generator">
|
||||
<configuration name="Attach to Glitchy Engine" type="ConnectRemote" factoryName="Mono Remote" show_console_on_std_err="false" show_console_on_std_out="false" port="2550" address="localhost">
|
||||
<option name="allowRunningInParallel" value="false" />
|
||||
<option name="listenPortForConnections" value="false" />
|
||||
<option name="projectPathOnTarget" />
|
||||
<option name="selectedOptions">
|
||||
<list />
|
||||
</option>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<list>
|
||||
<item itemvalue="Mono Remote.Attach to Glitchy Engine" />
|
||||
</list>
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
||||
// die einer Assembly zugeordnet sind.
|
||||
[assembly: AssemblyTitle("[ProjectName]")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("[ProjectName]")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2023")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
|
||||
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
|
||||
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
||||
[assembly: Guid("7a70819c-5317-402a-8553-95c5a001e370")]
|
||||
|
||||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||
//
|
||||
// Hauptversion
|
||||
// Nebenversion
|
||||
// Buildnummer
|
||||
// Revision
|
||||
//
|
||||
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
|
||||
// indem Sie "*" wie unten gezeigt eingeben:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -1,54 +1,19 @@
|
||||
<?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')" />
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{7A70819C-5317-402A-8553-95C5A001E370}</ProjectGuid>
|
||||
<!-- General configuration -->
|
||||
<AssemblyTitle>[ProjectName]</AssemblyTitle>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>[ProjectName]</RootNamespace>
|
||||
<AssemblyName>[ProjectName]</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>portable</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>
|
||||
<Nullable>enable</Nullable>
|
||||
<!-- Define the output path -->
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>./.cache/bin/</OutputPath>
|
||||
<!-- Generates a runtimeconfig-file and copies nuget references to the output -->
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ScriptCore, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>[CoreLibPath]</HintPath>
|
||||
<Reference Include="ScriptCore" OutputItemType="Analyzer" ReferenceOutputAssembly="true">
|
||||
<HintPath>/ScriptCore.dll</HintPath>
|
||||
</Reference>
|
||||
<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="Properties\AssemblyInfo.cs" />
|
||||
<!-- Script Files -->
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 17767079304205108419
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
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}") = "[ProjectName]", "[ProjectName].csproj", "{7A70819C-5317-402A-8553-95C5A001E370}"
|
||||
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
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B5D43D8D-D80B-41AA-BE40-1DC507D87148}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,3 @@
|
||||
<Solution>
|
||||
<Project Path="[ProjectName].csproj" />
|
||||
</Solution>
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 15274144675004602184
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 5360037389314667000
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 928998782684229984
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 3971197750817239312
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 15597986932192091061
|
||||
}
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// TODO: General pipeline for post processing effects
|
||||
|
||||
Texture2D Texture : register(t0);
|
||||
SamplerState TextureSampler : register(s0);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 315199643377983302
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 7082287996527160060
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 17297254839128368932
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 8508337008960456447
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma EngineBuffer[ Name = "SceneConstants"; Binding = "Scene" ]
|
||||
/**
|
||||
* Contains constants that apply to the current scene.
|
||||
*/
|
||||
cbuffer SceneConstants : register(b0)
|
||||
{
|
||||
/**
|
||||
* View projection matrix of the active camera.
|
||||
*/
|
||||
float4x4 ViewProjection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the data provided by the engine for a 2D vertex shader.
|
||||
*/
|
||||
struct VS_Input
|
||||
{
|
||||
// Geometry
|
||||
|
||||
/**
|
||||
* Vertex position
|
||||
*/
|
||||
float2 Position : POSITION;
|
||||
/**
|
||||
* Vertex texture coordinate
|
||||
*/
|
||||
float2 Texcoord : TEXCOORD0;
|
||||
|
||||
// Instance data
|
||||
/**
|
||||
* model to world transform matrix of the current quad.
|
||||
*/
|
||||
float4x4 Transform : TRANSFORM;
|
||||
/**
|
||||
* Color of the current quad.
|
||||
*/
|
||||
float4 Color : COLOR;
|
||||
/**
|
||||
* model to world transform matrix of the current quad.
|
||||
*/
|
||||
float4 UVTransform : TEXCOORD1;
|
||||
#ifdef EDITOR
|
||||
/**
|
||||
* The ECS ID of the entity associated with the current quad.
|
||||
*/
|
||||
uint EntityId : ENTITYID;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains the output data of the vertex shader that will be passed into the pixel shader.
|
||||
*/
|
||||
typedef struct PS_Input
|
||||
{
|
||||
/**
|
||||
* The screen space position of the current vertex.
|
||||
*/
|
||||
float4 Position : SV_Position;
|
||||
/**
|
||||
* The transformed texture coordinates of the current vertex.
|
||||
*/
|
||||
float2 Texcoord : TEXCOORD;
|
||||
/**
|
||||
* The color of the current vertex.
|
||||
*/
|
||||
float4 Color : COLOR;
|
||||
#ifdef EDITOR
|
||||
/**
|
||||
* The ECS ID of the entity associated with the current quad.
|
||||
* This should always just be passed through.
|
||||
*/
|
||||
nointerpolation uint EntityId : ENTITYID;
|
||||
#endif
|
||||
} VS_Output;
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 15977579743485633133
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 17917061300990589192
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 5789702360838490150
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 8336560834161185660
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 13875759478508290092
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "GlitchyEngine.hlsl"
|
||||
|
||||
struct VS_IN
|
||||
{
|
||||
float3 Position : POSITION;
|
||||
};
|
||||
|
||||
struct PS_IN
|
||||
{
|
||||
float4 Position : SV_POSITION;
|
||||
};
|
||||
|
||||
PS_IN VS(VS_IN input)
|
||||
{
|
||||
PS_IN output;
|
||||
|
||||
float4 worldPosition = mul(Transform, float4(input.Position, 1));
|
||||
output.Position = mul(ViewProjection, worldPosition);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
float4 PS(PS_IN input) : SV_TARGET
|
||||
{
|
||||
return float4(1, 0, 1, 1);
|
||||
}
|
||||
|
||||
#pragma Effect[VS = VS; PS = PS]
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 652951988555027469
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "GlitchyEngine.hlsl"
|
||||
|
||||
struct VS_IN
|
||||
{
|
||||
float3 Position : POSITION;
|
||||
};
|
||||
|
||||
struct PS_IN
|
||||
{
|
||||
float4 Position : SV_POSITION;
|
||||
};
|
||||
|
||||
PS_IN VS(VS_IN input)
|
||||
{
|
||||
PS_IN output;
|
||||
|
||||
float4 worldPosition = mul(Transform, float4(input.Position, 1));
|
||||
output.Position = mul(ViewProjection, worldPosition);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
float4 PS(PS_IN input) : SV_TARGET
|
||||
{
|
||||
return float4(0, 1, 1, 1);
|
||||
}
|
||||
|
||||
#pragma Effect[VS = VS; PS = PS]
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 3052446500956620342
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
#include "ShaderHelpers.hlsl"
|
||||
|
||||
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_ZERO 0
|
||||
#define SWIZZLE_ONE 1
|
||||
#define SWIZZLE_R 2
|
||||
#define SWIZZLE_G 3
|
||||
#define SWIZZLE_B 4
|
||||
#define SWIZZLE_A 5
|
||||
#define SWIZZLE_ONE_MINUS_R 6
|
||||
#define SWIZZLE_ONE_MINUS_G 7
|
||||
#define SWIZZLE_ONE_MINUS_B 8
|
||||
#define SWIZZLE_ONE_MINUS_A 9
|
||||
|
||||
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;
|
||||
|
||||
bool ShowTexelBorders = true;
|
||||
float TexelBorderCutoff = 10;
|
||||
}
|
||||
|
||||
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_ZERO:
|
||||
output = 0.0;
|
||||
break;
|
||||
case SWIZZLE_ONE:
|
||||
output = 1.0;
|
||||
break;
|
||||
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;
|
||||
case SWIZZLE_ONE_MINUS_R:
|
||||
output = 1.0 - colorToSwizzle.r;
|
||||
break;
|
||||
case SWIZZLE_ONE_MINUS_G:
|
||||
output = 1.0 - colorToSwizzle.g;
|
||||
break;
|
||||
case SWIZZLE_ONE_MINUS_B:
|
||||
output = 1.0 - colorToSwizzle.b;
|
||||
break;
|
||||
case SWIZZLE_ONE_MINUS_A:
|
||||
output = 1.0 - colorToSwizzle.a;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the current pixel lies on the border between two texels.
|
||||
* @param texelPosition The position in texel coordinates [0...Texels)
|
||||
* @param color The sampled color at the current position. This will be modified if the pixel is on a texel border.
|
||||
*/
|
||||
void ApplyTexelBorder(float2 texelPosition, inout float4 color)
|
||||
{
|
||||
// Calculate derivatives of texelPosition -> how many texels to we move if we move one pixel in x/y direction
|
||||
float2 grad = fwidth(texelPosition);
|
||||
float texelsPerPixel = min(grad.x, grad.y);
|
||||
float pixelsPerTexel = 1.0 / texelsPerPixel;
|
||||
|
||||
// Calculate the distance between the screen pixel and the texel border in texels.
|
||||
float2 fracPart = frac(texelPosition);
|
||||
float2 distanceToBorder = min(fracPart, 1.0 - fracPart);
|
||||
|
||||
// One, if texel is closer to border than threshold.
|
||||
// Threshold is half the size of a screen pixel in texel-space.
|
||||
float2 borderTest = step(distanceToBorder, texelsPerPixel / 2.0);
|
||||
//float2 borderTest = saturate(1.0 - distanceToBorder / grad);
|
||||
float isOnLine = max(borderTest.x, borderTest.y);
|
||||
|
||||
float fade = saturate((pixelsPerTexel - TexelBorderCutoff / 2.0) / (TexelBorderCutoff / 2.0));
|
||||
|
||||
// TODO: This can flicker while moving/zooming, because we are by design on a texel border!
|
||||
float luminance = ColorToLuminance(color.rgb);
|
||||
// Ensure the border is always at least 0.5 luminance away from the texel.
|
||||
float4 borderColor = float4(((luminance + 0.5) % 1).xxx, 1.0);
|
||||
|
||||
color = lerp(color, borderColor, isOnLine * fade * 0.8);
|
||||
}
|
||||
|
||||
float4 PS(PS_Input input) : SV_Target0
|
||||
{
|
||||
float4 outputColor;
|
||||
|
||||
float2 texelPosition = input.Texcoord * Texels;
|
||||
|
||||
if (Mode == 0)
|
||||
{
|
||||
//float4 color = Texture.SampleLevel(Sampler, float3(input.Texcoord, ArraySlice), MipLevel);
|
||||
// Quasi next neighbor
|
||||
float4 color = Texture.Load(int4(texelPosition, ArraySlice, MipLevel));
|
||||
outputColor = float4(ColorOffset.xxx, AlphaOffset) + color * float4(ColorScale.xxx, AlphaScale);
|
||||
}
|
||||
else if (Mode == 1)
|
||||
{
|
||||
int4 color = IntTexture.Load(int4(texelPosition, ArraySlice, MipLevel));
|
||||
outputColor = float4(ColorOffset.xxx, AlphaOffset) + color * float4(ColorScale.xxx, AlphaScale);
|
||||
}
|
||||
else if (Mode == 2)
|
||||
{
|
||||
uint4 color = UIntTexture.Load(int4(texelPosition, 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);
|
||||
|
||||
if (ShowTexelBorders)
|
||||
{
|
||||
ApplyTexelBorder(texelPosition, swizzledOutput);
|
||||
}
|
||||
|
||||
return swizzledOutput;
|
||||
}
|
||||
|
||||
#pragma Effect[VS=VS; PS=PS]
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 17953005509950866729
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
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]
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 1518912886664733757
|
||||
}
|
||||
@@ -72,7 +72,7 @@ float3x3 ConstructTangentSpace(float3 normal, float3 tangent, float3 sigma)
|
||||
*/
|
||||
float ColorToLuminance(float3 rgb)
|
||||
{
|
||||
return rgb.r * 0.212639 + rgb.g * 0.715169 + rgb.b * 0.072192;
|
||||
return dot(rgb, float3(0.212639,0.715169,0.072192));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,4 +87,15 @@ float GetBitangentHandedness(float tangentz)
|
||||
return (z & 1) > 0 ? 1.0 : -1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the given texture coordinates using the provided UV transform.
|
||||
* @param texcoords The texture coordinates to transform.
|
||||
* @param uvTransform the UV transform. X, Y: UV offset | Z, W: UV scaling.
|
||||
* @return The transformed uv coordinates.
|
||||
*/
|
||||
float2 TransformTexcoords(float2 texcoords, float4 uvTransform)
|
||||
{
|
||||
return uvTransform.xy + uvTransform.zw * texcoords;
|
||||
}
|
||||
|
||||
#endif // __SHADER_HELPERS_HLSL__
|
||||
@@ -1,5 +1,11 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 13663488523997149295
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 15853638422335091598
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 16539769359681819317
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 9450907808112906391
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 15706760057808399494
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 4086392147967882920
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 10636491756936205086
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 3929078354809894230
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 5888610203248969416
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 5414648614681927202
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#define EDITOR
|
||||
|
||||
#include "GlitchyEngine2D.hlsl"
|
||||
#include "ShaderHelpers.hlsl"
|
||||
|
||||
Texture2D<float3> Texture : register(t0);
|
||||
SamplerState Sampler : register(s0);
|
||||
|
||||
cbuffer Material
|
||||
{
|
||||
float2 UnitRange;
|
||||
}
|
||||
|
||||
VS_Output VS(VS_Input input)
|
||||
{
|
||||
VS_Output output;
|
||||
|
||||
output.Position = mul(ViewProjection, mul(input.Transform, float4(input.Position, 0.0f, 1.0f)));
|
||||
output.Texcoord = TransformTexcoords(input.Texcoord, input.UVTransform);
|
||||
output.Color = input.Color;
|
||||
|
||||
#ifdef EDITOR
|
||||
output.EntityId = input.EntityId;
|
||||
#endif
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
float median(float r, float g, float b)
|
||||
{
|
||||
return max(min(r, g), min(max(r, g), b));
|
||||
}
|
||||
|
||||
float ScreenPxRange(float2 texcoord)
|
||||
{
|
||||
float2 screenTexSize = 1.0f / fwidth(texcoord);
|
||||
return max(0.5f * dot(UnitRange, screenTexSize), 1.0f);
|
||||
}
|
||||
|
||||
struct PS_Output
|
||||
{
|
||||
float4 Color : SV_Target0;
|
||||
#ifdef EDITOR
|
||||
uint EntityId : SV_TARGET1;
|
||||
#endif
|
||||
};
|
||||
|
||||
PS_Output PS(PS_Input input)
|
||||
{
|
||||
PS_Output output;
|
||||
|
||||
float3 msd = Texture.Sample(Sampler, input.Texcoord);
|
||||
float sd = median(msd.r, msd.g, msd.b);
|
||||
float screenPxDistance = ScreenPxRange(input.Texcoord) * (sd - 0.5);
|
||||
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||
|
||||
clip(opacity - 0.001f);
|
||||
|
||||
output.Color = float4(input.Color.rgb, input.Color.a * opacity);
|
||||
|
||||
#ifdef EDITOR
|
||||
output.EntityId = input.EntityId;
|
||||
#endif
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
#pragma Effect[VS=VS; PS=PS]
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 9062891433306436746
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
Texture2D<float3> Texture : register(t0);
|
||||
SamplerState Sampler : register(s0);
|
||||
|
||||
cbuffer Constants
|
||||
{
|
||||
float4x4 ViewProjection;
|
||||
float2 UnitRange;
|
||||
float screenPixelRange = 2;
|
||||
}
|
||||
|
||||
struct VS_Input
|
||||
{
|
||||
float2 Position : POSITION;
|
||||
float2 Texcoord : TEXCOORD0;
|
||||
float4x4 Tranform : TRANSFORM;
|
||||
float4 Color : COLOR;
|
||||
float4 UVTransform : TEXCOORD1;
|
||||
};
|
||||
|
||||
struct PS_Input
|
||||
{
|
||||
float4 Position : SV_Position;
|
||||
float2 TexCoord : TEXCOORD0;
|
||||
float4 Color : COLOR;
|
||||
};
|
||||
|
||||
PS_Input VS(VS_Input input)
|
||||
{
|
||||
PS_Input output;
|
||||
|
||||
output.Position = mul(ViewProjection, mul(input.Tranform, float4(input.Position, 0.0f, 1.0f)));
|
||||
output.TexCoord = input.UVTransform.xy + input.UVTransform.zw * input.Texcoord;
|
||||
output.Color = input.Color;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/*
|
||||
in vec2 texCoord;
|
||||
out vec4 color;
|
||||
uniform sampler2D msdf;
|
||||
uniform vec4 bgColor;
|
||||
uniform vec4 fgColor;
|
||||
|
||||
float median(float r, float g, float b) {
|
||||
return max(min(r, g), min(max(r, g), b));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 msd = texture(msdf, texCoord).rgb;
|
||||
float sd = median(msd.r, msd.g, msd.b);
|
||||
float screenPxDistance = screenPxRange()*(sd - 0.5);
|
||||
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||
color = mix(bgColor, fgColor, opacity);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
float median(float r, float g, float b)
|
||||
{
|
||||
return max(min(r, g), min(max(r, g), b));
|
||||
}
|
||||
|
||||
float ScreenPxRange(float2 texcoord)
|
||||
{
|
||||
float2 screenTexSize = 1.0f / fwidth(texcoord);
|
||||
return max(0.5f * dot(UnitRange, screenTexSize), 1.0f);
|
||||
}
|
||||
|
||||
float4 PS(PS_Input input) : SV_Target0
|
||||
{
|
||||
float3 msd = Texture.Sample(Sampler, input.TexCoord);
|
||||
float sd = median(msd.r, msd.g, msd.b);
|
||||
float screenPxDistance = ScreenPxRange(input.TexCoord) * (sd - 0.5);
|
||||
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||
|
||||
clip(opacity - 0.001f);
|
||||
|
||||
//return float4(input.Color.rgb, opacity * input.Color.a) * 0.0001f + float4(opacity.xxxx);
|
||||
return input.Color * opacity; // TODO: This is not quite right // float4(input.Color.rgb, input.Color.a * opacity);
|
||||
//return float4(input.Color.rgb, opacity * input.Color.a) * 0.0001f + float4(msd, 1.0f);
|
||||
}
|
||||
|
||||
/*
|
||||
// 2D
|
||||
float4 PS(PS_Input input) : SV_Target0
|
||||
{
|
||||
float3 msd = Texture.Sample(Sampler, input.TexCoord).rgb;
|
||||
float sd = median(msd.r, msd.g, msd.b);
|
||||
float screenPxDistance = screenPixelRange*(sd - 0.5);
|
||||
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||
|
||||
return float4(input.Color.rgb, opacity * input.Color.a);
|
||||
}
|
||||
*/
|
||||
|
||||
#pragma Effect[VS=VS; PS=PS]
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 17001330646928133106
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 2172704887168568810
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 2913314865396454015
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 1519310858892551656
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 9047671870217222298
|
||||
}
|
||||
+6
-30
@@ -1,41 +1,17 @@
|
||||
#define EDITOR
|
||||
|
||||
#include "GlitchyEngine2D.hlsl"
|
||||
#include "ShaderHelpers.hlsl"
|
||||
|
||||
Texture2D Texture : register(t0);
|
||||
SamplerState Sampler : register(s0);
|
||||
|
||||
cbuffer Constants : register(b0)
|
||||
VS_Output VS(VS_Input input)
|
||||
{
|
||||
float4x4 ViewProjection;
|
||||
};
|
||||
|
||||
struct VS_Input
|
||||
{
|
||||
float2 Position : POSITION;
|
||||
float2 Texcoord : TEXCOORD0;
|
||||
float4x4 Transform : TRANSFORM;
|
||||
float4 Color : COLOR;
|
||||
float4 UVTransform : TEXCOORD1;
|
||||
#ifdef EDITOR
|
||||
uint EntityId : ENTITYID;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct PS_Input
|
||||
{
|
||||
float4 Position : SV_Position;
|
||||
float2 Texcoord : TEXCOORD;
|
||||
float4 Color : COLOR;
|
||||
#ifdef EDITOR
|
||||
nointerpolation uint EntityId : ENTITYID;
|
||||
#endif
|
||||
};
|
||||
|
||||
PS_Input VS(VS_Input input)
|
||||
{
|
||||
PS_Input output;
|
||||
VS_Output output;
|
||||
|
||||
output.Position = mul(ViewProjection, mul(input.Transform, float4(input.Position, 0.0f, 1.0f)));
|
||||
output.Texcoord = input.UVTransform.xy + input.UVTransform.zw * input.Texcoord;
|
||||
output.Texcoord = TransformTexcoords(input.Texcoord, input.UVTransform);
|
||||
output.Color = input.Color;
|
||||
|
||||
// Premultiply Alpha
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 7757902626263730438
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 14473519405124742382
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = "ShaderImporter",
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = {
|
||||
_compression = .None
|
||||
},
|
||||
AssetHandle = 8094944104856236365
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
AssetLoader = "EffectAssetLoader",
|
||||
Config = (GlitchyEditor.Assets.EffectAssetLoaderConfig){}/* No reflection data for GlitchyEditor.Assets.EffectAssetLoaderConfig. Add [BonTarget] or force it */,
|
||||
AssetHandle = 10276018270467402814
|
||||
}
|
||||
@@ -11,14 +11,14 @@ public class [EntityName] : Entity
|
||||
/// <summary>
|
||||
/// OnCreate is called once before the first call to OnUpdate.
|
||||
/// </summary>
|
||||
void OnCreate()
|
||||
protected override void OnCreate()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnUpdate is called once every frame.
|
||||
/// </summary>
|
||||
void OnUpdate(float deltaTime)
|
||||
protected override void OnUpdate(float deltaTime)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 14385412659829048739
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 3965163548234580201
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
AssetLoader = null,
|
||||
Config = null,
|
||||
Importer = null,
|
||||
ImporterConfig = null,
|
||||
Processor = null,
|
||||
ProcessorConfig = null,
|
||||
Exporter = null,
|
||||
ExporterConfig = null,
|
||||
AssetHandle = 14643606651153291317
|
||||
}
|
||||
@@ -122,7 +122,10 @@ class AssetFile
|
||||
CreateDefaultAssetLoader();
|
||||
}
|
||||
|
||||
_lastConfigEditTime = File.GetLastWriteTimeUtc(_assetConfigPath);
|
||||
if (File.GetLastWriteTimeUtc(_assetConfigPath) not case .Ok(out _lastConfigEditTime))
|
||||
{
|
||||
_lastConfigEditTime = .MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateAssetHandle()
|
||||
@@ -134,7 +137,7 @@ class AssetFile
|
||||
{
|
||||
String fileExtension = Path.GetExtension(_assetFile.Path, .. scope .());
|
||||
|
||||
Log.EngineLogger.Info($"Created config for {_assetFile.Path}");
|
||||
Log.EngineLogger.Info($"Creating asset config for {_assetFile.Path}");
|
||||
|
||||
_assetConfig = new AssetConfig();
|
||||
|
||||
@@ -150,22 +153,21 @@ class AssetFile
|
||||
|
||||
var assetLoader = _contentManager.GetDefaultAssetLoader(fileExtension);
|
||||
|
||||
// We don't have a loader -> we don't need a config
|
||||
if (assetLoader == null && assetImporter == null)
|
||||
return;
|
||||
|
||||
if (assetLoader != null)
|
||||
if (assetLoader != null || assetImporter != null)
|
||||
{
|
||||
_assetConfig.AssetLoader = new String();
|
||||
assetLoader.GetType().GetName(_assetConfig.AssetLoader);
|
||||
|
||||
|
||||
_assetConfig.Config = assetLoader?.GetDefaultConfig();
|
||||
_assetConfig.Config?.[Friend]_changed = true;
|
||||
}
|
||||
|
||||
_assetConfig.Importer = new String();
|
||||
assetImporter?.GetType()?.GetName(_assetConfig.Importer);
|
||||
_assetConfig.Importer = new String();
|
||||
assetImporter?.GetType()?.GetName(_assetConfig.Importer);
|
||||
}
|
||||
|
||||
// We might not have a loader, but we want to retain the AssetHandle:
|
||||
// e. g. a .txt File can't be reasonably imported, but might still need to be packed into the final
|
||||
// application bundle and be read in a script via AssetHandle.
|
||||
SaveAssetConfig();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,14 @@ using System.Collections;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using GlitchyEngine.Content;
|
||||
using GlitchyEditor.Multithreading;
|
||||
|
||||
namespace GlitchyEditor.Assets;
|
||||
|
||||
class AssetHierarchy
|
||||
{
|
||||
FileSystemWatcher fsw ~ {
|
||||
_.StopRaisingEvents();
|
||||
_?.StopRaisingEvents();
|
||||
delete _;
|
||||
};
|
||||
|
||||
@@ -65,6 +66,30 @@ class AssetHierarchy
|
||||
Log.EngineLogger.Trace($"Created root node");
|
||||
}
|
||||
|
||||
public void DeletePathsBackground(Span<StringView> paths)
|
||||
{
|
||||
DeleteBackgroundTask deleteTask = new .(paths);
|
||||
deleteTask.DeleteWhenEnded = true;
|
||||
|
||||
for (StringView path in paths)
|
||||
{
|
||||
Result<TreeNode<AssetNode>> nodeResult = GetNodeFromPath(path);
|
||||
|
||||
if (nodeResult case .Ok(TreeNode<AssetNode> assetNode))
|
||||
{
|
||||
StringView assetDescriptorPath = assetNode->AssetFile?.AssetConfigPath ?? "";
|
||||
|
||||
// If it exists, also try to delete the .ass file
|
||||
if (File.Exists(assetDescriptorPath))
|
||||
{
|
||||
deleteTask.AddPath(assetDescriptorPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorApp.Instance.BackgroundTaskManager.StartBackgroundTask(deleteTask);
|
||||
}
|
||||
|
||||
/// Deletes the file that belongs to the given assetNode
|
||||
public void DeleteFile(AssetNode assetNode)
|
||||
{
|
||||
@@ -646,4 +671,115 @@ class AssetHierarchy
|
||||
public delegate void FileRenamedFunc(AssetNode node, StringView oldName);
|
||||
|
||||
public Event<FileRenamedFunc> OnFileRenamed ~ _.Dispose();
|
||||
|
||||
public enum CopyError
|
||||
{
|
||||
TargetDirectoryIsNotDirectory,
|
||||
SourceDoesntExist,
|
||||
TargetExists
|
||||
}
|
||||
|
||||
public enum OverwriteMode
|
||||
{
|
||||
None,
|
||||
Overwrite,
|
||||
KeepBoth,
|
||||
Combine
|
||||
}
|
||||
|
||||
/*public Result<BackgroundTask> CopyExternFileToNodeBackground(TreeNode<AssetNode> targetDirectory, String sourcePath, OverwriteMode overwrite)
|
||||
{
|
||||
if (!targetDirectory->IsDirectory)
|
||||
return .Err;
|
||||
|
||||
FileInfo sourceInfo = scope FileInfo(sourcePath);
|
||||
|
||||
if (!sourceInfo.Exists)
|
||||
return .Err;
|
||||
|
||||
CopyBackgroundTask copyTask = new CopyBackgroundTask(sourcePath, targetDirectory->Path);
|
||||
|
||||
EditorApp.Instance.BackgroundTaskManager.StartBackgroundTask(copyTask);
|
||||
|
||||
return copyTask;
|
||||
}*/
|
||||
|
||||
public Result<BackgroundTask> CopyExternFilesToNodeBackground(TreeNode<AssetNode> targetDirectory, List<String> sourcePaths, OverwriteMode overwrite)
|
||||
{
|
||||
if (!targetDirectory->IsDirectory)
|
||||
return .Err;
|
||||
|
||||
/*FileInfo sourceInfo = scope FileInfo(sourcePath);
|
||||
|
||||
if (!sourceInfo.Exists)
|
||||
return .Err;*/
|
||||
|
||||
CopyBackgroundTask copyTask = new CopyBackgroundTask(sourcePaths, targetDirectory->Path);
|
||||
copyTask.DeleteWhenEnded = true;
|
||||
|
||||
EditorApp.Instance.BackgroundTaskManager.StartBackgroundTask(copyTask);
|
||||
|
||||
return copyTask;
|
||||
}
|
||||
|
||||
public Result<void, CopyError> CopyExternFileToNode(TreeNode<AssetNode> targetDirectory, String sourcePath, OverwriteMode overwrite)
|
||||
{
|
||||
if (!targetDirectory->IsDirectory)
|
||||
return .Err(.TargetDirectoryIsNotDirectory);
|
||||
|
||||
if (!File.Exists(sourcePath))
|
||||
return .Err(.SourceDoesntExist);
|
||||
|
||||
|
||||
FileInfo sourceInfo = scope FileInfo(sourcePath);
|
||||
|
||||
String fileName = scope .();
|
||||
Path.GetFileName(sourcePath, fileName);
|
||||
|
||||
String targetPath = scope .();
|
||||
Path.Combine(targetPath, targetDirectory->Path, fileName);
|
||||
|
||||
FileInfo targetInfo = scope FileInfo(targetPath);
|
||||
|
||||
if (sourceInfo.IsDirectory)
|
||||
{
|
||||
bool forceOverwrite = false;
|
||||
if (targetInfo.Exists)
|
||||
{
|
||||
switch (overwrite)
|
||||
{
|
||||
case .KeepBoth:
|
||||
String fileExtension = scope .();
|
||||
Path.GetExtension(targetPath, fileExtension);
|
||||
Path.FindFreePath(targetDirectory->Path, fileName.Substring(0..<^fileExtension.Length), fileExtension, targetPath..Clear());
|
||||
case .Overwrite:
|
||||
forceOverwrite = true;
|
||||
default:
|
||||
return .Err(.TargetExists);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool forceOverwrite = false;
|
||||
if (targetInfo.Exists)
|
||||
{
|
||||
switch (overwrite)
|
||||
{
|
||||
case .KeepBoth:
|
||||
String fileExtension = scope .();
|
||||
Path.GetExtension(targetPath, fileExtension);
|
||||
Path.FindFreePath(targetDirectory->Path, fileName.Substring(0..<^fileExtension.Length), fileExtension, targetPath..Clear());
|
||||
case .Overwrite:
|
||||
forceOverwrite = true;
|
||||
default:
|
||||
return .Err(.TargetExists);
|
||||
}
|
||||
}
|
||||
|
||||
File.Copy(sourcePath, targetPath, forceOverwrite);
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace GlitchyEditor.Assets;
|
||||
|
||||
[Obsolete]
|
||||
abstract class AssetPropertiesEditor
|
||||
{
|
||||
private AssetFile _asset;
|
||||
|
||||
@@ -18,14 +18,15 @@ class AssetThumbnailManager
|
||||
if (assetNode.Path.EndsWith(".scene"))
|
||||
return _icons.File_Scene;
|
||||
|
||||
if (assetNode.Path.EndsWith(".mat"))
|
||||
return _icons.File_Material;
|
||||
|
||||
if (assetNode.Path.EndsWith(".cs"))
|
||||
return _icons.File_CSharpScript;
|
||||
|
||||
if (assetNode.Path.EndsWith(".hlsl"))
|
||||
return _icons.File_Shader;
|
||||
return _icons.File_Hlsl;
|
||||
if (assetNode.Path.EndsWith(".fx"))
|
||||
return _icons.File_Effect;
|
||||
if (assetNode.Path.EndsWith(".mat"))
|
||||
return _icons.File_Material;
|
||||
|
||||
return _icons.File;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
using GlitchyEngine.Renderer;
|
||||
using ImGui;
|
||||
using System;
|
||||
using GlitchyEngine.Math;
|
||||
using GlitchyEditor.EditWindows;
|
||||
using GlitchyEngine.Content;
|
||||
|
||||
namespace GlitchyEditor.Assets.Editors;
|
||||
|
||||
class MaterialEditor
|
||||
{
|
||||
public static bool ShowEditor(AssetFile assetFile)
|
||||
{
|
||||
bool isDirty = false;
|
||||
|
||||
let material = assetFile.LoadedAsset as Material;
|
||||
|
||||
if (material == null)
|
||||
return isDirty;
|
||||
|
||||
ImGui.PropertyTableStartNewProperty("Base Material");
|
||||
|
||||
if (material.Parent != null)
|
||||
ImGui.TextUnformatted(material.Parent.Identifier);
|
||||
else
|
||||
ImGui.TextUnformatted("None");
|
||||
|
||||
ImGui.PropertyTableStartNewProperty("Shader");
|
||||
|
||||
ImGui.TextUnformatted(material.Effect.Identifier);
|
||||
|
||||
ImGui.PropertyTableStartNewRow();
|
||||
if (ImGui.CollapsingHeader("Parameters", .DefaultOpen | .AllowOverlap | .Framed | .SpanFullWidth | .SpanAllColumns))
|
||||
{
|
||||
String prettyVariableName = scope .();
|
||||
for (let (variableName, bufferVariable) in material.Variables)
|
||||
{
|
||||
// Don't show engine buffer variables
|
||||
if (!bufferVariable.ConstantBuffer.EngineBufferName.IsEmpty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ImGui.PushID(variableName);
|
||||
|
||||
ImGui.PropertyTableStartNewRow();
|
||||
|
||||
bool readOnly = bufferVariable.Flags.HasFlag(.Readonly);
|
||||
|
||||
ImGui.BeginDisabled(readOnly);
|
||||
|
||||
if (DrawLockButton(bufferVariable.Flags.HasFlag(.Locked)) && !readOnly)
|
||||
{
|
||||
bufferVariable.[Friend]_flags ^= .Locked;
|
||||
// Also mark as dirty so we update our children!
|
||||
bufferVariable.[Friend]_flags |= .Dirty;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
StringView displayName = bufferVariable.PreviewName;
|
||||
|
||||
if (displayName.IsEmpty)
|
||||
{
|
||||
ToPrettyName(variableName, prettyVariableName);
|
||||
displayName = prettyVariableName;
|
||||
}
|
||||
|
||||
ImGui.PropertyTableName(displayName);
|
||||
|
||||
// TODO: Somehow pass display name and type from imported shader to here!
|
||||
|
||||
bool handled = false;
|
||||
|
||||
switch (bufferVariable.EditorTypeName)
|
||||
{
|
||||
case "Color", "ColorHDR":
|
||||
if (bufferVariable.ElementType != .Float || bufferVariable.Rows != 1 ||
|
||||
bufferVariable.Columns < 3)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui.ColorEditFlags flags = .None;
|
||||
|
||||
if (_ == "ColorHDR")
|
||||
{
|
||||
flags |= .HDR | .Float;
|
||||
}
|
||||
|
||||
if (bufferVariable.Columns == 3)
|
||||
{
|
||||
material.GetVariable<float3>(bufferVariable.Name, var value);
|
||||
if (ImGui.ColorEdit3("", ref *(float[3]*)&value, flags))
|
||||
material.SetVariable(bufferVariable.Name, value);
|
||||
}
|
||||
else if (bufferVariable.Columns == 4)
|
||||
{
|
||||
material.GetVariable<float4>(bufferVariable.Name, var value);
|
||||
if (ImGui.ColorEdit4("", ref *(float[4]*)&value, flags))
|
||||
material.SetVariable(bufferVariable.Name, value);
|
||||
}
|
||||
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (!handled)
|
||||
{
|
||||
switch (bufferVariable.ElementType)
|
||||
{
|
||||
case .Float:
|
||||
// TODO: min and max values specified in shader
|
||||
// TODO: support drag and enter number (specified in shader)
|
||||
|
||||
//for (int r < bufferVariable.Rows)
|
||||
if (bufferVariable.Rows == 1)
|
||||
{
|
||||
switch (bufferVariable.Columns)
|
||||
{
|
||||
case 1:
|
||||
material.GetVariable<float>(bufferVariable.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.VectorEditor<1>("", ref *(float[1]*)&value, .(), 0.1f /*, minV, maxV*/))
|
||||
material.SetVariable(bufferVariable.Name, value);
|
||||
case 2:
|
||||
material.GetVariable<float2>(bufferVariable.Name, var value);
|
||||
if (ImGui.Float2Editor("", ref value, .Zero, 0.1f, 100.0f))
|
||||
material.SetVariable(bufferVariable.Name, value);
|
||||
case 3:
|
||||
material.GetVariable<float3>(bufferVariable.Name, var value);
|
||||
if (ImGui.Float3Editor("", ref value, .Zero, 0.1f, 100.0f))
|
||||
material.SetVariable(bufferVariable.Name, value);
|
||||
case 4:
|
||||
material.GetVariable<float4>(bufferVariable.Name, var value);
|
||||
if (ImGui.Float4Editor("", ref value, .Zero, 0.1f, 100.0f))
|
||||
material.SetVariable(bufferVariable.Name, value);
|
||||
}
|
||||
}
|
||||
default:
|
||||
ImGui.TextUnformatted(scope $"Element Type {_} not supported");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndDisabled();
|
||||
|
||||
ImGui.PopID();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.PropertyTableStartNewRow();
|
||||
if (ImGui.CollapsingHeader("Textures", .DefaultOpen | .AllowOverlap | .Framed | .SpanFullWidth | .SpanAllColumns))
|
||||
{
|
||||
String prettyVariableName = scope .();
|
||||
for (var (textureName, texture) in ref material.Textures)
|
||||
{
|
||||
ImGui.PushID(textureName);
|
||||
|
||||
ImGui.PropertyTableStartNewRow();
|
||||
|
||||
bool readOnly = texture.Flags.HasFlag(.Readonly);
|
||||
|
||||
ImGui.BeginDisabled(readOnly);
|
||||
|
||||
if (DrawLockButton(texture.Flags.HasFlag(.Locked)) && !readOnly)
|
||||
{
|
||||
texture.Flags ^= .Locked;
|
||||
// Also mark as dirty so we update our children!
|
||||
texture.Flags |= .Dirty;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
// TODO: Preview Name for textures
|
||||
StringView displayName = "";//texture.PreviewName;
|
||||
|
||||
if (displayName.IsEmpty)
|
||||
{
|
||||
ToPrettyName(textureName, prettyVariableName);
|
||||
displayName = prettyVariableName;
|
||||
}
|
||||
|
||||
|
||||
ImGui.PropertyTableName(displayName);
|
||||
|
||||
if (ComponentEditWindow.ShowAssetDropTarget<Texture>(ref texture.TextureHandle))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ImGui.EndDisabled();
|
||||
|
||||
ImGui.PopID();
|
||||
}
|
||||
}
|
||||
|
||||
return isDirty;
|
||||
}
|
||||
|
||||
private static bool DrawLockButton(bool isLocked)
|
||||
{
|
||||
ImGui.PushStyleVar(.ItemInnerSpacing, ImGui.Vec2(0, 0));
|
||||
ImGui.PushStyleColor(.Button, ImGui.Vec4(0, 0, 0, 0));
|
||||
|
||||
let colors = ImGui.GetStyle().Colors;
|
||||
|
||||
ImGui.Vec4 hoveredColor = colors[(int)ImGui.Col.ButtonHovered];
|
||||
hoveredColor.w = 0.5f;
|
||||
|
||||
ImGui.Vec4 activeColor = colors[(int)ImGui.Col.ButtonActive];
|
||||
activeColor.w = 0.5f;
|
||||
|
||||
ImGui.PushStyleColor(.ButtonHovered, hoveredColor);
|
||||
ImGui.PushStyleColor(.ButtonActive, activeColor);
|
||||
|
||||
float padding = 2.0f;
|
||||
|
||||
float size = ImGui.GetTextLineHeight() - 2 * padding;
|
||||
|
||||
ImGui.PushID(0);
|
||||
|
||||
bool result = ImGui.ImageButton("", isLocked ? EditorIcons.Instance.Icon_Locked : EditorIcons.Instance.Icon_Unlocked, .(size, size), .Zero, .Ones);
|
||||
|
||||
ImGui.PopID();
|
||||
|
||||
if (isLocked)
|
||||
ImGui.AttachTooltip("Click to unlock. The property is locked and cannot be changed by children.");
|
||||
else
|
||||
ImGui.AttachTooltip("Click to lock. The property is unlocked and can be changed by children.");
|
||||
|
||||
ImGui.PopStyleColor(3);
|
||||
ImGui.PopStyleVar(1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a variable name to a pretty name as good as reasonably possible.
|
||||
/// </summary>
|
||||
/// <param name="uglyName">The name of a variable to prettify.</param>
|
||||
/// <returns>The pretty string.</returns>
|
||||
public static void ToPrettyName(StringView uglyName, String outPrettyName)
|
||||
{
|
||||
outPrettyName.PrepareBuffer(uglyName.Length);
|
||||
outPrettyName.Clear();
|
||||
|
||||
bool wasUpper = false;
|
||||
bool inWord = false;
|
||||
bool inNumber = false;
|
||||
|
||||
for (char8 c in uglyName)
|
||||
{
|
||||
if (c.IsLetter)
|
||||
{
|
||||
if (inNumber)
|
||||
{
|
||||
outPrettyName.Append(' ');
|
||||
inNumber = false;
|
||||
}
|
||||
|
||||
bool newWord = !inWord;
|
||||
|
||||
if (c.IsUpper && !wasUpper)
|
||||
{
|
||||
newWord = true;
|
||||
wasUpper = true;
|
||||
|
||||
if (inWord)
|
||||
{
|
||||
outPrettyName.Append(' ');
|
||||
}
|
||||
}
|
||||
else if (c.IsLower)
|
||||
{
|
||||
wasUpper = false;
|
||||
}
|
||||
|
||||
outPrettyName.Append(newWord ? c.ToUpper : c.ToLower);
|
||||
|
||||
inWord = true;
|
||||
}
|
||||
else if (c.IsDigit)
|
||||
{
|
||||
if (inWord)
|
||||
{
|
||||
outPrettyName.Append(' ');
|
||||
inWord = false;
|
||||
inNumber = true;
|
||||
}
|
||||
|
||||
outPrettyName.Append(c);
|
||||
}
|
||||
else if (inWord || inNumber)
|
||||
{
|
||||
outPrettyName.Append(' ');
|
||||
inWord = false;
|
||||
inNumber = false;
|
||||
wasUpper = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using Bon;
|
||||
using GlitchyEngine.Content;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEngine.Renderer;
|
||||
|
||||
namespace GlitchyEditor.Assets;
|
||||
|
||||
class EffectAssetPropertiesEditor : AssetPropertiesEditor
|
||||
{
|
||||
public this(AssetFile asset) : base(asset)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void ShowEditor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public static AssetPropertiesEditor Factory(AssetFile assetFile)
|
||||
{
|
||||
return new Self(assetFile);
|
||||
}
|
||||
}
|
||||
|
||||
[BonTarget, BonPolyRegister]
|
||||
class EffectAssetLoaderConfig : AssetLoaderConfig
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class EffectAssetLoader : IAssetLoader //, IReloadingAssetLoader
|
||||
{
|
||||
private static readonly List<StringView> _fileExtensions = new .(){".hlsl"} ~ delete _;
|
||||
|
||||
public static List<StringView> FileExtensions => _fileExtensions;
|
||||
|
||||
public AssetLoaderConfig GetDefaultConfig()
|
||||
{
|
||||
return new EffectAssetLoaderConfig();
|
||||
}
|
||||
|
||||
public Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager)
|
||||
{
|
||||
Effect effect = new Effect(file, assetIdentifier, contentManager);
|
||||
|
||||
return effect;
|
||||
}
|
||||
|
||||
public Asset GetPlaceholderAsset(Type assetType)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public Asset GetErrorAsset(Type assetType)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using GlitchyEngine.Renderer;
|
||||
using GlitchyEditor.Assets.Processors;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEditor.Assets.Importers;
|
||||
using GlitchyEngine.Content;
|
||||
using System.Diagnostics;
|
||||
namespace GlitchyEditor.Assets.Exporters;
|
||||
|
||||
|
||||
class MaterialExporter : IAssetExporter
|
||||
{
|
||||
public static AssetType ExportedAssetType => .Material;
|
||||
|
||||
public AssetExporterConfig CreateDefaultConfig()
|
||||
{
|
||||
return new AssetExporterConfig();
|
||||
}
|
||||
|
||||
public Result<void> Export(Stream stream, ProcessedResource processedResource, AssetConfig config)
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(processedResource is ProcessedMaterial);
|
||||
|
||||
ProcessedMaterial processedMaterial = (.)processedResource;
|
||||
|
||||
Compiler.Assert(sizeof(AssetHandle) == 8);
|
||||
|
||||
/*
|
||||
File Format:
|
||||
Effect Asset Handle (8 byte)
|
||||
Texture Count (uint16)
|
||||
Textures
|
||||
{
|
||||
Texture Handle (8 byte)
|
||||
Texture Name Length (int16)
|
||||
Texture Name Data...
|
||||
}
|
||||
Buffer Count (uint16)
|
||||
Buffers
|
||||
{
|
||||
Buffer Size (int64)
|
||||
Buffer Name Length (int16)
|
||||
Buffer Name Data...
|
||||
Raw Buffer Data...
|
||||
}
|
||||
*/
|
||||
|
||||
Try!(stream.Write((AssetHandle)processedMaterial.EffectHandle));
|
||||
Try!(WriteTextures(stream, processedMaterial));
|
||||
Try!(WriteBuffers(stream, processedMaterial));
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private Result<void> WriteTextures(Stream stream, ProcessedMaterial processedMaterial)
|
||||
{
|
||||
Log.EngineLogger.Assert(processedMaterial.Textures.Count < int16.MaxValue);
|
||||
|
||||
Try!(stream.Write((uint16)processedMaterial.Textures.Count));
|
||||
|
||||
for (let (textureName, textureHandle) in processedMaterial.Textures)
|
||||
{
|
||||
Try!(stream.Write((AssetHandle)textureHandle));
|
||||
|
||||
Log.EngineLogger.Assert(textureName.Length < int16.MaxValue);
|
||||
|
||||
Try!(stream.Write((int16)textureName.Length));
|
||||
Try!(stream.Write(textureName));
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
|
||||
private Result<void> WriteBuffers(Stream stream, ProcessedMaterial processedMaterial)
|
||||
{
|
||||
Log.EngineLogger.Assert(processedMaterial.Buffers.Count < int16.MaxValue);
|
||||
|
||||
Try!(stream.Write((uint16)processedMaterial.Buffers.Count));
|
||||
|
||||
for (let (bufferName, bufferData) in processedMaterial.Buffers)
|
||||
{
|
||||
Try!(stream.Write((int64)bufferData.Count));
|
||||
|
||||
Log.EngineLogger.Assert(bufferName.Length < int16.MaxValue);
|
||||
|
||||
Try!(stream.Write((int16)bufferName.Length));
|
||||
Try!(stream.Write(bufferName));
|
||||
|
||||
Try!(stream.Write(Span<uint8>(bufferData)));
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
using GlitchyEngine;
|
||||
using GlitchyEditor.Assets.Importers;
|
||||
using GlitchyEditor.Assets.Processors;
|
||||
using GlitchyEngine.Content;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GlitchyEditor.Assets.Exporters;
|
||||
|
||||
class ShaderExporter : IAssetExporter
|
||||
{
|
||||
public static AssetType ExportedAssetType => .Shader;
|
||||
|
||||
public AssetExporterConfig CreateDefaultConfig() => new AssetExporterConfig();
|
||||
|
||||
public Result<void> Export(Stream stream, ProcessedResource processedResource, AssetConfig config)
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(processedResource is ProcessedShader);
|
||||
|
||||
ProcessedShader shader = (.)processedResource;
|
||||
|
||||
/*
|
||||
|
||||
File Format:
|
||||
Vertex Shader Blob Length (uint64 / 8 bytes) (0 if null)
|
||||
Pixel Shader Blob Length (uint64 / 8 bytes) (0 if null)
|
||||
Vertex Shader Data...
|
||||
Pixel Shader Data...
|
||||
Texture Count (uint16)
|
||||
Textures
|
||||
{
|
||||
Texture Dimension (1 byte)
|
||||
Vertex Shader Bind Point (int32, 4 bytes) # TODO: ASSUME same bind point for all shader stages?
|
||||
Pixel Shader Bind Point (int32, 4 bytes) # TODO: ASSUME same bind point for all shader stages?
|
||||
Texture Name Length (uint16)
|
||||
Texture Name Data...
|
||||
}
|
||||
Buffer Count (uint16)
|
||||
Buffers
|
||||
{
|
||||
Buffer Size (int64)
|
||||
Vertex Shader Bind Point (int32, 4 bytes) # TODO: ASSUME same bind point for all shader stages?
|
||||
Pixel Shader Bind Point (int32, 4 bytes) # TODO: ASSUME same bind point for all shader stages?
|
||||
Buffer Name Length (uint16)
|
||||
Buffer Name Data...
|
||||
Engine Buffer Name Length (uint16)
|
||||
Engine Buffer Name Data...
|
||||
Variable Count (uint16)
|
||||
Variables
|
||||
{
|
||||
Offset (uint64)
|
||||
Size In Bytes (uint64)
|
||||
IsUsed (bool, 1 byte)
|
||||
ShaderVariableType (1 Byte)
|
||||
Rows (uint8)
|
||||
Columns (uint8)
|
||||
ArraySize (uint64)
|
||||
Name Length (uint16)
|
||||
Name Data...
|
||||
Preview Name Length (uint16)
|
||||
Preview Name Data...
|
||||
Type Editor Name Length (uint16)
|
||||
Type Editor Name Data...
|
||||
# TODO: Min and Max
|
||||
}
|
||||
RawData...
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
Span<uint8> vsData = shader.VertexShader?.Blob ?? Span<uint8>();
|
||||
Span<uint8> psData = shader.PixelShader?.Blob ?? Span<uint8>();
|
||||
|
||||
Try!(stream.Write((uint64)vsData.Length));
|
||||
Try!(stream.Write((uint64)psData.Length));
|
||||
|
||||
if (vsData.Ptr != null)
|
||||
Try!(stream.Write(vsData));
|
||||
|
||||
if (psData.Ptr != null)
|
||||
Try!(stream.Write(psData));
|
||||
|
||||
Try!(WriteTextures(stream, shader));
|
||||
Try!(WriteConstantBuffers(stream, shader));
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private Result<void> WriteTextures(Stream stream, ProcessedShader shader)
|
||||
{
|
||||
Log.EngineLogger.Assert(shader.Textures.Count < int16.MaxValue);
|
||||
|
||||
Try!(stream.Write((uint16)shader.Textures.Count));
|
||||
|
||||
for (let (textureName, textureEntry) in shader.Textures)
|
||||
{
|
||||
Try!(stream.Write((uint8)textureEntry.TextureDimension));
|
||||
Try!(stream.Write((int32)textureEntry.VertexShaderBindPoint));
|
||||
Try!(stream.Write((int32)textureEntry.PixelShaderBindPoint));
|
||||
WriteSizedString<uint16>(stream, textureName);
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private Result<void> WriteConstantBuffers(Stream stream, ProcessedShader shader)
|
||||
{
|
||||
Log.EngineLogger.Assert(shader.ConstantBuffers.Count < int16.MaxValue);
|
||||
|
||||
Try!(stream.Write((uint16)shader.ConstantBuffers.Count));
|
||||
|
||||
for (let (bufferName, bufferEntry) in shader.ConstantBuffers)
|
||||
{
|
||||
ReflectedConstantBuffer buffer = bufferEntry.ConstantBuffer;
|
||||
|
||||
Try!(stream.Write((uint64)buffer.Size));
|
||||
|
||||
Try!(stream.Write((int32)bufferEntry.VertexShaderBindPoint));
|
||||
Try!(stream.Write((int32)bufferEntry.PixelShaderBindPoint));
|
||||
|
||||
WriteSizedString<uint16>(stream, bufferName);
|
||||
|
||||
WriteSizedString<uint16>(stream, bufferEntry.ConstantBuffer.EngineBufferName);
|
||||
|
||||
Log.EngineLogger.Assert(buffer.Variables.Count < int16.MaxValue);
|
||||
|
||||
Try!(stream.Write((uint16)buffer.Variables.Count));
|
||||
|
||||
for (let (variableName, variable) in buffer.Variables)
|
||||
{
|
||||
Try!(stream.Write((uint64)variable.Offset));
|
||||
Try!(stream.Write((uint64)variable.SizeInBytes));
|
||||
Try!(stream.Write((uint8)(variable.IsUsed ? 1 : 0)));
|
||||
Try!(stream.Write((uint8)(variable.ElementType)));
|
||||
|
||||
Log.EngineLogger.Assert(variable.Rows < int8.MaxValue);
|
||||
Log.EngineLogger.Assert(variable.Columns < int8.MaxValue);
|
||||
|
||||
Try!(stream.Write((uint8)variable.Rows));
|
||||
Try!(stream.Write((uint8)variable.Columns));
|
||||
Try!(stream.Write((uint64)variable.ArraySize));
|
||||
|
||||
WriteSizedString<uint16>(stream, variableName);
|
||||
WriteSizedString<uint16>(stream, variable.PreviewName);
|
||||
WriteSizedString<uint16>(stream, variable.EditorType);
|
||||
}
|
||||
|
||||
Try!(stream.Write(Span<uint8>(buffer.RawData, 0, buffer.Size)));
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<void> WriteSizedString<Tint>(Stream stream, StringView string)
|
||||
where Tint : IInteger
|
||||
where int : operator explicit Tint
|
||||
where Tint : operator explicit int
|
||||
where Tint : struct
|
||||
{
|
||||
Log.EngineLogger.Assert(string.Length < (int)typeof(Tint).MaxValue);
|
||||
|
||||
Try!(stream.Write((Tint)string.Length));
|
||||
|
||||
if (string.Length > 0)
|
||||
Try!(stream.WriteStrUnsized(string));
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class TextureExporter : IAssetExporter
|
||||
/*
|
||||
|
||||
File Format:
|
||||
TextureType (1 byte)
|
||||
Texture Dimension (1 byte)
|
||||
Pixel Format (4 bytes)
|
||||
Width of larges mip-slice (4 bytes)
|
||||
Height of larges mip-slice (4 bytes)
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace GlitchyEditor.Assets.Importers;
|
||||
abstract class Config
|
||||
{
|
||||
[BonIgnore]
|
||||
protected bool _changed = true;
|
||||
protected bool _changed = false;
|
||||
|
||||
public bool Changed => _changed;
|
||||
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using GlitchyEngine.Content;
|
||||
using GlitchyEngine.Renderer;
|
||||
using Bon;
|
||||
using GlitchyEngine.Math;
|
||||
using System.IO;
|
||||
using Bon.Integrated;
|
||||
using GlitchyEngine;
|
||||
|
||||
namespace GlitchyEditor.Assets.Importers;
|
||||
|
||||
[BonTarget]
|
||||
class MaterialVariable
|
||||
{
|
||||
public ShaderVariableType ElementType;
|
||||
public int MatrixColumns;
|
||||
public int MatrixRows;
|
||||
public int ArrayElements;
|
||||
|
||||
public uint8[] RawData ~ delete _;
|
||||
|
||||
public this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public this(ShaderVariableType elementType, int matrixColumns, int matrixRows, int arrayElements)
|
||||
{
|
||||
ElementType = elementType;
|
||||
MatrixColumns = matrixColumns;
|
||||
MatrixRows = matrixRows;
|
||||
ArrayElements = arrayElements;
|
||||
|
||||
RawData = new uint8[elementType.ElementSizeInBytes() * matrixColumns * matrixRows * arrayElements];
|
||||
}
|
||||
|
||||
static this()
|
||||
{
|
||||
gBonEnv.typeHandlers.Add(typeof(Self),
|
||||
((.)new => VariableValueSerialize, (.)new => VariableValueDeserialize));
|
||||
}
|
||||
static void VariableValueSerialize(BonWriter writer, ValueView value, BonEnvironment env, SerializeValueState state)
|
||||
{
|
||||
/*Log.EngineLogger.Assert(value.type == typeof(Self));
|
||||
|
||||
let variableValue = value.Get<Self>();
|
||||
|
||||
writer.Type(variableValue)
|
||||
using (writer.ObjectBlock())
|
||||
{
|
||||
Serialize.Value(writer, nameof(MaterialFile.Effect), materialFile.Effect, env);
|
||||
Serialize.Value(writer, nameof(MaterialFile.Textures), materialFile.Textures, env);
|
||||
|
||||
|
||||
}*/
|
||||
}
|
||||
|
||||
static Result<void> VariableValueDeserialize(BonReader reader, ValueView val, BonEnvironment env, DeserializeValueState state)
|
||||
{
|
||||
MaterialVariable output = val.Get<MaterialVariable>();
|
||||
|
||||
StringView typeName = Try!(reader.EnumName());
|
||||
|
||||
// If we don't find a digit, we assume entire type name is element type and rows string ends up being an empty string.
|
||||
int firstDigitIndex = typeName.Length;
|
||||
|
||||
for (char8 c in typeName)
|
||||
{
|
||||
if (c.IsDigit)
|
||||
{
|
||||
firstDigitIndex = @c.Index;
|
||||
}
|
||||
}
|
||||
|
||||
Result<ShaderVariableType> elementTypeResult = Enum.Parse<ShaderVariableType>(typeName.Substring(0, firstDigitIndex), true);
|
||||
|
||||
if (elementTypeResult case .Err)
|
||||
{
|
||||
Deserialize.Error!("Unknown element type.", reader);
|
||||
}
|
||||
|
||||
int xIndex = typeName.LastIndexOf('x');
|
||||
|
||||
StringView rowsString = null;
|
||||
StringView columnsString = null;
|
||||
|
||||
if (xIndex != -1)
|
||||
{
|
||||
rowsString = typeName.Substring(firstDigitIndex ..< xIndex);
|
||||
columnsString = typeName.Substring(xIndex + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
rowsString = typeName.Substring(firstDigitIndex);
|
||||
}
|
||||
|
||||
Result<int, Int.ParseError> rowsResult = int.Parse(rowsString);
|
||||
|
||||
if (rowsResult case .Err(let error))
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case .NoValue:
|
||||
rowsResult = 1;
|
||||
case .Overflow:
|
||||
Deserialize.Error!("Integer overflow in row count.", reader);
|
||||
case .InvalidChar:
|
||||
Deserialize.Error!("Invalid character in row count.", reader);
|
||||
default:
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
|
||||
Result<int, Int.ParseError> columnsResult = int.Parse(columnsString);
|
||||
|
||||
if (columnsResult case .Err(let error))
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case .NoValue:
|
||||
columnsResult = 1;
|
||||
case .Overflow:
|
||||
Deserialize.Error!("Integer overflow in column count.", reader);
|
||||
case .InvalidChar:
|
||||
Deserialize.Error!("Invalid character in column count.", reader);
|
||||
default:
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Array
|
||||
/*if (reader.[Friend]Check('['))
|
||||
{
|
||||
|
||||
}*/
|
||||
|
||||
output.ElementType = elementTypeResult.Get();
|
||||
output.MatrixRows = rowsResult.Get();
|
||||
output.MatrixColumns = columnsResult.Get();
|
||||
output.ArrayElements = 1;
|
||||
|
||||
int elementSize = output.ElementType.ElementSizeInBytes();
|
||||
int elementCount = output.MatrixColumns * output.MatrixRows;
|
||||
|
||||
output.RawData = new uint8[elementSize * elementCount * output.ArrayElements];
|
||||
|
||||
Try!(reader.ObjectBlock());
|
||||
|
||||
for (int currentElementIndex < elementCount)
|
||||
{
|
||||
switch (output.ElementType)
|
||||
{
|
||||
case .Float:
|
||||
StringView valueString = Try!(reader.Floating());
|
||||
|
||||
float* rawFloats = (float*)output.RawData.Ptr;
|
||||
|
||||
rawFloats[currentElementIndex] = Try!(float.Parse(valueString));
|
||||
case .Bool:
|
||||
// TODO: I'm pretty sure this is wrong, bools in c buffers are usually 32 bit
|
||||
bool value = Try!(reader.Bool());
|
||||
((bool*)&output.RawData)[currentElementIndex] = value;
|
||||
case .Int:
|
||||
StringView valueString = Try!(reader.Integer());
|
||||
((int32*)&output.RawData)[currentElementIndex] = Try!(int32.Parse(valueString));
|
||||
case .UInt:
|
||||
StringView valueString = Try!(reader.Integer());
|
||||
((uint32*)&output.RawData)[currentElementIndex] = Try!(uint32.Parse(valueString));
|
||||
}
|
||||
|
||||
if (reader.ObjectHasMore())
|
||||
{
|
||||
Try!(reader.EntryEnd());
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.EngineLogger.Warning("Vector/Matrix doesn't contain enough elements.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Try!(reader.ObjectBlockEnd());
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
|
||||
[BonTarget]
|
||||
class NewMaterialFile : ImportedResource
|
||||
{
|
||||
public AssetHandle<Effect> Effect;
|
||||
|
||||
public Dictionary<String, AssetHandle<Texture>> Textures = new .() ~ DeleteDictionaryAndKeys!(_);
|
||||
public Dictionary<String, MaterialVariable> Constants = new .() ~ DeleteDictionaryAndKeysAndValues!(_);
|
||||
|
||||
public this(AssetIdentifier ownAssetIdentifier) : base(ownAssetIdentifier)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class MaterialImporter: IAssetImporter
|
||||
{
|
||||
private static readonly List<StringView> _fileExtensions = new .(){".mat"} ~ delete _;
|
||||
|
||||
public static List<StringView> FileExtensions => _fileExtensions;
|
||||
|
||||
public static Type ProcessedAssetType => typeof(NewMaterialFile);
|
||||
|
||||
public AssetImporterConfig CreateDefaultConfig()
|
||||
{
|
||||
return new AssetImporterConfig();
|
||||
}
|
||||
|
||||
public Result<ImportedResource> Import(StringView fullFileName, AssetIdentifier assetIdentifier, AssetConfig config)
|
||||
{
|
||||
NewMaterialFile material = new .(new AssetIdentifier(assetIdentifier.FullIdentifier));
|
||||
|
||||
defer
|
||||
{
|
||||
if (@return case .Err)
|
||||
{
|
||||
delete material;
|
||||
}
|
||||
}
|
||||
|
||||
String fullText = scope .();
|
||||
Try!(File.ReadAllText(fullFileName, fullText, true));
|
||||
|
||||
Try!(Bon.Deserialize<NewMaterialFile>(ref material, fullText));
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using Bon;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using GlitchyEngine.Content;
|
||||
using System.IO;
|
||||
|
||||
namespace GlitchyEditor.Assets.Importers;
|
||||
|
||||
class ImportedShader : ImportedResource
|
||||
{
|
||||
private String _hlslCode = new .() ~ delete _;
|
||||
|
||||
public StringView HlslCode
|
||||
{
|
||||
get => _hlslCode;
|
||||
}
|
||||
|
||||
public this(AssetIdentifier ownAssetIdentifier) : base(ownAssetIdentifier)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[BonTarget, BonPolyRegister]
|
||||
class ShaderImporterConfig : AssetImporterConfig
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class ShaderImporter : IAssetImporter
|
||||
{
|
||||
private static readonly List<StringView> _fileExtensions = new .(){".fx"} ~ delete _;
|
||||
|
||||
public static List<StringView> FileExtensions => _fileExtensions;
|
||||
|
||||
public static Type ProcessedAssetType => typeof(ImportedShader);
|
||||
|
||||
public AssetImporterConfig CreateDefaultConfig()
|
||||
{
|
||||
return new AssetImporterConfig();
|
||||
}
|
||||
|
||||
public Result<ImportedResource> Import(StringView fullFileName, AssetIdentifier assetIdentifier, AssetConfig config)
|
||||
{
|
||||
ImportedShader shader = new ImportedShader(new AssetIdentifier(assetIdentifier));
|
||||
|
||||
Try!(File.ReadAllText(fullFileName, shader.[Friend]_hlslCode, true));
|
||||
shader.[Friend]_hlslCode.Append('\n');
|
||||
|
||||
return shader;
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,7 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
|
||||
{
|
||||
if (previewType.Get<String>().Equals("Color", .InvariantCultureIgnoreCase))
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(variable.Type == .Float && variable.Rows == 1);
|
||||
Log.EngineLogger.AssertDebug(variable.ElementType == .Float && variable.Rows == 1);
|
||||
|
||||
if (variable.Columns == 3)
|
||||
{
|
||||
@@ -173,7 +173,7 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
|
||||
}
|
||||
else if (previewType.Get<String>().Equals("ColorHDR", .InvariantCultureIgnoreCase))
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(variable.Type == .Float && variable.Rows == 1);
|
||||
Log.EngineLogger.AssertDebug(variable.ElementType == .Float && variable.Rows == 1);
|
||||
|
||||
if (variable.Columns == 3)
|
||||
{
|
||||
@@ -203,7 +203,7 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
|
||||
}
|
||||
//ImGui.ColorEdit3("", null, .HDR | .Float)
|
||||
|
||||
else if (variable.Type == .Float && variable.Rows == 1)
|
||||
else if (variable.ElementType == .Float && variable.Rows == 1)
|
||||
{
|
||||
bool hasMin = TryGetValue(arguments, "Min", var min);
|
||||
bool hasMax = TryGetValue(arguments, "Max", var max);
|
||||
@@ -255,311 +255,3 @@ class MaterialAssetPropertiesEditor : AssetPropertiesEditor
|
||||
return new Self(assetFile);
|
||||
}
|
||||
}
|
||||
|
||||
[BonTarget, BonPolyRegister]
|
||||
class MaterialAssetLoaderConfig : AssetLoaderConfig
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[BonTarget]
|
||||
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 Float2(float2 Value);
|
||||
case Float3(float3 Value);
|
||||
case Float4(float4 Value);
|
||||
case ColorRGB(ColorRGB Value);
|
||||
case ColorRGBA(ColorRGBA Value);
|
||||
case None;
|
||||
|
||||
/*static this()
|
||||
{
|
||||
gBonEnv.typeHandlers.Add(typeof(Self),
|
||||
((.)new => VariableValueSerialize, (.)new => VariableValueDeserialize));
|
||||
}
|
||||
|
||||
static void VariableValueSerialize(BonWriter writer, ValueView value, BonEnvironment env)
|
||||
{
|
||||
Log.EngineLogger.Assert(value.type == typeof(Self));
|
||||
|
||||
let variableValue = value.Get<Self>();
|
||||
|
||||
writer.Type(variableValue)
|
||||
using (writer.ObjectBlock())
|
||||
{
|
||||
Serialize.Value(writer, nameof(MaterialFile.Effect), materialFile.Effect, env);
|
||||
Serialize.Value(writer, nameof(MaterialFile.Textures), materialFile.Textures, env);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static Result<void> VariableValueDeserialize(BonReader reader, ValueView val, BonEnvironment env)
|
||||
{
|
||||
return .Ok;
|
||||
}*/
|
||||
}
|
||||
|
||||
[BonTarget]
|
||||
class MaterialFile
|
||||
{
|
||||
public String Effect ~ delete _;
|
||||
|
||||
//public Dictionary<String, String> Textures ~ DeleteDictionaryAndKeysAndValues!(_);
|
||||
public Dictionary<String, AssetHandle<Texture>> Textures ~ DeleteDictionaryAndKeys!(_);
|
||||
public Dictionary<String, VariableValue> Variables ~
|
||||
{
|
||||
if (_ != null)
|
||||
{
|
||||
for (var entry in _)
|
||||
{
|
||||
delete entry.key;
|
||||
//delete entry.value;
|
||||
/*if (entry.value.HasValue)
|
||||
entry.value->Dispose();*/
|
||||
}
|
||||
|
||||
delete _;
|
||||
}
|
||||
};
|
||||
|
||||
/*static this()
|
||||
{
|
||||
gBonEnv.typeHandlers.Add(typeof(Self),
|
||||
((.)new => MaterialSerialize, (.)new => MaterialDeserialize));
|
||||
}
|
||||
|
||||
static void MaterialSerialize(BonWriter writer, ValueView value, BonEnvironment env)
|
||||
{
|
||||
Log.EngineLogger.Assert(value.type == typeof(Self));
|
||||
|
||||
let materialFile = value.Get<Self>();
|
||||
|
||||
using (writer.ObjectBlock())
|
||||
{
|
||||
Serialize.Value(writer, nameof(MaterialFile.Effect), materialFile.Effect, env);
|
||||
Serialize.Value(writer, nameof(MaterialFile.Textures), materialFile.Textures, env);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void SerializeVariablesDictionary(BonWriter writer, MaterialFile materialFile, BonEnvironment env)
|
||||
{
|
||||
using (writer.ArrayBlock())
|
||||
{
|
||||
for (let (name, value) in materialFile.Variables)
|
||||
{
|
||||
let keyVal = ValueView(typeof(String), name);
|
||||
Serialize.Value(writer, keyVal, env);
|
||||
writer.Pair();
|
||||
|
||||
ValueView valueVal;// = ValueView(, entriesPtr + (currentIndex * entryStride) + entryValueOffset);
|
||||
switch(value.GetType())
|
||||
{
|
||||
case typeof(ColorRGBA):
|
||||
writer.Identifier("ColorRGBA");
|
||||
default:
|
||||
|
||||
}
|
||||
|
||||
Serialize.Value(writer, valueVal, env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Result<void> MaterialDeserialize(BonReader reader, ValueView val, BonEnvironment env)
|
||||
{
|
||||
return .Ok;
|
||||
}*/
|
||||
}
|
||||
|
||||
class MaterialAssetLoader : IAssetLoader, IAssetSaver //, IReloadingAssetLoader
|
||||
{
|
||||
private static readonly List<StringView> _fileExtensions = new .(){".mat"} ~ delete _;
|
||||
|
||||
public static List<StringView> FileExtensions => _fileExtensions;
|
||||
|
||||
public AssetLoaderConfig GetDefaultConfig()
|
||||
{
|
||||
return new ModelAssetLoaderConfig();
|
||||
}
|
||||
|
||||
public Asset LoadAsset(Stream file, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager)
|
||||
{
|
||||
StreamReader reader = scope .(file);
|
||||
|
||||
String text = scope .();
|
||||
|
||||
reader.ReadToEnd(text);
|
||||
|
||||
MaterialFile materialFile = scope .();
|
||||
|
||||
var result = Bon.Deserialize<MaterialFile>(ref materialFile, text);
|
||||
|
||||
if (result case .Err)
|
||||
{
|
||||
Log.EngineLogger.Error("Failed to load material.");
|
||||
Debug.SafeBreak();
|
||||
return null;
|
||||
}
|
||||
|
||||
Effect fx = Content.GetAsset<Effect>(contentManager.LoadAsset(materialFile.Effect, true), contentManager);
|
||||
|
||||
Material material = new Material(fx);
|
||||
|
||||
for (let (slotName, textureHandle) in materialFile.Textures)
|
||||
{
|
||||
AssetHandle<Texture> texture = contentManager.LoadAsset(textureHandle);
|
||||
|
||||
if (texture.IsInvalid)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to load texture \"{textureHandle}\".");
|
||||
}
|
||||
|
||||
material.SetTexture(slotName, texture);
|
||||
}
|
||||
|
||||
for (let (slotName, variableValue) in materialFile.Variables)
|
||||
{
|
||||
switch (variableValue)
|
||||
{
|
||||
case .ColorRGBA(let value):
|
||||
material.SetVariable(slotName, value);
|
||||
case .ColorRGB(let value):
|
||||
material.SetVariable(slotName, value);
|
||||
case .Float(let value):
|
||||
material.SetVariable(slotName, value);
|
||||
case .Float2(let value):
|
||||
material.SetVariable(slotName, value);
|
||||
case .Float3(let value):
|
||||
material.SetVariable(slotName, value);
|
||||
case .Float4(let value):
|
||||
material.SetVariable(slotName, value);
|
||||
case .None:
|
||||
default:
|
||||
Log.EngineLogger.Error($"Unknown variable type of variable {slotName}: {variableValue}");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
public Result<void> EditorSaveAsset(Stream file, Asset asset, AssetLoaderConfig config, StringView assetIdentifier, StringView? subAsset, IContentManager contentManager)
|
||||
{
|
||||
Material material = asset as Material;
|
||||
|
||||
if (material == null)
|
||||
{
|
||||
Log.EngineLogger.Error("Asset must be a Material!");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
MaterialFile materialFile = scope .();
|
||||
|
||||
materialFile.Effect = new String(material.Effect?.Identifier ?? "");
|
||||
materialFile.Textures = new .();
|
||||
materialFile.Variables = new .();
|
||||
|
||||
for (let (slotName, texture) in material.[Friend]_textures)
|
||||
{
|
||||
materialFile.Textures.Add(new String(slotName), texture.Handle);
|
||||
}
|
||||
|
||||
Effect effect = material.Effect;
|
||||
|
||||
if (effect != null)
|
||||
{
|
||||
for (let (name, arguments) in effect.[Friend]_variableDescriptions)
|
||||
{
|
||||
VariableValue variableValue = .None;
|
||||
|
||||
let variable = effect.Variables[name];
|
||||
|
||||
bool hasPreviewType = MaterialAssetPropertiesEditor.TryGetValue(arguments, "Type", var previewType);
|
||||
|
||||
if (hasPreviewType && previewType.Get<String>() == "Color")
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(variable.Type == .Float && variable.Rows == 1);
|
||||
|
||||
if (variable.Columns == 3)
|
||||
{
|
||||
material.GetVariable<ColorRGB>(variable.Name, var value);
|
||||
|
||||
value = ColorRGB.LinearToSRGB((ColorRGB)value);
|
||||
|
||||
//variantValue = new box value;
|
||||
variableValue = .ColorRGB(value);
|
||||
}
|
||||
else if (variable.Columns == 4)
|
||||
{
|
||||
material.GetVariable<ColorRGBA>(variable.Name, var value);
|
||||
|
||||
value = ColorRGBA.LinearToSRGB((ColorRGBA)value);
|
||||
|
||||
variableValue = .ColorRGBA(value);
|
||||
}
|
||||
}
|
||||
else if (variable.Type == .Float && variable.Rows == 1)
|
||||
{
|
||||
switch (variable.Columns)
|
||||
{
|
||||
case 1:
|
||||
material.GetVariable<float>(variable.Name, let value);
|
||||
variableValue = .Float(value);
|
||||
case 2:
|
||||
material.GetVariable<float2>(variable.Name, let value);
|
||||
variableValue = .Float2(value);
|
||||
case 3:
|
||||
material.GetVariable<float3>(variable.Name, let value);
|
||||
variableValue = .Float3(value);
|
||||
case 4:
|
||||
material.GetVariable<float4>(variable.Name, let value);
|
||||
variableValue = .Float4(value);
|
||||
}
|
||||
}
|
||||
|
||||
materialFile.Variables.Add(new String(name), variableValue);
|
||||
}
|
||||
}
|
||||
|
||||
String text = scope .();
|
||||
|
||||
gBonEnv.serializeFlags |= .IncludeDefault | .Verbose;
|
||||
|
||||
Bon.Serialize<MaterialFile>(materialFile, text);
|
||||
|
||||
StreamWriter writer = scope .(file, .UTF8, 1024);
|
||||
writer.Write(text);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
Material _placeholder;
|
||||
Material _error;
|
||||
|
||||
public Asset GetPlaceholderAsset(Type assetType)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public Asset GetErrorAsset(Type assetType)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using GlitchyEditor.Assets.Importers;
|
||||
using GlitchyEngine.Renderer;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEngine.Content;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace GlitchyEditor.Assets.Processors;
|
||||
|
||||
class ProcessedMaterial : ProcessedResource
|
||||
{
|
||||
public AssetHandle EffectHandle;
|
||||
|
||||
private Dictionary<String, uint8[]> _bufferData = new .() ~ DeleteDictionaryAndKeysAndValues!(_);
|
||||
private Dictionary<String, AssetHandle> _textures = new .() ~ DeleteDictionaryAndKeys!(_);
|
||||
|
||||
public override AssetType AssetType => .Material;
|
||||
|
||||
public Dictionary<String, uint8[]> Buffers => _bufferData;
|
||||
public Dictionary<String, AssetHandle> Textures => _textures;
|
||||
|
||||
public this(AssetIdentifier ownAssetIdentifier, AssetHandle assetHandle) : base(ownAssetIdentifier, assetHandle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Span<uint8> CloneBuffer(String name, ConstantBuffer cBuffer)
|
||||
{
|
||||
String copyName = new String(name);
|
||||
uint8[] data = new uint8[cBuffer.RawData.Length];
|
||||
cBuffer.RawData.CopyTo(data);
|
||||
|
||||
_bufferData.Add(copyName, data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public void AddTexture(StringView name, AssetHandle textureHandle)
|
||||
{
|
||||
String copyName = new String(name);
|
||||
_textures.Add(copyName, textureHandle);
|
||||
}
|
||||
}
|
||||
|
||||
class MaterialProcessor : IAssetProcessor
|
||||
{
|
||||
public AssetProcessorConfig CreateDefaultConfig()
|
||||
{
|
||||
return new AssetProcessorConfig();
|
||||
}
|
||||
|
||||
public static Type ProcessedAssetType => typeof(NewMaterialFile);
|
||||
|
||||
public Result<void> Process(ImportedResource importedResource, AssetConfig config, List<ProcessedResource> outProcessedResources)
|
||||
{
|
||||
NewMaterialFile importedMaterial = importedResource as NewMaterialFile;
|
||||
if (importedMaterial == null)
|
||||
{
|
||||
Log.EngineLogger.Error($"Expected type {nameof(NewMaterialFile)} but received {importedResource.GetType()} instead.");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
Effect effect = Content.GetAsset<Effect>(importedMaterial.Effect, blocking: true);
|
||||
|
||||
if (effect == null)
|
||||
// TODO: Don't error out, use the error-effect
|
||||
return .Err;
|
||||
|
||||
ProcessedMaterial processedMaterial = new .(new AssetIdentifier(importedMaterial.AssetIdentifier), config.AssetHandle);
|
||||
|
||||
processedMaterial.EffectHandle = importedMaterial.Effect;
|
||||
|
||||
for (let buffer in effect.Buffers)
|
||||
{
|
||||
// TODO: Skip engine buffers
|
||||
|
||||
if (buffer.Buffer == null)
|
||||
continue;
|
||||
|
||||
ConstantBuffer cBuffer = buffer.Buffer as ConstantBuffer;
|
||||
|
||||
if (cBuffer == null)
|
||||
{
|
||||
Log.EngineLogger.Error("The buffers of a material must be ConstantBuffers.");
|
||||
continue;
|
||||
// TODO: Why even allow any other kind of Buffer for Materials? Will this make sense later if we can create and bind Buffers from C#?
|
||||
}
|
||||
|
||||
Span<uint8> data = processedMaterial.CloneBuffer(buffer.Name, cBuffer);
|
||||
|
||||
for (BufferVariable effectVariable in cBuffer.Variables)
|
||||
{
|
||||
if (importedMaterial.Constants.TryGetValue(effectVariable.Name, let materialValue))
|
||||
{
|
||||
if (effectVariable.ElementType != materialValue.ElementType)
|
||||
{
|
||||
Log.EngineLogger.Error("Element type doesn't match between material and effect.");
|
||||
continue;
|
||||
}
|
||||
|
||||
int effectVariableSize = effectVariable.Rows * effectVariable.Columns * Math.Max(1, effectVariable.ArrayElements) * effectVariable.ElementType.ElementSizeInBytes();
|
||||
|
||||
if (effectVariableSize != materialValue.RawData.Count)
|
||||
{
|
||||
Log.EngineLogger.Warning("Warning, matrix size in matrix and effect doesn't match. Truncating value.");
|
||||
}
|
||||
|
||||
int bytesToCopy = Math.Min(effectVariableSize, materialValue.RawData.Count);
|
||||
Debug.Assert(effectVariable.Offset + bytesToCopy <= data.Length, "Material value goes out of bounds ");
|
||||
|
||||
Internal.MemCpy(data.Ptr + effectVariable.Offset, materialValue.RawData.Ptr, bytesToCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let (textureName, textureBinding) in effect.Textures)
|
||||
{
|
||||
if (importedMaterial.Textures.TryGetValue(textureName, let textureHandle))
|
||||
{
|
||||
processedMaterial.AddTexture(textureName, textureHandle);
|
||||
}
|
||||
}
|
||||
|
||||
outProcessedResources.Add(processedMaterial);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEngine.Math;
|
||||
|
||||
namespace GlitchyEditor.Assets.Processors;
|
||||
|
||||
static class ShaderCodePreprocessor
|
||||
{
|
||||
public static Result<void> ProcessFileContent(String fileContent, String outVsName, String outPsName,
|
||||
Dictionary<StringView, ShaderVariable> outVarDescs,
|
||||
List<String> outBufferNames, Dictionary<StringView, StringView> outEngineBuffers)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
Dictionary<StringView, StringView> arguments = scope .();
|
||||
|
||||
int index = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if ((int Start, int End) value = GetNextPreprocessor(fileContent, index, let name, arguments..Clear()))
|
||||
{
|
||||
index = value.End;
|
||||
|
||||
switch(name)
|
||||
{
|
||||
case "Effect":
|
||||
for (let (argName, argValue) in arguments)
|
||||
{
|
||||
switch(argName)
|
||||
{
|
||||
case "VS", "VertexShader":
|
||||
outVsName.Append(argValue);
|
||||
case "PS", "PixelShader":
|
||||
outPsName.Append(argValue);
|
||||
default:
|
||||
Log.EngineLogger.Error($"Unknown parameter name \"{name}\".");
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
case "EditorVariable":
|
||||
Try!(ProcessEditorVariables(arguments, outVarDescs));
|
||||
case "EngineBuffer":
|
||||
Try!(ProcessEngineBuffer(arguments, outBufferNames, outEngineBuffers));
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
CommentLine(fileContent, value.Start);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static void CommentLine(StringView code, int commentPosition)
|
||||
{
|
||||
code[commentPosition] = '/';
|
||||
code[commentPosition + 1] = '/';
|
||||
}
|
||||
|
||||
private static Result<(int Start, int End)> GetNextPreprocessor(StringView code, int startindex, out StringView name, Dictionary<StringView, StringView> arguments)
|
||||
{
|
||||
var startindex;
|
||||
|
||||
name = .();
|
||||
|
||||
int startOfLine = -1;
|
||||
int startOfPragma = -1;
|
||||
int endOfLine = startindex;
|
||||
do
|
||||
{
|
||||
repeat
|
||||
{
|
||||
startindex = endOfLine;
|
||||
|
||||
startOfPragma = code.IndexOf("#pragma", startindex);
|
||||
|
||||
if (startOfPragma == -1)
|
||||
return .Err;
|
||||
|
||||
endOfLine = code.IndexOf('\n', startOfPragma);
|
||||
|
||||
startOfLine = code[...startOfPragma].LastIndexOf('\n');
|
||||
|
||||
// If the line is commented out, look for the next
|
||||
} while (startOfLine != -1 && code[startOfLine...startOfPragma].Contains("//"));
|
||||
|
||||
StringView line = (endOfLine != -1) ? code.Substring(startOfPragma, endOfLine - startOfPragma) : code.Substring(startOfPragma);
|
||||
|
||||
// cut off the #pragma
|
||||
line = line.Substring(7);
|
||||
|
||||
int lBracketIndex = line.IndexOf('[');
|
||||
|
||||
if (lBracketIndex == -1)
|
||||
{
|
||||
name = line..Trim();
|
||||
break;
|
||||
}
|
||||
|
||||
name = line.Substring(0, lBracketIndex);
|
||||
name.Trim();
|
||||
|
||||
int rBracketIndex = line.IndexOf(']');
|
||||
|
||||
if (rBracketIndex == -1)
|
||||
{
|
||||
Log.EngineLogger.Error($"Pragma is missing closing Bracket (\"{line}\")");
|
||||
rBracketIndex = line.Length;
|
||||
}
|
||||
|
||||
StringView argumentText = line.Substring(lBracketIndex + 1, rBracketIndex - lBracketIndex - 1);
|
||||
|
||||
for (StringView argument in argumentText.Split(';'))
|
||||
{
|
||||
int equalsIndex = argument.IndexOf('=');
|
||||
|
||||
StringView argumentName = .();
|
||||
StringView argumentValue = .();
|
||||
|
||||
if (equalsIndex == -1)
|
||||
{
|
||||
argumentName = argument;
|
||||
argumentName.Trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
argumentName = argument.Substring(0, equalsIndex);
|
||||
argumentName.Trim();
|
||||
|
||||
argumentValue = argument.Substring(equalsIndex + 1);
|
||||
argumentValue.Trim();
|
||||
}
|
||||
|
||||
if (arguments.ContainsKey(argumentName))
|
||||
{
|
||||
Log.EngineLogger.Error($"Arguments \"{argumentName}\" already exists.");
|
||||
continue;
|
||||
}
|
||||
|
||||
arguments.Add(argumentName, argumentValue);
|
||||
}
|
||||
}
|
||||
|
||||
return .Ok((startOfPragma, endOfLine));
|
||||
}
|
||||
|
||||
private static Result<void> ProcessEngineBuffer(Dictionary<StringView, StringView> arguments,
|
||||
List<String> outBufferNames, Dictionary<StringView, StringView> outEngineBuffers)
|
||||
{
|
||||
String nameInEngine = null;
|
||||
String nameInShader = null;
|
||||
|
||||
defer
|
||||
{
|
||||
if (@return case .Err)
|
||||
{
|
||||
delete nameInEngine;
|
||||
delete nameInShader;
|
||||
}
|
||||
}
|
||||
|
||||
for (var (argName, argValue) in arguments)
|
||||
{
|
||||
if (argValue.StartsWith('"') && argValue.EndsWith('"'))
|
||||
{
|
||||
argValue = argValue[1...^2];
|
||||
}
|
||||
switch (argName)
|
||||
{
|
||||
case "Name":
|
||||
nameInShader = new String(argValue);
|
||||
case "Binding":
|
||||
nameInEngine = new String(argValue);
|
||||
default:
|
||||
Log.EngineLogger.Error($"Unknown parameter for EngineBuffer: \"{argName}\".");
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
|
||||
if (String.IsNullOrWhiteSpace(nameInEngine) || String.IsNullOrWhiteSpace(nameInShader))
|
||||
{
|
||||
Log.EngineLogger.Error($"Name and Binding need to be defined.");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
outBufferNames.Add(nameInShader);
|
||||
outBufferNames.Add(nameInEngine);
|
||||
outEngineBuffers.Add(nameInShader, nameInEngine);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<void> ProcessEditorVariables(Dictionary<StringView, StringView> arguments, Dictionary<StringView, ShaderVariable> outVarDescs)
|
||||
{
|
||||
ShaderVariable variable = new .();
|
||||
|
||||
defer
|
||||
{
|
||||
if (@return case .Err)
|
||||
delete variable;
|
||||
}
|
||||
|
||||
for (var (name, value) in arguments)
|
||||
{
|
||||
if (value.StartsWith('"') && value.EndsWith('"'))
|
||||
{
|
||||
value = value[1...^2];
|
||||
}
|
||||
|
||||
switch(name)
|
||||
{
|
||||
case "Name":
|
||||
variable.Name = value;
|
||||
case "Preview":
|
||||
variable.PreviewName = value;
|
||||
case "Type":
|
||||
variable.EditorType = value;
|
||||
case "Min":
|
||||
variable.MinValue = Try!(ParseVariableValue(value));
|
||||
case "Max":
|
||||
variable.MaxValue = Try!(ParseVariableValue(value));
|
||||
default:
|
||||
Variant paramValue = Variant.Create(new String(value), true);
|
||||
variable.AddParameter(name, paramValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (variable.Name.IsWhiteSpace)
|
||||
{
|
||||
Log.EngineLogger.Error("Failed to process shader: Missing argument \"Name\" int variable description.");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
outVarDescs.Add(variable.Name, variable);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<Variant> ParseVariableValue(StringView valueString)
|
||||
{
|
||||
// TODO: Simply always use double?
|
||||
|
||||
if (valueString[0].IsDigit || valueString[0] == '-')
|
||||
{
|
||||
var valueString;
|
||||
|
||||
if (valueString.EndsWith('f'))
|
||||
valueString.Length--;
|
||||
|
||||
float value = Try!(float.Parse(valueString));
|
||||
|
||||
return Variant.Create(value);
|
||||
}
|
||||
else if (valueString.StartsWith("float"))
|
||||
{
|
||||
int index = 5;
|
||||
|
||||
int numComponents = valueString[index++] - '0';
|
||||
|
||||
while (valueString[index] != '(')
|
||||
{
|
||||
if (!valueString[index].IsWhiteSpace)
|
||||
{
|
||||
Log.EngineLogger.Error("Failed to process shader: Expected '('.");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if (numComponents < 2 || numComponents > 4)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to process shader: Unsupported component count {numComponents}. Value must be between 2 and 4.");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
float[] floats = scope float[numComponents];
|
||||
|
||||
for (int i < numComponents)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
char8 c = valueString[++index];
|
||||
|
||||
if (c.IsDigit || c == '.' || c == '-')
|
||||
break;
|
||||
}
|
||||
|
||||
int start = index;
|
||||
|
||||
while (true)
|
||||
{
|
||||
char8 c = valueString[++index];
|
||||
|
||||
if (!c.IsDigit && c != '.')
|
||||
break;
|
||||
}
|
||||
|
||||
int end = index;
|
||||
|
||||
StringView numberView = .(valueString, start, end - start);
|
||||
|
||||
var result = float.Parse(numberView);
|
||||
|
||||
if (result case .Ok(let value))
|
||||
{
|
||||
floats[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (numComponents == 2)
|
||||
return Variant.Create(*(float2*)floats.Ptr);
|
||||
else if (numComponents == 3)
|
||||
return Variant.Create(*(float3*)floats.Ptr);
|
||||
else
|
||||
return Variant.Create(*(float4*)floats.Ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to process shader: Unsupported variable value: \"{valueString}\".");
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,556 @@
|
||||
using System;
|
||||
using DirectX.Common;
|
||||
using GlitchyEngine;
|
||||
using System.IO;
|
||||
using DirectX.D3DCompiler;
|
||||
using GlitchyEngine.Renderer;
|
||||
using DirectX.D3D11Shader;
|
||||
using System.Collections;
|
||||
using GlitchyEngine.Content;
|
||||
|
||||
namespace GlitchyEditor.Assets.Processors;
|
||||
|
||||
using internal GlitchyEditor.Assets.Processors;
|
||||
|
||||
class ShaderDefineValue
|
||||
{
|
||||
private String _name ~ delete _;
|
||||
private String _definition ~ delete _;
|
||||
|
||||
public StringView Name
|
||||
{
|
||||
get => _name;
|
||||
set => String.NewOrSet!(_name, value);
|
||||
}
|
||||
|
||||
public StringView Definition
|
||||
{
|
||||
get => _definition;
|
||||
set => String.NewOrSet!(_definition, value);
|
||||
}
|
||||
}
|
||||
|
||||
class CompiledShader
|
||||
{
|
||||
private Dictionary<StringView, ReflectedConstantBuffer> _buffers = new .() ~ DeleteDictionaryAndValues!(_);
|
||||
private Dictionary<StringView, ReflectedTexture> _textures = new .() ~ DeleteDictionaryAndValues!(_);
|
||||
|
||||
public Dictionary<StringView, ReflectedConstantBuffer> ConstantBuffers => _buffers;
|
||||
public Dictionary<StringView, ReflectedTexture> Textures => _textures;
|
||||
|
||||
public extern Span<uint8> Blob {get;}
|
||||
|
||||
public void AddConstantBuffer(ReflectedConstantBuffer buffer)
|
||||
{
|
||||
_buffers.Add(buffer.Name, buffer);
|
||||
}
|
||||
|
||||
public void AddTexture(ReflectedTexture texture)
|
||||
{
|
||||
_textures.Add(texture.Name, texture);
|
||||
}
|
||||
}
|
||||
|
||||
class ReflectedConstantBuffer
|
||||
{
|
||||
private String _name ~ delete _;
|
||||
private String _engineBufferName ~ delete _;
|
||||
|
||||
public int Size;
|
||||
|
||||
public int BindPoint;
|
||||
|
||||
public StringView Name
|
||||
{
|
||||
get => _name;
|
||||
set => String.NewOrSet!(_name, value);
|
||||
}
|
||||
|
||||
public StringView EngineBufferName
|
||||
{
|
||||
get => _engineBufferName;
|
||||
set => String.NewOrSet!(_engineBufferName, value);
|
||||
}
|
||||
|
||||
private Dictionary<StringView, ReflectedConstantBufferVariable> _variables = new .() ~ DeleteDictionaryAndValues!(_);
|
||||
|
||||
public Dictionary<StringView, ReflectedConstantBufferVariable> Variables => _variables;
|
||||
|
||||
public void AddVariable(ReflectedConstantBufferVariable vaiable)
|
||||
{
|
||||
_variables.Add(vaiable.Name, vaiable);
|
||||
}
|
||||
|
||||
public uint8[] RawData ~ delete _;
|
||||
}
|
||||
|
||||
class ReflectedTexture
|
||||
{
|
||||
private String _name ~ delete _;
|
||||
|
||||
public uint32 BindPoint;
|
||||
|
||||
public TextureDimension TextureDimension;
|
||||
|
||||
public StringView Name
|
||||
{
|
||||
get => _name;
|
||||
set => String.NewOrSet!(_name, value);
|
||||
}
|
||||
|
||||
public this(StringView name, uint32 bindPoint, TextureDimension textureDimension)
|
||||
{
|
||||
Name = name;
|
||||
BindPoint = bindPoint;
|
||||
TextureDimension = textureDimension;
|
||||
}
|
||||
}
|
||||
|
||||
class ReflectedConstantBufferVariable
|
||||
{
|
||||
private String _name ~ delete _;
|
||||
private String _previewName ~ delete _;
|
||||
private String _editorType ~ delete _;
|
||||
|
||||
public StringView Name
|
||||
{
|
||||
get => _name;
|
||||
set => String.NewOrSet!(_name, value);
|
||||
}
|
||||
|
||||
public StringView PreviewName
|
||||
{
|
||||
get => _previewName;
|
||||
set => String.NewOrSet!(_previewName, value);
|
||||
}
|
||||
|
||||
public StringView EditorType
|
||||
{
|
||||
get => _editorType;
|
||||
set => String.NewOrSet!(_editorType, value);
|
||||
}
|
||||
|
||||
public int Offset;
|
||||
public int SizeInBytes;
|
||||
public bool IsUsed;
|
||||
|
||||
public GlitchyEngine.Renderer.ShaderVariableType ElementType;
|
||||
public int Rows;
|
||||
public int Columns;
|
||||
|
||||
public int ArraySize;
|
||||
|
||||
public Variant MinValue;
|
||||
public Variant MaxValue;
|
||||
}
|
||||
|
||||
// TODO: Dx11
|
||||
extension CompiledShader
|
||||
{
|
||||
internal ID3DBlob* _shaderBlob;
|
||||
|
||||
public override Span<uint8> Blob => Span<uint8>((uint8*)_shaderBlob.GetBufferPointer(), (int)_shaderBlob.GetBufferSize());
|
||||
}
|
||||
|
||||
class ShaderCompiler
|
||||
{
|
||||
public static Result<CompiledShader> CompileAndReflectShader(StringView code, AssetIdentifier assetIdentifier, StringView entryPoint, StringView compileTarget, Span<ShaderDefineValue> defines)
|
||||
{
|
||||
defer {
|
||||
if (@return case .Err)
|
||||
delete shader;
|
||||
}
|
||||
|
||||
CompiledShader shader = new CompiledShader();
|
||||
|
||||
ShaderCompilationContext context = scope .();
|
||||
|
||||
Try!(PlatformCompileShaderFromSource(code, assetIdentifier, entryPoint, compileTarget, defines, shader, context));
|
||||
|
||||
Try!(PlatformReflectShader(shader, context));
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
protected static extern Result<void> PlatformCompileShaderFromSource(StringView code, AssetIdentifier fileName, StringView entryPoint, StringView compileTarget, Span<ShaderDefineValue> defines, CompiledShader outShader, ShaderCompilationContext context);
|
||||
|
||||
protected static extern Result<void> PlatformReflectShader(CompiledShader shader, ShaderCompilationContext context);
|
||||
}
|
||||
|
||||
class ShaderCompilationContext
|
||||
{
|
||||
public append String VsName = String();
|
||||
public append String PsName = String();
|
||||
|
||||
public append Dictionary<StringView, ShaderVariable> Variables = .() ~ ClearDictionaryAndDeleteValues!(_);
|
||||
public append List<String> BufferNames = .() ~ ClearAndDeleteItems!(_);
|
||||
// Name in Shader -> Name in Engine
|
||||
public append Dictionary<StringView, StringView> EngineBuffers = .();
|
||||
}
|
||||
|
||||
// TODO: Dx11
|
||||
extension ShaderCompiler
|
||||
{
|
||||
// TODO: This should be a setting per shader, really!
|
||||
protected const ShaderCompileFlags DefaultCompileFlags = .EnableStrictness |
|
||||
#if DEBUG
|
||||
.Debug;
|
||||
#else
|
||||
.OptimizationLevel3;
|
||||
#endif
|
||||
|
||||
protected static override Result<void> PlatformCompileShaderFromSource(StringView code, AssetIdentifier fileName, StringView entryPoint, StringView compileTarget, Span<ShaderDefineValue> defines, CompiledShader outShader, ShaderCompilationContext context)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
Log.EngineLogger.AssertDebug(outShader._shaderBlob == null);
|
||||
|
||||
ShaderMacro* nativeMacros = defines.Length == 0 ? null : new:ScopedAlloc! ShaderMacro[defines.Length]*;
|
||||
|
||||
for(int i < defines.Length)
|
||||
{
|
||||
nativeMacros[i].Name = defines[i].Name.ToScopedNativeWChar!::();
|
||||
nativeMacros[i].Definition = defines[i].Definition.ToScopedNativeWChar!::();
|
||||
}
|
||||
|
||||
// Todo: sourceName, includes,
|
||||
// Todo: variable shader target?
|
||||
|
||||
ID3DBlob* errorBlob = null;
|
||||
|
||||
String directory = scope .();
|
||||
|
||||
Path.GetDirectoryPath(fileName, directory);
|
||||
|
||||
String shaderCode = new:ScopedAlloc! String(code);
|
||||
|
||||
Try!(ShaderCodePreprocessor.ProcessFileContent(shaderCode, context.VsName, context.PsName, context.Variables, context.BufferNames, context.EngineBuffers));
|
||||
|
||||
using (let includer = PreprocessingIncluder(directory, context))
|
||||
{
|
||||
var result = D3DCompiler.D3DCompile(code.Ptr, (.)code.Length, fileName.FullIdentifier.ToScopeCStr!(), nativeMacros, &includer, entryPoint.ToScopeCStr!(),
|
||||
compileTarget.ToScopeCStr!(), DefaultCompileFlags /* Pass down compile flags? */, .None, &outShader._shaderBlob, &errorBlob);
|
||||
|
||||
if(result.Failed)
|
||||
{
|
||||
StringView str = StringView((char8*)errorBlob.GetBufferPointer(), (int)errorBlob.GetBufferSize());
|
||||
Log.EngineLogger.Error($"Failed to compile Shader: Error Code({(int)result}): {result} | Error Message: {str}");
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
|
||||
Log.EngineLogger.Assert(outShader._shaderBlob != null, "Shader compilation failed.");
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
protected override static Result<void> PlatformReflectShader(CompiledShader shader, ShaderCompilationContext context)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
ID3D11ShaderReflection* reflection = null;
|
||||
defer { reflection?.Release(); }
|
||||
|
||||
var reflectionResult = D3DCompiler.D3DReflect(shader._shaderBlob.GetBufferPointer(), shader._shaderBlob.GetBufferSize(), &reflection);
|
||||
if (reflectionResult.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to reflect shader: ({(int)reflectionResult}) {reflectionResult}");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
var getDescResult = reflection.GetDescription(let desc);
|
||||
if (getDescResult.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"GetDescription failed: ({(int)getDescResult}) {getDescResult}");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
uint32 resourceCount = desc.BoundResources;
|
||||
for (uint32 i < resourceCount)
|
||||
{
|
||||
var res = reflection.GetResourceBindingDescription(i, let bindDesc);
|
||||
|
||||
if (res.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Error({(int)res}) {res}: Failed to get resource binding desc for resource {i}");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
switch(bindDesc.Type)
|
||||
{
|
||||
case .ConstantBuffer:
|
||||
//var bufferReflection = reflection.GetConstantBufferByName(bindDesc.Name);
|
||||
var bufferReflection = reflection.GetConstantBufferByName(bindDesc.Name);
|
||||
|
||||
bufferReflection.GetDescription(let bufferDesc);
|
||||
|
||||
// ConstantBuffer
|
||||
if(bufferDesc.Type == .D3D11_CT_CBUFFER)
|
||||
{
|
||||
ReflectedConstantBuffer cbuffer = Try!(ReflectConstantBuffer(bindDesc, bufferReflection, context));
|
||||
shader.AddConstantBuffer(cbuffer);
|
||||
}
|
||||
case .Texture:
|
||||
TextureDimension textureDimension;
|
||||
|
||||
switch (bindDesc.Dimension)
|
||||
{
|
||||
case .Texture1D:
|
||||
textureDimension = .Texture1D;
|
||||
case .Texture1DArray:
|
||||
textureDimension = .Texture1DArray;
|
||||
case .Texture2D:
|
||||
textureDimension = .Texture2D;
|
||||
case .Texture2DArray:
|
||||
textureDimension = .Texture2DArray;
|
||||
case .Texture3D:
|
||||
textureDimension = .Texture3D;
|
||||
case .TextureCube:
|
||||
textureDimension = .TextureCube;
|
||||
case .TextureCubeArray:
|
||||
textureDimension = .TextureCubeArray;
|
||||
default:
|
||||
textureDimension = .Unknown;
|
||||
}
|
||||
|
||||
shader.AddTexture(new ReflectedTexture(StringView(bindDesc.Name), bindDesc.BindPoint, textureDimension));
|
||||
case .Sampler:
|
||||
// There is nothing to do for samplers
|
||||
default:
|
||||
Log.EngineLogger.Warning($"Unhandled shader resource type: \"{bindDesc.Type}\"");
|
||||
}
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<ReflectedConstantBuffer> ReflectConstantBuffer(ShaderInputBindDescription bindDesc, ID3D11ShaderReflectionConstantBuffer* bufferReflection, ShaderCompilationContext context)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
var buffer = new ReflectedConstantBuffer();
|
||||
|
||||
HResult result = bufferReflection.GetDescription(let bufferDescription);
|
||||
Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to get buffer description. Error({(int)result}): {result}");
|
||||
|
||||
Log.EngineLogger.AssertDebug(bufferDescription.Type == .D3D_CT_CBUFFER, "The buffer is not of type \"D3D_CT_CBUFFER\"");
|
||||
|
||||
buffer.Name = StringView(bufferDescription.Name);
|
||||
buffer.Size = bufferDescription.Size;
|
||||
buffer.BindPoint = bindDesc.BindPoint;
|
||||
buffer.RawData = new uint8[buffer.Size];
|
||||
|
||||
for(uint32 v = 0; v < bufferDescription.Variables; v++)
|
||||
{
|
||||
ID3D11ShaderReflectionVariable* variableReflection = bufferReflection.GetVariableByIndex(v);
|
||||
|
||||
if (ReflectConstantBufferVariable(buffer, variableReflection, context) case .Err)
|
||||
{
|
||||
delete buffer;
|
||||
return .Err;
|
||||
}
|
||||
}
|
||||
|
||||
if (context.EngineBuffers.TryGetValue(buffer.Name, let engineBufferName))
|
||||
{
|
||||
buffer.EngineBufferName = engineBufferName;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static Result<void> ReflectConstantBufferVariable(ReflectedConstantBuffer buffer, ID3D11ShaderReflectionVariable* variableReflection, ShaderCompilationContext context)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
HResult getDescResult = variableReflection.GetDescription(let variableDescription);
|
||||
if (getDescResult.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to get variable description. Error({(int)getDescResult}): {getDescResult}");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
let variableType = variableReflection.GetVariableType();
|
||||
let getTypeDescResult = variableType.GetDescription(let shaderTypeDescription);
|
||||
if (getTypeDescResult.Failed)
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to get variable description. Error({(int)getDescResult}): {getDescResult}");
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
ReflectedConstantBufferVariable variable = new ReflectedConstantBufferVariable();
|
||||
variable.Name = StringView(variableDescription.Name);
|
||||
|
||||
variable.Offset = variableDescription.StartOffset;
|
||||
variable.SizeInBytes = variableDescription.Size;
|
||||
variable.IsUsed = variableDescription.uFlags.HasFlag(.Used);
|
||||
|
||||
variable.Columns = shaderTypeDescription.Columns;
|
||||
variable.Rows = shaderTypeDescription.Rows;
|
||||
variable.ArraySize = shaderTypeDescription.Elements;
|
||||
|
||||
switch(shaderTypeDescription.Type)
|
||||
{
|
||||
case .Bool:
|
||||
variable.ElementType = .Bool;
|
||||
case .Float:
|
||||
variable.ElementType = .Float;
|
||||
case .Int:
|
||||
variable.ElementType = .Int;
|
||||
case .UInt:
|
||||
variable.ElementType = .UInt;
|
||||
default:
|
||||
Log.EngineLogger.Error($"Unhandled shader variable type: {shaderTypeDescription.Type}.");
|
||||
delete variable;
|
||||
return .Err;
|
||||
}
|
||||
|
||||
if (variableDescription.DefaultValue == null)
|
||||
{
|
||||
Internal.MemSet(&buffer.RawData[variable.Offset], 0, variable.SizeInBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Internal.MemCpy(&buffer.RawData[variable.Offset], variableDescription.DefaultValue, variable.SizeInBytes);
|
||||
}
|
||||
|
||||
if (context.Variables.TryGetValue(variable.Name, let variableAnnotation))
|
||||
{
|
||||
variable.PreviewName = variableAnnotation.PreviewName;
|
||||
variable.EditorType = variableAnnotation.EditorType;
|
||||
|
||||
// TODO: We need to somehow validate against our variable type
|
||||
variable.MinValue = variableAnnotation.MinValue;
|
||||
variable.MaxValue = variableAnnotation.MaxValue;
|
||||
}
|
||||
|
||||
buffer.AddVariable(variable);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
|
||||
struct PreprocessingIncluder : ID3DInclude, IDisposable
|
||||
{
|
||||
private VTable _vTable;
|
||||
|
||||
private Dictionary<StringView, (String FileName, String FileContent)> _loadedFiles;
|
||||
|
||||
private String _parentFileDirectory;
|
||||
|
||||
private ShaderCompilationContext _context;
|
||||
|
||||
public this(String parentFileDirectory, ShaderCompilationContext context)
|
||||
{
|
||||
_parentFileDirectory = parentFileDirectory;
|
||||
_loadedFiles = new Dictionary<StringView, (String FileName, String FileContent)>();
|
||||
|
||||
_vTable.Open = => Open;
|
||||
_vTable.Close = => Close;
|
||||
|
||||
_vt = &_vTable;
|
||||
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
for (let (key, value) in _loadedFiles)
|
||||
{
|
||||
delete value.FileName;
|
||||
delete value.FileContent;
|
||||
}
|
||||
|
||||
delete _loadedFiles;
|
||||
}
|
||||
|
||||
public static HResult Open(ID3DInclude* self, IncludeType includeType, char8* fileNamePtr, void* parentData, void** data, uint32* bytes)
|
||||
{
|
||||
PreprocessingIncluder* includer = (.)self;
|
||||
|
||||
StringView fileName = StringView(fileNamePtr);
|
||||
|
||||
if (includer._loadedFiles.TryGetValue(fileName, let value))
|
||||
{
|
||||
*data = (void*)value.FileContent.Ptr;
|
||||
*bytes = (uint32)value.FileContent.Length;
|
||||
|
||||
return .S_OK;
|
||||
}
|
||||
|
||||
String fullPath = scope .();
|
||||
|
||||
Path.Combine(fullPath, includer._parentFileDirectory, fileName);
|
||||
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
let treeResult = Editor.Instance.ContentManager.AssetHierarchy.GetNodeFromIdentifier(scope AssetIdentifier(fileName));
|
||||
|
||||
if (var assetNode = treeResult)
|
||||
{
|
||||
fullPath.Set(assetNode->Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to include shader file \"{fileName}\"");
|
||||
return .E_FILENOTFOUND;
|
||||
}
|
||||
}
|
||||
|
||||
Stream fileStream = Application.Instance.ContentManager.GetStream(fullPath);
|
||||
|
||||
if (fileStream == null)
|
||||
{
|
||||
fileStream = Application.Instance.ContentManager.GetStream(StringView(fileName));
|
||||
}
|
||||
|
||||
if (fileStream == null)
|
||||
{
|
||||
return .E_FILENOTFOUND;
|
||||
}
|
||||
|
||||
String fileContent = new String();
|
||||
|
||||
{
|
||||
StreamReader reader = scope .(fileStream);
|
||||
|
||||
reader.ReadToEnd(fileContent);
|
||||
|
||||
String fileNameStr = new String(fileName);
|
||||
|
||||
includer._loadedFiles.Add(fileNameStr, (fileNameStr, fileContent));
|
||||
}
|
||||
|
||||
delete fileStream;
|
||||
|
||||
String trashbin = scope String();
|
||||
|
||||
if (ShaderCodePreprocessor.ProcessFileContent(fileContent, trashbin, trashbin, includer._context.Variables, includer._context.BufferNames, includer._context.EngineBuffers) case .Err)
|
||||
{
|
||||
return .E_FAIL;
|
||||
}
|
||||
|
||||
*data = (void*)fileContent.Ptr;
|
||||
*bytes = (uint32)fileContent.Length;
|
||||
|
||||
return .S_OK;
|
||||
}
|
||||
|
||||
public static HResult Close(ID3DInclude* self, void** data)
|
||||
{
|
||||
PreprocessingIncluder* includer = (.)self;
|
||||
|
||||
for (var v in includer._loadedFiles)
|
||||
{
|
||||
if (v.value.FileContent.Ptr == data)
|
||||
{
|
||||
includer._loadedFiles.Remove(v.key);
|
||||
delete v.value.FileContent;
|
||||
delete v.value.FileName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return .S_OK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
using GlitchyEditor.Assets.Importers;
|
||||
using System;
|
||||
using GlitchyEngine.Content;
|
||||
using System.Collections;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEngine.Math;
|
||||
using GlitchyEngine.Renderer;
|
||||
|
||||
namespace GlitchyEditor.Assets.Processors;
|
||||
|
||||
class ProcessedShader : ProcessedResource
|
||||
{
|
||||
public struct TextureEntry
|
||||
{
|
||||
public StringView Name;
|
||||
public TextureDimension TextureDimension;
|
||||
public int32 VertexShaderBindPoint;
|
||||
public int32 PixelShaderBindPoint;
|
||||
|
||||
public static readonly TextureEntry Default = .() {
|
||||
Name = null,
|
||||
TextureDimension = .Unknown,
|
||||
VertexShaderBindPoint = -1,
|
||||
PixelShaderBindPoint = -1
|
||||
};
|
||||
}
|
||||
|
||||
public struct ConstantBufferEntry
|
||||
{
|
||||
public ReflectedConstantBuffer ConstantBuffer;
|
||||
public int32 VertexShaderBindPoint;
|
||||
public int32 PixelShaderBindPoint;
|
||||
|
||||
public static readonly Self Default = .() {
|
||||
ConstantBuffer = null,
|
||||
VertexShaderBindPoint = -1,
|
||||
PixelShaderBindPoint = -1,
|
||||
};
|
||||
}
|
||||
|
||||
public override AssetType AssetType => .Shader;
|
||||
|
||||
public CompiledShader VertexShader ~ delete _;
|
||||
public CompiledShader PixelShader ~ delete _;
|
||||
|
||||
Dictionary<StringView, ConstantBufferEntry> _constantBuffers = new .() ~ delete _; // Only delete container, Buffers come from shaders
|
||||
|
||||
Dictionary<StringView, TextureEntry> _textures = new .() ~ delete _;
|
||||
|
||||
public Dictionary<StringView, ConstantBufferEntry> ConstantBuffers => _constantBuffers;
|
||||
public Dictionary<StringView, TextureEntry> Textures => _textures;
|
||||
|
||||
public this(AssetIdentifier ownAssetIdentifier, AssetHandle assetHandle) : base(ownAssetIdentifier, assetHandle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void AddConstantBuffer(ConstantBufferEntry buffer)
|
||||
{
|
||||
_constantBuffers.Add(buffer.ConstantBuffer.Name, buffer);
|
||||
}
|
||||
|
||||
public void AddTextureEntry(TextureEntry textureEntry)
|
||||
{
|
||||
_textures.Add(textureEntry.Name, textureEntry);
|
||||
}
|
||||
}
|
||||
|
||||
class ShaderVariable
|
||||
{
|
||||
private String _name ~ delete _;
|
||||
private String _previewName ~ delete _;
|
||||
private String _editorType ~ delete _;
|
||||
|
||||
private Dictionary<String, Variant> _parameters = new .() ~ {
|
||||
for (var (entryKey, entry) in _)
|
||||
{
|
||||
delete entryKey;
|
||||
entry.Dispose();
|
||||
}
|
||||
delete _;
|
||||
};
|
||||
|
||||
public StringView Name
|
||||
{
|
||||
get => _name;
|
||||
set => String.NewOrSet!(_name, value);
|
||||
}
|
||||
|
||||
public StringView PreviewName
|
||||
{
|
||||
get => _previewName;
|
||||
set => String.NewOrSet!(_previewName, value);
|
||||
}
|
||||
|
||||
public StringView EditorType
|
||||
{
|
||||
get => _editorType;
|
||||
set => String.NewOrSet!(_editorType, value);
|
||||
}
|
||||
|
||||
public Variant MinValue ~ _.Dispose();
|
||||
public Variant MaxValue ~ _.Dispose();
|
||||
|
||||
public Dictionary<String, Variant> Parameters => _parameters;
|
||||
|
||||
public void AddParameter(StringView name, Variant value)
|
||||
{
|
||||
_parameters.Add(new String(name), value);
|
||||
}
|
||||
}
|
||||
|
||||
class ShaderProcessor : IAssetProcessor
|
||||
{
|
||||
public AssetProcessorConfig CreateDefaultConfig()
|
||||
{
|
||||
return new AssetProcessorConfig();
|
||||
}
|
||||
|
||||
public static Type ProcessedAssetType => typeof(ImportedShader);
|
||||
|
||||
public Result<void> Process(ImportedResource importedResource, AssetConfig config, List<ProcessedResource> outProcessedResources)
|
||||
{
|
||||
Log.EngineLogger.AssertDebug(importedResource is ImportedShader);
|
||||
|
||||
Try!(ProcessShader(importedResource as ImportedShader, config, outProcessedResources));
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private static Result<void> ProcessShader(ImportedShader importedShader, AssetConfig config, List<ProcessedResource> outProcessedResources)
|
||||
{
|
||||
String vsName = scope String();
|
||||
String psName = scope String();
|
||||
|
||||
Dictionary<StringView, ShaderVariable> variables = scope .();
|
||||
List<String> bufferNames = scope .();
|
||||
// Name in Shader -> Name in Engine
|
||||
Dictionary<StringView, StringView> engineBuffers = scope .();
|
||||
|
||||
ProcessedShader processedShader = null;
|
||||
|
||||
defer
|
||||
{
|
||||
ClearDictionaryAndDeleteValues!(variables);
|
||||
ClearAndDeleteItems!(bufferNames);
|
||||
|
||||
if (@return case .Err)
|
||||
{
|
||||
delete processedShader;
|
||||
}
|
||||
}
|
||||
|
||||
String tmpCode = new String(importedShader.HlslCode);
|
||||
|
||||
// TODO: This is terrible. Currently we process too often... (here + each shader stage)
|
||||
Try!(ShaderCodePreprocessor.ProcessFileContent(tmpCode , vsName, psName, variables, bufferNames, engineBuffers));
|
||||
|
||||
delete tmpCode;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(vsName) && String.IsNullOrWhiteSpace(psName))
|
||||
{
|
||||
// this is not an effect -> we don't need to compile it
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
processedShader = new ProcessedShader(new AssetIdentifier(importedShader.AssetIdentifier), config.AssetHandle);
|
||||
|
||||
Try!(CompileAndReflect(vsName, psName, importedShader, importedShader.HlslCode, processedShader));
|
||||
|
||||
Try!(MergeResources(processedShader));
|
||||
|
||||
outProcessedResources.Add(processedShader);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<void> MergeResources(ProcessedShader processedShader)
|
||||
{
|
||||
Try!(MergeConstantBuffers(processedShader));
|
||||
Try!(MergeTextures(processedShader));
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<void> MergeConstantBuffers(ProcessedShader processedShader)
|
||||
{
|
||||
HashSet<StringView> bufferNames = scope .();
|
||||
|
||||
void AddBufferNames(CompiledShader shader)
|
||||
{
|
||||
for (StringView name in shader.ConstantBuffers.Keys)
|
||||
{
|
||||
bufferNames.Add(name);
|
||||
}
|
||||
}
|
||||
|
||||
AddBufferNames(processedShader.VertexShader);
|
||||
AddBufferNames(processedShader.PixelShader);
|
||||
|
||||
for (StringView bufferName in bufferNames)
|
||||
{
|
||||
Result<ReflectedConstantBuffer> vsBufferResult = processedShader.VertexShader.ConstantBuffers.GetValue(bufferName);
|
||||
Result<ReflectedConstantBuffer> psBufferResult = processedShader.PixelShader.ConstantBuffers.GetValue(bufferName);
|
||||
|
||||
ProcessedShader.ConstantBufferEntry constantBufferEntry = .Default;
|
||||
|
||||
if (vsBufferResult case .Ok(let vsBuffer) && psBufferResult case .Ok(let psBuffer))
|
||||
{
|
||||
// Choose larger buffer
|
||||
constantBufferEntry.ConstantBuffer = (vsBuffer.Size >= psBuffer.Size) ? vsBuffer : psBuffer;
|
||||
constantBufferEntry.VertexShaderBindPoint = (.)vsBuffer.BindPoint;
|
||||
constantBufferEntry.PixelShaderBindPoint = (.)psBuffer.BindPoint;
|
||||
}
|
||||
else if (vsBufferResult case .Ok(let vsBuffer))
|
||||
{
|
||||
constantBufferEntry.ConstantBuffer = vsBuffer;
|
||||
constantBufferEntry.VertexShaderBindPoint = (.)vsBuffer.BindPoint;
|
||||
}
|
||||
else if (psBufferResult case .Ok(let psBuffer))
|
||||
{
|
||||
constantBufferEntry.ConstantBuffer = psBuffer;
|
||||
constantBufferEntry.PixelShaderBindPoint = (.)psBuffer.BindPoint;
|
||||
}
|
||||
|
||||
Log.EngineLogger.Assert(constantBufferEntry.ConstantBuffer != null);
|
||||
Log.EngineLogger.Assert(constantBufferEntry.VertexShaderBindPoint > -1 || constantBufferEntry.PixelShaderBindPoint > -1);
|
||||
|
||||
processedShader.AddConstantBuffer(constantBufferEntry);
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
static ref ProcessedShader.TextureEntry AddOrGetTextureEntry(ProcessedShader processedShader, ReflectedTexture reflectedTexture)
|
||||
{
|
||||
if (!processedShader.Textures.ContainsKey(reflectedTexture.Name))
|
||||
{
|
||||
ProcessedShader.TextureEntry textureEntry = .Default;
|
||||
textureEntry.Name = reflectedTexture.Name;
|
||||
textureEntry.TextureDimension = reflectedTexture.TextureDimension;
|
||||
|
||||
processedShader.Textures.Add(textureEntry.Name, textureEntry);
|
||||
}
|
||||
|
||||
ref ProcessedShader.TextureEntry entry = ref processedShader.Textures[reflectedTexture.Name];
|
||||
|
||||
return ref entry;
|
||||
}
|
||||
|
||||
private static Result<void> MergeTextures(ProcessedShader processedShader)
|
||||
{
|
||||
for (let (textureName, reflectedTexture) in processedShader.VertexShader.Textures)
|
||||
{
|
||||
ref ProcessedShader.TextureEntry textureEntry = ref AddOrGetTextureEntry(processedShader, reflectedTexture);
|
||||
textureEntry.VertexShaderBindPoint = (int32)reflectedTexture.BindPoint;
|
||||
}
|
||||
|
||||
for (let (textureName, reflectedTexture) in processedShader.PixelShader.Textures)
|
||||
{
|
||||
ref ProcessedShader.TextureEntry textureEntry = ref AddOrGetTextureEntry(processedShader, reflectedTexture);
|
||||
textureEntry.PixelShaderBindPoint = (int32)reflectedTexture.BindPoint;
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<void> CompileAndReflect(StringView vsName, StringView psName, ImportedShader shader, StringView code, ProcessedShader processedShader)
|
||||
{
|
||||
if (!vsName.IsWhiteSpace)
|
||||
{
|
||||
processedShader.VertexShader = Try!(CompileAndReflectVertexShader(vsName, shader, code));
|
||||
}
|
||||
|
||||
if (!psName.IsWhiteSpace)
|
||||
{
|
||||
processedShader.PixelShader = Try!(CompileAndReflectPixelShader(psName, shader, code));
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static Result<CompiledShader> CompileAndReflectVertexShader(StringView vsName, ImportedShader importedShader, StringView code)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
return ShaderCompiler.CompileAndReflectShader(code, importedShader.AssetIdentifier, vsName, "vs_5_0", .());
|
||||
}
|
||||
|
||||
private static Result<CompiledShader> CompileAndReflectPixelShader(StringView vsName, ImportedShader importedShader, StringView code)
|
||||
{
|
||||
Debug.Profiler.ProfileResourceFunction!();
|
||||
|
||||
return ShaderCompiler.CompileAndReflectShader(code, importedShader.AssetIdentifier, vsName, "ps_5_0", .());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using DirectX.Common;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEditor.Platform.Windows.Com;
|
||||
|
||||
namespace GlitchyEditor.CodeEditors;
|
||||
|
||||
/// A request to open the script solution (or a file in it) in Visual Studio.
|
||||
/// Runs on its own worker thread so the editor never blocks on a busy or starting Visual Studio.
|
||||
class DteOpenRequest
|
||||
{
|
||||
private const int ColdStartMaxPolls = 120;
|
||||
private const int ColdStartPollIntervalMs = 500;
|
||||
|
||||
private append String _solutionPath = .();
|
||||
private append String _devenvPath = .();
|
||||
private append String _fileName = .();
|
||||
|
||||
public StringView SolutionPath
|
||||
{
|
||||
get => _solutionPath;
|
||||
set => _solutionPath.Set(value);
|
||||
}
|
||||
|
||||
public StringView DevenvPath
|
||||
{
|
||||
get => _devenvPath;
|
||||
set => _devenvPath.Set(value);
|
||||
}
|
||||
|
||||
/// Leave empty to just open the project.
|
||||
public StringView FileName
|
||||
{
|
||||
get => _fileName;
|
||||
set => _fileName.Set(value);
|
||||
}
|
||||
|
||||
public int LineNumber;
|
||||
|
||||
private static int32 sWorkerActive = 0;
|
||||
|
||||
public static void Start(DteOpenRequest ownRequest)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref sWorkerActive, 0, 1) != 0)
|
||||
{
|
||||
Log.EngineLogger.Info("Visual Studio is already being opened, ignoring the request.");
|
||||
delete ownRequest;
|
||||
return;
|
||||
}
|
||||
|
||||
Thread workerThread = new Thread(new () =>
|
||||
{
|
||||
ownRequest.Run();
|
||||
delete ownRequest;
|
||||
|
||||
Interlocked.Exchange(ref sWorkerActive, 0);
|
||||
});
|
||||
workerThread.SetName("Visual Studio DTE worker");
|
||||
// Don't block editor shutdown on a worker that is still polling; skipping the COM cleanup
|
||||
// at process exit is harmless.
|
||||
workerThread.IsBackground = true;
|
||||
workerThread.Start(true);
|
||||
}
|
||||
|
||||
private void Run()
|
||||
{
|
||||
// The worker does its own COM initialization, the editor main thread (STA via OleInitialize)
|
||||
// stays untouched.
|
||||
if (CoInitializeEx(null, COINIT_APARTMENTTHREADED).Failed(let result))
|
||||
{
|
||||
Log.EngineLogger.Error(scope $"CoInitializeEx failed: {result} ({(uint32)result:X8})");
|
||||
return;
|
||||
}
|
||||
|
||||
defer CoUninitialize();
|
||||
|
||||
if (VisualStudioDte.FindRunningInstance(SolutionPath) case .Ok(let dte))
|
||||
{
|
||||
if (FileName.IsEmpty)
|
||||
VisualStudioDte.ActivateMainWindow(dte);
|
||||
else
|
||||
VisualStudioDte.OpenFileAtLine(dte, FileName, LineNumber).IgnoreError();
|
||||
|
||||
delete dte;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Log.EngineLogger.Info(scope $"No running Visual Studio instance has \"{SolutionPath}\" open, starting a new one.");
|
||||
|
||||
ProcessStartInfo startInfo = scope .();
|
||||
startInfo.SetFileName(DevenvPath);
|
||||
startInfo.SetArguments(scope $"\"{SolutionPath}\"");
|
||||
|
||||
if (scope SpawnedProcess().Start(startInfo) case .Err)
|
||||
{
|
||||
Log.EngineLogger.Error(scope $"Failed to start Visual Studio (\"{DevenvPath}\").");
|
||||
return;
|
||||
}
|
||||
|
||||
if (FileName.IsEmpty)
|
||||
return;
|
||||
|
||||
// Wait for the new instance to finish loading the solution (Solution.FullName stays empty
|
||||
// until then), then open the file in it.
|
||||
for (int i < ColdStartMaxPolls)
|
||||
{
|
||||
Thread.Sleep(ColdStartPollIntervalMs);
|
||||
|
||||
if (VisualStudioDte.FindRunningInstance(SolutionPath) case .Ok(let startedDte))
|
||||
{
|
||||
VisualStudioDte.OpenFileAtLine(startedDte, FileName, LineNumber).IgnoreError();
|
||||
|
||||
delete startedDte;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log.EngineLogger.Warning(scope $"Visual Studio was started but its automation interface never became available, \"{FileName}\" was not opened automatically.");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,236 @@
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using DirectX.Common;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEditor.Platform.Windows.Com;
|
||||
using System.IO;
|
||||
|
||||
namespace GlitchyEditor.CodeEditors;
|
||||
|
||||
/// Talks to running Visual Studio instances via their EnvDTE automation objects, which are
|
||||
/// published in the Running Object Table as "!VisualStudio.DTE.<version>:<pid>".
|
||||
/// All methods must be called from a thread that is initialized for COM (CoInitializeEx).
|
||||
static class VisualStudioDte
|
||||
{
|
||||
private const String MonikerPrefix = "!VisualStudio.DTE";
|
||||
/// EnvDTE.Constants.vsViewKindCode
|
||||
private const String ViewKindCode = "{7651A701-06E5-11D1-8EBD-00A0C90F26EA}";
|
||||
|
||||
/// Searches the Running Object Table for a Visual Studio instance that has the given solution open.
|
||||
public static Result<ComDispatch> FindRunningInstance(StringView solutionPath)
|
||||
{
|
||||
IRunningObjectTable* rot = null;
|
||||
if (GetRunningObjectTable(0, out rot).Failed(var result) || rot == null)
|
||||
{
|
||||
Log.EngineLogger.Error(scope $"GetRunningObjectTable failed: {result} ({(uint32)result:X8})");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
defer rot.Release();
|
||||
|
||||
IBindCtx* bindCtx = null;
|
||||
if (CreateBindCtx(0, out bindCtx).Failed(out result) || bindCtx == null)
|
||||
{
|
||||
Log.EngineLogger.Error(scope $"CreateBindCtx failed: {result} ({(uint32)result:X8})");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
defer bindCtx.Release();
|
||||
|
||||
IEnumMoniker* enumMoniker = null;
|
||||
if (rot.EnumRunning(out enumMoniker).Failed(out result) || enumMoniker == null)
|
||||
{
|
||||
Log.EngineLogger.Error(scope $"IRunningObjectTable.EnumRunning failed: {result} ({(uint32)result:X8})");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
defer enumMoniker.Release();
|
||||
|
||||
IMoniker* moniker = null;
|
||||
while (enumMoniker.Next(1, out moniker, ?) == .S_OK)
|
||||
{
|
||||
ComDispatch dte = GetDteFromMoniker(rot, bindCtx, moniker);
|
||||
|
||||
ReleaseAndNullify!(moniker);
|
||||
|
||||
if (dte == null)
|
||||
continue;
|
||||
|
||||
String solutionFullName = scope .();
|
||||
|
||||
if (TryGetSolutionFullName(dte, solutionFullName))
|
||||
{
|
||||
Log.EngineLogger.Trace(scope $"Found running Visual Studio instance with solution \"{solutionFullName}\".");
|
||||
|
||||
if (SolutionPathsEqual(solutionFullName, solutionPath))
|
||||
return dte;
|
||||
}
|
||||
|
||||
// No match, cleanup.
|
||||
delete dte;
|
||||
}
|
||||
|
||||
return .Err;
|
||||
}
|
||||
|
||||
/// Brings the main window of the given Visual Studio instance to the foreground.
|
||||
public static void ActivateMainWindow(ComDispatch dte)
|
||||
{
|
||||
if (!(dte.GetObjectProperty("MainWindow") case .Ok(let mainWindow)))
|
||||
{
|
||||
Log.EngineLogger.Warning("Failed to get the Visual Studio main window.");
|
||||
return;
|
||||
}
|
||||
|
||||
defer delete mainWindow;
|
||||
|
||||
if (mainWindow.InvokeMethod("Activate") case .Ok(var activateResult))
|
||||
VariantClear(&activateResult);
|
||||
|
||||
// Activate alone often isn't enough to raise a window of another process,
|
||||
// so additionally restore and foreground it via its HWND.
|
||||
if (mainWindow.GetProperty("HWnd") case .Ok(var hwndValue))
|
||||
{
|
||||
if (hwndValue.GetHwnd() case .Ok(let hwnd))
|
||||
{
|
||||
if (IsIconic(hwnd))
|
||||
ShowWindow(hwnd, SW_RESTORE);
|
||||
|
||||
SetForegroundWindow(hwnd);
|
||||
}
|
||||
|
||||
VariantClear(&hwndValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens the given file in the code editor of the given Visual Studio instance and
|
||||
/// jumps to lineNumber (1-based, ignored if <= 0).
|
||||
public static Result<void> OpenFileAtLine(ComDispatch dte, StringView fileName, int lineNumber)
|
||||
{
|
||||
ComDispatch itemOperations = Try!(dte.GetObjectProperty("ItemOperations"));
|
||||
defer delete itemOperations;
|
||||
|
||||
VARIANT[] openFileArgs = scope .(VARIANT.FromBStr(fileName), VARIANT.FromBStr(ViewKindCode));
|
||||
|
||||
if (itemOperations.InvokeMethod("OpenFile", openFileArgs) case .Ok(var window))
|
||||
{
|
||||
VariantClear(&window);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.EngineLogger.Error(scope $"Failed to open \"{fileName}\" in Visual Studio.");
|
||||
return .Err;
|
||||
}
|
||||
|
||||
if (lineNumber > 0)
|
||||
GoToLine(dte, lineNumber);
|
||||
|
||||
ActivateMainWindow(dte);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
private static ComDispatch GetDteFromMoniker(IRunningObjectTable* rot, IBindCtx* bindCtx, IMoniker* moniker)
|
||||
{
|
||||
char16* displayNameW = null;
|
||||
|
||||
if (moniker.GetDisplayName(bindCtx, null, out displayNameW).Failed || displayNameW == null)
|
||||
return null;
|
||||
|
||||
String displayName = scope .();
|
||||
displayName.Append(displayNameW);
|
||||
CoTaskMemFree(displayNameW);
|
||||
|
||||
if (!displayName.StartsWith(MonikerPrefix))
|
||||
return null;
|
||||
|
||||
IUnknown* unknown = null;
|
||||
defer { unknown?.Release(); }
|
||||
|
||||
if (rot.GetObject(moniker, out unknown).Failed || unknown == null)
|
||||
return null;
|
||||
|
||||
IDispatch* dispatch = null;
|
||||
|
||||
if (unknown.QueryInterface<IDispatch>(out dispatch).Succeeded)
|
||||
return new ComDispatch(dispatch);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool TryGetSolutionFullName(ComDispatch dte, String outFullName)
|
||||
{
|
||||
if (!(dte.GetObjectProperty("Solution") case .Ok(let solution)))
|
||||
return false;
|
||||
|
||||
defer delete solution;
|
||||
|
||||
return solution.GetStringProperty("FullName", outFullName) case .Ok;
|
||||
}
|
||||
|
||||
private static bool SolutionPathsEqual(StringView left, StringView right)
|
||||
{
|
||||
String normalizedLeft = NormalizePath(left, .. scope .());
|
||||
String normalizedRight = NormalizePath(right, .. scope .());
|
||||
|
||||
// Be tolerant about the solution extension: it isn't guaranteed that the DTE reports
|
||||
// the same extension for .slnx solutions that we expect.
|
||||
return StripSolutionExtension(normalizedLeft) == StripSolutionExtension(normalizedRight);
|
||||
}
|
||||
|
||||
private static void NormalizePath(StringView path, String output)
|
||||
{
|
||||
output.Append(path);
|
||||
output.Replace('/', '\\');
|
||||
output.ToLower();
|
||||
}
|
||||
|
||||
private static StringView StripSolutionExtension(StringView path)
|
||||
{
|
||||
if (path.EndsWith(".slnx"))
|
||||
return path.Substring(0, path.Length - ".slnx".Length);
|
||||
|
||||
if (path.EndsWith(".sln"))
|
||||
return path.Substring(0, path.Length - ".sln".Length);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private static void GoToLine(ComDispatch dte, int lineNumber)
|
||||
{
|
||||
ComDispatch document = null;
|
||||
defer { delete document; }
|
||||
|
||||
// Directly after OpenFile the ActiveDocument might not be set yet, so retry for a bit.
|
||||
for (int i < 10)
|
||||
{
|
||||
if (dte.GetObjectProperty("ActiveDocument") case .Ok(out document))
|
||||
break;
|
||||
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
if (document == null)
|
||||
{
|
||||
Log.EngineLogger.Warning("Could not jump to the requested line: no active document.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (document.GetObjectProperty("Selection") not case .Ok(let selection))
|
||||
{
|
||||
Log.EngineLogger.Warning("Could not jump to the requested line: failed to get the text selection.");
|
||||
return;
|
||||
}
|
||||
|
||||
VARIANT[2] gotoLineArgs = .(VARIANT.FromInt32((int32)lineNumber), VARIANT.FromBool(false));
|
||||
|
||||
if (selection.InvokeMethod("GotoLine", gotoLineArgs) case .Ok(var gotoResult))
|
||||
VariantClear(&gotoResult);
|
||||
|
||||
delete selection;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -3,9 +3,7 @@ namespace GlitchyEditor.CodeEditors;
|
||||
|
||||
interface IIdeAdapter
|
||||
{
|
||||
static void OpenScript(StringView fileName);
|
||||
void OpenScript(StringView fileName, int lineNumber = 0);
|
||||
|
||||
static void OpenScript(StringView fileName, int lineNumber);
|
||||
|
||||
static void OpenScriptProject();
|
||||
void OpenScriptProject();
|
||||
}
|
||||
|
||||
@@ -2,37 +2,64 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using GlitchyEngine;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using GlitchyEditor.EditWindows;
|
||||
using ImGui;
|
||||
using GlitchyEditor.Settings;
|
||||
|
||||
namespace GlitchyEditor.CodeEditors;
|
||||
|
||||
class RiderIdeAdapter : IIdeAdapter
|
||||
{
|
||||
public static void OpenScript(StringView fileName)
|
||||
private IdeInstallation _ideInstallation;
|
||||
|
||||
public this(IdeInstallation ideInstallation)
|
||||
{
|
||||
OpenScript(fileName, 0);
|
||||
Debug.Assert(ideInstallation.Ide == .Rider);
|
||||
|
||||
_ideInstallation = ideInstallation;
|
||||
}
|
||||
|
||||
public static void OpenScript(StringView fileName, int lineNumber)
|
||||
public void OpenScript(StringView fileName, int lineNumber)
|
||||
{
|
||||
String solutionPath = scope .();
|
||||
Editor.Instance.CurrentProject.PathInProject(solutionPath, scope $"{Editor.Instance.CurrentProject.Name}.sln");
|
||||
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
||||
|
||||
ProcessStartInfo startInfo = scope .();
|
||||
startInfo.SetFileName(Application.Instance.Settings.ScriptSettings.RiderPath);
|
||||
startInfo.SetFileName(_ideInstallation.Path);
|
||||
startInfo.SetArguments(scope $"{solutionPath} --line {lineNumber} {fileName}");
|
||||
|
||||
scope SpawnedProcess().Start(startInfo);
|
||||
}
|
||||
|
||||
public static void OpenScriptProject()
|
||||
public void OpenScriptProject()
|
||||
{
|
||||
String solutionPath = scope .();
|
||||
Editor.Instance.CurrentProject.PathInProject(solutionPath, scope $"{Editor.Instance.CurrentProject.Name}.sln");
|
||||
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
||||
|
||||
if (!File.Exists(solutionPath))
|
||||
{
|
||||
Log.EngineLogger.Error("Could not find solution file?");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(_ideInstallation.Path))
|
||||
{
|
||||
Editor.Instance.ShowSettings();
|
||||
Editor.Instance.SettingsWindow.HighlightSetting("Tools", "Rider path");
|
||||
|
||||
PopupService.Instance.ShowMessageBox("Rider not found.",
|
||||
"The rider path could not be found. Please select the correct path.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessStartInfo startInfo = scope .();
|
||||
startInfo.SetFileName(Application.Instance.Settings.ScriptSettings.RiderPath);
|
||||
startInfo.SetFileName(_ideInstallation.Path);
|
||||
startInfo.SetArguments(solutionPath);
|
||||
|
||||
scope SpawnedProcess().Start(startInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
GlitchyEditor/src/CodeEditors/RiderPathLocator.bf (and the platform specific implementations)
|
||||
are a Beef port of the Rider path locator from the JetBrains "Rider Source Code Access" Unreal Engine plugin:
|
||||
|
||||
https://github.com/JetBrains/RiderSourceCodeAccess
|
||||
Source/RiderSourceCodeAccess/Private/RiderPathLocator/
|
||||
|
||||
The original work and this port are licensed under the Apache License, Version 2.0,
|
||||
reproduced in full below.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,445 @@
|
||||
// Beef port of JetBrains "Rider Source Code Access":
|
||||
// https://github.com/JetBrains/RiderSourceCodeAccess/tree/main/Source/RiderSourceCodeAccess/Private/RiderPathLocator
|
||||
//
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
// Licensed under the Apache License, Version 2.0 - see LICENSE-RiderPathLocator.txt
|
||||
//
|
||||
// Changes from the original: rewritten in Beef against corlib instead of the
|
||||
// Unreal Engine types (FString/TArray/TOptional/FPaths/FRegexPattern/FJsonSerializer/...).
|
||||
// The "plugins/rider-cpp" requirement, the ESupportUproject state and the
|
||||
// RiderLocations.txt resource-file lookup were removed because they only make sense for Unreal projects.
|
||||
//
|
||||
// This file holds the platform independent part, the platform specific parts live in the extensions (e.g. RiderPathLocatorWindows.bf).
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using Beefy.utils;
|
||||
|
||||
namespace GlitchyEditor.CodeEditors;
|
||||
|
||||
/// A dot-separated version number, e.g. "2024.1.2" or the build number "241.15989.155".
|
||||
struct RiderVersion : IHashable
|
||||
{
|
||||
public const int32 InvalidVersion = -1;
|
||||
|
||||
private const int32 MaxParts = 4;
|
||||
|
||||
private int32[MaxParts] _parts;
|
||||
private int32 _count;
|
||||
|
||||
public bool IsInitialized => _count != 0;
|
||||
|
||||
public int32 Major => _count >= 1 ? _parts[0] : InvalidVersion;
|
||||
public int32 Minor => _count >= 2 ? _parts[1] : InvalidVersion;
|
||||
public int32 Patch => _count >= 3 ? _parts[2] : InvalidVersion;
|
||||
|
||||
public static RiderVersion Parse(StringView versionString)
|
||||
{
|
||||
RiderVersion version = .();
|
||||
|
||||
for (StringView part in versionString.Split('.'))
|
||||
{
|
||||
if (version._count >= MaxParts)
|
||||
break;
|
||||
|
||||
// Note: the original uses FCString::Atoi, which yields 0 for garbage instead of failing.
|
||||
int32 value = 0;
|
||||
if (int32.Parse(part) case .Ok(let parsedValue))
|
||||
value = parsedValue;
|
||||
|
||||
version._parts[version._count] = value;
|
||||
version._count++;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
/// Compares two versions element by element.
|
||||
/// @returns A negative value if lhs is older than rhs, a positive value if it is newer; otherwise 0.
|
||||
public static int Compare(Self lhs, Self rhs)
|
||||
{
|
||||
int32 sharedCount = Math.Min(lhs._count, rhs._count);
|
||||
|
||||
for (int32 i < sharedCount)
|
||||
{
|
||||
if (lhs._parts[i] != rhs._parts[i])
|
||||
return lhs._parts[i] < rhs._parts[i] ? -1 : 1;
|
||||
}
|
||||
|
||||
// Equal prefix: the shorter version is the older one, so that "5.0" is older than "5.0.1".
|
||||
return lhs._count <=> rhs._count;
|
||||
}
|
||||
|
||||
public static bool operator<(Self lhs, Self rhs) => Compare(lhs, rhs) < 0;
|
||||
|
||||
public static bool operator==(Self lhs, Self rhs) => Compare(lhs, rhs) == 0;
|
||||
|
||||
public static bool operator!=(Self lhs, Self rhs) => Compare(lhs, rhs) != 0;
|
||||
|
||||
public int GetHashCode()
|
||||
{
|
||||
int hash = 0;
|
||||
|
||||
for (int32 i < _count)
|
||||
hash = (hash * 31) + (int)_parts[i];
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
if (_count == 0)
|
||||
{
|
||||
strBuffer.Append("<unknown>");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int32 i < _count)
|
||||
{
|
||||
if (i != 0)
|
||||
strBuffer.Append('.');
|
||||
|
||||
_parts[i].ToString(strBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// How a Rider installation was set up.
|
||||
enum RiderInstallType
|
||||
{
|
||||
/// Installed by the regular Rider installer.
|
||||
Installed,
|
||||
/// Installed through the JetBrains Toolbox.
|
||||
Toolbox,
|
||||
/// Provided by the user.
|
||||
Custom
|
||||
}
|
||||
|
||||
/// A single Rider installation found on this machine.
|
||||
class RiderInstallInfo
|
||||
{
|
||||
/// The full path of the Rider executable (rider64.exe).
|
||||
public String Path ~ delete _;
|
||||
public RiderVersion Version;
|
||||
public RiderInstallType InstallType;
|
||||
public String Name ~ delete _;
|
||||
|
||||
public this(StringView path, RiderVersion version, RiderInstallType installType, StringView name)
|
||||
{
|
||||
Path = new String(path);
|
||||
Version = version;
|
||||
InstallType = installType;
|
||||
Name = new String(name);
|
||||
}
|
||||
}
|
||||
|
||||
/// Locates the JetBrains Rider installations on this machine.
|
||||
static class RiderPathLocator
|
||||
{
|
||||
/// Maximum directory depth we descend into while looking for the Rider executable.
|
||||
/// The original search is unbounded, but it searches locations like "%LOCALAPPDATA%/Programs".
|
||||
protected const int MaxSearchDepth = 8;
|
||||
/// Maximum number of directories visited by a single recursive search.
|
||||
protected const int MaxSearchedDirectories = 4096;
|
||||
|
||||
/// Creates the installation info for the given Rider executable.
|
||||
/// @param riderExePath The path of a rider64.exe.
|
||||
/// @returns The installation info; or .Err if the given path is not a usable Rider installation.
|
||||
public static extern Result<RiderInstallInfo> GetInstallInfoFromRiderPath(StringView riderExePath, RiderInstallType installType);
|
||||
|
||||
/// Collects every Rider installation that can be found on this machine.
|
||||
/// The entries are deduplicated by path and sorted, newest version first.
|
||||
/// @param outInstalls Receives the installations. The caller takes ownership of the entries.
|
||||
public static extern void CollectAllPaths(List<RiderInstallInfo> outInstalls);
|
||||
|
||||
/// Gets the file name of the Rider executable on this platform (e.g. "rider64.exe").
|
||||
protected static extern StringView GetRiderExecutableName();
|
||||
|
||||
/// Gets the default location the Toolbox V2 installs IDEs into.
|
||||
protected static extern Result<void> GetDefaultIdeInstallLocationForToolboxV2(String outPath);
|
||||
|
||||
/// Moves the located installations into outInstalls, dropping the ones that were found more
|
||||
/// than once, and sorts them so that the newest installation comes first.
|
||||
/// @param found The located installations. The entries are moved into outInstalls or deleted.
|
||||
/// @param outInstalls Receives the installations. The caller takes ownership of the entries.
|
||||
protected static void DeduplicateAndSort(List<RiderInstallInfo> found, List<RiderInstallInfo> outInstalls)
|
||||
{
|
||||
// The same installation usually shows up through more than one source.
|
||||
for (int i < found.Count)
|
||||
{
|
||||
RiderInstallInfo install = found[i];
|
||||
|
||||
bool isDuplicate = false;
|
||||
|
||||
for (RiderInstallInfo existing in outInstalls)
|
||||
{
|
||||
if (IsSamePath(existing.Path, install.Path))
|
||||
{
|
||||
isDuplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDuplicate)
|
||||
continue;
|
||||
|
||||
outInstalls.Add(install);
|
||||
// Ownership moved to outInstalls, make sure the caller's cleanup doesn't delete it.
|
||||
found[i] = null;
|
||||
}
|
||||
|
||||
// Newest first, so that the caller can simply take the first entry.
|
||||
outInstalls.Sort(scope (lhs, rhs) => RiderVersion.Compare(rhs.Version, lhs.Version));
|
||||
}
|
||||
|
||||
/// Gets the installation directory of Rider from the path of its executable.
|
||||
/// The executable is expected to live in a "bin" directory, i.e. "<riderDirectory>/bin/rider64.exe".
|
||||
protected static Result<void> GetRiderDirectory(StringView riderExePath, String outDirectory)
|
||||
{
|
||||
// The original matches the regex "(.*)(?:\\|/)bin" against the executable path,
|
||||
// which is the same as taking the grandparent directory of a file inside "bin".
|
||||
String binDirectory = scope .();
|
||||
Try!(Path.GetDirectoryPath(riderExePath, binDirectory));
|
||||
|
||||
String binDirectoryName = scope .();
|
||||
Path.GetFileName(binDirectory, binDirectoryName);
|
||||
|
||||
if (StringView.Compare(binDirectoryName, "bin", true) != 0)
|
||||
return .Err;
|
||||
|
||||
return Path.GetDirectoryPath(binDirectory, outDirectory);
|
||||
}
|
||||
|
||||
/// Compares two paths, ignoring case and the difference between the directory separators.
|
||||
protected static bool IsSamePath(StringView left, StringView right)
|
||||
{
|
||||
if (left.Length != right.Length)
|
||||
return false;
|
||||
|
||||
for (int i < left.Length)
|
||||
{
|
||||
char8 leftChar = left[i];
|
||||
char8 rightChar = right[i];
|
||||
|
||||
if (Path.IsDirectorySeparatorChar(leftChar) && Path.IsDirectorySeparatorChar(rightChar))
|
||||
continue;
|
||||
|
||||
if (leftChar.ToLower != rightChar.ToLower)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Collects the Rider installations managed by the JetBrains Toolbox at the given location.
|
||||
protected static void CollectPathsFromToolbox(StringView toolboxPath, List<RiderInstallInfo> outInstalls)
|
||||
{
|
||||
if (toolboxPath.IsEmpty || !Directory.Exists(toolboxPath))
|
||||
return;
|
||||
|
||||
String customInstallLocation = scope .();
|
||||
ExtractPathFromSettingsJson(toolboxPath, customInstallLocation);
|
||||
|
||||
if (!customInstallLocation.IsEmpty)
|
||||
{
|
||||
// Toolbox V1 puts the IDEs into an "apps" subdirectory of the install location.
|
||||
String appsPath = scope .();
|
||||
Path.Combine(appsPath, customInstallLocation, "apps");
|
||||
|
||||
int countBefore = outInstalls.Count;
|
||||
CollectPathsFromToolboxDirectory(appsPath, outInstalls);
|
||||
|
||||
if (outInstalls.Count != countBefore)
|
||||
return;
|
||||
|
||||
// Toolbox V2 puts them directly into the install location.
|
||||
CollectPathsFromToolboxDirectory(customInstallLocation, outInstalls);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Toolbox V1 default install location.
|
||||
String defaultAppsPath = scope .();
|
||||
Path.Combine(defaultAppsPath, toolboxPath, "apps");
|
||||
|
||||
int countBefore = outInstalls.Count;
|
||||
CollectPathsFromToolboxDirectory(defaultAppsPath, outInstalls);
|
||||
|
||||
if (outInstalls.Count != countBefore)
|
||||
return;
|
||||
|
||||
// Toolbox V2 default install location.
|
||||
String defaultInstallLocation = scope .();
|
||||
if (GetDefaultIdeInstallLocationForToolboxV2(defaultInstallLocation) case .Ok)
|
||||
CollectPathsFromToolboxDirectory(defaultInstallLocation, outInstalls);
|
||||
}
|
||||
|
||||
/// Searches the given directory for Rider executables and creates the installation infos for them.
|
||||
protected static void CollectPathsFromToolboxDirectory(StringView toolboxRiderRootPath, List<RiderInstallInfo> outInstalls)
|
||||
{
|
||||
if (toolboxRiderRootPath.IsEmpty || !Directory.Exists(toolboxRiderRootPath))
|
||||
return;
|
||||
|
||||
List<String> riderPaths = scope .();
|
||||
defer { ClearAndDeleteItems!(riderPaths); }
|
||||
|
||||
int searchedDirectories = 0;
|
||||
FindFilesRecursive(toolboxRiderRootPath, GetRiderExecutableName(), riderPaths, MaxSearchDepth, ref searchedDirectories);
|
||||
|
||||
// Reused across the iterations, see the note in FindFilesRecursive.
|
||||
String historyJsonPath = scope .();
|
||||
|
||||
for (String riderPath in riderPaths)
|
||||
{
|
||||
if (!(GetInstallInfoFromRiderPath(riderPath, .Toolbox) case .Ok(let installInfo)))
|
||||
continue;
|
||||
|
||||
// The Toolbox leaves the directories of old versions behind after an update, so we
|
||||
// check which build it actually considers the current one for this installation.
|
||||
historyJsonPath.Clear();
|
||||
|
||||
if (GetHistoryJsonPath(riderPath, historyJsonPath) case .Ok)
|
||||
{
|
||||
RiderVersion lastBuildVersion = GetLastBuildVersion(historyJsonPath);
|
||||
|
||||
if (lastBuildVersion.IsInitialized && installInfo.Version != lastBuildVersion)
|
||||
{
|
||||
delete installInfo;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
outInstalls.Add(installInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively searches the given directory for files with the given name.
|
||||
/// @remarks corlib's Directory.EnumerateFiles doesn't search subdirectories.
|
||||
protected static void FindFilesRecursive(StringView directory, StringView fileName, List<String> outPaths,
|
||||
int remainingDepth, ref int searchedDirectories)
|
||||
{
|
||||
if (remainingDepth <= 0 || searchedDirectories >= MaxSearchedDirectories)
|
||||
return;
|
||||
|
||||
searchedDirectories++;
|
||||
|
||||
for (FileFindEntry entry in Directory.EnumerateFiles(directory, fileName))
|
||||
{
|
||||
String path = new String();
|
||||
entry.GetFilePath(path);
|
||||
outPaths.Add(path);
|
||||
}
|
||||
|
||||
// Reused across the iterations: a "scope" allocation inside a loop body lives until the
|
||||
// enclosing method returns, so allocating per iteration would grow the stack unboundedly.
|
||||
String entryName = scope .();
|
||||
String subDirectory = scope .();
|
||||
|
||||
for (FileFindEntry entry in Directory.EnumerateDirectories(directory))
|
||||
{
|
||||
entryName.Clear();
|
||||
entry.GetFileName(entryName);
|
||||
|
||||
if (entryName == "." || entryName == "..")
|
||||
continue;
|
||||
|
||||
subDirectory.Clear();
|
||||
entry.GetFilePath(subDirectory);
|
||||
|
||||
FindFilesRecursive(subDirectory, fileName, outPaths, remainingDepth - 1, ref searchedDirectories);
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the custom install location out of the Toolbox' ".settings.json".
|
||||
protected static void ExtractPathFromSettingsJson(StringView toolboxPath, String outInstallLocation)
|
||||
{
|
||||
String settingsJsonPath = scope .();
|
||||
Path.Combine(settingsJsonPath, toolboxPath, ".settings.json");
|
||||
|
||||
if (!File.Exists(settingsJsonPath))
|
||||
return;
|
||||
|
||||
StructuredData data = scope .();
|
||||
if (data.Load(settingsJsonPath) case .Err)
|
||||
return;
|
||||
|
||||
data.GetString("install_location", outInstallLocation);
|
||||
}
|
||||
|
||||
/// Reads the version of a Rider installation out of its "product-info.json".
|
||||
protected static RiderVersion ParseProductInfoJson(StringView productInfoJsonPath, String outName, String outVersion)
|
||||
{
|
||||
StructuredData data = scope .();
|
||||
if (data.Load(productInfoJsonPath) case .Err)
|
||||
return .();
|
||||
|
||||
String buildNumber = scope .();
|
||||
data.GetString("buildNumber", buildNumber);
|
||||
|
||||
data.GetString("name", outName);
|
||||
data.GetString("version", outVersion);
|
||||
|
||||
return RiderVersion.Parse(buildNumber);
|
||||
}
|
||||
|
||||
/// Walks up from the given Rider executable looking for the Toolbox' ".history.json".
|
||||
protected static Result<void> GetHistoryJsonPath(StringView riderPath, String outPath)
|
||||
{
|
||||
String directory = scope .();
|
||||
Try!(Path.GetDirectoryPath(riderPath, directory));
|
||||
|
||||
String parentDirectory = scope .();
|
||||
String historyPath = scope .();
|
||||
|
||||
// Bounded so that we don't walk all the way up to the drive root.
|
||||
for (int i < 10)
|
||||
{
|
||||
if (!Directory.Exists(directory))
|
||||
break;
|
||||
|
||||
historyPath.Clear();
|
||||
Path.Combine(historyPath, directory, ".history.json");
|
||||
|
||||
if (File.Exists(historyPath))
|
||||
{
|
||||
outPath.Append(historyPath);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
parentDirectory.Clear();
|
||||
if (Path.GetDirectoryPath(directory, parentDirectory) case .Err)
|
||||
break;
|
||||
|
||||
if (parentDirectory.IsEmpty || parentDirectory == directory)
|
||||
break;
|
||||
|
||||
directory.Set(parentDirectory);
|
||||
}
|
||||
|
||||
return .Err;
|
||||
}
|
||||
|
||||
/// Reads the build number of the most recent entry of the Toolbox' ".history.json".
|
||||
protected static RiderVersion GetLastBuildVersion(StringView historyJsonPath)
|
||||
{
|
||||
StructuredData data = scope .();
|
||||
if (data.Load(historyJsonPath) case .Err)
|
||||
return .();
|
||||
|
||||
String lastBuild = scope .();
|
||||
|
||||
// The most recent build is the last entry of the "history" array.
|
||||
for (data.Enumerate("history"))
|
||||
{
|
||||
lastBuild.Clear();
|
||||
|
||||
using (data.Open("item"))
|
||||
{
|
||||
data.GetString("build", lastBuild);
|
||||
}
|
||||
}
|
||||
|
||||
return RiderVersion.Parse(lastBuild);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
// Windows implementation of the RiderPathLocator, see RiderPathLocator.bf for the platform independent part and the license of the original code.
|
||||
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace GlitchyEditor.CodeEditors;
|
||||
|
||||
extension RiderPathLocator
|
||||
{
|
||||
private const String RiderExecutableName = "rider64.exe";
|
||||
|
||||
/// The maximum length of a registry key name.
|
||||
private const int MaxRegistryKeyNameLength = 255;
|
||||
|
||||
[Import("advapi32.lib"), CLink, CallingConvention(.Stdcall)]
|
||||
private static extern int32 RegEnumKeyExW(Windows.HKey hKey, uint32 dwIndex, char16* lpName,
|
||||
uint32* lpcchName, uint32* lpReserved, char16* lpClass, uint32* lpcchClass,
|
||||
void* lpftLastWriteTime);
|
||||
|
||||
protected static override StringView GetRiderExecutableName() => RiderExecutableName;
|
||||
|
||||
public static override Result<RiderInstallInfo> GetInstallInfoFromRiderPath(StringView riderExePath, RiderInstallType installType)
|
||||
{
|
||||
if (!File.Exists(riderExePath))
|
||||
return .Err;
|
||||
|
||||
String riderDirectory = scope .();
|
||||
Try!(GetRiderDirectory(riderExePath, riderDirectory));
|
||||
|
||||
RiderVersion version = .();
|
||||
|
||||
String installName = scope .();
|
||||
String installVersion = scope .();
|
||||
|
||||
String productInfoPath = scope .();
|
||||
Path.Combine(productInfoPath, riderDirectory, "product-info.json");
|
||||
|
||||
if (File.Exists(productInfoPath))
|
||||
version = ParseProductInfoJson(productInfoPath, installName, installVersion);
|
||||
else
|
||||
{
|
||||
installName.Set("Rider");
|
||||
version.ToString(installVersion..Clear());
|
||||
}
|
||||
|
||||
if (!version.IsInitialized)
|
||||
{
|
||||
// Toolbox installations are laid out as "<...>/<build number>/bin/rider64.exe",
|
||||
// so the name of the installation directory is the version.
|
||||
String directoryName = scope .();
|
||||
Path.GetFileName(riderDirectory, directoryName);
|
||||
|
||||
version = RiderVersion.Parse(directoryName);
|
||||
}
|
||||
|
||||
return new RiderInstallInfo(riderExePath, version, installType, scope $"{installName} {installVersion}");
|
||||
}
|
||||
|
||||
public static override void CollectAllPaths(List<RiderInstallInfo> outInstalls)
|
||||
{
|
||||
const String UninstallKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
|
||||
const String UninstallKey32 = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
|
||||
const String RiderKey = @"SOFTWARE\JetBrains\Rider";
|
||||
const String RiderKey32 = @"SOFTWARE\WOW6432Node\JetBrains\Rider";
|
||||
|
||||
List<RiderInstallInfo> found = scope .();
|
||||
// Everything that isn't moved into outInstalls (i.e. the duplicates) is deleted here.
|
||||
defer { ClearAndDeleteItems!(found); }
|
||||
|
||||
// Regular installations register themselves in the uninstall list.
|
||||
CollectPathsFromRegistry(Windows.HKEY_CURRENT_USER, UninstallKey, "InstallLocation", "Rider", found);
|
||||
CollectPathsFromRegistry(Windows.HKEY_LOCAL_MACHINE, UninstallKey, "InstallLocation", "Rider", found);
|
||||
CollectPathsFromRegistry(Windows.HKEY_CURRENT_USER, UninstallKey32, "InstallLocation", "Rider", found);
|
||||
CollectPathsFromRegistry(Windows.HKEY_LOCAL_MACHINE, UninstallKey32, "InstallLocation", "Rider", found);
|
||||
|
||||
// dotUltimate installations use their own key.
|
||||
CollectPathsFromRegistry(Windows.HKEY_CURRENT_USER, RiderKey, "InstallDir", "", found);
|
||||
CollectPathsFromRegistry(Windows.HKEY_LOCAL_MACHINE, RiderKey, "InstallDir", "", found);
|
||||
CollectPathsFromRegistry(Windows.HKEY_CURRENT_USER, RiderKey32, "InstallDir", "", found);
|
||||
CollectPathsFromRegistry(Windows.HKEY_LOCAL_MACHINE, RiderKey32, "InstallDir", "", found);
|
||||
|
||||
// Toolbox installations aren't registered anywhere, we have to go looking for them.
|
||||
String defaultToolboxPath = scope .();
|
||||
if (GetToolboxPathFromEnvironment(defaultToolboxPath) case .Ok)
|
||||
CollectPathsFromToolbox(defaultToolboxPath, found);
|
||||
|
||||
CollectPathsFromToolboxRegistry(Windows.HKEY_CURRENT_USER, @"Software\JetBrains\Toolbox\", found);
|
||||
CollectPathsFromToolboxRegistry(Windows.HKEY_LOCAL_MACHINE, @"Software\JetBrains\Toolbox\", found);
|
||||
CollectPathsFromToolboxRegistry(Windows.HKEY_CURRENT_USER, @"Software\JetBrains s.r.o.\JetBrainsToolbox\", found);
|
||||
CollectPathsFromToolboxRegistry(Windows.HKEY_LOCAL_MACHINE, @"Software\JetBrains s.r.o.\JetBrainsToolbox\", found);
|
||||
|
||||
DeduplicateAndSort(found, outInstalls);
|
||||
}
|
||||
|
||||
/// Enumerates the subkeys of the given registry key and creates an installation info for
|
||||
/// every subkey that points at a Rider installation.
|
||||
/// @param rootKey The registry hive to look in.
|
||||
/// @param subKeyPath The key whose subkeys will be enumerated.
|
||||
/// @param valueName The name of the value that holds the installation directory.
|
||||
/// @param keyNameFilter If not empty, only subkeys whose name contains this string are looked at.
|
||||
private static void CollectPathsFromRegistry(Windows.HKey rootKey, StringView subKeyPath,
|
||||
StringView valueName, StringView keyNameFilter, List<RiderInstallInfo> outInstalls)
|
||||
{
|
||||
Windows.HKey key;
|
||||
if (Windows.RegOpenKeyExW(rootKey, subKeyPath.ToScopedNativeWChar!(), 0, (.)Windows.KEY_READ, out key) != Windows.ERROR_SUCCESS)
|
||||
return;
|
||||
|
||||
defer Windows.RegCloseKey(key);
|
||||
|
||||
List<String> subKeyNames = scope .();
|
||||
defer { ClearAndDeleteItems!(subKeyNames); }
|
||||
|
||||
if (EnumerateSubKeys(key, subKeyNames) case .Err)
|
||||
return;
|
||||
|
||||
// All of these are reused across the iterations: a "scope" allocation inside a loop body
|
||||
// lives until the enclosing method returns, and the uninstall key has hundreds of subkeys.
|
||||
char16[MaxRegistryKeyNameLength + 1] subKeyNameBuffer = ?;
|
||||
String installLocation = scope .();
|
||||
String exePath = scope .();
|
||||
|
||||
for (String subKeyName in subKeyNames)
|
||||
{
|
||||
if (!keyNameFilter.IsEmpty && !subKeyName.Contains(keyNameFilter))
|
||||
continue;
|
||||
|
||||
int encodedLength = UTF16.GetEncodedLen(subKeyName);
|
||||
if (encodedLength > MaxRegistryKeyNameLength)
|
||||
continue;
|
||||
|
||||
UTF16.Encode(subKeyName, &subKeyNameBuffer, encodedLength).IgnoreError();
|
||||
subKeyNameBuffer[encodedLength] = 0;
|
||||
|
||||
Windows.HKey subKey;
|
||||
if (Windows.RegOpenKeyExW(key, &subKeyNameBuffer, 0, (.)Windows.KEY_READ, out subKey) != Windows.ERROR_SUCCESS)
|
||||
continue;
|
||||
|
||||
installLocation.Clear();
|
||||
Result<void> valueResult = subKey.GetValue(valueName, installLocation);
|
||||
|
||||
Windows.RegCloseKey(subKey);
|
||||
|
||||
if (valueResult case .Err)
|
||||
continue;
|
||||
|
||||
if (installLocation.IsWhiteSpace)
|
||||
continue;
|
||||
|
||||
exePath.Clear();
|
||||
Path.Combine(exePath, installLocation, "bin", RiderExecutableName);
|
||||
|
||||
if (GetInstallInfoFromRiderPath(exePath, .Installed) case .Ok(let installInfo))
|
||||
outInstalls.Add(installInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumerates the names of all subkeys of the given registry key.
|
||||
/// @remarks Windows.HKey.EnumValue enumerates values (and is unimplemented), there is no
|
||||
/// counterpart for subkeys in corlib.
|
||||
private static Result<void> EnumerateSubKeys(Windows.HKey key, List<String> outNames)
|
||||
{
|
||||
// Reused across the iterations, see the note in CollectPathsFromRegistry.
|
||||
char16[MaxRegistryKeyNameLength + 1] nameBuffer = ?;
|
||||
|
||||
for (uint32 index = 0; true; index++)
|
||||
{
|
||||
uint32 nameLength = nameBuffer.Count;
|
||||
|
||||
int32 result = RegEnumKeyExW(key, index, &nameBuffer, &nameLength, null, null, null, null);
|
||||
|
||||
if (result == Windows.ERROR_NO_MORE_ITEMS)
|
||||
break;
|
||||
|
||||
if (result != Windows.ERROR_SUCCESS)
|
||||
return .Err;
|
||||
|
||||
// RegEnumKeyExW null-terminates, but be explicit about it since we reuse the buffer.
|
||||
nameBuffer[Math.Min((int)nameLength, MaxRegistryKeyNameLength)] = 0;
|
||||
|
||||
outNames.Add(new String(&nameBuffer));
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
/// Gets the default location of the JetBrains Toolbox ("%LOCALAPPDATA%/JetBrains/Toolbox").
|
||||
private static Result<void> GetToolboxPathFromEnvironment(String outPath)
|
||||
{
|
||||
String localAppData = scope .();
|
||||
Try!(Environment.GetEnvironmentVariable("LOCALAPPDATA", localAppData));
|
||||
|
||||
Path.Combine(outPath, localAppData, "JetBrains", "Toolbox");
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
protected static override Result<void> GetDefaultIdeInstallLocationForToolboxV2(String outPath)
|
||||
{
|
||||
String localAppData = scope .();
|
||||
Try!(Environment.GetEnvironmentVariable("LOCALAPPDATA", localAppData));
|
||||
|
||||
Path.Combine(outPath, localAppData, "Programs");
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
/// Looks up the location of the JetBrains Toolbox in the registry and collects the Rider
|
||||
/// installations it manages.
|
||||
private static void CollectPathsFromToolboxRegistry(Windows.HKey rootKey, StringView subKeyPath, List<RiderInstallInfo> outInstalls)
|
||||
{
|
||||
Windows.HKey key;
|
||||
if (Windows.RegOpenKeyExW(rootKey, subKeyPath.ToScopedNativeWChar!(), 0, (.)Windows.KEY_READ, out key) != Windows.ERROR_SUCCESS)
|
||||
return;
|
||||
|
||||
defer Windows.RegCloseKey(key);
|
||||
|
||||
// The default value of the key contains the path of the Toolbox' "bin" directory.
|
||||
String toolboxBinPath = scope .();
|
||||
if (key.GetValue("", toolboxBinPath) case .Err)
|
||||
return;
|
||||
|
||||
if (toolboxBinPath.IsWhiteSpace)
|
||||
return;
|
||||
|
||||
String binDirectoryName = scope .();
|
||||
Path.GetFileName(toolboxBinPath, binDirectoryName);
|
||||
|
||||
if (StringView.Compare(binDirectoryName, "bin", true) != 0)
|
||||
return;
|
||||
|
||||
String toolboxPath = scope .();
|
||||
if (Path.GetDirectoryPath(toolboxBinPath, toolboxPath) case .Err)
|
||||
return;
|
||||
|
||||
CollectPathsFromToolbox(toolboxPath, outInstalls);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,65 +1,70 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using GlitchyEngine;
|
||||
using System.Collections;
|
||||
using GlitchyEditor.EditWindows;
|
||||
using GlitchyEditor.Settings;
|
||||
|
||||
namespace GlitchyEditor.CodeEditors;
|
||||
|
||||
class VisualStudioIdeAdapter : IIdeAdapter
|
||||
{
|
||||
public static bool IsRunning()
|
||||
private IdeInstallation _ideInstallation;
|
||||
|
||||
public this(IdeInstallation ideInstallation)
|
||||
{
|
||||
List<Process> processes = scope .();
|
||||
Debug.Assert(ideInstallation.Ide == .VisualStudio);
|
||||
|
||||
if (Process.GetProcesses(processes) case .Err)
|
||||
{
|
||||
Log.EngineLogger.Error("Failed to get running processes.");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Process process in processes)
|
||||
{
|
||||
}
|
||||
|
||||
ClearAndDeleteItems!(processes);
|
||||
|
||||
return false;
|
||||
_ideInstallation = ideInstallation;
|
||||
}
|
||||
|
||||
public static void OpenScript(StringView fileName)
|
||||
public void OpenScript(StringView fileName, int lineNumber)
|
||||
{
|
||||
if (IsRunning())
|
||||
{
|
||||
ProcessStartInfo startInfo = scope .();
|
||||
startInfo.SetFileName(Application.Instance.Settings.ScriptSettings.VisualStudioPath);
|
||||
startInfo.SetArguments(scope $"/Edit {fileName}");
|
||||
|
||||
scope SpawnedProcess().Start(startInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessStartInfo startInfo = scope .();
|
||||
startInfo.SetFileName(Application.Instance.Settings.ScriptSettings.VisualStudioPath);
|
||||
startInfo.SetArguments(scope $"/Edit {fileName}");
|
||||
|
||||
scope SpawnedProcess().Start(startInfo);
|
||||
}
|
||||
Open(fileName, lineNumber);
|
||||
}
|
||||
|
||||
public static void OpenScript(StringView fileName, int lineNumber)
|
||||
public void OpenScriptProject()
|
||||
{
|
||||
OpenScript(fileName);
|
||||
Open(null, 0);
|
||||
}
|
||||
|
||||
public static void OpenScriptProject()
|
||||
private void Open(StringView fileName, int lineNumber)
|
||||
{
|
||||
String solutionPath = scope .();
|
||||
Editor.Instance.CurrentProject.PathInProject(solutionPath, scope $"{Editor.Instance.CurrentProject.Name}.sln");
|
||||
String solutionPath = scope String();
|
||||
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
||||
|
||||
ProcessStartInfo psi = scope .();
|
||||
psi.SetFileName("devenv");
|
||||
psi.SetArguments(solutionPath);
|
||||
if (!File.Exists(solutionPath))
|
||||
{
|
||||
Log.EngineLogger.Error(scope $"Could not find solution file \"{solutionPath}\".");
|
||||
return;
|
||||
}
|
||||
|
||||
scope SpawnedProcess().Start(psi);
|
||||
if (!File.Exists(_ideInstallation.Path))
|
||||
{
|
||||
Editor.Instance.ShowSettings();
|
||||
Editor.Instance.SettingsWindow.HighlightSetting("Tools", "IDE");
|
||||
|
||||
PopupService.Instance.ShowMessageBox("Visual Studio not found.",
|
||||
"The Visual Studio path could not be found. Please select a different IDE.");
|
||||
return;
|
||||
}
|
||||
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
// Reuses a running Visual Studio instance that has our solution open (via EnvDTE),
|
||||
// otherwise starts a new one.
|
||||
DteOpenRequest request = new .();
|
||||
request.SolutionPath = solutionPath;
|
||||
request.DevenvPath = _ideInstallation.Path;
|
||||
request.FileName = fileName;
|
||||
request.LineNumber = lineNumber;
|
||||
|
||||
DteOpenRequest.Start(request);
|
||||
#else
|
||||
ProcessStartInfo startInfo = scope .();
|
||||
startInfo.SetFileName(_ideInstallation.Path);
|
||||
startInfo.SetArguments(scope $"\"{solutionPath}\"");
|
||||
|
||||
scope SpawnedProcess().Start(startInfo);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class AssetViewer : EditorWindow
|
||||
|
||||
private AssetHandle _selectedAsset;
|
||||
|
||||
append TexturererViewerer _textureViewer = .();
|
||||
append TextureViewer _textureViewer = .();
|
||||
|
||||
public this(EditorContentManager contentManager)
|
||||
{
|
||||
@@ -133,7 +133,7 @@ class AssetViewer : EditorWindow
|
||||
}
|
||||
}
|
||||
|
||||
class TexturererViewerer
|
||||
class TextureViewer
|
||||
{
|
||||
enum BackgroundMode : int32
|
||||
{
|
||||
@@ -152,35 +152,55 @@ class TexturererViewerer
|
||||
|
||||
enum ColorChannelSwizzle : int32
|
||||
{
|
||||
None,
|
||||
Zero,
|
||||
One,
|
||||
R,
|
||||
G,
|
||||
B,
|
||||
A
|
||||
A,
|
||||
OneMinusR,
|
||||
OneMinusG,
|
||||
OneMinusB,
|
||||
OneMinusA
|
||||
}
|
||||
|
||||
AssetHandle<Effect> _effect;
|
||||
AssetHandle<Effect> _renderTargetEffect;
|
||||
//AssetHandle<Effect> _renderTargetEffect;
|
||||
Material _renderTargetMaterial ~ _.ReleaseRef();
|
||||
|
||||
float _zoom = 1.0f;
|
||||
bool _showTexelBorder = false;
|
||||
|
||||
BackgroundMode _backgroundMode = .Checkerboard;
|
||||
ColorRGBA _backgroundColor;
|
||||
|
||||
SampleMode _sampleMode = .Linear;
|
||||
|
||||
RenderTarget2D _target ~ _?.ReleaseRef();
|
||||
// TODO: we don't need depth!
|
||||
DepthStencilTarget _depth ~ _?.ReleaseRef();
|
||||
RenderTargetGroup _targets ~ _?.ReleaseRef();
|
||||
|
||||
public this()
|
||||
{
|
||||
_effect = Content.LoadAsset("Resources/Shaders/textureViewerShader.hlsl");
|
||||
_renderTargetEffect = Content.LoadAsset("Resources/Shaders/RenderTargetGroupViewer.hlsl");
|
||||
_effect = Content.LoadAsset("Resources/Shaders/textureViewerShader.fx", null, true);
|
||||
AssetHandle<Effect> effect = Content.LoadAsset("Resources/Shaders/RenderTargetGroupViewer.fx", null, true);
|
||||
|
||||
_renderTargetMaterial = new Material(effect);
|
||||
|
||||
_targets = new RenderTargetGroup(.(){
|
||||
Width = 100,
|
||||
Height = 100,
|
||||
ColorTargetDescriptions = TargetDescription[](
|
||||
.(.R8G8B8A8_UNorm, clearColor: .Color(ColorRGBA.Pink), ownDebugName: new $"Color"),
|
||||
.(.R32_UInt, ownDebugName: new $"Enity ID (Unused)")
|
||||
),
|
||||
DepthTargetDescription = .(.D24_UNorm_S8_UInt, ownDebugName: new $"Depth Stencil")
|
||||
});
|
||||
_targets.[Friend]Identifier = "Asset Viewer";
|
||||
}
|
||||
|
||||
float2 _position;
|
||||
|
||||
float4 _viewAreaRect;
|
||||
|
||||
bool _moving;
|
||||
|
||||
float _colorOffset = 0;
|
||||
@@ -204,10 +224,33 @@ class TexturererViewerer
|
||||
if (ImGui.CollapsingHeader("View"))
|
||||
{
|
||||
ImGui.SliderFloat("Zoom", &_zoom, 0.01f, 100.0f);
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Reset##ResetZoom"))
|
||||
{
|
||||
_zoom = 1.0f;
|
||||
}
|
||||
|
||||
float maxDimension = max(width, height);
|
||||
|
||||
ImGui.SliderFloat2("Position", ref *(float[2]*)&_position, 2 * -maxDimension * _zoom, 2 * maxDimension * _zoom);
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.Button("Reset##ResetPos"))
|
||||
{
|
||||
_position = 0.0f;
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
if (ImGui.Button("Fit to View"))
|
||||
{
|
||||
_position = 0.0f;
|
||||
|
||||
_zoom = min(_targets.Width / width, _targets.Height / height);
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
@@ -215,7 +258,8 @@ class TexturererViewerer
|
||||
|
||||
if (_backgroundMode == .CustomColor)
|
||||
{
|
||||
ImGui.ColorPicker4("Color", ref _backgroundColor);
|
||||
ImGui.ColorEdit3("Color", ref _backgroundColor);
|
||||
_backgroundColor.A = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,22 +269,22 @@ class TexturererViewerer
|
||||
|
||||
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.SliderInt("Mip Level", &_mipLevel, 0, (int32)maxMips - 1);
|
||||
|
||||
ImGui.BeginDisabled(arraySize <= 1);
|
||||
ImGui.SliderInt("Array Slice", &_arraySlice, 0, (int32)arraySize - 1);
|
||||
ImGui.EndDisabled();
|
||||
|
||||
if (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 ?? "???")}");
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.CollapsingHeader("Color and Transparency"))
|
||||
{
|
||||
ImGui.TextUnformatted("Channel Swizzle:");
|
||||
|
||||
if (ImGui.BeginTable("ColorAndAlpha", 4))
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
@@ -296,6 +340,10 @@ class TexturererViewerer
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
ImGui.Checkbox("Show texel grid", &_showTexelBorder);
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
ImGui.TextUnformatted("Channel Swizzle:");
|
||||
|
||||
if (ImGui.BeginTable("SwizzleTable", 9))
|
||||
@@ -324,6 +372,18 @@ class TexturererViewerer
|
||||
ImGui.EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.CollapsingHeader("Info"))
|
||||
{
|
||||
ImGui.Text($"Resolution: {width} x {height}");
|
||||
ImGui.Text($"Array Size: {arraySize}");
|
||||
ImGui.Text($"Mip Levels: {maxMips}");
|
||||
|
||||
if (rtGroup != null)
|
||||
{
|
||||
ImGui.Text($"Group Size: {rtGroup.TargetCount}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SwizzleCombo(StringView text, ref ColorChannelSwizzle swizzle)
|
||||
@@ -334,6 +394,12 @@ class TexturererViewerer
|
||||
|
||||
if (ImGui.BeginCombo(scope $"##swizzle{text}", scope $"{swizzle}"))
|
||||
{
|
||||
if (ImGui.Selectable("0", swizzle == .Zero))
|
||||
swizzle = .Zero;
|
||||
|
||||
if (ImGui.Selectable("1", swizzle == .One))
|
||||
swizzle = .One;
|
||||
|
||||
if (ImGui.Selectable("R", swizzle == .R))
|
||||
swizzle = .R;
|
||||
|
||||
@@ -345,17 +411,26 @@ class TexturererViewerer
|
||||
|
||||
if (ImGui.Selectable("A", swizzle == .A))
|
||||
swizzle = .A;
|
||||
|
||||
if (ImGui.Selectable("1 - R", swizzle == .OneMinusR))
|
||||
swizzle = .OneMinusR;
|
||||
|
||||
if (ImGui.Selectable("1 - G", swizzle == .OneMinusG))
|
||||
swizzle = .OneMinusG;
|
||||
|
||||
if (ImGui.Selectable("None", swizzle == .None))
|
||||
swizzle = .None;
|
||||
if (ImGui.Selectable("1 - B", swizzle == .OneMinusB))
|
||||
swizzle = .OneMinusB;
|
||||
|
||||
if (ImGui.Selectable("1 - A", swizzle == .OneMinusA))
|
||||
swizzle = .OneMinusA;
|
||||
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
}
|
||||
|
||||
public void ViewTexture(RenderTargetGroup texture)
|
||||
public void ViewTexture(RenderTargetGroup textureGroup)
|
||||
{
|
||||
ShowSettings(texture.Width, texture.Height, texture.MipLevels - 1, texture.ArraySize - 1, texture);
|
||||
ShowSettings(textureGroup.Width, textureGroup.Height, textureGroup.MipLevels, textureGroup.ArraySize, textureGroup);
|
||||
|
||||
if (ImGui.BeginChild("imageChild"))
|
||||
{
|
||||
@@ -365,21 +440,19 @@ class TexturererViewerer
|
||||
|
||||
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)
|
||||
|
||||
if (_targets.Width != viewportSize.x || _targets.Height != viewportSize.y)
|
||||
{
|
||||
_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);
|
||||
_targets.Resize((.)viewportSize.x, (.)viewportSize.y);
|
||||
}
|
||||
|
||||
|
||||
RenderBackground();
|
||||
|
||||
RenderTexture(texture);
|
||||
|
||||
ImGui.Image(_target, viewportSize);
|
||||
RenderTexture(textureGroup);
|
||||
|
||||
ImGui.Image(_targets.GetViewBinding(0), viewportSize);
|
||||
|
||||
_lastPreviewTopLeft = (.)ImGui.GetItemRectMin();
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
@@ -387,7 +460,7 @@ class TexturererViewerer
|
||||
|
||||
public void ViewTexture(Texture texture)
|
||||
{
|
||||
ShowSettings(texture.Width, texture.Height, texture.MipLevels - 1, texture.ArraySize - 1, null);
|
||||
ShowSettings(texture.Width, texture.Height, texture.MipLevels, texture.ArraySize, null);
|
||||
|
||||
if (ImGui.BeginChild("imageChild"))
|
||||
{
|
||||
@@ -398,20 +471,18 @@ class TexturererViewerer
|
||||
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)
|
||||
if (_targets.Width != viewportSize.x || _targets.Height != viewportSize.y)
|
||||
{
|
||||
_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);
|
||||
_targets.Resize((.)viewportSize.x, (.)viewportSize.y);
|
||||
}
|
||||
|
||||
RenderBackground();
|
||||
|
||||
RenderTexture(texture);
|
||||
|
||||
ImGui.Image(_target, viewportSize);
|
||||
|
||||
ImGui.Image(_targets.GetViewBinding(0), viewportSize);
|
||||
|
||||
_lastPreviewTopLeft = (.)ImGui.GetItemRectMin();
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
@@ -419,11 +490,11 @@ class TexturererViewerer
|
||||
|
||||
float lastWheel;
|
||||
|
||||
float2 _lastPreviewTopLeft;
|
||||
|
||||
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();
|
||||
|
||||
@@ -441,18 +512,21 @@ class TexturererViewerer
|
||||
float mouseWheel = ImGui.GetIO().MouseWheel;
|
||||
|
||||
float delta = mouseWheel - lastWheel;
|
||||
|
||||
float2 mousePosInObject = .(mousePos.x, mousePos.y) - _lastPreviewTopLeft;
|
||||
|
||||
float2 mouseOffsetFromCenter = mousePosInObject - float2(_targets.Width, _targets.Height) / 2.0f;
|
||||
|
||||
if(delta != 0)
|
||||
{
|
||||
_position -= mouseInWindow;
|
||||
_position -= mouseOffsetFromCenter;
|
||||
_position /= _zoom;
|
||||
|
||||
_zoom *= Math.Pow(1.1f, delta);
|
||||
|
||||
_position *= _zoom;
|
||||
_position += mouseInWindow;
|
||||
_position += mouseOffsetFromCenter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(_moving)
|
||||
@@ -468,19 +542,21 @@ class TexturererViewerer
|
||||
|
||||
private void RenderBackground()
|
||||
{
|
||||
Viewport vp = .(0, 0, _target.Width, _target.Height);
|
||||
Viewport vp = .(0, 0, _targets.Width, _targets.Height);
|
||||
RenderCommand.SetViewport(vp);
|
||||
|
||||
RenderCommand.Clear(_targets, .ColorDepth);
|
||||
// TODO: don't clear pink!
|
||||
RenderCommand.Clear(_target, .Pink);
|
||||
RenderCommand.Clear(_depth, .Depth, 1.0f, 0);
|
||||
//RenderCommand.Clear(_target, .Pink);
|
||||
//RenderCommand.Clear(_depth, .Depth, 1.0f, 0);
|
||||
|
||||
//_target.Bind();
|
||||
RenderCommand.SetRenderTarget(_target);
|
||||
RenderCommand.SetDepthStencilTarget(_depth);
|
||||
RenderCommand.SetRenderTargetGroup(_targets);
|
||||
//RenderCommand.SetRenderTarget(_idTarget, 1);
|
||||
//RenderCommand.SetDepthStencilTarget(_depth);
|
||||
RenderCommand.BindRenderTargets();
|
||||
|
||||
float2 targetSize = float2(_target.Width, _target.Height);
|
||||
float2 targetSize = float2(_targets.Width, _targets.Height);
|
||||
|
||||
_camera.Left = 0;
|
||||
_camera.Top = 0;
|
||||
@@ -520,9 +596,9 @@ class TexturererViewerer
|
||||
Renderer2D.EndScene();
|
||||
}
|
||||
|
||||
private void RenderTexture(TextureViewBinding viewedTexture, float2 textureSize, Format format)
|
||||
private void RenderTexture(Asset textureAsset, float2 textureSize, Format format, int32? groupTargetIndex = null)
|
||||
{
|
||||
float2 mippedTextureSize = float2((int)textureSize.X >> _mipLevel, (int)textureSize.X >> _mipLevel);
|
||||
float2 mippedTextureSize = float2((int)textureSize.X >> _mipLevel, (int)textureSize.Y >> _mipLevel);
|
||||
|
||||
// Sicherstellen, dass die Auflösung nicht 0 wird
|
||||
mippedTextureSize = max(mippedTextureSize, 1);
|
||||
@@ -541,55 +617,64 @@ class TexturererViewerer
|
||||
viewedTexture.SamplerState = SamplerStateManager.LinearClamp;
|
||||
}*/
|
||||
|
||||
//Renderer2D.BeginScene(_camera, .SortByTexture, _effect);
|
||||
RenderCommand.Clear(_targets, .Depth);
|
||||
|
||||
// float3(_position * .(1, -1), 0)
|
||||
RenderCommand.Clear(_depth, .Depth, 1.0f, 0);
|
||||
Matrix matrix = .Translation(float3((_position + float2(_targets.Width, _targets.Height) / 2) * .(1, -1), 0)) * .Scaling(float3(zoomedTextureSize, 1));
|
||||
|
||||
Matrix matrix = .Translation(float3(_position * .(1, -1), 0)) * .Scaling(float3(zoomedTextureSize, 1));
|
||||
_renderTargetMaterial.SetVariable("WorldViewProjection", _camera.ViewProjection * matrix);
|
||||
_renderTargetMaterial.SetVariable("ColorOffset", _colorOffset);
|
||||
_renderTargetMaterial.SetVariable("ColorScale", _colorScale);
|
||||
_renderTargetMaterial.SetVariable("AlphaOffset", _alphaOffset);
|
||||
_renderTargetMaterial.SetVariable("AlphaScale", _alphaScale);
|
||||
|
||||
// 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);
|
||||
_renderTargetMaterial.SetVariable("Texels", mippedTextureSize);
|
||||
|
||||
_renderTargetEffect.Variables["Texels"].SetData(mippedTextureSize);
|
||||
_renderTargetMaterial.SetVariable("MipLevel", (float)_mipLevel);
|
||||
|
||||
_renderTargetEffect.Variables["MipLevel"].SetData((float)_mipLevel);
|
||||
_renderTargetMaterial.SetVariable("Swizzle", int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA));
|
||||
|
||||
_renderTargetMaterial.SetVariable("ShowTexelBorders", _showTexelBorder);
|
||||
_renderTargetMaterial.SetVariable("TexelBorderCutoff", 10.0f);
|
||||
|
||||
_renderTargetEffect.Variables["Swizzle"].SetData(int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA));
|
||||
int textureSlot = -1;
|
||||
|
||||
if (((DirectX.DXGI.Format)format).IsInt())
|
||||
if (format.IsInt())
|
||||
{
|
||||
// Int Texture
|
||||
_renderTargetEffect.Variables["Mode"].SetData(1);
|
||||
_renderTargetEffect.SetTexture("IntTexture", viewedTexture);
|
||||
_renderTargetEffect.SetTexture("UIntTexture", null);
|
||||
_renderTargetEffect.SetTexture("Texture", null);
|
||||
// Int Texture
|
||||
_renderTargetMaterial.SetVariable("Mode", 1);
|
||||
textureSlot = _renderTargetMaterial.Effect.Textures["IntTexture"].PsSlot.Index;
|
||||
}
|
||||
else if (format.IsUInt())
|
||||
{
|
||||
// UInt Texture
|
||||
_renderTargetEffect.Variables["Mode"].SetData(2);
|
||||
_renderTargetEffect.SetTexture("IntTexture", null);
|
||||
_renderTargetEffect.SetTexture("UIntTexture", viewedTexture);
|
||||
_renderTargetEffect.SetTexture("Texture", null);
|
||||
_renderTargetMaterial.SetVariable("Mode", 2);
|
||||
textureSlot = _renderTargetMaterial.Effect.Textures["UIntTexture"].PsSlot.Index;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Float Texture
|
||||
_renderTargetEffect.Variables["Mode"].SetData(0);
|
||||
_renderTargetEffect.SetTexture("IntTexture", null);
|
||||
_renderTargetEffect.SetTexture("UIntTexture", null);
|
||||
_renderTargetEffect.SetTexture("Texture", viewedTexture);
|
||||
_renderTargetMaterial.SetVariable("Mode", 0);
|
||||
textureSlot = _renderTargetMaterial.Effect.Textures["Texture"].PsSlot.Index;
|
||||
}
|
||||
|
||||
_renderTargetEffect.Variables["Swizzle"].SetData(int4((int32)_swizzleR, (int32)_swizzleG, (int32)_swizzleB, (int32)_swizzleA));
|
||||
_renderTargetMaterial.Bind();
|
||||
|
||||
_renderTargetEffect.ApplyChanges();
|
||||
_renderTargetEffect.Bind();
|
||||
if (let renderTargetGroup = textureAsset as RenderTargetGroup)
|
||||
{
|
||||
using (let viewBinding = renderTargetGroup.GetViewBinding((.)groupTargetIndex))
|
||||
{
|
||||
RenderCommand.BindTexture(viewBinding, textureSlot, .Pixel);
|
||||
// TODO: BindSampler command?
|
||||
// TODO: Allow manually setting sampler for view binding?
|
||||
}
|
||||
}
|
||||
else if (let texture = textureAsset as Texture)
|
||||
{
|
||||
using (let viewBinding = texture.GetViewBinding())
|
||||
{
|
||||
RenderCommand.BindTexture(viewBinding, textureSlot, .Pixel);
|
||||
}
|
||||
}
|
||||
|
||||
Quad.Draw();
|
||||
}
|
||||
@@ -598,11 +683,11 @@ class TexturererViewerer
|
||||
{
|
||||
var desc = _groupIndex >= 0 ? viewedTexture.[Friend]_colorTargetDescriptions[_groupIndex] : viewedTexture.[Friend]_depthTargetDescription;
|
||||
|
||||
RenderTexture(viewedTexture.GetViewBinding(_groupIndex), float2(viewedTexture.Width, viewedTexture.Height), (.)desc.Format.GetShaderViewFormat());
|
||||
RenderTexture(viewedTexture, float2(viewedTexture.Width, viewedTexture.Height), (.)desc.Format.GetShaderViewFormat(), _groupIndex);
|
||||
}
|
||||
|
||||
private void RenderTexture(Texture viewedTexture)
|
||||
{
|
||||
RenderTexture(viewedTexture.GetViewBinding(), float2(viewedTexture.Width, viewedTexture.Height), viewedTexture.Format);
|
||||
RenderTexture(viewedTexture, float2(viewedTexture.Width, viewedTexture.Height), viewedTexture.Format, -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +470,8 @@ namespace GlitchyEditor.EditWindows
|
||||
spriteRendererComponent.Color = ColorRGBA.SRgbToLinear(spriteColor);
|
||||
|
||||
ImGui.PropertyTableStartNewProperty("Texture");
|
||||
ImGui.Button("...");
|
||||
ShowAssetDropTarget(ref spriteRendererComponent.Sprite);
|
||||
/*ImGui.Button("...");
|
||||
|
||||
if (ImGui.BeginDragDropTarget())
|
||||
{
|
||||
@@ -486,10 +487,86 @@ namespace GlitchyEditor.EditWindows
|
||||
}
|
||||
|
||||
ImGui.EndDragDropTarget();
|
||||
}
|
||||
}*/
|
||||
|
||||
ImGui.PropertyTableStartNewProperty("UV Transform");
|
||||
ImGui.Float4Editor("##UV Transform", ref spriteRendererComponent.UvTransform, resetValues: float4(0, 0, 1, 1));
|
||||
|
||||
ImGui.PropertyTableStartNewProperty("Material");
|
||||
|
||||
ShowAssetDropTarget<Material>(ref spriteRendererComponent.Material);
|
||||
}
|
||||
|
||||
public static bool ShowAssetDropTarget(ref AssetHandle target)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
Asset currentAsset = Content.GetAsset(target);
|
||||
|
||||
StringView identifier = currentAsset?.Identifier ?? (target.IsValid ? "<Missing Asset>" : "None");
|
||||
|
||||
if (ImGui.Button(identifier.ToScopeCStr!()))
|
||||
{
|
||||
// TODO: Show asset selector
|
||||
}
|
||||
|
||||
if (ImGui.BeginDragDropTarget())
|
||||
{
|
||||
ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem);
|
||||
|
||||
if (payload != null)
|
||||
{
|
||||
Log.EngineLogger.Warning("");
|
||||
|
||||
// We shouldn't pass the path/identifier. We should carry ID and Type!
|
||||
StringView path = .((char8*)payload.Data, (int)payload.DataSize);
|
||||
|
||||
// TODO: Somehow validate the type, please!
|
||||
target = (AssetHandle)Content.LoadAsset(path);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
ImGui.EndDragDropTarget();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
public static bool ShowAssetDropTarget<T>(ref AssetHandle<T> target) where T : Asset
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
T currentAsset = target.Get();
|
||||
|
||||
StringView identifier = (target.IsValid ? "<Missing Asset>" : "None");
|
||||
|
||||
if (currentAsset != null)
|
||||
identifier = currentAsset.Identifier;
|
||||
|
||||
if (ImGui.Button(identifier.ToScopeCStr!()))
|
||||
{
|
||||
// TODO: Show asset selector
|
||||
}
|
||||
|
||||
if (ImGui.BeginDragDropTarget())
|
||||
{
|
||||
ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem);
|
||||
|
||||
if (payload != null)
|
||||
{
|
||||
Log.EngineLogger.Warning("");
|
||||
|
||||
StringView path = .((char8*)payload.Data, (int)payload.DataSize);
|
||||
|
||||
// TODO: Somehow validate the type, please!
|
||||
target = (AssetHandle<T>)Content.LoadAsset(path);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
ImGui.EndDragDropTarget();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static void ShowCircleRendererComponentEditor(Entity entity, CircleRendererComponent* circleRendererComponent)
|
||||
@@ -607,31 +684,7 @@ namespace GlitchyEditor.EditWindows
|
||||
{
|
||||
ImGui.PropertyTableStartNewProperty("Material");
|
||||
|
||||
Material material = meshRendererComponent.Material;
|
||||
|
||||
StringView identifier = material?.Identifier ?? "None";
|
||||
ImGui.Button(identifier.ToScopeCStr!());
|
||||
|
||||
if (ImGui.BeginDragDropTarget())
|
||||
{
|
||||
ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem);
|
||||
|
||||
if (payload != null)
|
||||
{
|
||||
StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize);
|
||||
|
||||
meshRendererComponent.Material = Content.LoadAsset(fullpath);
|
||||
}
|
||||
|
||||
ImGui.EndDragDropTarget();
|
||||
}
|
||||
|
||||
/*Effect effect = material?.Effect;
|
||||
|
||||
if (effect == null)
|
||||
return;*/
|
||||
|
||||
// Show a preview of the material here!
|
||||
ShowAssetDropTarget<Material>(ref meshRendererComponent.Material);
|
||||
}
|
||||
|
||||
private static void ShowRigidBody2DComponentEditor(Entity entity, Rigidbody2DComponent* rigidBodyComponent)
|
||||
@@ -901,50 +954,8 @@ namespace GlitchyEditor.EditWindows
|
||||
private static void ShowMeshComponentEditor(Entity entity, MeshComponent* meshComponent)
|
||||
{
|
||||
ImGui.PropertyTableStartNewProperty("Mesh");
|
||||
|
||||
GeometryBinding mesh = meshComponent.Mesh;
|
||||
|
||||
StringView identifier = mesh?.Identifier ?? (meshComponent.Mesh.IsValid ? "<Missing Asset>" : "None");
|
||||
ImGui.Button(identifier.ToScopeCStr!());
|
||||
|
||||
if (ImGui.BeginDragDropTarget())
|
||||
{
|
||||
ImGui.Payload* payload = ImGui.AcceptDragDropPayload(.ContentBrowserItem);
|
||||
|
||||
if (payload != null)
|
||||
{
|
||||
StringView fullpath = .((char8*)payload.Data, (int)payload.DataSize);
|
||||
|
||||
/*int idx = fullpath.IndexOf('#');
|
||||
|
||||
if (idx == -1)
|
||||
{
|
||||
// Doesn't make sense here, we NEED a sub asset
|
||||
Runtime.NotImplemented();
|
||||
}
|
||||
|
||||
StringView filePath = fullpath.Substring(0, idx);
|
||||
StringView meshName = fullpath.Substring(idx + 1);*/
|
||||
|
||||
meshComponent.Mesh = Content.LoadAsset(fullpath);
|
||||
|
||||
// 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))
|
||||
{
|
||||
meshComponent.Mesh = binding;
|
||||
}*/
|
||||
|
||||
//ModelLoader.LoadModel(scope .(path), )
|
||||
|
||||
/*using (Texture2D newTexture = new Texture2D(path, true))
|
||||
{
|
||||
newTexture.SamplerState = SamplerStateManager.AnisotropicWrap;
|
||||
material.SetTexture(texture.key, newTexture);
|
||||
}*/
|
||||
}
|
||||
|
||||
ImGui.EndDragDropTarget();
|
||||
}
|
||||
|
||||
ShowAssetDropTarget<GeometryBinding>(ref meshComponent.Mesh);
|
||||
}
|
||||
|
||||
private static void ShowAddComponentButton(Entity entity)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user