ralpha-assets/repomix-output.txt

1538 lines
50 KiB
Plaintext

=================================================================================
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
=================================================================================