1414using Rac . Rendering . Camera ;
1515using Silk . NET . Input ;
1616using Silk . NET . Maths ;
17+ using System . Collections . Concurrent ;
1718
1819namespace Rac . Engine ;
1920
@@ -22,6 +23,9 @@ public class EngineFacade : IEngineFacade
2223 private readonly GameEngine . Engine _inner ;
2324 private readonly IWindowManager _windowManager ;
2425 private readonly TransformSystem _transformSystem ;
26+ private readonly ConcurrentDictionary < Type , FileAssetService > _assetServices = new ( ) ;
27+ private readonly Dictionary < Type , string > _assetTypePaths = new ( ) ;
28+ private string _defaultAssetBasePath ;
2529
2630 public EngineFacade (
2731 IWindowManager windowManager ,
@@ -49,9 +53,12 @@ ConfigManager configManager
4953 Audio = new NullAudioService ( ) ;
5054 }
5155
56+ // Initialize default asset base path
57+ _defaultAssetBasePath = Path . Combine ( AppContext . BaseDirectory , "Assets" ) ;
58+
5259 // Initialize asset service with default configuration
5360 Assets = AssetServiceBuilder . Create ( )
54- . WithBasePath ( "Assets" )
61+ . WithBasePath ( _defaultAssetBasePath )
5562 . Build ( ) ;
5663
5764 // Initialize transform system (required for container operations)
@@ -92,7 +99,7 @@ ConfigManager configManager
9299 public SystemScheduler Systems { get ; }
93100 public IRenderer Renderer => _inner . Renderer ;
94101 public IAudioService Audio { get ; }
95- public IAssetService Assets { get ; }
102+ public IAssetService Assets { get ; private set ; }
96103 public ICameraManager CameraManager { get ; }
97104 public IWindowManager WindowManager => _windowManager ;
98105 public IContainerService Container { get ; }
@@ -326,7 +333,7 @@ public Texture LoadTexture(string filename)
326333
327334 try
328335 {
329- return Assets . LoadAsset < Texture > ( filename ) ;
336+ return GetAssetServiceForType < Texture > ( ) . LoadAsset < Texture > ( filename ) ;
330337 }
331338 catch ( FileNotFoundException )
332339 {
@@ -390,7 +397,7 @@ public AudioClip LoadAudio(string filename)
390397
391398 try
392399 {
393- return Assets . LoadAsset < AudioClip > ( filename ) ;
400+ return GetAssetServiceForType < AudioClip > ( ) . LoadAsset < AudioClip > ( filename ) ;
394401 }
395402 catch ( FileNotFoundException )
396403 {
@@ -456,7 +463,7 @@ public string LoadShaderSource(string filename)
456463
457464 try
458465 {
459- return Assets . LoadAsset < string > ( filename ) ;
466+ return GetAssetServiceForType < string > ( ) . LoadAsset < string > ( filename ) ;
460467 }
461468 catch ( FileNotFoundException )
462469 {
@@ -472,4 +479,209 @@ public string LoadShaderSource(string filename)
472479 $ "Ensure the file uses a supported text encoding (UTF-8 recommended).", ex ) ;
473480 }
474481 }
482+
483+ // ═══════════════════════════════════════════════════════════════════════════
484+ // ASSET PATH CONFIGURATION METHODS
485+ // ═══════════════════════════════════════════════════════════════════════════
486+
487+ /// <summary>
488+ /// Sets the base path for all asset types (fallback for specific types).
489+ ///
490+ /// EDUCATIONAL PURPOSE:
491+ /// This method demonstrates flexible asset organization:
492+ /// - Allows changing the default asset location at runtime
493+ /// - Provides fallback path for asset types without specific configuration
494+ /// - Recreates asset services to use the new path
495+ /// - Maintains backward compatibility with existing code
496+ /// </summary>
497+ /// <param name="basePath">Base directory for asset files</param>
498+ /// <exception cref="ArgumentNullException">Thrown when basePath is null</exception>
499+ /// <example>
500+ /// <code>
501+ /// // Set all assets to load from "GameContent" folder
502+ /// engine.SetAssetBasePath("GameContent");
503+ ///
504+ /// // Set absolute path
505+ /// engine.SetAssetBasePath(@"C:\MyGame\Assets");
506+ /// </code>
507+ /// </example>
508+ public void SetAssetBasePath ( string basePath )
509+ {
510+ if ( basePath == null )
511+ throw new ArgumentNullException ( nameof ( basePath ) ) ;
512+
513+ _defaultAssetBasePath = Path . IsPathRooted ( basePath )
514+ ? basePath
515+ : Path . Combine ( AppContext . BaseDirectory , basePath ) ;
516+
517+ RecreateAssetServices ( ) ;
518+ }
519+
520+ /// <summary>
521+ /// Sets the base path specifically for audio assets.
522+ ///
523+ /// EDUCATIONAL PURPOSE:
524+ /// Type-specific asset organization enables better project structure:
525+ /// - Audio files can be organized in dedicated folders
526+ /// - Different asset types can have different storage strategies
527+ /// - Fallback to default path ensures compatibility
528+ /// - Runtime configuration allows flexible deployment
529+ /// </summary>
530+ /// <param name="basePath">Base directory for audio asset files</param>
531+ /// <exception cref="ArgumentNullException">Thrown when basePath is null</exception>
532+ /// <example>
533+ /// <code>
534+ /// // Audio files in "Audio" folder
535+ /// engine.SetAudioBasePath("Audio");
536+ ///
537+ /// // Then load audio normally
538+ /// var sound = engine.LoadAudio("jump.wav"); // Loads from Audio/jump.wav
539+ /// </code>
540+ /// </example>
541+ public void SetAudioBasePath ( string basePath )
542+ {
543+ if ( basePath == null )
544+ throw new ArgumentNullException ( nameof ( basePath ) ) ;
545+
546+ var fullPath = Path . IsPathRooted ( basePath )
547+ ? basePath
548+ : Path . Combine ( AppContext . BaseDirectory , basePath ) ;
549+
550+ _assetTypePaths [ typeof ( AudioClip ) ] = fullPath ;
551+ RecreateAssetServiceForType < AudioClip > ( ) ;
552+ }
553+
554+ /// <summary>
555+ /// Sets the base path specifically for texture assets.
556+ ///
557+ /// EDUCATIONAL PURPOSE:
558+ /// Texture-specific organization demonstrates asset categorization:
559+ /// - Visual assets often have different storage requirements
560+ /// - Texture files can be large and benefit from dedicated organization
561+ /// - Separate paths enable different caching strategies
562+ /// - Artist-friendly folder structures improve workflow
563+ /// </summary>
564+ /// <param name="basePath">Base directory for texture asset files</param>
565+ /// <exception cref="ArgumentNullException">Thrown when basePath is null</exception>
566+ /// <example>
567+ /// <code>
568+ /// // Texture files in "Textures" folder
569+ /// engine.SetTextureBasePath("Textures");
570+ ///
571+ /// // Then load textures normally
572+ /// var texture = engine.LoadTexture("player.png"); // Loads from Textures/player.png
573+ /// </code>
574+ /// </example>
575+ public void SetTextureBasePath ( string basePath )
576+ {
577+ if ( basePath == null )
578+ throw new ArgumentNullException ( nameof ( basePath ) ) ;
579+
580+ var fullPath = Path . IsPathRooted ( basePath )
581+ ? basePath
582+ : Path . Combine ( AppContext . BaseDirectory , basePath ) ;
583+
584+ _assetTypePaths [ typeof ( Texture ) ] = fullPath ;
585+ RecreateAssetServiceForType < Texture > ( ) ;
586+ }
587+
588+ /// <summary>
589+ /// Sets the base path specifically for text assets (shaders, configs, etc.).
590+ ///
591+ /// EDUCATIONAL PURPOSE:
592+ /// Text asset organization enables data-driven development:
593+ /// - Shader files can be organized separately from other assets
594+ /// - Configuration files can have dedicated storage
595+ /// - Text assets often have different versioning requirements
596+ /// - Enables separate processing pipelines for different text types
597+ /// </summary>
598+ /// <param name="basePath">Base directory for text asset files</param>
599+ /// <exception cref="ArgumentNullException">Thrown when basePath is null</exception>
600+ /// <example>
601+ /// <code>
602+ /// // Text files in "Data" folder
603+ /// engine.SetTextBasePath("Data");
604+ ///
605+ /// // Then load text assets normally
606+ /// var shader = engine.LoadShaderSource("basic.vert"); // Loads from Data/basic.vert
607+ /// </code>
608+ /// </example>
609+ public void SetTextBasePath ( string basePath )
610+ {
611+ if ( basePath == null )
612+ throw new ArgumentNullException ( nameof ( basePath ) ) ;
613+
614+ var fullPath = Path . IsPathRooted ( basePath )
615+ ? basePath
616+ : Path . Combine ( AppContext . BaseDirectory , basePath ) ;
617+
618+ _assetTypePaths [ typeof ( string ) ] = fullPath ;
619+ RecreateAssetServiceForType < string > ( ) ;
620+ }
621+
622+ /// <summary>
623+ /// Recreates all asset services to use updated base paths.
624+ /// Educational note: Ensures all services use current path configuration.
625+ /// </summary>
626+ private void RecreateAssetServices ( )
627+ {
628+ // Dispose existing services
629+ foreach ( var service in _assetServices . Values )
630+ {
631+ service . Dispose ( ) ;
632+ }
633+ _assetServices . Clear ( ) ;
634+
635+ // Recreate main asset service
636+ if ( Assets is IDisposable disposableAssets )
637+ {
638+ disposableAssets . Dispose ( ) ;
639+ }
640+ Assets = AssetServiceBuilder . Create ( )
641+ . WithBasePath ( _defaultAssetBasePath )
642+ . Build ( ) ;
643+ }
644+
645+ /// <summary>
646+ /// Recreates the asset service for a specific type.
647+ /// Educational note: Selective recreation minimizes impact on other asset types.
648+ /// </summary>
649+ private void RecreateAssetServiceForType < T > ( ) where T : class
650+ {
651+ var type = typeof ( T ) ;
652+
653+ if ( _assetServices . TryRemove ( type , out var existingService ) )
654+ {
655+ existingService . Dispose ( ) ;
656+ }
657+
658+ // The service will be recreated lazily when needed
659+ }
660+
661+ /// <summary>
662+ /// Gets the appropriate asset service for the specified type.
663+ /// Educational note: Lazy initialization with type-specific path resolution.
664+ /// </summary>
665+ private IAssetService GetAssetServiceForType < T > ( ) where T : class
666+ {
667+ var type = typeof ( T ) ;
668+
669+ if ( _assetServices . TryGetValue ( type , out var existingService ) )
670+ {
671+ return existingService ;
672+ }
673+
674+ // Determine base path for this type
675+ var basePath = _assetTypePaths . TryGetValue ( type , out var specificPath )
676+ ? specificPath
677+ : _defaultAssetBasePath ;
678+
679+ // Create service for this type
680+ var service = AssetServiceBuilder . Create ( )
681+ . WithBasePath ( basePath )
682+ . Build ( ) ;
683+
684+ _assetServices [ type ] = service ;
685+ return service ;
686+ }
475687}
0 commit comments