mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-08-03 01:09:29 -04:00
* DDGI stub with rasterized path * Debug probes and fix stuff * Simplify all of this, add system later * Iterate, start integrate * Integrate depth, allow copying depth to another format * Iterate, fix depth, send ddgi volume data to gpu, start integrating on lighting compositing, almost there * DDGIVolume gets bake button, doesnt bake automatically, correct transformation * DDGI Visibility Tests wip * DDGI Volumetric Fog https://files.facepunch.com/sampavlovic/1b2911b1/sbox-dev_UCziuQjdTQ.mp4 * Fix ddgi depth integration, iterate * Simplify this and correct octahedral coordinates seams * Iterate * Probe is 8x8, do normalization directly, cleanup * Bias probe position, visibility adjustments, make eveyrthing smoother * Unify integration in SampleProbeData, still need to clean it further * Unify integrate passes * Add view bias and use border as per papers, fixes most distortion when using visibility at the same time https://files.facepunch.com/sampavlovic/1b0411b1/sbox-dev_laDCpwFxk5.png * Cleanup and fixes for border, cleanup sampling code too * Proper compositing for DDGI for maps that have baked lighting already, could still have more thought to avoid uncessesary branching * DDGI System, allow for multiple volumes in scene, cleanup https://files.facepunch.com/sampavlovic/1b0711b1/sbox-dev_xcJUeit2s4.png https://files.facepunch.com/sampavlovic/1b0711b1/sbox-dev_c59O7Bea6p.png * Rebase fixes, the way to do this sucks * Caching tests, Texture.GetPixels3D seems a bit messed * ddgi.texturecache texturegenerator, much simpler, caches texture nicely and doesnt lead residues on ram after uploaded * Add LPVDebugGrid on managed, make DDGIVolume use it, dont save cache texture on disable, works way faster than gizmos * Update volume only if it's enabled * DDGI on AmbientLight.hlsl * Simplify and fixes * ExtendToSceneBounds, rebake envmaps when bake finished, normalbias * RENDER_DEFAULT_STATE_RASTERIZER probably shouldnt remove override of rasterizer state * Cleanup * [Pick] Fix TextureCompiler not compiling 3D textures properly, make Texture.GetBitmap work with 3D textures * Add NoPrepassCulling, avoids "rays" from probes that are inside geometry from leaking light https://files.facepunch.com/sampavlovic/1b1611b1/sbox-dev_bQfsZlWwop.png * Final adjustments and cleanupj, name this Indirect Light Volume * Indirect Light Volume prefab * Remove log infos, adjust sliders * Whoops * format * Rebase fix * Re-apply the currently tracked cull mode so the override state binds immediately, makes sure that SetLayerNoCull works as intended * Enqueue probes that affect geometry first, this gives quicker iteration results https://files.facepunch.com/sampavlovic/1b1911b1/Screen_Recording_2026-01-19_at_16.16.36.mov * float16 guards for ddgi_integrate, avoid NaNs * Texture.Save supports volume textures * DDGIVolume saves using the super nice Texture.Save api instead of texutre generator * Do the same color punchy look thing we do for envmap probes in ddgi probes https://files.facepunch.com/sampavlovic/1b1911b1/ezgif-13a1dd53fc7688fe.gif * Don't save ddgi async, just do it directly * DDGI editor icon https://files.facepunch.com/sampavlovic/1b2011b1/F1I0KaZt5j.png * Shaders * Format * Add EditorSystem.Camera * Move texture creation to DDGIProbeUpdaterCubemapper, build probes in order from the camera, use async instead of an update * Dont multiply DDGI diffuse by AO, that's done on the shadingmodel, this gives accurate multibounce as well * Update probes sequentially again, makes sure results are consistent, if we do near camera again, should do it with same logic to keep probes that affect geometry first too * Move NoPrepassCulling to RenderToCubeTexture, can't figure out why behaviour is different between these but solves light leak issue * Fix overriding array not working for depth textures, make this clearer * Overriden rasterizer state actually overrides when doing SetCullMode * Rework border logic, should be seamless now * Experiment with shadow sharpness being soft, with borders being fine the variance shadow map stuff from DDGI paper looks correct https://files.facepunch.com/sampavlovic/1b2211b1/sbox-dev_UNTiRmJ7Fw.png * Make Depth Sharpness a Property instead * Delete copy_border shader, do everything within the same shader with groupshared memory, actually sample borders correctly, this reduces octahedral border aliasing to a minimum https://files.facepunch.com/sampavlovic/1b2311b1/sbox-dev_FqPPpRZ6MD.png https://files.facepunch.com/sampavlovic/1b2311b1/Source2Viewer_TiqGxdYWwX.png * Distance calculations use unbiased probe-space position so when we move ddgi transform it doesnt fuck visibility up * Make DDGI Debug Grid use gizmo pooling so it follows gizmo visibility rules (hidden when gizmos disabled, not in cubemaps) * DDGI Relocation proto, Moves probes out of geometry to unfuck artifacts https://files.facepunch.com/sampavlovic/1b2311b1/ezgif-1afaed4e1c2ac9a5.gif https://files.facepunch.com/sampavlovic/1b2311b1/ezgif-11a3e64fd2952590.gif * DDGI less contrasty * Parallelize relocation * More resilient tracing tests & give option to deactivate if probe is fully inside of geometry instead of relocating those fully occluded * Simplify LPVDebugGrid, remove all the fucking mess from it * Simplify DDGIVolume, hide all the bullshit * VTexWriter allows to save as uncompressed, make Relocation texture uncompressed since we want alpha to be flags * Add Graphics.FlushGPU Forces the GPU to flush all pending commands and wait for completion. Useful when you need to ensure GPU work is finished before proceeding. Can be called outside of a render block. * DDGIVolume uses Graphics.FlushGPU instead of dumb Task.Await, density goes up to 10, Debug Grid updates if we change probes * Format * Doubt we are going to use flags, just store alpha as active state directly, makes it more resilient to eventual compression * Sprite was calculating offset incorrectly and causing ambient lighting to be fucked, fog was fucked too https://files.facepunch.com/sampavlovic/1b2611b1/sbox-dev_i3h4RcWncI.png * DDGI should really be under Light not Misc * Keep LPVDebugGrid render bounds infinite, SceneCusotmObject shouldnt change it from there * Move RelocationTexture to same Generated behaviour so we dont override the property it while we are rendering, make probes default visible * Remove shitty hack from scenecamera and make DDGIVolumeUpdater take scene renderattributes, makes DDGI bounces retrofeed from itself Should fix terrain rendering being fucked in DDGI too Will see to make this more generic https://files.facepunch.com/sampavlovic/1b2611b1/sbox-dev_uzXYN0Qzw6.png * Make Energy Loss an attribute for DDGI, I prefer a more contrasty look but there are scenes where you'd want more accurate energy conservation, first picture is an example where energy loss contrast makes it look worse https://files.facepunch.com/sampavlovic/1b2611b1/sbox-dev_kdTH1qhEwR.png https://files.facepunch.com/sampavlovic/1b2611b1/sbox-dev_gMoRxorzV0.png * Progress system can be toasts * Clean up progress system, always available now via Application.Editor. * Added Scene/Bake DDGI Volumes * Changed DDGIVolume to IndirectLightVolume * Refinement steps for probe relocation * Kill existing bake if disabled or ticking bake again * EnergyLoss is Contrast and increase max density, keep default contrast to the value I'm happy with * Don't call it the implementation name to the user * formet * Build shaders * Bind dummy DDGI_Volumes buffer as default on high level --------- Co-authored-by: Garry Newman <garrynewman@users.noreply.github.com>
341 lines
9.5 KiB
C#
341 lines
9.5 KiB
C#
using Sandbox.Engine;
|
|
using System;
|
|
using System.Threading;
|
|
|
|
namespace Editor;
|
|
|
|
public static class LibrarySystem
|
|
{
|
|
static HashSet<LibraryProject> _all = new();
|
|
|
|
static string LibraryFolder { get; set; }
|
|
|
|
/// <summary>
|
|
/// Get all active libraries
|
|
/// </summary>
|
|
static public IEnumerable<LibraryProject> All => _all;
|
|
|
|
/// <summary>
|
|
/// Scan this project's Libraries folder and add all of the library projects from it.
|
|
/// </summary>
|
|
internal static void InitializeFromProject( Project project )
|
|
{
|
|
LibraryFolder = System.IO.Path.Combine( project.RootDirectory.FullName, "Libraries" );
|
|
|
|
foreach ( var folder in System.IO.Directory.EnumerateDirectories( LibraryFolder ) )
|
|
{
|
|
AddFromFolder( folder );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a library project from a specific folder
|
|
/// </summary>
|
|
private static LibraryProject AddFromFolder( string folder )
|
|
{
|
|
var configs = System.IO.Directory.EnumerateFiles( folder, "*.sbproj" ).ToArray();
|
|
if ( configs.Length != 1 ) return default;
|
|
|
|
var project = Project.AddFromFile( configs[0], true );
|
|
if ( project is null ) return default;
|
|
|
|
var existing = _all.SingleOrDefault( x => x.Project == project );
|
|
|
|
if ( existing is not null )
|
|
{
|
|
// Re-read the version - in case it updated
|
|
existing.ReadVersionFromFile();
|
|
return existing;
|
|
}
|
|
|
|
var lib = new LibraryProject( project );
|
|
_all.Add( lib );
|
|
return lib;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a library from this folder
|
|
/// </summary>
|
|
public static async Task Add( string folderName, CancellationToken token )
|
|
{
|
|
var fullPath = FileSystem.Libraries.GetFullPath( folderName );
|
|
if ( !System.IO.Directory.Exists( fullPath ) )
|
|
{
|
|
Log.Warning( "Tried to add library but it doesn't exist" );
|
|
return;
|
|
}
|
|
|
|
var project = AddFromFolder( fullPath );
|
|
if ( project is null )
|
|
{
|
|
Log.Warning( "Tried to add library but it couldn't create project" );
|
|
return;
|
|
}
|
|
|
|
// If we have an assets path, then AddFromFolder called the LibraryProject constructor
|
|
// which added a new CONTENT path. So here we tell the engine to rebuild the content
|
|
// paths, so it finds all the new assets.
|
|
if ( project.Project.HasAssetsPath() )
|
|
{
|
|
Editor.FileSystem.RebuildContentPath();
|
|
|
|
if ( !Sandbox.Application.IsUnitTest )
|
|
{
|
|
IAssetSystem.UpdateMods();
|
|
}
|
|
|
|
ResourceLoader.LoadAllGameResource( FileSystem.Mounted );
|
|
}
|
|
|
|
// install it
|
|
await PackageManager.InstallAsync( new PackageLoadOptions( project.Project.Package.FullIdent, "local", token ) );
|
|
await PackageManager.InstallAsync( new PackageLoadOptions( project.Project.Package.FullIdent, "gamemenu", token ) );
|
|
|
|
// update the sln with the new projects
|
|
await Project.GenerateSolution();
|
|
|
|
// I don't know if this does anything?
|
|
IAssetSystem.UpdateMods();
|
|
|
|
|
|
|
|
// Trigger a rebuild of all projects and wait for the result
|
|
await EditorUtility.Projects.Updated( Project.Current );
|
|
|
|
// Recreate the project filesystem
|
|
//FileSystem.InitializeFromProject( Project.Current );
|
|
|
|
// Reload the game project, which'll load the new library too
|
|
await GameInstanceDll.Current.LoadGamePackageAsync( Project.Current.Package.FullIdent, GameLoadingFlags.Host | GameLoadingFlags.Reload, token );
|
|
}
|
|
|
|
|
|
internal static void RemoveLibrary( LibraryProject proj )
|
|
{
|
|
_all.Remove( proj );
|
|
|
|
var path = proj.Project.GetRootPath();
|
|
|
|
DeleteFolderWithRetry( path );
|
|
|
|
EditorUtility.DisplayDialog( "Library Removed", "This library has been deleted. Please restart the editor to fully remove it. This is horrible I am so sorry." );
|
|
}
|
|
|
|
private static void DeleteFolderWithRetry( string path )
|
|
{
|
|
// try to delete the folder a few times
|
|
// expect trouble, so retry and ignore some errors
|
|
for ( int i = 0; i < 10; i++ )
|
|
{
|
|
try
|
|
{
|
|
System.IO.Directory.Delete( path, true );
|
|
}
|
|
catch ( System.IO.DirectoryNotFoundException )
|
|
{
|
|
// Good - that's what we want
|
|
break;
|
|
}
|
|
catch ( System.Exception )
|
|
{
|
|
Thread.Sleep( 100 );
|
|
}
|
|
}
|
|
}
|
|
|
|
[ConCmd( "library_install", ConVarFlags.Protected )]
|
|
internal static void InstallLibrary( string name )
|
|
{
|
|
if ( !Package.TryParseIdent( name, out var ident ) )
|
|
{
|
|
Log.Warning( "Invalid package ident" );
|
|
return;
|
|
}
|
|
|
|
Log.Warning( $"Installing Library {ident.org}.{ident.package}" );
|
|
_ = Install( $"{ident.org}.{ident.package}", ident.version ?? -1 );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Install a library from a package. This will download the package and install it in the project's Library folder.
|
|
/// </summary>
|
|
public static async Task<bool> Install( string ident, long versionId = -1, CancellationToken token = default )
|
|
{
|
|
//
|
|
// TODO - We need a scoped thing to prevent filesystem reacting to file changes
|
|
// something like using( FileSystem.DeferChanges() ), but globally
|
|
//
|
|
|
|
using var progress = Application.Editor.ProgressSection();
|
|
progress.Title = "Installing Package";
|
|
|
|
var package = await Package.FetchAsync( ident, false );
|
|
if ( package is null )
|
|
{
|
|
Log.Warning( "Failed to install library, package not found" );
|
|
return false;
|
|
}
|
|
|
|
// no version specified - then use the current live version
|
|
if ( versionId <= 0 )
|
|
{
|
|
versionId = package.Revision.VersionId;
|
|
}
|
|
|
|
// get the version info
|
|
var version = (await Package.FetchVersions( ident )).Where( x => x.VersionId == versionId ).FirstOrDefault();
|
|
if ( version == null )
|
|
{
|
|
Log.Warning( "Failed to install library, version wasn't found" );
|
|
return false;
|
|
}
|
|
|
|
var folderName = package.FullIdent;
|
|
var versionPath = $"{folderName}/.version";
|
|
var versionIdent = new Version( 1, 0, (int)version.VersionId );
|
|
|
|
// Download the manifest
|
|
await version.DownloadManifestAsync( token );
|
|
|
|
// TODO: Remove this once we have FileSystem.DeferChanges()
|
|
FileWatch.SuppressWatchers = RealTime.Now + 999;
|
|
|
|
// does this already exist?
|
|
if ( FileSystem.Libraries.FileExists( versionPath ) )
|
|
{
|
|
var versionText = FileSystem.Libraries.ReadAllText( versionPath );
|
|
|
|
if ( Version.TryParse( versionText.Trim(), out var result ) )
|
|
{
|
|
if ( versionIdent == result )
|
|
{
|
|
Log.Warning( "Updating library to the same version. Erasing local changes." );
|
|
}
|
|
else
|
|
{
|
|
Log.Info( $"Updating library from {result} to {versionIdent}" );
|
|
}
|
|
}
|
|
|
|
// loop over existing files and delete any with changes or that are no longer in the manifest
|
|
var existingFiles = FileSystem.Libraries.FindFile( folderName, "*", true );
|
|
foreach ( var existingFile in existingFiles )
|
|
{
|
|
var newFile = version.Manifest.Files.FirstOrDefault( x => x.Path == existingFile );
|
|
var fullPath = $"{folderName}/{existingFile}";
|
|
if ( !version.Manifest.Files.Any( x => x.Path == existingFile ) || FileSystem.Libraries.GetCrc( fullPath ) != Convert.ToUInt64( newFile.Crc, 16 ) )
|
|
{
|
|
FileSystem.Libraries.DeleteFile( fullPath );
|
|
progress.Title = $"Deleting {existingFile}";
|
|
}
|
|
}
|
|
}
|
|
|
|
FileSystem.Libraries.CreateDirectory( folderName );
|
|
|
|
// Write the version id
|
|
FileSystem.Libraries.WriteAllText( versionPath, versionIdent.ToString() );
|
|
|
|
// Download each new file (this is done in parallel, attempting to DL up to 10mb at once)
|
|
var filesToDownload = new List<ManifestSchema.File>();
|
|
var filesDownloading = new List<ManifestSchema.File>();
|
|
foreach ( var file in version.Manifest.Files )
|
|
{
|
|
var libraryPath = $"{folderName}/{file.Path}";
|
|
if ( FileSystem.Libraries.FileExists( libraryPath ) )
|
|
{
|
|
continue;
|
|
}
|
|
filesToDownload.Add( file );
|
|
}
|
|
|
|
long bytesDownloading = 0;
|
|
while ( filesToDownload.Count > 0 )
|
|
{
|
|
if ( bytesDownloading < 10_000 && filesToDownload.Count > 0 )
|
|
{
|
|
var file = filesToDownload[0];
|
|
filesToDownload.RemoveAt( 0 );
|
|
Parallel.Invoke( async () =>
|
|
{
|
|
filesDownloading.Add( file );
|
|
bytesDownloading += file.Size;
|
|
var libraryPath = $"{folderName}/{file.Path}";
|
|
var targetPath = FileSystem.Libraries.GetFullPath( libraryPath );
|
|
await EditorUtility.DownloadAsync( file.Url, targetPath, null, token );
|
|
progress.Title = file.Path;
|
|
bytesDownloading -= file.Size;
|
|
filesDownloading.Remove( file );
|
|
} );
|
|
}
|
|
await Task.Delay( 1 );
|
|
}
|
|
|
|
FileWatch.SuppressWatchers = RealTime.Now;
|
|
await Add( folderName, token );
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public class LibraryProject
|
|
{
|
|
public Version Version { get; set; }
|
|
|
|
public Project Project { get; }
|
|
|
|
internal LibraryProject( Project project )
|
|
{
|
|
Project = project;
|
|
ReadVersionFromFile();
|
|
|
|
// add the content path
|
|
if ( project.HasAssetsPath() )
|
|
{
|
|
var assetPath = project.GetAssetsPath();
|
|
|
|
// Add to our c# content search path
|
|
FileSystem.Content.CreateAndMount( assetPath );
|
|
FileSystem.Mounted.CreateAndMount( assetPath );
|
|
|
|
// make content available to the game
|
|
EngineFileSystem.LibraryContent.CreateAndMount( assetPath );
|
|
|
|
// Add to the c++ filesystem search path
|
|
NativeEngine.FullFileSystem.AddProjectPath( project.Config.FullIdent, project.GetAssetsPath() );
|
|
}
|
|
|
|
if ( project.HasCodePath() )
|
|
{
|
|
// make .scss etc available to the game
|
|
EngineFileSystem.LibraryContent.CreateAndMount( project.GetCodePath() );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to read the library version from /.version
|
|
/// </summary>
|
|
internal void ReadVersionFromFile()
|
|
{
|
|
Version = new Version( 1, 1, 1 );
|
|
|
|
var versionInfo = System.IO.Path.Combine( Project.GetRootPath(), ".version" );
|
|
if ( !System.IO.File.Exists( versionInfo ) ) return;
|
|
|
|
var versionText = System.IO.File.ReadAllText( versionInfo );
|
|
|
|
if ( Version.TryParse( versionText.Trim(), out var result ) )
|
|
{
|
|
Version = result;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove and delete this library, and folder
|
|
/// </summary>
|
|
public void RemoveAndDelete()
|
|
{
|
|
LibrarySystem.RemoveLibrary( this );
|
|
}
|
|
}
|