================================================================================= RALPHA-UE5 REPOSITORY - Repomix Output Generated: Tue Jan 20 16:29:38 PST 2026 ================================================================================= ## REPOSITORY STRUCTURE ================================================================================= ./.gitattributes ./.gitignore ./Config/DefaultEditor.ini ./Config/DefaultEngine.ini ./Config/DefaultGame.ini ./Config/DefaultInput.ini ./LICENSE_POLICY.md ./Ralpha.uproject ./RalphaData/catalogue/catalogue.json ./RalphaData/catalogue/catalogue.schema.json ./RalphaData/presets/lighting/natural_golden_hour.json ./RalphaData/presets/lighting/studio_3point_soft.json ./RalphaData/presets/post/pp_cinematic.json ./RalphaData/recipes/living_room_modern.json ./RalphaData/recipes/office_corporate.json ./RalphaData/recipes/portrait_studio_white.json ./RalphaData/recipes/recipe.schema.json ./RalphaData/scripts/generate_catalogue.py ./README.md ## TEXT FILES (Full Content) ================================================================================= -------------------------------------------------------------------------------- File: README.md -------------------------------------------------------------------------------- # Ralpha UE5 - Kitchen Sink Asset Project Comprehensive UE5 asset library and scene templates for AI-controlled rendering with [Ralpha](https://github.com/jamestagg/ralpha). ## Quick Start ```bash # Clone with LFS git lfs install git clone https://github.com/jamestagg/ralpha-ue5.git # Open in UE5.4+ # File > Open Project > ralpha-ue5/Ralpha.uproject ``` ## Architecture This repo contains **assets only**. The MCP plugin and tooling live in the main [ralpha](https://github.com/jamestagg/ralpha) repo. ``` ralpha-ue5/ <- You are here (assets, templates, presets) │ └── Plugins/RalphaPlugin/ <- Symlink to ralpha/plugin │ ├── MCP Server (port 30010) └── 69 Command Handlers ralpha/ <- Main repo (tooling, API, frontend) │ ├── plugin/ <- UE5 plugin source ├── ralpha-api/ <- Backend API └── frontend/ <- Web interface ``` ## Repository Structure ``` ralpha-ue5/ ├── RalphaData/ # NOT in LFS - text/JSON files │ ├── catalogue/ │ │ ├── catalogue.json # Machine-readable asset index │ │ └── catalogue.schema.json # JSON schema │ ├── thumbnails/ # Small preview images │ ├── recipes/ # Scene composition templates │ │ ├── recipe.schema.json │ │ ├── living_room_modern.json │ │ ├── portrait_studio_white.json │ │ └── office_corporate.json │ ├── presets/ │ │ ├── lighting/ # Lighting configurations │ │ ├── post/ # Post-processing profiles │ │ ├── scene/ # Full scene configs │ │ └── camera/ # Camera presets │ └── scripts/ │ └── generate_catalogue.py # Asset indexing tool │ ├── Content/ # IN LFS - large binary files │ └── Ralpha/ │ ├── Maps/ # Template levels │ │ ├── Template_Empty.umap │ │ ├── Template_Studio.umap │ │ ├── Template_Outdoor.umap │ │ └── Template_Interior.umap │ ├── Assets/ # Props, furniture, etc. │ │ ├── Furniture/ │ │ ├── Electronics/ │ │ ├── Food/ │ │ ├── Vehicles/ │ │ ├── Nature/ │ │ └── ... │ ├── Materials/ │ ├── HDRI/ │ └── LUTs/ │ └── Plugins/ └── RalphaPlugin/ # Symlink to ralpha/plugin ``` ## Key Concepts ### Template Maps vs Heavy Maps Instead of 50+ bespoke maps, we use **4 templates** + **JSON presets**: | Template | Use Case | |----------|----------| | `Template_Empty` | Bare minimum - sky + ground | | `Template_Studio` | Portrait/product - cyclorama + lights | | `Template_Outdoor` | Landscapes - terrain + atmosphere | | `Template_Interior` | Rooms - walls + ceiling + basic lighting | Scene variation comes from presets, not map duplication. ### Recipe System Recipes define composite scenes that can be spawned instantly: ```json { "recipe_id": "living_room_modern", "components": [ {"asset": "sofa_modern_3seat", "position": "center"}, {"asset": "coffee_table_rect", "position": "in_front_of:sofa"}, {"asset": "tv_65inch", "position": "wall:opposite_sofa"} ], "lighting_preset": "Interior_Evening" } ``` MCP command: `{"type":"spawn_recipe","parameters":{"recipe":"living_room_modern"}}` ### Asset Catalogue All assets are indexed in `catalogue.json` for programmatic discovery: ```json { "id": "sofa_modern_3seat", "name": "Modern 3-Seat Sofa", "category": "Furniture/Seating", "unreal_path": "/Game/Ralpha/Assets/Furniture/Seating/Sofa_Modern_3Seat", "tags": ["sofa", "couch", "seating", "modern", "living room"], "bounds": {"x": 220, "y": 90, "z": 85}, "placement_modes": ["floor"] } ``` MCP command: `{"type":"search_catalogue","parameters":{"query":"modern sofa"}}` ## MCP Commands These commands interface with this asset library: | Command | Description | |---------|-------------| | `load_template` | Load a base template map | | `apply_scene_preset` | Apply lighting/post/camera preset | | `spawn_recipe` | Spawn a full scene composition | | `spawn_catalogue_item` | Spawn single asset from catalogue | | `search_catalogue` | Search assets by tags/name | | `get_version` | Get plugin + catalogue version | ## Asset Standards All assets must meet these requirements: - **Scale**: 1 unit = 1 cm - **Pivot**: Bottom center (for floor items) - **Collision**: Present for interactive items - **LODs**: At least 2 levels for complex meshes - **Materials**: PBR with proper slots - **Naming**: `Category_Type_Variant` (e.g., `Sofa_Modern_Grey`) ## Regenerating Catalogue After adding assets, regenerate the catalogue: ```bash cd ralpha-ue5 python RalphaData/scripts/generate_catalogue.py --scan-only ``` For full metadata extraction (requires UE Editor): ```bash python RalphaData/scripts/generate_catalogue.py --project Ralpha.uproject --ue-path /path/to/UnrealEditor ``` ## License Policy See [LICENSE_POLICY.md](LICENSE_POLICY.md) for asset provenance requirements. - **CC0/Public Domain**: Preferred, no restrictions - **CC-BY**: Allowed, attribution in catalogue - **Megascans**: Allowed (free with UE) - **Marketplace**: Allowed for purchased assets - **Restricted**: Separate folder, excluded by default ## Related Repositories - [ralpha](https://github.com/jamestagg/ralpha) - Main tooling, plugin, API - [ralpha-ue5-metahumans](https://github.com/jamestagg/ralpha-ue5-metahumans) - MetaHuman characters (optional) ## Contributing 1. Import asset following standards above 2. Run `generate_catalogue.py` 3. Add to appropriate recipe if applicable 4. Submit PR with asset source/license info -------------------------------------------------------------------------------- File: LICENSE_POLICY.md -------------------------------------------------------------------------------- # Asset License Policy All assets in this repository must have clear licensing and provenance. ## Allowed License Types ### Tier 1: Unrestricted (Preferred) | License | Requirements | Folder | |---------|--------------|--------| | **CC0 / Public Domain** | None | `Content/Ralpha/Assets/` | | **Unlicense** | None | `Content/Ralpha/Assets/` | | **MIT** | Include license file | `Content/Ralpha/Assets/` | ### Tier 2: Attribution Required | License | Requirements | Folder | |---------|--------------|--------| | **CC-BY** | Credit in catalogue.json | `Content/Ralpha/Assets/` | | **CC-BY-SA** | Credit + share-alike | `Content/Ralpha/Assets/` | ### Tier 3: Platform-Specific | License | Requirements | Folder | |---------|--------------|--------| | **Quixel Megascans** | Free with UE, UE projects only | `Content/Ralpha/Assets/` | | **UE Marketplace** | Purchased, UE projects only | `Content/Ralpha/Assets/` | | **Sketchfab Store** | Purchased, per-asset license | `Content/Ralpha/Assets/` | ### Tier 4: Restricted | License | Requirements | Folder | |---------|--------------|--------| | **NC (Non-Commercial)** | Non-commercial use only | `Content/Ralpha/Restricted/` | | **Custom/Unknown** | Case-by-case review | `Content/Ralpha/Restricted/` | ## Prohibited - **ND (No Derivatives)** - Cannot modify, doesn't work for our use case - **Copyrighted without license** - No unlicensed assets - **AI-generated without rights** - Must have clear generation rights ## Catalogue Requirements Every asset in `catalogue.json` must include: ```json { "license": { "type": "CC0", // Required: license identifier "source": "Poly Haven", // Required: where asset came from "attribution": null, // Required for CC-BY: credit text "url": "https://..." // Optional: source URL } } ``` ## Recommended Sources ### Free / CC0 | Source | License | Quality | Notes | |--------|---------|---------|-------| | [Poly Haven](https://polyhaven.com) | CC0 | Excellent | HDRIs, textures, models | | [Sketchfab CC0](https://sketchfab.com/search?features=downloadable&licenses=cc0) | CC0 | Variable | Filter by license | | [cgbookcase](https://cgbookcase.com) | CC0 | Good | Textures | | [ambientCG](https://ambientcg.com) | CC0 | Good | PBR textures | | [Kenney](https://kenney.nl) | CC0 | Stylized | Low-poly assets | ### With UE License | Source | License | Quality | Notes | |--------|---------|---------|-------| | [Quixel Megascans](https://quixel.com/megascans) | Megascans | Excellent | Free with UE | | [UE Marketplace](https://unrealengine.com/marketplace) | Marketplace | Variable | Many free monthly | ### Paid | Source | License | Quality | Notes | |--------|---------|---------|-------| | [TurboSquid](https://turbosquid.com) | Per-asset | Variable | Check each license | | [CGTrader](https://cgtrader.com) | Per-asset | Variable | Check each license | | [Sketchfab Store](https://sketchfab.com/store) | Per-asset | Variable | Royalty-free options | ## Adding New Assets 1. **Verify license** before importing 2. **Document source** in commit message 3. **Add to catalogue** with full license info 4. **Place in correct folder** based on license tier ## Audit Process Periodically run: ```bash # Find assets missing license info python RalphaData/scripts/audit_licenses.py ``` ## Questions? If unsure about an asset's license: 1. Don't add it 2. Ask in PR/issue 3. Find alternative with clear license --- *Last Updated: 2026-01-20* -------------------------------------------------------------------------------- File: .gitignore -------------------------------------------------------------------------------- # UE5 Project .gitignore # Build artifacts (rebuilt locally) Binaries/ Intermediate/ DerivedDataCache/ Build/ # Saved data (user-specific, logs, autosaves) Saved/ # Plugin binaries (rebuilt per machine) Plugins/**/Binaries/ Plugins/**/Intermediate/ # Visual Studio .vs/ *.sln *.suo *.sdf *.VC.db *.VC.opendb # JetBrains Rider .idea/ # VS Code .vscode/ # macOS .DS_Store *.DS_Store # Windows Thumbs.db Desktop.ini # Cooked content (generated) **/Cooked/ # Starter content (if using) **/StarterContent/ -------------------------------------------------------------------------------- File: .gitattributes -------------------------------------------------------------------------------- # UE5 LFS tracking # Unreal binary assets *.uasset filter=lfs diff=lfs merge=lfs -text *.umap filter=lfs diff=lfs merge=lfs -text # Textures *.png filter=lfs diff=lfs merge=lfs -text *.jpg filter=lfs diff=lfs merge=lfs -text *.jpeg filter=lfs diff=lfs merge=lfs -text *.tga filter=lfs diff=lfs merge=lfs -text *.bmp filter=lfs diff=lfs merge=lfs -text *.psd filter=lfs diff=lfs merge=lfs -text *.exr filter=lfs diff=lfs merge=lfs -text *.hdr filter=lfs diff=lfs merge=lfs -text # Audio *.wav filter=lfs diff=lfs merge=lfs -text *.mp3 filter=lfs diff=lfs merge=lfs -text *.ogg filter=lfs diff=lfs merge=lfs -text # Video *.mp4 filter=lfs diff=lfs merge=lfs -text *.mov filter=lfs diff=lfs merge=lfs -text *.avi filter=lfs diff=lfs merge=lfs -text # 3D formats *.fbx filter=lfs diff=lfs merge=lfs -text *.obj filter=lfs diff=lfs merge=lfs -text *.gltf filter=lfs diff=lfs merge=lfs -text *.glb filter=lfs diff=lfs merge=lfs -text *.abc filter=lfs diff=lfs merge=lfs -text # Fonts *.ttf filter=lfs diff=lfs merge=lfs -text *.otf filter=lfs diff=lfs merge=lfs -text # Compiled/binary *.dll filter=lfs diff=lfs merge=lfs -text *.so filter=lfs diff=lfs merge=lfs -text *.dylib filter=lfs diff=lfs merge=lfs -text # UE bulk data *.ubulk filter=lfs diff=lfs merge=lfs -text *.uexp filter=lfs diff=lfs merge=lfs -text *.upk filter=lfs diff=lfs merge=lfs -text # Keep RalphaData in regular git (not LFS) # These are small JSON/text files for tooling RalphaData/** !filter !diff !merge text RalphaData/**/*.json text RalphaData/**/*.py text RalphaData/**/*.md text # Small thumbnails OK in regular git RalphaData/thumbnails/*.png !filter !diff !merge -text -------------------------------------------------------------------------------- File: Ralpha.uproject -------------------------------------------------------------------------------- { "FileVersion": 3, "EngineAssociation": "5.7", "Category": "", "Description": "AI-driven style transfer for infinite world generation", "Modules": [], "Plugins": [ { "Name": "RalphaPlugin", "Enabled": true }, { "Name": "Water", "Enabled": true } ], "TargetPlatforms": [ "Mac", "Windows" ] } -------------------------------------------------------------------------------- File: Config/DefaultEditor.ini -------------------------------------------------------------------------------- [/Script/UnrealEd.EditorPerformanceSettings] bMonitorEditorPerformance=True [/Script/UnrealEd.CrashReporterSettings] bHideLogFilesOption=False bHideRestartOption=False [ContentBrowser] ShowEngineContent=False ShowPluginContent=True -------------------------------------------------------------------------------- File: Config/DefaultEngine.ini -------------------------------------------------------------------------------- [/Script/EngineSettings.GameMapsSettings] EditorStartupMap=/Game/Maps/Main GameDefaultMap=/Game/Maps/Main [/Script/Engine.RendererSettings] r.DefaultFeature.AutoExposure=True r.DefaultFeature.MotionBlur=True r.DefaultFeature.Bloom=True r.DefaultFeature.AmbientOcclusion=True r.DefaultFeature.AmbientOcclusionStaticFraction=True r.Lumen.DiffuseIndirect.Allow=True r.Lumen.Reflections.Allow=True r.RayTracing=False [/Script/WindowsTargetPlatform.WindowsTargetSettings] DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 -D3D12TargetedShaderFormats=PCD3D_SM5 +D3D12TargetedShaderFormats=PCD3D_SM5 -D3D11TargetedShaderFormats=PCD3D_SM5 +D3D11TargetedShaderFormats=PCD3D_SM5 bGenerateNaniteFallbackMeshes=True Compiler=Default AudioSampleRate=48000 AudioCallbackBufferFrameSize=1024 AudioNumBuffersToEnqueue=1 AudioMaxChannels=0 AudioNumSourceWorkers=4 SpatializationPlugin= SourceDataOverridePlugin= ReverbPlugin= OcclusionPlugin= CompressionOverrides=(bOverrideCompressionTimes=False,DurationThreshold=5.000000,MaxNumRandomBranches=0,SoundCueQualityIndex=0) CacheSizeKB=65536 MaxChunkSizeOverrideKB=0 bResampleForDevice=False MaxSampleRate=48000.000000 HighSampleRate=32000.000000 MedSampleRate=24000.000000 LowSampleRate=12000.000000 MinSampleRate=8000.000000 CompressionQualityModifier=1.000000 AutoStreamingThreshold=0.000000 SoundCueCookQualityIndex=-1 [/Script/Engine.Engine] +ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/Ralpha") +ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/Ralpha") [URL] GameName=Ralpha [/Script/Engine.GarbageCollectionSettings] gc.MaxObjectsNotConsideredByGC=1 [ConsoleVariables] r.Lumen.ScreenProbeGather.FullResolutionJitterWidth=1 r.RayTracing.EnableOnDemandShaderCompilation=0 r.D3D12.DXR.AllowSM6ShaderWarning=0 r.SkinCache.CompileShaders=1 [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] bEnablePlugin=True bAllowNetworkConnection=True SecurityToken=48F557544C0FBE97F9394186D19510CE bIncludeInShipping=False bAllowExternalStartInShipping=False bCompileAFSProject=False bUseCompression=False bLogFiles=False bReportStats=False ConnectionType=USBOnly bUseManualIPAddress=False ManualIPAddress= -------------------------------------------------------------------------------- File: Config/DefaultGame.ini -------------------------------------------------------------------------------- [/Script/EngineSettings.GeneralProjectSettings] ProjectID=Ralpha2026 ProjectName=Ralpha CompanyName=Ralpha CopyrightNotice=Copyright Ralpha Team Description=AI-driven style transfer for infinite world generation bStartInVR=False bStartInAR=False -------------------------------------------------------------------------------- File: Config/DefaultInput.ini -------------------------------------------------------------------------------- [/Script/Engine.InputSettings] -AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) -AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) -AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) -AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f)) -AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) -AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) -AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f)) +AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) bAltEnterTogglesFullscreen=True bF11TogglesFullscreen=True bUseMouseForTouch=False bEnableMouseSmoothing=True bEnableFOVScaling=True bCaptureMouseOnLaunch=True bEnableLegacyInputScales=True bEnableMotionControls=True bFilterInputByPlatformUser=False bShouldFlushPressedKeysOnViewportFocusLost=True bAlwaysShowTouchInterface=False bShowConsoleOnFourFingerTap=True bEnableGestureRecognizer=False bUseAutocorrect=False bEnabledLegacyMappingDeprecationWarnings=True DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown DefaultViewportMouseLockMode=DoNotLock FOVScale=0.011110 DoubleClickTime=0.200000 DeprecatedActionAndAxisNames=() DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks -ConsoleKeys=Tilde +ConsoleKeys=Tilde -------------------------------------------------------------------------------- File: RalphaData/catalogue/catalogue.json -------------------------------------------------------------------------------- { "$schema": "./catalogue.schema.json", "version": "1.0.0", "generated_at": "2026-01-20T00:00:00Z", "asset_count": 0, "categories": [ "Furniture/Seating", "Furniture/Tables", "Furniture/Storage", "Furniture/Bedroom", "Electronics/Computing", "Electronics/Entertainment", "Electronics/Appliances", "Food/Packaged", "Food/Fresh", "Food/Prepared", "Food/Beverages", "Kitchenware", "Office", "Personal", "Decor/Wall", "Decor/Tabletop", "Decor/Lighting", "Vehicles/Cars", "Vehicles/Motorcycles", "Vehicles/Bicycles", "Vehicles/Other", "Nature/Trees", "Nature/Plants", "Nature/Rocks", "Architecture/Structural", "Architecture/Exterior", "Urban", "Sports", "Tools", "Animals" ], "assets": [] } -------------------------------------------------------------------------------- File: RalphaData/catalogue/catalogue.schema.json -------------------------------------------------------------------------------- { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Ralpha Asset Catalogue", "description": "Machine-readable index of all assets in ralpha-ue5", "type": "object", "required": ["version", "generated_at", "assets"], "properties": { "version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" }, "generated_at": { "type": "string", "format": "date-time" }, "asset_count": { "type": "integer" }, "categories": { "type": "array", "items": { "type": "string" } }, "assets": { "type": "array", "items": { "$ref": "#/definitions/Asset" } } }, "definitions": { "Asset": { "type": "object", "required": ["id", "name", "category", "unreal_path"], "properties": { "id": { "type": "string", "description": "Stable unique identifier (snake_case)", "pattern": "^[a-z][a-z0-9_]*$" }, "name": { "type": "string", "description": "Human-readable display name" }, "category": { "type": "string", "description": "Category path (e.g., Furniture/Seating)" }, "unreal_path": { "type": "string", "description": "Full Unreal asset path", "pattern": "^/Game/" }, "tags": { "type": "array", "items": { "type": "string" }, "description": "Searchable tags/synonyms" }, "thumbnail": { "type": "string", "description": "Relative path to thumbnail image" }, "bounds": { "type": "object", "description": "Bounding box in cm", "properties": { "x": { "type": "number" }, "y": { "type": "number" }, "z": { "type": "number" } } }, "pivot": { "type": "string", "enum": ["bottom_center", "center", "bottom_back", "custom"], "default": "bottom_center" }, "placement_modes": { "type": "array", "items": { "type": "string", "enum": ["floor", "wall", "ceiling", "surface", "hanging", "floating"] }, "default": ["floor"] }, "collision": { "type": "boolean", "default": true }, "lods": { "type": "integer", "description": "Number of LOD levels" }, "triangle_count": { "type": "integer", "description": "LOD0 triangle count" }, "materials": { "type": "array", "items": { "type": "string" }, "description": "Material slot names" }, "variants": { "type": "array", "items": { "type": "string" }, "description": "IDs of variant assets (e.g., color options)" }, "license": { "type": "object", "properties": { "type": { "type": "string", "enum": ["CC0", "CC-BY", "CC-BY-SA", "Megascans", "Marketplace", "Custom", "Restricted"] }, "source": { "type": "string" }, "attribution": { "type": "string" } } }, "added_at": { "type": "string", "format": "date-time" } } } } } -------------------------------------------------------------------------------- File: RalphaData/recipes/living_room_modern.json -------------------------------------------------------------------------------- { "$schema": "./recipe.schema.json", "recipe_id": "living_room_modern", "name": "Modern Living Room", "description": "Contemporary living room with sofa, coffee table, TV, and accent lighting", "category": "interior", "template_map": "Template_Interior", "components": [ { "asset": "sofa_modern_3seat", "position": "center", "facing": "focal_point", "placement": "floor" }, { "asset": "coffee_table_rect_glass", "position": "in_front_of:sofa_modern_3seat", "offset": [0, 100, 0], "placement": "floor" }, { "asset": "tv_65inch_mounted", "position": "wall:opposite_sofa", "offset": [0, 0, 120], "placement": "wall", "facing": "sofa_modern_3seat" }, { "asset": "tv_stand_modern", "position": "below:tv_65inch_mounted", "placement": "floor" }, { "asset": "floor_lamp_arc", "position": "beside:sofa_modern_3seat", "offset": [-50, 0, 0], "placement": "floor" }, { "asset": "side_table_round", "position": "beside:sofa_modern_3seat", "offset": [50, 0, 0], "placement": "floor" }, { "asset": "plant_potted_fiddle", "position": "corner:back_left", "placement": "floor", "optional": true }, { "asset": "rug_modern_geometric", "position": "under:coffee_table_rect_glass", "placement": "floor", "optional": true }, { "asset": "book_stack_decorative", "position": "on:coffee_table_rect_glass", "placement": "surface", "optional": true } ], "lighting_preset": "Interior_Evening", "post_preset": "PP_Warm", "camera_preset": "Interior_Wide", "tags": ["living room", "modern", "contemporary", "interior", "home"] } -------------------------------------------------------------------------------- File: RalphaData/recipes/office_corporate.json -------------------------------------------------------------------------------- { "$schema": "./recipe.schema.json", "recipe_id": "office_corporate", "name": "Corporate Office", "description": "Professional office space with desk, computer, and meeting area", "category": "interior", "template_map": "Template_Interior", "components": [ { "asset": "desk_executive", "position": "center_back", "facing": "camera", "placement": "floor" }, { "asset": "chair_office_executive", "position": "behind:desk_executive", "facing": "camera", "placement": "floor" }, { "asset": "monitor_27inch", "position": "on:desk_executive", "offset": [0, -20, 0], "placement": "surface" }, { "asset": "keyboard_wireless", "position": "on:desk_executive", "offset": [0, 20, 0], "placement": "surface" }, { "asset": "mouse_wireless", "position": "on:desk_executive", "offset": [30, 20, 0], "placement": "surface" }, { "asset": "phone_desk", "position": "on:desk_executive", "offset": [-40, 10, 0], "placement": "surface", "optional": true }, { "asset": "chair_guest", "position": "in_front_of:desk_executive", "offset": [-60, 150, 0], "facing": "desk_executive", "placement": "floor" }, { "asset": "chair_guest", "position": "in_front_of:desk_executive", "offset": [60, 150, 0], "facing": "desk_executive", "placement": "floor" }, { "asset": "bookshelf_modern", "position": "wall:left", "placement": "floor", "optional": true }, { "asset": "plant_potted_snake", "position": "corner:back_right", "placement": "floor", "optional": true } ], "lighting_preset": "Interior_Office", "post_preset": "PP_Neutral", "camera_preset": "Interior_Medium", "tags": ["office", "corporate", "business", "professional", "desk", "workspace"] } -------------------------------------------------------------------------------- File: RalphaData/recipes/portrait_studio_white.json -------------------------------------------------------------------------------- { "$schema": "./recipe.schema.json", "recipe_id": "portrait_studio_white", "name": "White Portrait Studio", "description": "Classic white cyclorama with 3-point lighting for portraits", "category": "portrait", "template_map": "Template_Studio", "components": [ { "asset": "cyclorama_white", "position": [0, 0, 0], "scale": 1.0, "placement": "floor" }, { "asset": "light_key_softbox", "position": [-200, 150, 200], "facing": "center", "placement": "floating" }, { "asset": "light_fill_panel", "position": [150, 100, 180], "facing": "center", "placement": "floating" }, { "asset": "light_rim_strip", "position": [0, -200, 220], "facing": "center", "placement": "floating" }, { "asset": "apple_box_full", "position": [0, 0, 0], "placement": "floor", "optional": true } ], "lighting_preset": "Studio_3Point_Soft", "post_preset": "PP_Portrait_Clean", "camera_preset": "Portrait_Headshot", "tags": ["portrait", "studio", "white", "cyclorama", "headshot", "professional"] } -------------------------------------------------------------------------------- File: RalphaData/recipes/recipe.schema.json -------------------------------------------------------------------------------- { "$schema": "http://json-schema.org/draft-07/schema#", "title": "Ralpha Scene Recipe", "description": "Composite scene definition for instant spawning", "type": "object", "required": ["recipe_id", "name", "components"], "properties": { "recipe_id": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, "name": { "type": "string" }, "description": { "type": "string" }, "category": { "type": "string", "enum": ["interior", "exterior", "portrait", "product", "vehicle", "custom"] }, "template_map": { "type": "string", "description": "Base template map to use", "enum": ["Template_Empty", "Template_Studio", "Template_Outdoor", "Template_Interior"] }, "components": { "type": "array", "items": { "$ref": "#/definitions/Component" } }, "lighting_preset": { "type": "string", "description": "Lighting preset ID to apply" }, "post_preset": { "type": "string", "description": "Post-processing preset ID to apply" }, "camera_preset": { "type": "string", "description": "Camera preset ID to apply" }, "sky_preset": { "type": "string", "description": "Sky/atmosphere preset to apply" }, "dependencies": { "type": "array", "items": { "type": "string" }, "description": "Other recipe IDs that must be spawned first" }, "tags": { "type": "array", "items": { "type": "string" } } }, "definitions": { "Component": { "type": "object", "required": ["asset"], "properties": { "asset": { "type": "string", "description": "Asset ID from catalogue" }, "position": { "oneOf": [ { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3, "description": "Absolute [x, y, z] position" }, { "type": "string", "description": "Relative position (e.g., 'center', 'in_front_of:Sofa', 'wall:north')" } ] }, "rotation": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3, "description": "[pitch, yaw, roll] in degrees" }, "scale": { "oneOf": [ { "type": "number" }, { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3 } ], "default": 1.0 }, "facing": { "type": "string", "description": "What the object should face", "enum": ["focal_point", "camera", "center", "north", "south", "east", "west", "away"] }, "placement": { "type": "string", "enum": ["floor", "wall", "surface", "hanging", "floating"], "default": "floor" }, "offset": { "type": "array", "items": { "type": "number" }, "minItems": 3, "maxItems": 3, "description": "Offset from calculated position" }, "attach_to": { "type": "string", "description": "Asset ID to attach to" }, "socket": { "type": "string", "description": "Socket name for attachment" }, "optional": { "type": "boolean", "default": false, "description": "Skip if asset not found" }, "variants": { "type": "object", "description": "Material/property overrides" } } } } } -------------------------------------------------------------------------------- File: RalphaData/presets/lighting/natural_golden_hour.json -------------------------------------------------------------------------------- { "preset_id": "Natural_GoldenHour", "name": "Golden Hour", "description": "Warm afternoon/evening sun with long shadows", "category": "natural", "directional_light": { "rotation": [-15, 45, 0], "intensity": 8.0, "color": "#FFB366", "temperature": 4000, "cast_shadows": true, "shadow_cascades": 4, "atmosphere_sun_light": true }, "sky_light": { "intensity": 1.0, "color": "#FFE4C4", "real_time_capture": true }, "sky_atmosphere": { "enabled": true, "rayleigh_scattering": [0.175, 0.409, 1.0], "mie_scattering_scale": 0.004, "absorption_scale": 0.75 }, "volumetric_cloud": { "enabled": true, "layer_bottom_altitude": 5.0, "layer_height": 10.0, "cloud_density": 0.3 }, "exponential_height_fog": { "enabled": true, "fog_density": 0.02, "fog_height_falloff": 0.2, "fog_inscattering_color": "#FFCC99", "volumetric_fog": true, "volumetric_fog_scattering": 0.5 } } -------------------------------------------------------------------------------- File: RalphaData/presets/lighting/studio_3point_soft.json -------------------------------------------------------------------------------- { "preset_id": "Studio_3Point_Soft", "name": "Studio 3-Point Soft", "description": "Classic soft 3-point lighting for portraits and products", "category": "studio", "lights": [ { "name": "KeyLight", "type": "rect", "position": [-200, 150, 200], "rotation": [0, -45, -30], "intensity": 15.0, "color": "#FFF5E6", "temperature": 5500, "source_width": 100, "source_height": 150, "barn_door_angle": 45, "cast_shadows": true, "soft_shadows": true }, { "name": "FillLight", "type": "rect", "position": [150, 100, 180], "rotation": [0, 45, -20], "intensity": 5.0, "color": "#E6F0FF", "temperature": 6500, "source_width": 120, "source_height": 120, "cast_shadows": false }, { "name": "RimLight", "type": "rect", "position": [0, -200, 220], "rotation": [0, 180, -30], "intensity": 8.0, "color": "#FFFFFF", "temperature": 6000, "source_width": 50, "source_height": 150, "cast_shadows": false } ], "sky_light": { "intensity": 0.5, "color": "#D4E5F7", "cubemap": null }, "environment": { "ambient_intensity": 0.1, "fog_enabled": false } } -------------------------------------------------------------------------------- File: RalphaData/presets/post/pp_cinematic.json -------------------------------------------------------------------------------- { "preset_id": "PP_Cinematic", "name": "Cinematic", "description": "Film-like look with crushed blacks and teal/orange color grading", "category": "stylized", "bloom": { "intensity": 0.5, "threshold": -1.0 }, "exposure": { "method": "manual", "exposure_compensation": 0.0, "min_brightness": 0.03, "max_brightness": 2.0 }, "color_grading": { "temperature": 6500, "tint": 0, "saturation": 1.05, "contrast": 1.1, "gamma": 1.0, "gain": 1.0, "offset": 0, "shadows_saturation": 0.9, "shadows_contrast": 1.1, "shadows_tint": "#1A3D5C", "highlights_saturation": 1.1, "highlights_tint": "#FFE4C4", "toe": 0.55, "shoulder": 0.26, "black_clip": 0.02, "white_clip": 0.04 }, "vignette": { "intensity": 0.3 }, "chromatic_aberration": { "intensity": 0.1, "start_offset": 0.5 }, "film_grain": { "intensity": 0.15, "response": 0.8 }, "lut": null } -------------------------------------------------------------------------------- File: RalphaData/scripts/generate_catalogue.py -------------------------------------------------------------------------------- #!/usr/bin/env python3 """ Generate catalogue.json from Unreal Engine assets. This script can be run: 1. Standalone with Python (parses .uasset files for metadata) 2. As a wrapper for UE commandlet (for full asset inspection) Usage: python generate_catalogue.py --project /path/to/Ralpha.uproject python generate_catalogue.py --scan-only # Just scan folders """ import argparse import json import os import subprocess import hashlib from datetime import datetime from pathlib import Path from typing import Any # Asset categories based on folder structure CATEGORY_MAP = { "Furniture/Seating": ["sofa", "chair", "stool", "bench", "armchair"], "Furniture/Tables": ["table", "desk", "counter"], "Furniture/Storage": ["shelf", "cabinet", "drawer", "wardrobe"], "Furniture/Bedroom": ["bed", "nightstand", "mattress"], "Electronics/Computing": ["laptop", "monitor", "keyboard", "mouse", "computer"], "Electronics/Entertainment": ["tv", "speaker", "console", "headphone"], "Electronics/Appliances": ["refrigerator", "microwave", "oven", "washer"], "Food/Packaged": ["can", "bottle", "box", "bag", "jar"], "Food/Fresh": ["fruit", "vegetable", "bread", "meat"], "Food/Prepared": ["pizza", "burger", "sandwich", "cake"], "Food/Beverages": ["coffee", "tea", "juice", "wine", "beer"], "Vehicles/Cars": ["sedan", "suv", "truck", "van", "sports"], "Vehicles/Motorcycles": ["motorcycle", "scooter", "bike"], "Nature/Trees": ["tree", "pine", "oak", "palm"], "Nature/Plants": ["plant", "flower", "bush", "grass"], "Nature/Rocks": ["rock", "boulder", "stone", "cliff"], } def scan_asset_folder(content_path: Path) -> list[dict[str, Any]]: """Scan Content/Ralpha/Assets for .uasset files.""" assets = [] asset_root = content_path / "Ralpha" / "Assets" if not asset_root.exists(): print(f"Warning: Asset folder not found: {asset_root}") return assets for uasset_path in asset_root.rglob("*.uasset"): # Skip generated files if "Intermediate" in str(uasset_path) or "Saved" in str(uasset_path): continue rel_path = uasset_path.relative_to(content_path) category = str(rel_path.parent).replace("Ralpha/Assets/", "").replace("/", "/") # Generate stable ID from path asset_name = uasset_path.stem asset_id = asset_name.lower().replace(" ", "_").replace("-", "_") # Build Unreal path unreal_path = f"/Game/{str(rel_path.with_suffix('')).replace(os.sep, '/')}" # Infer tags from name tags = [word.lower() for word in asset_name.replace("_", " ").split()] asset = { "id": asset_id, "name": asset_name.replace("_", " ").title(), "category": category, "unreal_path": unreal_path, "tags": tags, "thumbnail": f"thumbnails/{asset_id}.png", "pivot": "bottom_center", "placement_modes": ["floor"], "collision": True, "license": { "type": "Unknown", "source": "TBD" }, "added_at": datetime.utcnow().isoformat() + "Z" } assets.append(asset) return assets def run_ue_commandlet(project_path: Path, ue_path: Path | None = None) -> list[dict[str, Any]]: """Run UE commandlet to extract full asset metadata.""" # This would invoke the UE editor in commandlet mode # For now, just use scan method print("Note: Full UE commandlet integration not implemented yet") print("Using folder scan method instead") content_path = project_path.parent / "Content" return scan_asset_folder(content_path) def generate_thumbnails(assets: list[dict], project_path: Path) -> None: """Generate thumbnail images for assets (placeholder).""" thumbnail_dir = project_path.parent / "RalphaData" / "thumbnails" thumbnail_dir.mkdir(parents=True, exist_ok=True) print(f"Thumbnail directory: {thumbnail_dir}") print("Note: Actual thumbnail generation requires UE Editor Utility") # Create placeholder file placeholder = thumbnail_dir / ".gitkeep" placeholder.touch() def compute_catalogue_hash(assets: list[dict]) -> str: """Compute hash of catalogue for version tracking.""" content = json.dumps(sorted([a["id"] for a in assets])) return hashlib.sha256(content.encode()).hexdigest()[:12] def main(): parser = argparse.ArgumentParser(description="Generate Ralpha asset catalogue") parser.add_argument("--project", type=Path, help="Path to .uproject file") parser.add_argument("--scan-only", action="store_true", help="Only scan folders, no UE") parser.add_argument("--output", type=Path, help="Output catalogue path") parser.add_argument("--ue-path", type=Path, help="Path to UE Editor executable") args = parser.parse_args() # Find project if args.project: project_path = args.project else: # Default to current directory project_files = list(Path.cwd().glob("*.uproject")) if project_files: project_path = project_files[0] else: print("Error: No .uproject file found. Use --project to specify.") return 1 print(f"Project: {project_path}") # Scan or run commandlet content_path = project_path.parent / "Content" if args.scan_only or not args.ue_path: assets = scan_asset_folder(content_path) else: assets = run_ue_commandlet(project_path, args.ue_path) print(f"Found {len(assets)} assets") # Build catalogue catalogue = { "$schema": "./catalogue.schema.json", "version": "1.0.0", "generated_at": datetime.utcnow().isoformat() + "Z", "asset_count": len(assets), "catalogue_hash": compute_catalogue_hash(assets), "categories": sorted(list(set(a["category"] for a in assets))), "assets": sorted(assets, key=lambda a: (a["category"], a["id"])) } # Output if args.output: output_path = args.output else: output_path = project_path.parent / "RalphaData" / "catalogue" / "catalogue.json" output_path.parent.mkdir(parents=True, exist_ok=True) with open(output_path, "w") as f: json.dump(catalogue, f, indent=2) print(f"Catalogue written to: {output_path}") print(f"Hash: {catalogue['catalogue_hash']}") # Generate thumbnails placeholder generate_thumbnails(assets, project_path) return 0 if __name__ == "__main__": exit(main()) ================================================================================= END OF REPOMIX =================================================================================