OBR Mod Creation Wiki
// by CosmicBoogaloo
ESC
Search every lesson, tool, hook, and reference
The Elder Scrolls IV: Oblivion Remastered

OBR Mod Creation Wiki

A complete guide from first texture swap to full custom armor with cloth physics, scripted spells, and new rooms - every step in order, every tool linked, honest about what works in mid-2026.

The One Thing You Need to Know

Oblivion Remastered is two game engines stacked together. Once you understand this, everything else clicks.

🎨

Unreal Engine 5.3.2 - visual

Models · textures · lighting · materials · animations · UI · cloth physics · audio (Wwise)

◇   Altar translation layer - the bridge between both worlds   ◇
🧠

Gamebryo 2006 - logic

Stats · quests · scripts · AI · die rolls · item records · cells · saves

What This Covers

  • Retextured & reskinned items
  • Brand-new weapons & armor (full 3D pipeline)
  • Capes & cloth physics workarounds
  • Custom animations & retargeting from any source
  • Scripted abilities via OBScript + Lua bridge
  • New interior rooms & player homes
  • Custom creatures via blueprint duplication
  • Facial animation with MetaHuman / DNA files
  • Wwise audio integration & External Sources
  • UE4SS Lua scripting - hooks, snippets, recipes
  • Blueprint behavior mods (custom UI, spawning)
  • Debugging, crash logs & rapid testing workflow
  • Mod distribution, compatibility patching & save safety
  • NPCs, custom races, hair modding
  • Dialogue, voice lines, lip sync workarounds

Known Limits (mid-2026)

  • UE-side terrain editing (no clean workflow yet)
  • New exterior worldspace cells (unsolved)
  • Custom Chaos Cloth Assets (crash on load)
  • Brand-new standalone sounds (experimental only)
  • NPC pathfinding in new cells (no navmesh)
  • Multiple enchantments (backend blocks it)
  • Fully custom voiced dialogue (BNK generation)
  • Game Pass / Epic stores (no OBSE64)
  • Classic .nif meshes (dead format for rendering)
  • Arbitrary body slot expansion (Altar enums fixed)

Your Learning Path

These steps are designed to be followed in order - each builds on the last.

1

How Modding Works

The dual-engine model. Ten minutes that make everything else make sense.

2

Set Up Your Tools

Every tool, in order, with links. FModel, xEdit, UE4SS, UE 5.3.2, Blender - all of it.

3

Make Mods: Basics

Retexture, swap sounds, edit stats, add new gear with existing models. Easiest first.

4

Make Mods: Advanced

Custom 3D armor, cloth physics, animations, MetaHuman faces, Wwise audio, new rooms.

5

Scripting & Custom Code

OBScript, OBSE64, UE4SS Lua - the deepest layer, where "impossible" mods live.

Reference - Always Here

CTD fixes, what-works status board, full glossary, console commands.

All Links & Resources

Every tool, every Nexus mod, every guide - every link in one place.

About This Codex Oblivion Remastered works differently from every older Elder Scrolls game. Old NIF formats don’t apply, there’s no official mod tool, and the community has reverse-engineered everything from scratch since April 2025. Old "how to mod Oblivion" guides actively mislead you. This codex is specific to the remaster - built by CosmicBoogaloo, author of C.A.F.E. and 60+ published mods with 15,000+ downloads, including work covered by PC Gamer, GameSpot, and others. Compiled from thousands of messages across every research channel in the OBR modding Discord.

Step 1 of 5

How Modding This Game Works

Before installing a single tool, understand the one idea that reframes every decision you will ever make about Oblivion Remastered modding.

The Dual-Engine Architecture

When you look at Oblivion Remastered you see Unreal Engine 5 graphics. But underneath, the original 2006 Gamebryo engine is still running the actual game - quests, stats, scripts, AI, die rolls. UE5 handles rendering only.

The original Oblivion.esm is essentially byte-identical to the classic game. The remaster’s changes are layered on top through the Altar ESPs rather than rewriting the master file.

🎨

The Face - Unreal Engine 5.3.2 visual

Models · textures · materials · lighting · menus · animations · cloth physics · audio (Wwise)

◇   Altar - proprietary translation layer developed by Virtuous   ◇
🧠

The Brain - Gamebryo 2006 logic

Logic · data · rules · OBScript · AI packages · inventory · quests · cells · saves

The One QuestionEvery time you want to make a mod, ask first: is the thing I want to change part of the brain (Gamebryo logic), the visual (UE5 rendering), or the bridge between them? Everything else is locating yourself on that map.

Two Kinds of Mod Files

brain   ESP / ESM - Logic

Classic Bethesda plugin files. Item names and stats, creature health, quest steps, AI packages, script behavior. Same format as the 2006 game.

Location: ...\\Content\\Dev\\ObvData\\Data\\

visual   PAK - Visuals

Unreal Engine content packages (.utoc + .ucas + .pak) containing all 3D models, textures, materials, and animations. All three files must be present together.

Location: ...\\Content\\Paks\\~mods\\

The Trap That Burns Classic Oblivion ModdersIn classic Oblivion, models were .nif files and textures were BSA-packed. None of that applies to the visuals here. UE5 reads all models and textures from PAK files. The old NIF format is dead for rendering. Loose BSA assets are simply ignored. This invalidates a huge amount of 15-year-old modding knowledge - exactly why this game-specific codex exists.

The Altar ESPs (never disable)

The remaster ships three special plugins that must stay enabled and correctly ordered at all times:

  • AltarESPMain.esp - applies the remaster’s record changes over the vanilla originals. Crucially, it rewrites all item names to UE5-managed localization keys (LOC_FN_IronSword, etc.). These keys are resolved at runtime from the UE localization system in the PAK files.
  • AltarDeluxe.esp - Deluxe Edition content (present in all editions).
  • AltarESPLocal.esp - referenced in Plugins.txt; may not be physically present on disk.
The Localization TrapBecause AltarESPMain rewrites every vanilla item name to a localization key, if your mod edits a vanilla record without forwarding that change, the display name reverts to the raw Gamebryo string and shows up tagged [nl]Something in-game. The NL-Tag Remover mod (mods/473) fixes this artifact on your new items.

The FormID Bridge - How the Engines Talk

Every item, NPC, and cell with a visual representation has a corresponding UE5 asset (Blueprint / form file) in the PAK files. This is how they connect:

  1. Gamebryo processes game logic (equipping a sword, placing an NPC) using internal FormIDs.
  2. Altar receives the FormID, converts it from hex to decimal integer, and looks it up in a pre-loaded map called the SyncMap.
  3. The SyncMap maps FormID → UE asset path.
  4. UE loads and renders the corresponding Blueprint/mesh/material.
FormID ConversionxEdit shows FormIDs as hex: 0x0003AA82. UE form files use the decimal equivalent: 240258. The first two hex digits (e.g. 03) are the load order index - always zero these out when referencing from the UE side. Formula: int(hexFormID, 16) with the first byte zeroed.
Adding a new sword requiresEngineFile type
The sword’s name, damage, weight, value, FormIDbrainESP record
The actual 3D model and texture the player seesvisualPAK asset
Wiring the ESP record to the PAK modelbridgeSyncMap .ini
The Big 2025–2026 BreakthroughTesSyncMapInjector (mods/1272) is a runtime UE4SS Lua mod that injects FormID-to-UE-asset mappings at game startup. This means a new item that reuses an existing vanilla model needs no PAK file at all - only an ESP and a small mapping .ini. A genuinely new model still requires the full UE5 asset pipeline.

The PAK Container Format

Oblivion Remastered uses UE5’s IO Store format, not traditional loose PAK files. This means every visual mod is three files that must all be present together:

  • .pak - loose files (config, fonts), packed with standard UnrealPak
  • .ucas - Content Addressable Store, contains all cooked .uasset data
  • .utoc - Table of Contents, the index for the .ucas file

The global.ucas / global.utoc pair provides dependency resolution for the whole game.

No Official Toolkit

  • Bethesda does not officially support mods for Oblivion Remastered and there is no Construction Kit built for the remaster.
  • The entire toolchain is community-built and partly reverse-engineered since April 2025. Capabilities change month to month.
  • The Xbox Game Pass version shipped with extra debug data which significantly accelerated community reverse engineering.
  • There is an estimated ~30% chance an official CK will be released - no announcement as of mid-2026.
  • The Altar plugin code is proprietary (licensed third-party components from Virtuous) and is extremely unlikely to ever be publicly released.
Date Your ClaimsAnything technical in this codex is “true as of mid-2026 - verify the version on the source page.” A research project today can be a one-click tool in three months.

Step 2 of 5

Set Up Your Tools

From zero to a fully working modding workbench. Follow these steps in order - each is the foundation for the next.

Before You StartRead Step 1 - How Modding Works first. Tags below indicate which engine each tool touches: brain = logic · visual = visuals · bridge = links them.
0

Confirm Your Install & Back Up

  • Steam build only. OBSE64 supports the Steam version only - not Game Pass, Microsoft Store, or Epic. If you own a non-Steam version, most of this codex still applies but scripting tools will not.
  • Note your exact game version (build number visible in-game) - several tools are matched to specific builds and will break on updates.
  • Back up your save files and make a clean copy of Plugins.txt before installing anything.
1

Mod Manager - pick one

  • pick oneMod Organizer 2 - profiles + virtual file system. Recommended for creators - keeps your game folder clean. Install the MO2 OR plugin (mods/366) for automatic argument passing.
  • altVortex - Nexus’s official manager, beginner-friendly. nexusmods.com/site/mods/1
  • altNORMM - dedicated Oblivion Remastered manager built specifically for this game.
2

Loaders & Bridge bridge

The foundation everything else runs on. Install in this exact order.

  • requiredOBSE64 - script extender / DLL plugin loader. Drop .dll + .exe in Binaries\Win64. Currently only a plugin loader - not a full script extender yet. Steam only.
  • requiredAddress Library for OBSE Plugins (mods/4475) - makes OBSE DLL plugins version-independent. Required by most plugins.
  • requiredUE4SS (mods/32, OR build v3.0.1) - Blueprint mod loader and Lua scripting runtime for UE5. Required by TesSyncMapInjector. Install to Binaries\Win64\. Use the Nexus OR build, not the GitHub experimental build - the generic build doesn’t hook into UE 5.3 properly.
  • requiredTesSyncMapInjector (mods/1272) - the bridge that links new ESP items to UE5 meshes at runtime. Runtime UE4SS Lua mod.
  • when neededMagicLoader + MagicPatcher (mods/1966) - enables new interior cells. Install when you reach Lesson 14.
  • handyOBRConsole (mods/2205) - lets UE4SS Lua mods run Oblivion console commands. ⚠ Crashes if you tab out of the game while active.
  • altSML / Simple BP Mod Loader (mods/1172) - alternative Blueprint loader with better input action recognition and documentation.
The #1 Setup CrashUE4SS and OBSE fight on first launch. Launch the game once without OBSE so UE4SS can configure itself, then launch with OBSE normally thereafter.
3

UE4SS Configuration visual

After installing UE4SS to Binaries\Win64\, configure it for development work:

UE4SS-settings.ini - enable the GUI console

[Debug]
ConsoleEnabled = 1
GuiConsoleEnabled = 1
GuiConsoleVisible = 1
GraphicsAPI = d3d11

With this enabled, UE4SS opens a GUI window alongside the game showing logs, a live object viewer, and function hooks. Press Ctrl+Numpad 6 in the GUI console to dump an up-to-date .usmap mappings file.

Generating Fresh MappingsAlways regenerate the .usmap file after any game patch - asset structures change between updates. The Nexus mappings file may be outdated. UE4SS generates your own from the live game.

Hot Reloading Lua Mods

Enable hot reloading in ue4ss-settings.ini and press Ctrl+R to reload Lua mods without restarting the game. Note: mods that hook into game-spawned actors may not hot-reload cleanly.

4

ESP / Data Tools brain

  • requiredxEdit 4.1.5n+ (TES4R / OR build) - primary ESP authoring and conflict detection. Get the OR-compatible build from the official xEdit Discord #xedit-builds channel - not the standard release. Launch with: TES4R64.exe -D:"...\ObvData\Data" -I:"...\ObvData\Oblivion.ini"
  • requiredNL-Tag Remover (mods/473) - fixes the [nl] artifact on new item names caused by Altar localization.
  • requiredLOOT - automatic load-order sorting. Sort with LOOT, then hand-fix the special cases below.
  • handyRuntime EditorIDs (mods/1331) - surfaces EditorIDs in the in-game console for quick testing.
  • situationalConstruction Set (2006 original) + CSE - visual editor for cells and containers. Setup details below. Never load the Altar ESPs in the CS - they crash it.

Construction Set Extender (CSE) Full Setup

  1. Download: OBSE for original Oblivion · CSE (mods/36370) · Official Construction Set · vcredist_x86.exe
  2. Extract all into your ObvData directory.
  3. Run TES4_Construction_Set_1.2.404.exe and install to your ObvData folder. Ignore the “Oblivion not installed” error.
  4. Edit Launch CSE.bat and replace its contents with: obse_loader.exe -editor -notimeout
  5. Right-click Launch CSE.batRun as Administrator.
CSE Tips
  • Your saved .esp appears in ObvData\Data\Data - move it up one level to ObvData\Data and add it to plugins.txt.
  • For protected directories: create a file named BGSEE_DirectoryCheckOverride (no extension) in ObvData.
  • For MO2 without elevation: create BGSEE_ElevatedPrivilegesCheckOverride (no extension) in ObvData.
  • Missing DLL error: install vcredist_x86.exe - the x64 version alone is insufficient.
  • Use the CSE beta from the Nexus Mirrors tab for fixes to script editor crashes and dialog size issues.
5

UE5 Asset Pipeline visual

Large downloads - install now so the workbench is complete before you need them.

  • requiredBlender + PSK/PSA plugin - model and animation authoring. If the current PSK plugin breaks on your imports, try the older Befzz plugin.
  • requiredUnreal Engine 5.3.2 - cook art assets. Use 5.3.2 specifically - not the latest version. Install via the Epic Games Launcher to an unprotected path like C:/, not Program Files or Desktop.
  • requiredFModel + mappings file (.usmap, mods/47) - browse and extract game assets. Set Archive Directory to game root (not a subfolder), UE Version to GAME_UE5_3.
  • requiredOR Mod Tools bundle (mods/3918) - retoc + UAssetGUI + oo2core in one package. Use the Nexus version of UAssetGUI, not the GitHub release (it has a critical PackageName bug fix).
  • materialsJsonAsAsset (C0bra5+Tectors fork recommended) + j0.dev - import vanilla materials from FModel JSON exports into your UE project.
  • armor/formsC.A.F.E. (mods/4891) - CosmicBoogaloo’s tool for setting asset paths in armor and weapon forms quickly.
  • texturesNNRM Merge/Split Tool (mods/3051) - split and recombine the NNRM channel-packed textures that OBR uses.
  • blueprintsOR SDK (Kein/Altar) + Visual Studio 2022 - the Altar stub project for custom blueprints and cloth physics. No full Unreal Engine source build required.
  • nanitesimple-nanite-parser (c0bra5) - extract full-detail Nanite meshes that FModel only exports the fallback version of.
  • audiosound2wem - convert audio files to Wwise .wem format for sound replacement.
  • audiowwiser (github.com/bnnm/wwiser) - browse Wwise .bnk soundbank files to locate specific .wem audio file IDs.
6

Build the Altar SDK Project (for Blueprints & Cloth Physics)

Required only if you’re doing custom blueprints, cloth physics, or C++ plugins. Skip if you’re only doing texture/sound/ESP work.

  1. Download and extract github.com/Kein/Altar.
  2. Install Visual Studio 2022 with the workload: Game Development with C++ + individual component: MSVC v143 - VS 2022 C++ x64/x86 build tools (v14.38-17.8).
  3. Right-click OblivionRemastered.uprojectGenerate Visual Studio project files.
  4. Open OblivionRemastered.sln in VS2022 → right-click the project in Solution Explorer → Build. Wait for it to finish, then close VS.
  5. Open the .uproject with UE 5.3.2.
  6. On first launch: if it warns about a missing water collision channel, click “Add to Engine.ini” and continue.
Toolchain Version ErrorIf the build fails with “Unable to find valid 14.38.33130 C++ toolchain”, edit C:\Users\YOU\AppData\Roaming\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml:
<Configuration xmlns="https://www.unrealengine.com/BuildConfiguration">
  <WindowsPlatform>
    <CompilerVersion>14.38.33130</CompilerVersion>
    <ToolchainVersion>14.38.33130</ToolchainVersion>
  </WindowsPlatform>
</Configuration>
7

Enable Chunking in UE (Required for Packaging)

  1. Edit → Project Settings → Packaging: enable Generate Chunks and Use IO Store. Disable Share Material Shader Code.
  2. Edit → Editor Preferences: search “chunk” → enable Allow ChunkID Assignments.

Creating a Chunk Assignment

  1. In the Content Browser, create a Data Asset → Primary Asset Label.
  2. Set: Priority = 1 · Chunk ID = any number 1–300 · uncheck Apply Recursively · Cook Rule = Always Cook · check Label Assets in My Directory.
  3. Name it ChunkYOURNUMBER for easy reference.
Chunking RulesNever chunk skeletons - the game already provides them. Never put blueprints in the same chunk as art assets - blueprint paks go in the LogicMods folder while art paks go in ~mods. Materials can “un-assign themselves from chunks” when re-opening the project - fix by ensuring Allow ChunkID Assignments is enabled.
8

First Launch & Verify

  1. Launch the game once WITHOUT OBSE so UE4SS can configure itself properly.
  2. Then launch through OBSE.
  3. Verify OBSE is working (in-game version check command: GetObseVersion in console).
  4. Verify UE4SS is working (its GUI console window should appear).
  5. Check load order in LOOT - Altar/base ESPs should be present and correctly ordered.
Make a SandboxIn MO2, create a dedicated test profile separate from your play profile. Park a throwaway save in a quiet interior for quick checks. Confirm your clean Plugins.txt backup is still safe before doing anything else.

Community Starter Packs

The community has assembled pre-packaged tool bundles to get you started faster:

Step 3 of 5

Make Mods: The Basics

Your first real mods - easiest first. Retexture, swap sounds, edit stats, add new gear without any 3D work required.

1

Retexture or Recolor Something visual

FModel · retoc · image editor - the single easiest mod type

Changing how an existing item, surface, or character looks by editing its texture. This is where everyone should start.

  1. Open FModel. Set Archive Directory to your game root (the folder containing OblivionRemastered.exe), UE Version to GAME_UE5_3, and load your .usmap mappings file.
  2. Browse the Folders tab to find your texture. Right-click → Save Texture. Always use TGA format, not PNG - TGA preserves all channels including alpha, which PNG discards.
  3. Edit the image in Photoshop, GIMP, or Substance. The game uses DirectX normal map format - if you’re working with normal maps, the Y channel is not inverted (unlike OpenGL).
  4. Import into your UE project at the exact same folder path as the original. Check FModel to match the original’s Texture Group and Compression Settings.
  5. Assign to a chunk. Package. Rename output files with _P suffix. Drop all three files (.pak/.ucas/.utoc) in ~mods.
The NNRM Texture System - What You Need to Know

OBR packs multiple maps into one image in the NNRM format (Normal/Normal/Roughness/Metallic):

ChannelContainsNotes
RNormal XDirectX format
GNormal YDirectX format (NOT inverted like OpenGL)
BRoughnessBlack = shiny, White = matte
A (alpha)MetallicBlack = non-metal, White = full metal

Variants: NNRS (specular instead of metallic) · NNRE (emissive) · NNR (no metallic) · NNRAO (ambient occlusion).

Import settings in UE: use BC7 compression and untick sRGB. Do not use the Normal compression preset for NNRMs - it breaks them.

In Blender: when working with OBR assets, invert the Green channel of the NNRM (UE uses DirectX -Y, Blender uses OpenGL Y). Set color space to Non-Color.

Use the free NNRM Merge/Split Tool (mods/3051) to split channels and recombine them.

Photoshop Alpha / Transparent Pixel WarningPhotoshop blacks out transparent pixel data when exporting PNG - it strips the color information from pixels with alpha=0. For diffuse textures that pack data in the alpha channel, use GIMP or this ffmpeg command to preserve RGB while setting alpha to opaque:

ffmpeg command - preserve RGB, set alpha=255

ffmpeg -i input.png -filter_complex "[0]geq=r='r(X,Y)':g='g(X,Y)':b='b(X,Y)':a='255'" out.png

Making an NNRM in Photoshop

  1. Open your Normal, Roughness, and Metallic maps.
  2. In the Channels panel of a new document: paste Roughness → Blue · Metallic → Alpha · Normal R → Red · Normal G → Green.
  3. Save as .TGA 32-bit - PNG will discard the alpha channel.

NNRM ffmpeg Split Scripts (c0bra5)

Split an NNRM into component images

# Normal XY channels (produces a blueish image - that's correct)
ffmpeg -hide_banner -v error -i "%1" -filter_complex "[0]geq=r='r(X,Y)':g='g(X,Y)':b='255':a='255'" "%~n1_n.png"
# Roughness (from B channel)
ffmpeg -hide_banner -v error -i "%1" -filter_complex "[0]geq=r='b(X,Y)':g='b(X,Y)':b='b(X,Y)':a='255'" "%~n1_r.png"
# Metallic (from Alpha channel)
ffmpeg -hide_banner -v error -i "%1" -filter_complex "[0]geq=r='255':g='255':b='255':a='p(X,Y)'" "%~n1_m.png"

For a model with no normal map detail, create a flat NNRM: set R=128, G=128 (neutral normal), fill B with your roughness value, fill Alpha with your metallic value.

Skin materials use SubsurfaceProfile (SP_SkinBase). Diffuse/BaseColor format: RGB = color, A = emissive mask. Hair textures use the _RAUD suffix multi-channel format - always export TGA from FModel, the alpha channel contains the opacity/strand mask.

2

Swap or Replace a Sound visual

Wwise .wem files · wwiser for finding voice line IDs

The game uses Audiokinetic Wwise for all audio. Sounds are stored as .wem files (Wwise Encoded Media) inside .bnk soundbank containers.

  1. Use FModel + retoc to unpack the .wem audio file you want to replace.
  2. Convert your replacement audio to .wem format using sound2wem. Alternatively, use older Wwise installer tools; see the RE Modding forum guide.
  3. Swap in your audio keeping the exact same filename. Repack with retoc into a _P pak. Drop in ~mods.

Finding a Specific Voice Line

  1. In FModel, browse to Localization/String Tables. Find the line you want and copy its event name number (looks like 00047660).
  2. Browse to WWise/Event/English. Find the .bnk bank file matching the NPC race and dialogue category.
  3. Open that bank in wwiser and navigate the tree to find your number in a CAkSound node. That node’s value is the SOURCE ID - the number matching the corresponding .wem file.
  4. Extract that .wem, replace with your converted audio, repack.
Adding Brand-New SoundsReplacing an existing sound works reliably. Adding a brand-new sound that wasn’t in the game requires full Wwise integration setup (version 2023.1.8.8601.3258, building against the Altar project, and the External Sources workflow) - see Lesson 12 in Step 4. Legacy ObScript PlaySound commands only work for a subset of sounds explicitly wired through Altar.

Dialogue Timing Without Voice

Placing .mp3 files in the legacy Sound/Voice/ folder matching the vanilla dialogue filename convention controls subtitle display duration, even though no audio plays. The mp3 file duration equals the subtitle display time - useful for localization subtitle syncing.

3

Unpack & Repack Game Files visual

retoc · UAssetGUI - the fundamental skill under every visual mod

The game’s files are sealed in the IO Store container format. Pull one out, change it, seal it back. This underlies almost every visual mod.

Extracting with retoc

retoc.exe to-legacy --filter "OblivionRemastered/Content/YourPath/YourFile" ^
  "[GameDir]\Content\Paks" "C:\Output"

Use forward slashes in the filter path. To extract multiple files at once, add multiple --filter arguments.

FModel vs retoc for Different File TypesFModel is great for browsing assets and exporting textures/meshes. But for .uasset/.uexp blueprint files (forms, BDPs), use retoc to extract, not FModel - FModel doesn’t export .uexp files from IO Store archives, giving you only half the file.

Editing with UAssetGUI

UAssetGUI needs both the .uasset and .uexp files in the same folder. Set the version dropdown to 5.3. Load your .usmap mappings file via Utils → Import Mappings. If you get “Failed to Parse X Exports”, extract the referenced dependency files alongside the main asset in the same directory structure.

Repacking with retoc

retoc.exe to-zen --version=UE5_3 "OblivionRemastered" "MyMod_P.utoc"

Input directory must be the OblivionRemastered folder (not deeper). This produces all three files (.pak, .ucas, .utoc) at once.

The ubulk / Blurry Texture FixUE5 may split large textures into .ubulk files. If retoc doesn’t pack these correctly, textures appear blurry in-game. Fix: set “Never Stream” on the texture in UE Editor (the old ini-based setting is from UE4 and doesn’t work in UE5). Update retoc to a recent version - GitHub issue #22 was resolved. Check: if the .ubulk is larger than .uexp → fix not applied. If .uexp is larger → fix worked.

You can combine art assets and form assets in the same retoc folder before packing to produce one single pak for your entire mod.

4

Change an Item’s Stats or Enchantment brain

xEdit - your first edit to the Gamebryo logic layer

Making a sword hit harder, armor weigh less, or adding enchantments. Uses xEdit to edit game data records on the Gamebryo side.

Launch xEdit with the correct flags

TES4R64.exe -D:"[GameDir]\Content\Dev\ObvData\Data" -I:"[GameDir]\Content\Dev\ObvData\Oblivion.ini"
  1. Let xEdit fully load Oblivion.esm and the DLC files.
  2. Find your item inside AltarESPMain.esp. Right-click it → “Copy as new record into…” a new mod file. Never edit the originals directly.
  3. Give it a fresh EditorID so it can’t conflict with anything, then edit the numbers in the DATA section - damage, weight, value, enchantments.
  4. Add your new .esp to Plugins.txt and test.
Renaming ItemsRenaming an item through a basic ESP edit makes the name show as [nl]Something in-game. Use the NL-Tag Remover mod (mods/473) to fix the broken tag, or use xEdit’s new-string workflow for properly localized names.
Beyond Enchantment Caps with xEditUse Fortify (actor value) to target any stat - including hidden ones like Shield (acts as extra armor rating) and Attack Bonus (increases damage). This bypasses the CS enchantment caps entirely.

Finding Your FormIDs In-Game

Your mod’s load order index determines the first two hex digits of all FormIDs it creates. Formula: (line number in plugins.txt minus the comment lines) → convert to hex. Example: your mod is the 23rd line → index 21 → hex 15 → your FormIDs start with 15XXXXXX. Install the Runtime EditorIDs mod (mods/1331) to surface EditorIDs directly in the console.

Game Settings (GMSTs) via GameSettings Loader

Use the Game Settings Loader (mods/833) to override game settings from a config file without touching ESPs:

[GameSettings]
fJumpHeightMin = 64
fJumpHeightMax = 128
fHealthRegenDelay = 999999
5

Add a New Item Using an Existing Model bridge

TesSyncMapInjector shortcut - new item, zero 3D work needed

Creating a genuinely new item - your own named, custom-stat weapon or armor - while reusing a model the game already has. No Blender or UE required thanks to TesSyncMapInjector.

  1. In xEdit, create your new item record (new EditorID, set your stats). In the model/mesh field, point it at an existing in-game model path you found in FModel.
  2. Run the Smart Mapper xEdit script. It reads your ESP and automatically writes a .ini link file to xEdit\SyncMap\.
  3. Copy that .ini file to [GameDir]\Content\Dev\ObvData\Data\SyncMap\.
  4. Put your .esp in ObvData\Data and add it to Plugins.txt.
  5. Critical load order rule: your plugin must appear above AltarDeluxe.esp in Plugins.txt. Below it, new items go invisible or crash on cell entry.
TSMI .ini Format

If you’re writing the SyncMap .ini manually:

[Meshes]
; DecimalFormID = /Game/path/to/UEasset.UEasset
002350=/Game/Forms/items/armor/BP_DaedricCuirass.BP_DaedricCuirass

Where the decimal FormID is converted from the hex FormID in xEdit (with load order bytes zeroed). Multiple mods coexist fine with separate INI files.

TSMI Enchanted Item BugCustom standalone armor becomes invisible when enchanted. When a player enchants an item, the game creates a new item with a dynamically assigned FormID at the high end of the address table. TSMI maps specific FormIDs to UE meshes - it doesn’t know about this new dynamic FormID. Result: enchanted version has no mesh. Workaround: don’t change the load order of the ESP after creating enchanted gear. If load order changed → the FormID changes → re-enchant. Status: TSMI developer researching a proper fix as of mid-2025.
6

Add a New Container or World Object bridge

Construction Set · MagicLoader - place new objects without crashing
  1. In the Construction Set, load MagicLoader’s example file (IntTestMod.esp) and click Set As Active File.
  2. Duplicate an existing chest or container, give it a new EditorID, say “Yes” to creating a new object. Place it in the world, save, close.
  3. Open the mod in xEdit (with all Altar files loaded) and run Haphestia’s Fix and Port Script (fixmod). This fixes the two missing parameters that the Remaster requires but the CS doesn’t add.
  4. Run the Smart Mapper xEdit script. Copy the resulting .ini to Data\SyncMap.
  5. Run MagicLoader → “Do Magic!” once to register the new cell entries.
  6. Enable your esp in Plugins.txt - above AltarDeluxe.esp.
MagicLoader v2MagicLoader has been updated. Follow its current Nexus page (mods/1966) if any steps look different from this description. It modifies the CellsToMapPath DataTable to link Gamebryo cells to UE map files.
✓ Basics CompleteYou can now retexture, replace sounds, edit item stats, add new gear with existing models, and place new world objects. Step 4 is where you start making things from scratch - your own 3D armor, cloth physics, animations, and more.

Step 4 of 5

Make Mods: Advanced

Custom 3D armor, cloth physics, animations, MetaHuman facial rigging, Wwise audio, world editing, new rooms. Requires Blender and Unreal Engine 5.3.2.

7

Build Custom 3D Armor from Scratch bridge

Blender → UE 5.3.2 → BDP Blueprint → C.A.F.E. → ESP → SyncMap

Asset Structure - What You Need to Create

Each armor piece in OBR consists of 7 components. For a mesh replacer you only need to swap the skeletal mesh. For a standalone new item you need all of them:

#ComponentPath patternRequired for
1ESP Form (Gamebryo side)ObvData/Data/MyMod.espAll new items
2Form Blueprint/Game/Forms/items/armor/ArmName.uassetNew items
3BDP Blueprint/Game/Forms/items/armor/BP_BDP_ArmName.uassetNew items - the key file
4Skeletal Mesh/Game/Art/Equipment/armor/type/SK_Piece.uassetReplacers + new items
5Material Instance (MIC)/Game/Materials/MIC_Piece.uassetCustom textures
6GND mesh (ground model)/Game/Art/armor/SM_Piece_gnd.uassetDropped item appearance
7Icon texture/Game/Art/UI/Icons/.../T_MyPieceInventory icon

A - Blender Setup

  1. Import SK_HumanoidFull (extracted from FModel via Save Model as PSK) as your body reference mesh. Color body yellow and head red for visual clarity.
  2. Import your armor mesh PSK. On PSK import in Blender: set Linear Color, scale 3m, scale factor 0.01.
  3. Rename the armature object to Armature in Blender’s Outliner. PSK meshes from FModel carry an extra root bone; this naming tells UE to skip that extra bone on import. Exception: do NOT rename NBO (New Body Options) skeletons - they are already set up correctly.
  4. For static meshes (SM_ prefix, like ground models): remove the armature entirely before exporting.
PSK vs UEFormat Tradeoffs

PSK issues: causes seam splitting and blendshape/morph artifacts; adds extra joints that cause Joint Count Mismatch errors.

UEFormat advantages/disadvantages: avoids morph issues. Problem: exports vertex colors as sRGB instead of linear, causing incorrect body part hiding in-game. Also doesn’t export sockets. Plugin: github.com/h4lfheart/UEFormat

Workaround: use UEFormat to get correct morphs, use PSK to get correct vertex colors, then copy the vertex color data from the PSK import to the UEFormat import in Blender.

B - Material Slots (Control First-Person Visibility)

SlotPurposeNotes
0Body main materialHidden in first-person view
1Sleeves / armsRemains visible in first-person
2Skirt / physics proxyShould have no material assigned - disable in LOD 0 sections
First-Person Invisible FixIf Blender merges both material slots because they share the same name, the item turns invisible in first-person view. Fix: rename one of the duplicate material slots to something different (e.g. MIC_Piece_TEMP) before exporting FBX. In UE, open the skeletal mesh editor and rename it back to the correct name, then assign the correct MIC to both slots.

C - Weight Painting

  1. Parent your clothing mesh to the armature: select mesh, Shift-click armature, Ctrl+PObject. For NBO armors: first un-parent with Alt+PKeep Transformations.
  2. Transfer weights using the Data Transfer modifier: Source = Humanoid/NBO mesh, Mapping = “Nearest Face Interpolated” → click Generate Data Layers → Apply.
  3. Add an Armature modifier - do NOT apply this one.
  4. Delete unused vertex groups (use a Blender plugin for bulk cleanup - vanilla body meshes ship with 407 vertex groups). Hand-paint any problem areas in Pose mode.
Static Helmets - Make Them Skeletal AnywayEven if a helmet is visually static and doesn’t deform, make it technically skeletal by weighting it entirely to the Head bone (paint all vertices red). Truly static meshes placed in UE end up at world origin, not on the character’s head.

Blender Sculpt Mode Tips

  • Use Sculpt Mode instead of Edit Mode for mesh fitting adjustments - prevents accidental backface selection.
  • Grab and Smooth tools are most useful for fine-tuned fitting to the body.
  • Enable Backface Culling in Viewport Shading to catch normal direction issues early.

Fixing Blade Mesh Shadow Issues

  • Don’t use Merge by Distance on blade edges - it breaks the sharp normals that give blades their edge.
  • Fix: in Edit Mode, select the sharp edges → Mark as Sharp (turns blue).
  • Or: Mesh → Normals → Average Vectors → Face Area.
  • To reset normals entirely: Mesh → Normals → Reset Vectors.

D - FBX Export Settings from Blender

Confirmed Export Settings Scale: 0.01 (critical - wrong value produces a massively oversized mesh in UE) · Forward: X · Up: Z · Smoothing: Edge (Face also works; some users get UE “unknown export” errors with Edge on specific meshes, switch to Face if that happens) · Vertex Colors: Linear (critical for correct body part hiding) · Apply Modifiers: ✓ · Only Deform Bones: ✓ · Add Leaf Bones: only for skeletal meshes - NOT for static meshes where it causes issues · Animation: unchecked.

Name skeletal meshes with SK_SetName_PieceName_m convention (_m for male, _f for female variants).

E - Import into UE 5.3.2

  1. Replicate the game’s exact directory structure in your UE Content folder. This is non-negotiable - the game resolves paths at runtime.
  2. For Skeletal Meshes: assign the correct skeleton on import:
    • Regular clothing/armor → SKEL_HumanoidSkeleton
    • NBO-specific female armor → SKEL_HumanoidFemaleAdd
    • Helmets → usually SKEL_HumanoidSkeleton or HumanoidHeadRig
  3. Set Import Normals and Tangents to preserve your custom normals (not Compute Normals).
  4. Enable Import Morph Targets if you’re editing head/face meshes.
  5. For Static Ground Meshes: set Collision preset to Custom. Enable Allow CPU Access (required for enchantment Niagara VFX on weapons). Set Collision Complexity to “Simple As Complex” and check Customized Collision.

F - Vertex Colors & Body Part Hiding System

Vertex colors on the body mesh control which parts disappear when armor is worn, via material function MF_HideByVertexColorInt3. They must be linear - the game crashes if exported as sRGB.

Complete Body Part Color Table (community research - deathwrench)
Body PartChannelValueBody PartChannelValue
pecs + mid-backR128shoulderG2
sternumR64upper-backG1
front-absR32bicepG4
side-abs + lumbarR16forearmG8
thighR8handG32
kneeR4underwearG128
low-calfR2braG64
foot-topR1low-ankleG16
high-ankleB64toes + foot-bottomB128
Reference: nyyxn body-part chart (mods/2583). Exact per-channel-to-body-part mapping is also documented in the Altar_enums.hpp dump under EBodyPartSlot. Note: Ears are NOT in this vertex color hiding system - hair/ear hiding is separate, handled by the head slot / biped slot. Merged armor meshes don’t support morphs. Use Blender’s Vertex Color Controls addon (BlenderMarket) to accurately sample linear RGBA values from imported meshes.

G - Bitmask Calculator (BP_BDP Body Section Hidden)

  1. Extract the item’s BP_BDP blueprint with retoc. Open in UAssetGUI. View → Expand All. Find MaleBodySectionHidden and FemaleBodySectionHidden. Add them if not present (add as 0 first, save, then change).
  2. Copy the current number into Windows Calculator → Programmer mode → Bit Toggling Keyboard.
  3. Using the body part color table above, set each bit to 1 (hidden) or 0 (visible).
  4. Copy the resulting number back into UAssetGUI. Save. Repack. Test.
The “0 Means Inherit” TrapIn a blueprint, setting the value to 0 means inherit hidden parts from parent blueprint - NOT “show all body parts.” To explicitly keep parts visible (e.g. sandals showing feet), set a non-zero value with all relevant bits cleared. The one exception: when editing directly in UAssetGUI, literal 0 works as true zero and doesn’t inherit.

Tails are separate - add or remove the HideTail property independently. Setting an item to the Amulet biped slot preserves hair and ears. Setting armor on the Tail biped slot makes it not render at all regardless of BDP settings.

Known working values: 4294967040 = hides everything except hair · 4294918400 = Mythic Dawn armor value · 10240 = hides head only · 0 = inherit from parent.

H - Materials in UE 5.3.2

You cannot create fully custom materials from scratch in UE - they crash on load. You can only inherit from vanilla base materials. The authoritative workflow from c0bra5:

  1. Pick a MIC from the game (inspect it in FModel → Export Properties JSON to see its parent chain).
  2. Create a new material in your UE project at the exact same path as the vanilla parent (e.g. /Game/Art/Character/Imperial/MIC_HumanUnderwear_M). Make it inherit from M_Base_Char.
  3. Create a new MIC (the location doesn’t matter) inheriting from that stub in step 2. Set up your textures only in this new MIC.
  4. Files to include in your mod pak: your own MICs + the skeletal mesh + your textures. Everything else (stubs, vanilla materials) stays out of the chunk - it’s reference-only.
Three Rules That Prevent Grey-World Disasters 1. Never change a static switch on a material - even to the same value. Results in the UE grey grid replacing your item.
2. Never pak the base material stub - packing it overwrites the real game material and greys out everything that shares it (potentially all weapons or all armor of one type).
3. Materials can un-assign themselves from chunks when re-opening the project. Fix: confirm Allow ChunkID Assignments is enabled in Editor Preferences and set Cook Rule to Always Cook.
Two Material Parents Worth Knowing
  • FPSClippingFix - the correct parent for any first-person-visible gear (weapons, gloves, cuirass sleeves). Prevents clipping in first-person view.
  • TWO-SIDED - good parent for ground drop meshes (SM_ prefix).

I - BP_BDP Blueprint Parent Chains

Your armor blueprint must inherit from the correct parent chain. Create dummy parent blueprints in Dev/clothing/GenericChild/ - they just need to exist with no content. Weapons do NOT require BDP blueprints.

Piece TypeRequired Parent Chain
CuirassVUpperBodyModularPart → BP_Generic_BDP_UpperBody → BP_Generic_BDP_UB_Cuirass
Full ArmorVUpperBodyModularPart → BP_Generic_BDP_UpperBody → BP_Generic_BDP_UB_FullArmor
GreavesBP_Generic_BDP_LowerBody → BP_Generic_BDP_LB_Greaves
Skeletal Helmet / HoodBP_Generic_BDP_SkeletalHelmet
AmuletVAmuletModularBodyPart

J - Ground Models (GND Assets)

When armor is dropped on the ground, it uses a separate static mesh (SM_ prefix, _gnd suffix convention). Where the GND reference is declared varies - Virtuos was inconsistent. Check both the main form asset (e.g. EbonyCuirass.uasset) and the BP_ form. To add a GND reference via UAssetGUI, add a NewWorldModels ArrayProperty of SoftObjectProperty to the BP form, then View → Recalculate Nodes. GND meshes must have proper collision or they fall through the floor. Object Channels must all be set; Complex Collision set to Default.

K - Forms with C.A.F.E., ESP, SyncMap & Packaging

  1. Use C.A.F.E. (mods/4891) to create armor forms. The form name must match the name in the asset path exactly.
  2. Create your ESP in xEdit with item records. Run Smart Mapper to generate Data\SyncMap\YourMod.ini.
  3. Place your ESP above AltarDeluxe.esp in Plugins.txt.
WhatWhere it goes
Body blueprints (LogicMods)...\\Content\\Paks\\LogicMods\\YourModName\\
Art assets + form blueprints...\\Content\\Paks\\~mods\\YourModName\\
ESP + SyncMap .iniContent\\Dev\\ObvData\\Data\ and \Data\SyncMap\

Additional Field Notes

  • Eye color editing is possible (discovered by _trungus) - method still being fully documented as of mid-2025. Check the OBR modding Discord #research-modeling for the current workflow.
  • Amulet biped slot preserves hair and ears - useful for items like head accessories or circlets that shouldn’t hide hair. Note the mesh will be positioned for the neck, so clipping adjustments may be needed.
  • Tail biped slot makes armor invisible regardless of BDP settings - don’t use it for regular armor.
  • Weapons do NOT require BDP blueprints - they are not assigned to body parts. Only armor/clothing needs them.
  • Vertex weight limits: too many bones deforming the same area produces warped/jagged edges. Delete unused vertex groups. Body meshes ship with 407 vertex groups - use a Blender plugin for bulk cleanup.

Beast Race Armor Compatibility

  • Import all skeletal meshes with SKEL_HumanoidSkeleton. Never include the skeleton in your pak chunk.
  • Never include physics assets - delete them from your project before packaging.
  • Khajiit material slot order must be preserved - procedural overlays depend on it.
  • Wrong skeleton assignment = “origami” deformed character model in-game.
8

Materials, Blueprints & Custom Icons visual

JsonAsAsset · UAssetGUI material override · ModActor blueprint entry point

Override Material via Blueprint (No Mesh Edit)

  1. Extract the item’s BP_BDP with retoc. Open in UAssetGUI → Import Data tab.
  2. In the bottom empty row, add: ClassPackage = Script/CoreUObject · ClassName = Package · OuterIndex = 0 · ObjectName = full game path of your material · BImportOptional = False. Click away and back to make it stick. A new empty row appears below.
  3. Fill the new row: ClassPackage = /Script/Engine · ClassName = MaterialInstanceConstant · OuterIndex = negative row number from step 2 · ObjectName = material name only.
  4. Go to Export Data tab → find the ChaosClothComponent (or appropriate mesh component) → look for or add OverrideMaterials ArrayProperty of ObjectProperty. Inside it, map slot numbers to your import rows.
  5. Save → repack with retoc → test.

JsonAsAsset (Import Vanilla Materials)

  1. Install the JAA plugin into Plugins/JsonAsAsset in your Altar project. Enable, restart, configure the export directory and mappings file path in plugin settings. Launch j0.dev.exe.
  2. In FModel, right-click a material → Export Properties (.json). Keep the export path short with no spaces, close to the root drive (e.g. C:\FModelExports).
  3. In UE, click the JAA plugin button → select the JSON → wait. Enable the Stubs checkbox (new in 1.4.1) for crash-free import of any material without a pre-made material store.
  4. MIC parents are not automatically assigned in 1.4.1 - set the parent manually after import. If the imported material is missing layers (the MIC Layers tab), edit that material in UAssetGUI instead - JAA doesn’t import the Layers tab.
  5. If JAA isn’t working: close FModel before running JAA; restart UE and re-check plugin settings.

Transparent / Masked Materials

MethodResultNotes
Blend Mode: Masked + Opacity MaskBinary cutout (no gradient)Works reliably for tattered cloth, hair cards
MIC_Necromancer_Amulet_GemTrue translucencyPath: Content/Art/Clothes/Amulet/
/Engine/EngineDebugMaterials/M_SimpleUnlitTranslucentUnlit translucencyColor only - can’t add textures or roughness

Custom Inventory Icons

  1. In UE: create folder Content/Art/UI/Icons/Dynamic_Icons/menus/Icons/armor/YourFolderName/
  2. Import your 256×256 PNG. In UE, prefix it with T_ (e.g. T_MyCuirass). Without the prefix, the game won’t detect the asset.
  3. In xEdit, set the Icon Image path to: Art/UI/Icons/.../YourFolderName/MyCuirass.dds - with .dds at the end even though there’s no .dds file. Do NOT include the T_ prefix in the xEdit path.

Blueprint Behavior Mods (ModActor Entry Point)

  1. Create Content\Mods\YourModName_P\ in your UE project. The folder name must match your final pak filenames exactly.
  2. Create a Blueprint of type Actor and name it exactly ModActor. UE4SS BPModLoaderMod auto-discovers and injects this on game load.
  3. Add a Widget Blueprint (WBP_ModHud) as your mod’s UI layer. In ModActor’s Event Graph, on Event BeginPlay: Create Widget → Add to Viewport. This persists across level changes.
  4. Use Event Tick + Was Input Key Just Pressed on the ModActor (not on a hidden widget - hidden widgets don’t tick) for keypress detection.
  5. Save mod data using SaveGame to slot "Mods/YourModName" (not the root, to prevent crashes when the mod is removed).
  6. Package and place files in Paks\LogicMods\YourModName_P\.
Scroll Box CrashUE’s standard ScrollBox widget crashes on some setups when included in a packaged pak. Workaround: create the ScrollBox at runtime using a Create Widget node rather than placing it in the widget designer. VModernScrollBox and VAltarNavigableScrollBox are custom Altar variants that partially address this but don’t solve all cases.
9

Add Cloth Physics to Clothing bridge

CCA crash explanation · blueprint workaround · standalone cape via amulet slot
Why Custom CCA Files Crash - The Deep ReasonCCA (Chaos Cloth Asset) files crash on load when custom-made - both empty and full CCAs crash. CCA files receive their physics data through a dataflow graph. These dataflows cannot be packed into a pak and are not present in the game files. The dataflow applies data onto the CCA itself at project build time in Virtuous’s internal tools. Without that step, the CCA is incomplete and crashes. Additionally: cloth paint on armor cuirasses renders the painted area static in-game. The suspected culprit is the SkeletalVariants property in generic armor BPs which forces CCA files to be mandatory. Hair and amulets work because they don’t have SkeletalVariants.
Hair & Amulets Skip Most of ThisHair simulates without the workaround - do only the UE Editor steps below and skip the blueprint setup. Amulets also work directly. The full blueprint workaround is only needed for cuirasses, greaves, and similar.

UE Editor Steps (Apply Cloth Physics)

  1. Import your cloth mesh separately from the armor body. They’ll be re-attached via blueprints.
  2. Open the skeletal mesh → right-click the section to simulate → Create Clothing Data from Section. Name the asset, leave Physics Asset as None for now.
  3. Right-click again → Apply Clothing Data.
  4. Click Activate Cloth Paint: white = simulated (moves freely), black = anchored (fixed to skeleton). Top edge = black, bottom hangs free.
  5. Tune ClothConfigs for physics behavior: stiffness, damping, gravity scale. Reference: UE cloth painting tutorial.
  6. Right-click mesh → Create Physics Asset. Name it PA_YourMod_Cloak.
  7. Open the Physics Asset → replace auto-generated capsules with hand-placed ones on the bones that matter. Set all capsule Physics Type to Kinematic.
  8. In the clothing config, assign your physics asset.

Physics Proxy Mesh

A low-poly duplicate mesh called “PhysicsProxy” is often included in OBR meshes. Physics is calculated on the low-poly version and then interpolated to the actual mesh. If your armor doesn’t need cloth physics: right-click the PhysicsProxy section in LOD 0 → Disable. Or delete the slot entirely. A material assigned to the proxy causes z-fighting and clipping artifacts.

Blueprint Setup for Cuirass + Cape

  1. Create Content/Mods/YourName/. Folder name must match pak filenames exactly.
  2. Create a blueprint named exactly ModActor (parent: Actor). UE4SS looks for this specific name.
  3. Create dummy parent blueprints for your armor type (see Lesson 7 parent chain table).
  4. Create your main blueprint (parent = deepest appropriate dummy). Set Male Mesh and Female Mesh to the armor body mesh (without cloth). Configure Material Slots Hidden In First Person.
  5. Add two Skeletal Mesh components as children of Root (one male cape, one female cape).
  6. Event graph: Event BeginPlay → Branch → Get Female Mesh → Is Valid Soft Object Reference → Set Leader Pose Component → Set Hidden In Game (male cape, check Hidden). Compile.
  7. Assign blueprint + ModActor to a different chunk number from your mesh assets. Package. Drop into Paks\LogicMods\YourName\.
  8. In UAssetGUI, update the BP_BDP path in your biped model form to point to your new blueprint.
Cloth Mesh Appears Doubled (Static + Dynamic)This happens when the cloth mesh is set as the Root Skeletal Mesh Component. The cloth mesh must be a child of the root, never the root itself.
v1.2 Changes to Cloth SetupAfter patch 1.2: VAmuletModularBodyPart behavior changed - you must now add the cape as a child to the Root Skeletal Mesh Component, not assign it directly. For the root, create an empty skeletal mesh (duplicate any SK → disable LOD0). Also: gender swapping via Get Female/Male Mesh nodes in the event graph no longer works as of patch 1.2.

Standalone Capes (Amulet Slot)

Route standalone capes through the amulet slot only - amulets are the only item type that accepts physics meshes directly without the CCA issue. Blueprint parent: VAmuletModularBodyPart. In xEdit, ensure the item uses the Amulet biped flag. Use the Hair mesh slot as an alternative if you don’t mind the workaround. Performance: cloth simulation is CPU-bound and varies significantly between systems. Use a proxy mesh (simplified low-poly version) for high-poly capes: proxy mesh guide.

10

Custom Animations & Replacers visual

JAA skeleton import · framerate · notifies · blendspaces · retargeting

Step 1 - Import SKEL_HumanoidSkeleton via JsonAsAsset (Critical First Step)

Use JAA 1.3.7 or newer. This imports the skeleton with virtual bones (IK targets) intact. Virtual bones cannot be recovered from a PSK export - JAA is the only reliable method. The correct skeleton has 378–379 bones (without sockets exported as bones).

Why JAA Skeleton Import Is MandatoryImporting from PSK/FBX without JAA produces a skeleton with a different bone index order. Wrong bone index order = animations that look perfect in the UE editor but play completely broken in-game. The “22 bones missing” warning that appears on import is normal - ignore it if the animation previews correctly.

Step 2 - Export SK_HumanoidFull from FModel Correctly

In FModel: Settings → Models → Socket Format → “Don’t Export Bone Sockets”. This prevents sockets from being exported as bones, which adds ~28 extra bones and breaks all IK at runtime.

Step 3 - Fix the Extra Root Bone in Blender

The PSK importer adds a root bone named after the mesh file (e.g. SK_HumanoidFull). Fix: rename the armature object to Armature in Blender’s Outliner. This makes UE skip the extra root, and the bone index order matches correctly.

Animation Framerate - Critical for Notify TimingWrong framerate = notifies fire at incorrect points in-game even if they appear correctly placed in the UE timeline.

Correct workflow:
1. Set Blender timeline to 30fps before importing PSA/PSK.
2. Edit or create your animation at 30fps.
3. Export as FBX.
4. Import into UE with custom sample rate of 60fps.

Common mistake: Blender defaults to 24fps. Importing at 24fps makes the total animation length longer than the vanilla version, shifting all notifies out of sync. Verify by exporting the vanilla animation’s JSON from FModel and comparing FrameCount and Duration.

Retargeting Animations from Other Games

Any animation from any source (Elden Ring, The Witcher 3, Skyrim, Paragon, stock UE Mannequin, Mixamo, CMU mocap library, Fab marketplace free tier) can be retargeted to the OBR skeleton.

Recommended UE 5.4 bridge method (krasuepisac):

  1. In your 5.3.2 modding project, migrate the OBR skeleton to a UE 5.4 project via Content → Migrate.
  2. In 5.4: create an IK Rig and IK Retargeter for the OBR skeleton. UE 5.4’s retargeting tools are 1–3 clicks vs. building an entire IK rig manually in 5.3.2.
  3. Retarget your source animations onto the OBR skeleton in 5.4. Export the retargeted animations as FBX.
  4. Re-import FBX into your 5.3.2 modding project. (Assets can’t be migrated back from 5.4 to 5.3.2, but FBX as an intermediate works perfectly.)

For Skyrim animations specifically: import NIF animations into Blender using the NIF importer plugin, export as FBX, then use the retargeter method above.

Animation Notifies - Combat Combo Chaining

Simply importing notifies via JAA is often insufficient - they may need to be added manually. For combat combos to chain correctly, both of the following notifies are required:

BP_ActionNotifyState_ChainWindow
  Begin Action Event Tag: ActionEvent.Attack.ChainingWindow.Enter
  End Action Event Tag:   ActionEvent.Attack.ChainingWindow.Exit

VAnimNotify_ActionNotifyState
  Begin Action Event Tag: ActionEvent.Attack.InputWindow.Enter
  (no End tag)

Removing either one breaks chaining. Confirmed by kei7855 on their shortsword animation replacer mod (mods/2489).

For block to interrupt an attack mid-swing, the start time of this notify must fall within both VAnim_ActionMeleeHitWindow and VAnimNotifyState_ImpactSystem:

BP_ActionNotifyState_TagContainer
  Tags To Transmit: [Input.Action.Combat.Block]
  BeginActionEventTag: ActionEvent.Attack.CancelTags.Add
  EndActionEventTag:   ActionEvent.Attack.CancelTags.Remove
TagContainer Blueprint Packaging RuleThe TagContainer Blueprint must NOT be assigned to any chunk or PrimaryAssetLabel. Its default values must be set to None. Violating either condition prevents tags from exporting correctly - the mod will cook without errors but behave incorrectly in-game with no error message.

Replacing Locomotion Blendspaces Persistently

Injecting a blendspace via AnimSet gets reset when the player equips or unequips a weapon, because the game stores all valid blendspaces in a TMap keyed by GameplayTags. Solution (krasuepisac’s discovery):

  1. Find the TMap that maps GameplayTags to blendspaces inside the AnimBP.
  2. Swap the entry for the relevant tag directly. Tag the actor after modification to avoid re-applying every frame.
  3. This persists through cell transitions - the player actor reference survives cell loads (unusual for UE games but intentional here).

Velocity reference values: Walk ~155 · Run (blendspace activates) ~509. Sprint uses a plain AnimSequence, not a blendspace. Root motion appears fully disabled - everything is root-locked; set root lock to zero on import.

Playing Animations at Runtime (Lua/Blueprint)

Triggering PlayAnimation from Lua works for the first play, but after the animation completes the character stops playing all normal animations permanently (frozen on last frame or T-pose). Use PlayMontage instead - montages blend in and out automatically via UE’s montage system and return the character to ABP-controlled state after finishing. The FullBody animation slot overwrites all other blending when active - use it for full-character override animations like emotes or cutscene poses.

ABP Architecture - Template & Linked Layers

The game uses a template ABP with linked layer instances. Each animation category (combat, idle, locomotion) is a separate linked layer instance within the template. Target the correct layer for the animation type you are replacing - this is why some replacements work and others break unexpectedly.

StrideWarping (standard UE plugin node) handles foot placement based on LocomotionSpeed. Foot lock positions only update when the character starts or stops moving.

Actor Persistence Across Cell Loads

  • The player actor reference persists through cell transitions (unusual for UE games). GameplayTags on the player also persist.
  • NPC actor references are completely destroyed on any cell load. Store persistent NPC mod data in a widget or custom UObject with a reference loop.

Mesh Merging & Morph Targets

UE’s Merge Meshes system combines all equipped armor pieces into one SkeletalMesh at runtime. This process destroys morph targets. NBO avoids this by using bone scaling instead - bone scale survives merging.

Gamebryo Animation (Legacy)

PlayGroup and PickIdle execute on the Gamebryo side but have no visual effect since rendering moved to UE5. AI Package idles still work. NPCs can get stuck in triggered idles - cast a spell on the NPC to break the animation lock.

Reference Mods & Resources

11

Facial Animation & the MetaHuman System visual

DNA files · RigLogic · Audio2Face · voice-line AnimSequence sync
Community DiscoveryThe game uses the MetaHuman system for facial animation. Face is animated entirely with bones, not morph targets (morph targets are only used in CharGen character creation). The face curves are identical to MetaHuman curves - confirmed by comparison with MetaHuman example projects. Discovered by izedev_55749.

Architecture

  • Every voice line has its own AnimSequence stored in Content/Art/Animation/Humanoid/Facial. Warning: the sheer number of files in this folder will freeze FModel on browse - navigate by known path instead.
  • Audio and face animation play independently - they start at the same time with no synchronization tracking. This means replacing either leaves the other running unchanged.
  • Mouth/speech sync uses a UE function called speech2face (procedural, no per-frame data).
  • TABP_FacialPose is the facial pose AnimBP.

Setup for Custom Head Facial Animation

  1. Enable the RigLogic, MetaHuman, and MetaHuman Identity plugins in your Altar UE project.
  2. Attach the appropriate .dna file to your face SkeletalMesh asset. DNA files are NOT standard .uasset files - they require CUE4Parse to extract from the game:
foreach (var kv in provider.Files) {
    if (kv.Key.EndsWith(".dna")) {
        var outPath = Path.Combine("extracted", Path.GetFileName(kv.Key));
        await using var stream = provider.SaveAsset(kv.Value,
            new FileStream(outPath, FileMode.Create));
    }
}
  1. Create and reference a dummy ABP_HeadPostProcess blueprint. This is required for the DNA to drive the facial bones.
MetaHuman Plugin WarningEnabling the MetaHuman plugin may permanently break your UE project for some users. Test in a project copy first. Khajiit uses a different head rig from the humanoid races.

Community DNA Files

WSDog extracted and publicly shared DNA files for all base races and named NPCs:

Generating New Facial Animations from Audio (Audio2Face Workflow)

  1. Use NVIDIA Audio2Face to generate blendshape/shape key animation from your audio file.
  2. Pipe through MetaHuman Live Link into UE 5.3.2. Record the result as a UE AnimSequence.
  3. OBR face curves share the exact same names as MetaHuman curves - they map directly with no translation needed.
  4. Replace the existing AnimSequence at the matching path in Content/Art/Animation/Humanoid/Facial.
  5. MetaHuman plugin supports batch processing for automating generation from multiple audio files at once.

Audio-Anim Sync for Localization

Since audio and face animation play independently, to fix out-of-sync dubbed audio (where the new audio is longer than the original): replace the AnimSequence at the correct path with a new one timed to match your audio duration.

12

Wwise Audio Integration visual

External Sources · full Wwise setup · custom .wem files at runtime

The game uses Audiokinetic Wwise 2023.1.8.8601.3258 for all audio. UE’s built-in audio system has only two dummy assets (DummySoundClass, DummySoundCue).

What Works Without Full Wwise Integration

  • Sound replacement: swap existing .wem files inside a pak (see Lesson 2)
  • Posting vanilla Wwise events from Blueprints: use the VAudioHandlers subsystem and BPF_PostEvent (or PostEventAtLocation for spatial audio)
  • Turning vanilla audio events on/off from Blueprints using vanilla AkAudioEvent class defaults

Full Wwise Integration Setup

Version Must Match ExactlyUse Wwise version 2023.1.8.8601.3258 exactly. Mismatched versions cause binary incompatibilities that are difficult to debug.
  1. Install Wwise from the Audiokinetic Launcher - select exactly the version above.
  2. In your Altar project folder, delete the existing Wwise and WwiseNiagara plugin folders.
  3. Use the Launcher: Unreal Engine → Integrate Wwise into Project.
  4. Fix the compile error in Source/Altar/Public/VAltarAkPortalComponent.h: update the include to "AkAcousticPortal.h" and parent class to public UAkPortalComponent (the Altar project stubs reference the outdated name AkPortalComponent).
  5. Build via Visual Studio. Name the Wwise project Altar and place it at C:\Altar-main\WwiseAltar\ to match paths in the game’s DefaultEngine.ini (not strictly required but simplifies cross-referencing).

External Sources - Custom .wem Files Without Rebuilding Soundbanks

External Sources allow specifying which .wem file Wwise loads at runtime without rebuilding soundbanks. This enables one framework mod to handle the Wwise plumbing while other mods simply ship .wem files.

Enable External Sources by overriding the game’s DefaultEngine.ini from your mod pak:

[Audio]
WwiseFileHandlerModuleName=WwiseSimpleExternalSource
Critical External Sources Bug (.yeah.nah.yeah.)External Sources produce a valid Playing ID and correct Wwise stats - but produce absolutely no audio unless the mod actor has a static mesh component with an actual mesh assigned and packaged. The moment a mesh is present in the packaged mod, External Sources work correctly. The reason for this requirement is not fully understood as of mid-2025.

Required Setup for External Sources

  1. Create a DataTable using the External Source cookie struct (provided by Wwise).
  2. Populate with: cookie value, media ID, codec ID, media name (filename of your .wem).
  3. Override DefaultEngine.ini to enable WwiseSimpleExternalSource.
  4. Include your .wem file in the pak at the correct staging path.
  5. Ensure your mod actor has a packaged static mesh component with a mesh assigned.

Getting a Free Non-Commercial Wwise License

Creating new Wwise audio events (new sound forms in the ESP with new UE audio) requires Wwise’s authoring tools to generate new .bnk soundbank files. Getting a free non-commercial license requires a manual review by Audiokinetic staff - fill out the form at audiokinetic.com/en/pricing honestly.

13

World & Mapping - What’s Actually Possible visual

CK vs UE terrain · cells · grass · navmesh · Nanite · heightmap tools
The Core Architecture BreakThe single most important thing to understand: terrain no longer lives in the Construction Set. All actual terrain geometry and heightmap data is on the UE side, stored in generated .umap files. The CK terrain editor exists in name but has no visible effect at runtime - Unreal’s terrain takes over.

Current Status Table (mid-2025 community research)

FeatureStatusNotes
New interior cellsWorkingVia MagicLoader (Haphestia’s tool)
New exterior cellsNot workingnafnaf_95 confirmed - as of mid-2025
New worldspacesNot workingAs of mid-2025
CK terrain editingNo effectUE terrain takes over; CK data unused
UE heightmap editingNo clean workflowNo polished mod tools yet
Grass type modification via CSNo effectGrass is UE-side foliage paint
Navmesh editingNot possibleBaked into UE umaps
LOD configurationNot neededNanite handles it automatically
.umap direct editingWorks via UAssetGUIVersion-sensitive; breaks on updates
Map bordersCan be disabledTerrain ends at square boundary
Custom map loadingWorkingMust use MagicLoader for Gamebryo sync

Grass & Foliage

All visible grass is part of generated UE maps - painted onto landscape materials during UE map generation. TESGrass records in the CS have no in-game effect. Adding custom trees and grass:

  • TESSyncMapInjector approach (most ergonomic): link a UE static mesh to a TES mesh record in the CS and place it using standard CS workflows. Demonstrated for water meshes (mods/4874) and trees (mods/4740).
  • Blueprint/Lua spawn approach: a custom Blueprint spawns a mesh at dynamic coordinates from Lua. Requires manual coordinate input.
  • Mad’s placement system (minuteready): spray mesh instances organically by running around in-game using Mad’s “rapid spell” + “add instance on X” system, writing positions to a text file, then importing them. Labor-intensive but gives natural placement feel.
  • Removing existing UE grass: edit each .umap via UAssetGUI to remove grass foliage actors (tedious, version-sensitive), or hook the grass Blueprint creation in Lua and destroy instances on spawn (theoretical).

Heightmap

The old Gamebryo heightmap still exists in game files but does NOT drive the visible terrain. The UE side has its own heightmap. No clean extraction or re-injection workflow exists yet.

Using surf / blenderumap3: importing .umap files via surf/blenderumap3 confirms the layer-per-category structure - exporting only the umap for a specific area returns a single tree mesh, not the full scene. The layers need to be loaded together to reconstruct the full environment. Tools: search for BlenderUMap2 and surf in the UE modding community.

The “All of Tamriel” heightmap from Nexus (Skyrim mods/52675) has been discussed as a source for adding adjacent provinces. Key note from grimlock_arts: TES game heightmap data is deceptively low detail - the games rely heavily on placed rock models pushed into the ground to create the impression of complex terrain. The raw heightmap is just a flat base layer.

Heightmap tools referenced by the community:

Map Borders & Open Space

Map borders can be disabled. A large open stretch of land runs from roughly 1/3 to 1/2 the size of the main game map, fully walkable and tree-free, extending all the way to the ocean. No water in the way. This is the most viable space for exterior expansion content as of mid-2025.

Navmesh

Navmesh editing is not currently possible from the modder side. NPCs cannot be directed reliably in new areas without navmesh. Workaround: add a small gatehouse cell or confined space to prevent NPCs from wandering beyond intended areas.

Nanite & LODs

OBR uses Nanite for static mesh geometry, which handles LODs automatically. You do not need to create LOD meshes or configure LOD settings. There is no DynDOLOD equivalent. FModel exports only the fallback mesh (low-poly version for Lumen), not the full Nanite detail. For full extraction use c0bra5’s simple-nanite-parser (Python-based, exports to GLTF with vertex colors).

CK–UE Terrain Height Mismatch Workaround

When building outside the vanilla map border, CK terrain height and UE terrain height diverge. Workaround (nafnaf_95): work one cell at a time, build at CK-default height, then select all objects and shift them vertically until they sit correctly on the UE terrain surface. Tedious but workable for exterior static object placement.

Trees & SpeedTree

Trees are handled via SpeedTree assets with built-in wind animation, baked into per-cell UE umap files with foliage instancers. Map file names are hashed making systematic modification very difficult. Adding new trees as individual Blueprint actors works but incurs significant performance cost. The SpeedTree SDK (required for full authoring) has enterprise-only commercial licensing (~$100k+).

14

Build a New Interior Room or Player Home bridge

Construction Set · MagicLoader · SyncMap - no NPC pathfinding

New interior cells work via MagicLoader. The tool modifies the CellsToMapPath DataTable with new entries that map Gamebryo cells to UE map files, making the game load the correct UE level when a cell is entered.

  1. In the Construction Set, load MagicLoader’s example file (IntTestMod.esp) → Set As Active File.
  2. In Cell view, create a new interior cell. Build the room using existing tileset pieces. Place furniture, containers, lights. Add a door and use Door Teleport to connect it to an exterior location.
  3. Save the esp. Open in xEdit (all Altar files loaded) and run Haphestia’s Fix and Port Script (fixmod).
  4. Run the Smart Mapper xEdit script. Copy the resulting .ini to Data\SyncMap.
  5. Run MagicPatcher to build the string table patch .json for room names and door labels.
  6. Run MagicLoader → “Do Magic!” once.
  7. Enable your esp in Plugins.txt - above AltarDeluxe.esp.
Every Placed Object Must Be SyncMappedEvery object you place that isn’t already in the base game’s SyncMap needs its own entry. Anything missing becomes invisible or causes a CTD on cell entry. Run Smart Mapper after every significant edit session.
Reference Proof-of-ConceptThe first mod to demonstrate placing static objects in a custom interior cell: “New Player Home - Abandoned House” (mods/828).
Known Limitation - No NPC PathfindingNPCs and creatures placed in new cells have no pathfinding data and stand frozen. No clean fix as of mid-2026. Design around this: player homes, storage rooms, and static setpiece spaces work great. NPCs are best avoided in new cells or confined with physical barriers.

MagicLoader Known Bug (Fixed in v1.1)

An early version of the Fix and Port Script overwrote FULL name fields on existing records, causing custom items and locations to lose their names. This was fixed in v1.1: “Fixes clearing IDs on newly added records by mistake.”

15

Custom Creatures via Blueprint Duplication bridge

The “Panther Method” - duplicate only as far up the chain as needed
  1. In FModel, find the base creature blueprint you want to modify. Note which blueprints it references.
  2. Trace references upward - those BPs reference their own parents. You’ll find a hierarchy of AI, animation, skeleton, etc.
  3. Duplicate only as many levels as you need. Stop at the level where you want to diverge. To replace mesh and textures while keeping vanilla AI and animations: duplicate only 2–3 levels.
  4. In your duplicated top-level blueprint, swap in your new mesh, textures, or behavior values.
  5. Create the ESP form for your new creature in xEdit. SyncMap it via TesSyncMapInjector. Package blueprints and form.
This Pattern GeneralizesParent-chain duplication applies to any actor edit where you want to keep most vanilla behavior and diverge at exactly one point. Works for NPCs, creatures, special actors - any blueprint hierarchy in the game.
New NPCs Crash Without SyncMappingNew NPC records created entirely from scratch crash the cell unless properly registered with TesSyncMapInjector. NPCs must reference an existing Blueprint for their UE-side representation. You can copy an existing NPC form and modify it - faces don’t always assign correctly but the NPC will function.
16

NPCs, Custom Races & Hair Modding bridge

Race form setup · AllRaceModifications table · hair phenotypes · strand hair

Adding New NPCs

  1. Create the NPC record in CS or xEdit. Use an existing vanilla race for reliable results - non-vanilla races default to Imperial appearance at runtime if the UE form can’t be found.
  2. For the UE side: clone an NPC form file from FModel that matches the intended race/gender.
  3. Edit the cloned form in UAssetGUI to change name references.
  4. Register in TesSyncMapInjector.
  5. Place the NPC in a cell via CS.
Custom Race NPC Loading OrderIf getting an NPC to use a non-vanilla race, ensure the custom race PAK loads early by prefixing the pak filename with 0000_. At runtime, if UE can’t find the race form, it silently defaults to Imperial.

Custom Races

Custom races require:

  1. An ESP with the new race record.
  2. A UE race form blueprint at /Game/Forms/actors/race/CustomRace.uasset.
  3. Entries in the AllRaceModifications data table (edit via JsonAsAsset or retoc + UAssetGUI).
  4. Phenotype and hair tables updated to include the race.

Adding fully custom playable races with all CharGen sliders is still in research as of mid-2026. Non-playable custom races (for NPCs) work when given a vanilla voice type. The race menu UI reads from UE-side data tables.

Hair Modding

Hair phenotype files live at /Game/Dev/Phenotypes/Hair/ as data tables listing available hairstyles per race.

  1. Extract the existing HP_[Race]_HR_[StyleName].uasset file using retoc.
  2. Inspect with UAssetGUI or JsonAsAsset.
  3. Create your new hair mesh in UE project.
  4. Add an entry to the race’s phenotype hair data table.
  5. Use RMU (Race Menu Utility) for runtime loading: rmu set hair /Game/Dev/Phenotypes/Hair/HP_RaceName_HR_HairName.HP_RaceName_HR_HairName
Hair Import Scale IssueA known issue causes hair meshes to import at the wrong scale in some workflows. If hair appears too large or too small in-game, check the mesh dimensions in UE against a known-working vanilla hair mesh and scale accordingly.

Hair texture format: use the _RAUD suffix multi-channel format. Always export as TGA from FModel - the alpha channel contains the opacity/strand mask, critical for proper hair card rendering.

Facial animation compatibility: modifying head meshes (including head-attached hair) can break facial lip-sync if the mesh topology changes significantly. Blinking and face morphs may continue to work even when lip-sync breaks.

Strand hair: true strand (groom) hair via UE5’s Groom system has been partially achieved but the full binding pipeline is not documented. Hair card replacement is the stable workflow.

17

Dialogue, Voice Lines & Lip Sync bridge

TSMI dialogue mapping · subtitle timing · lip sync workarounds

Text / Subtitle Changes

ESP-side text: changing dialogue text in CS/xEdit works for most dialogue. Display names use the LOC_FN prefix system - forward AltarESPMain changes or names break. Localization files: some UI strings are in UE localization tables inside the PAK and require editing the localization .uasset files. Note: only one mod’s localization table wins in case of PAK conflict.

Reusing Existing Voice Lines for New NPCs

  1. Create the dialogue topic in your ESP.
  2. Use TesSyncMapInjector to map your new dialogue FormID to an existing dialogue form blueprint path. Format: TOPICTYPE_espname_FormID. Example:
    GREETING_MyMod_00000ED6=/Game/Forms/miscellaneous/dialog/GREETING_Someactor_000479e1.GREETING_Someactor_000479e1
  3. The NPC’s voice type (set in the race ESP record) must match the target dialogue form’s voice set. Using a non-vanilla race? Set the race to a vanilla voice type to prevent crashes.

Subtitle Display Duration Without Voice

Placing .mp3 files in the legacy Sound/Voice/ folder matching the vanilla dialogue filename convention controls subtitle display duration, even though no actual audio plays. The mp3 file’s duration equals the subtitle display time - useful for subtitle-only localization mods.

Lip Sync

Lip sync is handled by UE animation assets linked to dialogue forms. Current workaround: use an existing dialogue form that has entries for multiple races - the game will attempt to use the matching race’s lip sync animation. Fully custom voiced dialogue (new audio + new lip sync) is not yet solved end-to-end - the WEM format is figured out but BNK generation is the blocker.

✓ You’ve Finished the Making-Things SectionEvery type of mod in Oblivion Remastered is covered. Step 5 is writing actual code - OBScript, OBSE64, and UE4SS Lua - to make the game do things impossible any other way.

Step 5 of 5

Scripting & Custom Code

Three scripting surfaces mirror the two-engine architecture. The real craft is making them communicate with each other.

The Three Scripting Surfaces

brain   1 · OBScript (Construction Set)

The classic Oblivion scripting language - bread and butter for enchantments, spell script effects, object/quest scripts, and AI packages. The UESP Construction Set wiki is ~20 years of OBScript documentation and is the essential reference. Any function marked as OBSE on that wiki will not work in the Remaster.

brain   2 · OBSE64 (C++ DLL Plugins)

OBSE64 is currently only a DLL/plugin loader - it does NOT extend OBScript with new functions, despite the misleading name. To write C++ DLL plugins, use commonlibob64 for reverse-engineered class headers. OBSE64 is version-locked: any game binary update breaks it until offsets are manually updated. See a working plugin example at cnf13/DeleteSpells.

visual   3 · UE4SS Lua + Blueprint Loaders

Some capabilities exist only on the Unreal side: hiding armor pieces, driving animation, Niagara VFX, spawning actors. Use the Nexus OR-specific UE4SS build (v3.0.1-394-g437a8ff) - the generic GitHub experimental build doesn’t hook into UE 5.3 properly and may not recognize Execute Console Command nodes from Blueprints.

Reality Check on Custom Magic EffectsCustom magic effects are hardcoded-unsupported in the Remaster - use OBScript Script Effects instead. The power comes from combining surfaces, which the notification bridge below enables.

The Notification Bridge - OBScript → Lua

Worked out by MadAborModding on top of a notification-parsing method by Dicene. This is the breakthrough that unlocks “impossible” mods like levitation, custom physics casts, and transformations.

OBScript can send on-screen notifications, and Lua can read and instantly hide them. A notification becomes a silent message channel from the Gamebryo brain to the UE face.

Brain side - OBScript attached to a spell, quest, or item

ScriptName madLevitationScript

begin ScriptEffectStart
    message "madLevitationScriptStart"
end

begin ScriptEffectFinish
    message "madLevitationScriptEnd"
end

Face side - Lua hook that reads, hides, and reacts

RegisterHook("Function /Script/Altar.VHUDSubtitleViewModel:ConsumeNotification",
function(hudVM)
    local hudVM = hudVM:get()
    local text = hudVM.Notification.Text:ToString()
    if text:match("madLevitationScriptStart") then
        hudVM.Notification.ShowSeconds = 0.0001   -- hide from player instantly
        ToggleFly()
        return
    end
    if text:match("madLevitationScriptEnd") then
        hudVM.Notification.ShowSeconds = 0.0001
        DispelFly()
        return
    end
end)

Setting ShowSeconds = 0.0001 effectively hides the notification before it can appear on screen. Source: VHUDSubtitleViewModel.cpp. Live reference implementation: Levitation – UE4SS (mods/3334) version “A”.

Post-June 2025 Patchconsole.ExecuteConsole() now requires a fourth true parameter to work correctly. If your console commands silently stopped working after a patch, add true as the fourth argument: Kismet:ExecuteConsoleCommand(pc.player, command, pc, true).

Lua → OBScript (The Reverse Direction)

Both OBScript and UE4SS have access to Oblivion global variables. This is the primary reverse channel:

  1. In Lua, on your event, call: Kismet:ExecuteConsoleCommand(pc.player, "ObvConsole set MyGlobalFlag to 1", pc, true)
  2. An Oblivion quest script (Begin GameMode) polls that global; when it sees 1, does its thing (can fire a “silent” notification back to Lua), then resets the global to 0.
  3. Lua reads the return notification and continues.

UE4SS Lua - API, Patterns & Gotchas

Essential Functions

  • RegisterKeyBind(Key.N, fn) - bind a hotkey
  • RegisterHook("Function /Script/Altar.Class:Method", fn) - run on an engine function call
  • NotifyOnNewObject(class, fn) - react when a new object of a class spawns; the standard way to grab a VPairedPawn reference instead of polling
  • FindAllOf("ClassName") - get all live instances of a class
  • StaticFindObject(path) / LoadAsset(path) - find or load an asset
  • require("UEHelpers") - player, controller, world, math helpers
  • ExecuteInGameThread(fn) - run on the game thread (required for spawning)
  • LoopAsync(ms, fn) - repeating timer; return true to stop; save the handle to cancel later
  • FName.new("E") - construct FNames correctly. Passing a raw string where an FName is expected hard-crashes.
Common Gotchas That Cost Hours
  • TArray iteration: use items:Get(i) and #items, NOT items[i]. Use value:get() inside loops to access the real object.
  • tostring() on a UE object can crash the game.
  • Post-hook doesn’t see changes a pre-hook made to local values - post-hook sees original values only.
  • Debug UE4SS build needs the right UE4SS-settings.ini - the end-user ini can crash on launch with “no debugging symbols.”
  • Many UE4SS mods together → CTD: a mod stable alone can crash when many UE4SS mods load together. Problem is timing/order via mods.txt/mods.json. Deferred hook system is the solution (see dotaxis’s snippet in #ue4ss-snippets).
  • RefreshAppearance crashes when multiple mods call it simultaneously. Limit to only the aspect you’re changing; call it twice within your own mod.
  • StaticFindObject('/Script/Engine.Default__KismetSystemLibrary') is the correct way to get KismetSystemLibrary - UClass.Load(...) is not valid in this context.
  • LiveView can crash if left focused on an Actor too long (confirmed crash on FakeRootDistanceInterpSpeedFactorCurve). Dump to file instead.

Deferred Hook System

Solves hooks failing when their target function isn’t loaded at Lua startup - waits until FindFirstOf succeeds. Source: #ue4ss-snippets Discord (dotaxis, May 2025). For TypeScript fans: TypeScript-to-Lua compiles TypeScript to UE4SS Lua with better type checking.

Spawning Actors from Oblivion Forms (Blueprint)

Use OblivionActorFactorySpawn Actor from Form at Location. Pass a live TESForm reference. Spawned actor may not have its pairing set correctly but can receive calls like Jump(). Example: blueprintue.com/blueprint/fo7y5owq/.

Widget & UI Blueprint Tips

  • Widget Switchers can replace Scroll Boxes if Scroll Boxes crash in packaged paks.
  • Hidden widgets do not tick - put tick/keypress logic on the mod actor instead. Or use opacity 0 + Not Hit-Testable (Self & All Children) visibility.
  • To get a ViewModel reference from Blueprints: find it from Lua and pass via console command or custom event.
  • References: Custom Logger (Dmgvol) · Config Variables & Key Remapping
Two Naming Conventions to InternalizeOblivion’s UE classes are prefixed V (e.g. VInventoryMenuViewModel, VMisc, VHUDSubtitleViewModel). The central actor class for player and NPCs is AVPairedPawn / AVPairedCharacter - most character data hangs off its components. Standard UE conventions: A = Actor · U = non-actor UObject · F = struct · T = container/template · leading lowercase b = boolean.

Finding Hooks

The Kein/Altar SDK repo is the hook directory. Search it on GitHub for a class/function name (e.g. repo:Kein/Altar UVActorBehaviorBase) to find the exact /Script/Altar.Class:Function path and its C++ signature. Also use the UE4SS GUI / LiveView. Use regex search: ^Function.+Cast to find hookable functions efficiently.

Console Commands from Lua (Robust Method)

local UEHelpers = require("UEHelpers")
local pc = UEHelpers.GetPlayerController()
local Kismet = StaticFindObject('/Script/Engine.Default__KismetSystemLibrary')
ExecuteInGameThread(function()
  if pc:IsValid() then
    Kismet:ExecuteConsoleCommand(pc.player, command, pc, true)  -- true required post-June 2025
  end
end)

The UE console connects to the Gamebryo console only after the game starts. Console commands work after load, not at startup. get commands via console do NOT return values to Lua - use FindFirstOf("ATMSubsystem"):GetTime() patterns for reading state.

Detecting Player vs NPC

  • actor:IsPlayerCharacter() → returns true for the player. Alternative: NPCs have .AVPairedPawnAIController; the player doesn’t.
  • No clean “player death” hook exists. Pattern: hook DoRagdoll (fires for any ragdoll, including paralyze/KO) and filter with IsPlayerCharacter() plus a real “is dead” check so KO and paralyze don’t false-trigger.

Inventory from Lua - Traps

Inventory ViewModel TrapsFindFirstOf("VInventoryMenuViewModel") returns a fake/empty instance. The real one is hidden inside WBPModernPlayer. Reading the fake one gives an empty list and items with .form = 0000. Fetch inventory right after the player loads and BEFORE opening the inventory menu - opening it wipes/reshuffles the exposed list. An AddItem mod can cause an inventory wipe that breaks other mods reading the viewmodel. Struct fields: Name, Price, Weight, WeaponDamage, ArmorRating, Health, Count, Icon, Type, bIsEquiped, bIsFavorite, InventoryIndex, form, StatusFlags.

Time & Fast Travel

Get current time: FindFirstOf("ATMSubsystem"):GetTime(). Enum: EATMTimeUpdateSource {FROM_INIT, FROM_TICK, FROM_GAMEPLAY, FROM_DEBUG}. To freeze time across fast travel: store the hour on OnFadeToBlackBeginEventReceived, restore on OnFadeToGameBeginEventReceived. Setting time via the property directly doesn’t persist - use set gamehour to N via console.

Magic / Enchantment Internals (Advanced)

  • TESSync.DynamicForms is a TMap (~246 entries) mapping runtime FormIDs → forms (e.g. 0xFF005EC7 → TESObjectCLOT /Game/Forms/items/clothing/TESObjectCLOT_2147479875). This is the way to reach dynamically-added forms at runtime.
  • Enchantment form chain: UTESEnchantment → UTESMagicItemForm → UTESForm. UTESEffectSetting holds EnchantEffect, EffectShader, CastingBlueprintClass, ProjectileBlueprintClass, AreaEffectBlueprintClass, HitEffectBlueprintClass, socket names, and GetEnchantEffectID/EffectShaderID/AssociatedItemID().
  • Magic VFX bounce: you can get the magic effect that shaped a projectile (e.g. FIDG for fire) but not the actual spell that was cast.
  • EVMusicType enum contains all music state data. No hooks have been found yet to intercept individual music state transitions reliably.

Inventory Struct Fields (Reference)

The full FOriginalInventoryMenuItemProperties struct: Name, Price, Weight, WeaponDamage, ArmorRating, Health, Count, Icon, Type, bIsEquiped, bIsFavorite, InventoryIndex, form (UTESForm*), StatusFlags, bIsInventoryItem, bIsInContainerMenu.

Built-in Debug Widgets

  • WBPPairedPawnDebugInfo_C - built-in AI debug widget that ships with the game. Use FindFirstOf("WBPPairedPawnDebugInfo_C") from Lua to get a live reference to NPC AI state without building your own debug overlay.
  • KismetDebugger - UE4SS includes this tool which works on your own custom Blueprints. You can step through your Blueprint mod logic in the UE4SS GUI while the game is running.

ESL Support

ESL (Light Plugin) format is not yet available in Oblivion Remastered. The 512 plugin slot limit from classic Oblivion applies. Plan your mod’s FormID space accordingly.

GitHub Actions for Lua Mod Releases

A GitHub Actions template for auto-releasing UE4SS Lua mods on tag push circulates in the OBR modding Discord. This enables CI/CD: push a git tag, and your .zip is automatically built and attached to a GitHub Release. Ask in #resources for the current template.

Known Unsolved Frontiers

  • frontier Spawning Niagara systems from pure Lua - not figured out.
  • frontier Adding new audio for shout/Fus-Ro-Dah-style effects - effect works, custom audio doesn’t.
  • frontier Chameleon visual bug - can force-remove it but it returns ~1s later 99% of the time. Effect ID: 1280133187.
  • frontier Multi-effect armor enchantment - backend validation blocks it despite UI hook research.
  • frontier Setting weapon enchantment charges - UVItemDetailsViewModel can read them but setters are UI-only.

Confirmed Hook Catalog

Hooks people actually got working. Format: /Script/Altar.Class:Function unless noted. Source: community research via Kein/Altar SDK.

Hook PathFires when / use for
VLevelChangeData:OnFadeToGameBeginEventReceivedWorld finished loading - best “after load screen” hook
VLevelChangeData:OnFadeToBlackBeginEventReceivedA fade-out starts (entering load / fast travel)
VLevelChangeData:OnFadeToBlackOverBeforeFastTravelJust before a fast travel resolves
Engine.PlayerController:ClientRestartPlayer (re)possessed / load finished - good init hook
VAltarTelemetrySubsystem:OnSaveStartedA save is starting
VPairedPawn:OnCombatHitTakenThe “something got damaged” hook
VPairedPawn:OnCombatHitDealtAn actor deals a hit
VPairedPawn:DoRagdollANY ragdoll (death, paralyze, knockdown). Filter with IsPlayerCharacter()
VPairedPawn:OnWeaponChangedEquipped weapon changed
VPairedPawn:OnChangeActionStateAction state changes (⚠ gives a pointer, not a name - can’t filter by string)
VHitBoxComponent:OnOverlapTriggered / StartHitMelee hit/impact detection
VAltarPlayerController:OnJumpPressedJump pressed
VEnhancedAltarPlayerController:ToggleSneakSneak toggled (hookable, but calling it to drive sneak does nothing)
VEnhancedAltarPlayerController:OnAttackRequestPressedAttack button pressed
VEnhancedAltarPlayerController:OnLoadFinishedPlayer controller load finished
VHUDSubtitleViewModel:ConsumeNotificationThe notification bridge (OBScript → Lua)
VMagicSpellVFX:OnSpellProjectileBounceSpell projectile bounced
VAmmunition:OnBounceArrow bounced
VActorValuesPairingComponent:OnAllActorValueChangedA stat/actor-value changed (⚠ delegate, not plain UFunction - needs different binding method)
VDoor:OnBeginOverlapPreLoadBoxDoor pre-load trigger
VOblivionPlayerCharacter:RequestPowerAttackPower attack requested
BP_OblivionPlayerCharacter_C:ReceiveTickPer-frame tick on the player (⚠ two variants exist with/without underscore - check your build)
BP_OblivionPlayerCharacter_C:OnEnterUnderwaterPlayer enters water
WBP_LegacyMenu_Main_C:OnConfirmNewGameNew game confirmed
WBP_Modern_MapWidget_C:OnIconHoveredMap icon hovered
WBPModernMenuEnchantmentMenu_C:OnEffectClickedEnchant menu effect clicked (used in multi-enchant research)
WBP_LockPick_C:OnFocus / BreakLockpickLockpicking UI events
AIModule.AIPerceptionComponent:GetPerceivedHostileActorsAI perception - who an NPC sees as hostile
VPhysicsControllerComponent:HandleCollisionSoundOnBeginOverlapPhysics collision sounds
VPairedPawn:OnHitReaction / OnCapsuleHit / OnDeathVFX / SendJumpHit reactions, capsule hits, death VFX, jump events
BPCI_StatusEffect_Light_C:OnStartPlayStaticLight status effect start (used for spawn-light recipe)
Engine.Actor:K2_GetActorRotation / K2_GetActorLocation / DisableInputCommon actor utilities. Rotation returns Yaw/Pitch/Roll.

OBScript Reference

Script Block Types

BlockWhen it fires
Begin GameModeEvery quest tick (~5 seconds default). Set fQuestDelayTime to change frequency.
Begin MenuMode [id]While a specific menu is open.
Begin OnEquip [actorID]When item is equipped. Runs even on broken items; does NOT run on unequip if item breaks.
Begin OnUnequip [actorID]When item is unequipped.
Begin ScriptEffectStartOnce when spell effect begins.
Begin ScriptEffectUpdateEvery frame while spell effect is active. Buggy in Remaster - use GetSecondsPassed workaround.
Begin ScriptEffectFinishOnce when spell effect ends.
Begin OnDeath [actorID]When scripted actor is killed. Parameter is the killer, not the target.
Begin OnHitWhen scripted actor is hit. Only works on NPCs and Creatures.
Begin OnActivate [actorID]When specified actor activates this object.
Begin OnLoadWhen the cell containing the object loads.

OBScript Bugs in the Remaster

  • Message formatting (%g, %.0f, etc.) does not work - the Altar interpretation layer does not support dynamic string formatting.
  • ScriptEffectUpdate runs inconsistently without a timer. Fix: set elapsed to elapsed + GetSecondsPassed, check if elapsed >= 0.016.
  • SetActorsAI 0 called on a ref in another cell crashes the game as of update 1.1.
  • PlayMagicEffectVisuals does not apply visual effects on actors.
  • SetPos z on actors is unreliable depending on terrain height.
  • All OBSE functions (HasSpell, IsKeyPressed, CloseAllMenus, etc.) do not work - OBSE64 is only a plugin loader.
  • return inside a flat if/endif does not work as expected - always nest logic properly.
  • Quest scripts may randomly lose their attached script in the CS - re-attach if behavior suddenly breaks.
  • ESPs created in the old CS are missing 2 parameters the Remaster requires. Fix with xEdit fix scripts before use.
  • HTML-style text formatting in books/messages (e.g. <br>) no longer works.

Useful OBScript Patterns

Toggle a spell without OBSE

If ( Player.IsSpellTarget MySpell == 0 )
    Player.AddSpell MySpell
Else
    Player.RemoveSpell MySpell
EndIf

Run code once on game start

scn MyOneTimeScript
Begin GameMode
    player.AddItem MyCustomItem 1
    StopQuest MyStartupQuest
End

Attach to a quest with Start Game Enabled checked. Quest runs once and stops itself.

ScriptEffectUpdate timer workaround

Begin ScriptEffectUpdate
    set elapsed to elapsed + GetSecondsPassed
    if elapsed >= 0.016
        ; your logic here
        set elapsed to 0
    endif
End

OBScript Pattern: Item Equip with Infamy Check

scn GrayFoxCuirassScript
begin OnEquip player
    if ( GetPCInfamy >= 10 ) && ( GetPCFame < GetPCInfamy )
        if ( GetPCInfamy < 20 )
            player.addspell GrayFoxArmorEnchant01
        elseif ( GetPCInfamy >= 20 )
            player.addspell GrayFoxArmorEnchant02
        endif
    endif
end
begin OnUnequip player
    player.removespell GrayFoxArmorEnchant01
end

Pattern: Menu After Inventory Closes

Opening ShowEnchantment while inventory is open freezes controls. Use a quest script waiting for GameMode:

; Item script fires on equip:
Begin OnEquip player
    StartQuest MySpellMenuQuest
End
; Quest script (only runs when no menus are open):
Begin GameMode
    ShowEnchantment
    StopQuest MySpellMenuQuest
End

Quest & AI Notes

  • Quest stages can only go forward. Enable Allow Repeated Stages for stages that need to fire multiple times.
  • Quest scripts die at stage 100 / StopQuest. Don’t mix stages into existing vanilla quests - breaks saves.
  • FollowPlayer AI packages may not work as expected - buggy in the Remaster.
  • PushActorAway with negative values may push instead of pull. Results are inconsistent.
  • NPCs activate furniture to use it; they don’t just play animations near it.

Console Commands for Debugging

ObvConsole set QuestName.VarName to 1    ; set a quest variable
ObvConsole show QuestName.VarName         ; read a single variable
sqv QuestName                             ; dump all variables for a quest
GetGlobalValue GlobalVarName              ; read a global variable
ToggleDebugCamera                         ; free camera (replaces old tfc)
Altar.StandOutOblivionAsset 1             ; turn legacy assets pink
Altar.Cheat.AllowSetStage 1              ; enable SetStage in console
SloMo 0                                   ; freeze time

Ready-to-Use Lua Snippets

Detect Weapon Type & Power Attack

-- pawn is AVPairedPawn reference
local function GetWeaponType(pawn)
    local wa = pawn.WeaponsPairingComponent.WeaponActor
    if wa and wa:IsValid() then
        return wa.WeaponTypeTag.TagName:ToString()
    end
    return "HandToHand"
end
-- Example output: "Weapon.Type.Sword.One.Hand", "Weapon.Type.Bow", "HandToHand"

local powerTag = { TagName = FName("Actor.Action.Combat.Attacking.Power") }
local function IsPowerAttack(pawn) return pawn:HasGameplayTag(powerTag) end

Detect Spell Cast Type

-- Returns: 0=Self, 1=Touch, 2=Target, 3=Unknown, 4=MAX, -1=Error
local function GetSpellCastType(pawn)
    local s = pawn.OblivionActorStatePairingComponent
    if not s or not s.SpellCastType then return -1 end
    return tonumber(s.SpellCastType)
end

Efficient Proximity Detection (SphereOverlapActors)

-- ~100x faster than iterating all actors and manually calculating distance
local playerLocation = playerPawn:K2_GetActorLocation()
local actorList = {}
UEHelpers.GetKismetSystemLibrary():SphereOverlapActors(
    UEHelpers.GetWorldContextObject(), playerLocation,
    500, { someClass }, someClass, { }, actorList)
for i = 1, #actorList do
    local actor = actorList[i]:get()
    -- do something with actor
end

Spawn a Blueprint Actor at Location

local UEHelpers = require("UEHelpers")
local function SpawnBP(path, location, rotation)
    local bp_path = path
    if bp_path:match("^OblivionRemastered/Content/.+%.uasset$") then
        bp_path = bp_path:gsub("OblivionRemastered/Content/", "/Game/")
        local fn = bp_path:match("/([a-zA-Z0-9_]+).uasset")
        local dir = bp_path:match("^(.+/)[a-zA-Z0-9_]+.uasset")
        bp_path = dir .. fn .. "." .. fn .. "_C"
    end
    LoadAsset(bp_path)
    local bp_class = StaticFindObject(bp_path) or CreateInvalidObject()
    if not bp_class:IsValid() then return end
    local loc = location or UEHelpers.GetPlayer():K2_GetActorLocation()
    local rot = rotation or {Pitch=0, Roll=0, Yaw=0}
    local spawned = UEHelpers.GetWorld():SpawnActor(bp_class, loc, rot) or CreateInvalidObject()
    if not spawned:IsValid() then return end
    spawned.Tags[#spawned.Tags + 1] = FName("ManuallySpawned")
    spawned:K2_GetRootComponent():SetMobility(2)   -- Movable
    spawned:SetActorEnableCollision(false)
    return spawned
end

RegisterKeyBind(Key.N, function()
    ExecuteInGameThread(function()
        SpawnBP("/Game/Dev/Creatures/BP_Generic_Flameatronach.BP_Generic_Flameatronach_C")
    end)
end)

Dump Player Inventory

-- IMPORTANT: fetch BEFORE opening the inventory menu, not after
local vm = FindAllOf("VInventoryMenuViewModel")[1]
for i, item in ipairs(vm:GetInventory()) do
    local d = item.DisplayName
    print(d.Name:ToString(), d.Weight, d.Price, d.Count, d.bisEquiped, d.form)
end

Get All Skeletal Mesh Sockets on a Pawn

-- pawn is a valid AVPairedPawn reference
local socketCount = pawn.MainSkeletalMeshComponent.SkeletalMeshAsset:NumSockets()
print("Number of sockets: " .. socketCount)
for i = 0, socketCount - 1 do
    local sock = pawn.MainSkeletalMeshComponent.SkeletalMeshAsset:GetSocketByIndex(i)
    print("Socket: " .. sock.SocketName:ToString() .. " | Bone: " .. sock.BoneName:ToString())
end

Smooth Tween / Interpolation Helper

Smoothly transition a numeric value over time using an easing function. Requires the easing_functions module. Visual reference: easings.net.

local Math = require("easing_functions")  -- provides Math.EasingOptions table
local CurrentTweenTask = nil

local function TweenToValue(GivenVariable, TotalDuration, StartingValue, EndingValue, EasingFunc)
    local startTime = os.clock()
    GivenVariable = StartingValue
    CurrentTweenTask = LoopAsync(1, function()
        local progress = (os.clock() - startTime) / TotalDuration
        GivenVariable = EasingFunc(StartingValue, EndingValue, progress)
        if GivenVariable >= EndingValue then
            GivenVariable = EndingValue
            return true  -- stops the loop
        end
        return false
    end)
end
-- Usage: TweenToValue(myVar, 2.0, 0.0, 100.0, Math.EasingOptions.EaseLinear)

Full Notification Bridge (Complete Pattern)

-- OBScript side: message "MY_SIGNAL" from any script block
-- Lua side: intercept, hide, react
RegisterHook("Function /Script/Altar.VHUDSubtitleViewModel:ConsumeNotification",
function(hudVM)
    local vm = hudVM:get()
    local text = vm.Notification.Text:ToString()
    if text:match("MY_UNIQUE_SIGNAL") then
        vm.Notification.ShowSeconds = 0.0001  -- hide from player
        -- do whatever you want here
        return
    end
end)
-- Lua -> OBScript reverse:
-- Kismet:ExecuteConsoleCommand(pc.player, "ObvConsole set MyFlag to 1", pc, true)
-- Quest script checks: if (MyFlag == 1) ... set MyFlag to 0 ... endif

Useful Classes & Structs Reference

ClassPurpose / Key Methods
VPairedPawnBase pawn for all characters. WeaponsPairingComponent, OnCombatHitTaken, DoRagdoll, IsPlayerCharacter()
VPairedCharacterCharacter subclass. Has VHumanoidHeadComponent, VHumanoidHeadAnimBP
VActorValuesPairingComponentAttribute values (Strength, Health, etc.). OnAllActorValueChanged delegate.
VHumanoidHeadComponentFacial expressions/emotion. Search “Emotion” in class dump.
VEnhancedAltarPlayerControllerPlayer controller. OnLoadStarted, OnLoadFinished, OnAttackRequestPressed, ToggleSneak.
VLevelChangeDataLevel transitions. OnFadeToGameBeginEventReceived - the primary “load screen done” hook.
VAltarUISubSystemUI subsystem. GetInventoryHoveredActor, menu access.
VInventoryMenuViewModelInventory menu data. Contains item arrays. See gotchas for fake-instance trap.
VHUDSubtitleViewModelHUD message notifications. The notification bridge key hook.
UTESMagicItemFormMagic item / spell form. FullName, EffectSettings TArray.
VOblivionGameInstanceSubSystemGame instance subsystem. Persistent across level loads.
UVTESObjectRefComponentObject reference component. FormIDInstance (uint32), TESForm*, GetHexFormRefID().

Object Browsers & Reference Resources

Reference

Fixing Problems & Glossary

CTD index, status board, field notes, resource directory, and full glossary. Always open in another tab when something breaks.

Debugging Workflow

Opening the In-Game Console

The in-game console key is ` (backtick/tilde). This opens the Oblivion console. UE console commands are also available. The UE4SS GUI console window is separate - it opens alongside the game window when UE4SS is active with GuiConsoleVisible = 1 in UE4SS-settings.ini.

Rapid Testing Commands

tgm                    ; god mode for safe testing
tdetect                ; disable NPC detection so you can work undisturbed
set timescale to 0     ; freeze game time
coc "TestingHall"      ; jump to a vanilla test cell (or any cell EditorID)
coc "ICMarketDistrict" ; jump to a known working exterior
player.moveto 00000014 ; teleport to player start
ToggleDebugCamera      ; free-fly camera for scene inspection

Reading UE4SS Crash Logs

UE4SS logs are at: [GameDir]\Binaries\Win64\ue4ss\Logs\UE4SS.log

Key things to look for in crash logs:

  • Unhandled exception + stack trace - the actual crash location
  • LogScript: Error entries - OBScript evaluation errors
  • LogBlueprintUserMessages - Blueprint print nodes output here
  • ModuleManager errors - a DLL plugin failed to load

UE crash reports go to: C:\Users\YOU\AppData\Local\CrashReportClient\

Mod Compatibility & Conflicts

ESP Record Conflicts

  • When two mods modify the same record, the last one in load order wins. Neither mod’s change is truly merged - one overwrites the other.
  • To merge changes: open both ESPs in xEdit, copy the conflicting record into a new patch ESP, and manually combine both sets of changes. This is a compatibility patch.
  • xEdit shows conflicts in the right-click context menu and flags records with colored backgrounds. Red = conflict you must resolve.

PAK Conflicts

  • In ~mods, PAK files load in alphabetical order by filename with later entries overwriting earlier ones.
  • For your mod to win over another: prefix with a lower letter (000_) or instruct users to rename.
  • PAK conflicts are silent - no error, the later file just wins entirely for each asset.
  • SyncMap .ini files from multiple TSMI mods do NOT conflict - they are additive. Multiple INI files coexist fine.

Dirty Edits & ITM Records

When you open a record in xEdit and accidentally touch it without changing anything, xEdit may mark it as modified. These “Identical To Master” (ITM) records bloat your ESP and can cause unnecessary conflicts. Before releasing any mod: in xEdit, right-click your plugin → Other → Check for Errors and Other → Apply Filter for Cleaning. Remove ITM records.

Save Safety

Mod TypeSafe to Add Mid-Playthrough?Safe to Remove?
Texture/sound replacers (PAK only)YesYes - reverts to vanilla on removal
ESP stat edits (existing records)YesMostly yes - existing items revert to vanilla stats
ESP new items (new FormIDs)YesRisky - missing FormIDs in save cause issues
New interior cells (MagicLoader)YesNo - removing breaks saves that visited the cell
OBScript changesCareful - running scripts may have already modified save stateDepends - permanent ModAV changes stay in save
UE4SS Lua modsYesYes - no save state touched
ModAV is PermanentModActorValue in OBScript makes permanent changes to actor values stored in the save file. A player’s Health modified with ModAV cannot be “healed back” by normal means. Use SetActorValue for temporary changes or enchantment Fortify effects instead.

Distribution Checklist

Before Releasing a Mod

  1. Clean your ESP in xEdit (remove ITM records, check for errors).
  2. Test on a clean install without your development tools installed - users don’t have your UE project or xEdit scripts.
  3. Document what files go where in your mod description: pak in ~mods, ESP in Data, SyncMap ini in Data\SyncMap, etc.
  4. State your dependencies: OBSE64, UE4SS, TesSyncMapInjector, Address Library - any loader your mod needs.
  5. State game version: OBSE64 and compiled Blueprint paks are version-locked. Mention which game build you tested against.
  6. Include a folder structure diagram for anything beyond a simple pak replacement. Users get confused by nested install paths.
  7. Test removal: verify removing your mod on an existing save doesn’t corrupt it, or warn users clearly if it does.

What Files Users Actually Need

If your mod isUsers need
Texture/sound replacerModName_P.pak + .ucas + .utoc in ~mods
New item (existing model)ESP in Data + SyncMap .ini in Data\SyncMap
New item (custom model)ESP + SyncMap .ini + PAK in ~mods
New interior cellESP + SyncMap .ini + MagicLoader must be installed
Blueprint behavior modPAK in LogicMods\ModName + UE4SS installed
Lua scripting modLua mod folder in ue4ss\Mods\ + UE4SS installed

Performance Considerations

  • Draw calls matter more than polygon count. One complex merged mesh is better than 50 simple separate meshes in the same cell.
  • Cloth simulation is CPU-bound and scales with complexity. Use PhysicsProxy low-poly meshes to drive simulation and interpolate to the high-poly result.
  • Blueprint Tick cost: Event Tick in a Blueprint runs every frame. Use event-driven patterns (RegisterHook, NotifyOnNewObject) wherever possible instead of polling on tick.
  • ESP record count: practically unlimited - the vanilla game has thousands. Not a real bottleneck.
  • Lua mod load timing: many UE4SS mods loading simultaneously can cause CTDs due to race conditions. Use the deferred hook pattern and mods.txt/mods.json load ordering to sequence initialization.
  • PAK file size: no hard limit but very large PAKs (>1GB) can increase load time noticeably. Split by content type if needed.

Game Version & Patch Stability

  • Keep a backup of your working game version before any game update. Steam can be set to not auto-update under Properties → Updates.
  • OBSE64 breaks on every game binary update until offsets are manually updated by the OBSE team. Check the Nexus page changelog after patches.
  • Compiled Blueprint paks may become incompatible after patches that change class layouts. The game will usually crash silently on load or produce broken behavior rather than a useful error.
  • UAssetGUI edits are binary-specific - changes made to assets from one game version may not work on another. Always extract fresh assets from the current game version.
  • Check the Nexus early modding overview and Discord #announcements after every patch for community-reported breakage.

CTD & Issue Index

SymptomLikely CauseFix
New object invisible / CTD entering new interiorMissing SyncMap entry or wrong load orderRun Smart Mapper; place plugin above AltarDeluxe.esp
New item named [nl]SomethingAltar localization artifactInstall NL-Tag Remover (mods/473)
Won’t launch after adding a modScrambled load order or bad DLLRestore clean Plugins.txt; remove last-added mod; reinstall one at a time
OBSE “version not detected”Wrong build / wrong folder / base exe launchedMatch build; dll+exe in Binaries\Win64; always launch via OBSE loader
UE4SS not loading / startup crashOBSE conflict on first launchLaunch once WITHOUT OBSE first so UE4SS configures itself
Cooked assets won’t load in-gameWrong UE version used to cookCook in UE 5.3.2 specifically - not 5.5 or any other version
UAssetGUI “Failed to Parse X Exports”Referenced dependency files not extracted alongsideExtract all referenced assets too in the same directory tree, then retry
.pak ignored by gameMissing _P suffix or wrong load priorityAdd _P in ~mods; or prefix with 000_ for Paks folder alphabetical priority
BODY SETUP 0: BAD NAME INDEX crashCollision Presets = Block All, or wrong material slot namesSet Collision Presets to Custom; match slot names from vanilla FModel JSON
Whole world renders as UE grey gridStatic switch changed, or base material packedNever edit static switches; pak only the material instance, never the parent
Unversioned properties crash (hard to diagnose)Mod cooked with Shipping instead of DevelopmentAlways cook in Development configuration
BP_ form files crash after patch 1.2Old blueprint forms incompatible with new game versionExtract fresh vanilla BP_ forms from 1.2 PAKs; rebuild. Gauntlets and greaves most affected.
NPCs / creatures frozen in new cellNo navmesh data in new interiorsKnown limitation; no fix. Design rooms to not need NPC movement.
First-person weapon / glove / sleeve clippingWrong material parent for first-person gearUse FPSClippingFix material parent
Boots hide feet (bitmask = 0)0 means inherit from parent, not “show all”Use a non-zero value with relevant bits cleared
Inventory icon missingMissing T_ prefix in UE, or wrong xEdit pathPrefix PNG with T_ in UE only; keep .dds extension in xEdit Icon Image path
Textures blurry in-gameubulk split issueSet “Never Stream” on texture in UE; update retoc (issue #22 fixed)
Cloth mesh doubled (static + dynamic)Cloth mesh is the Root Skeletal Mesh ComponentCloth mesh must be a child of root, never root itself
Custom armor invisible when enchantedDynamic FormID not in TSMI SyncMapKnown bug; don’t change load order after enchanting; re-enchant if needed
Lua ExecuteConsole silently broken after patchAPI signature changedAdd fourth true parameter
JAA imports missing MIC layersJAA doesn’t import the MIC Layers tabEdit that material in UAssetGUI instead
Material un-assigns from chunk on project reopenAllow ChunkID Assignments not enabledEnable in Editor Preferences + set Cook Rule to Always Cook
Animation looks fine in editor, broken in-gameWrong bone index order from PSK skeletonImport SKEL_HumanoidSkeleton via JAA, not from PSK export
Notifies fire at wrong time in-gameWrong framerate (Blender default 24fps)Set Blender to 30fps; import UE at custom sample rate 60fps
RefreshAppearance crash with multiple modsMultiple mods calling it simultaneouslyLimit calls to only the aspect you’re changing
Enchantment VFX missing on custom weapon meshbAllowCPUAccess not set on the static meshSet Allow CPU Access = True in UAssetGUI on your SM_ asset

What Works - Status Board (mid-2026)

Mature & Stable
Texture/sound replacement · ESP property edits · vanilla-mesh standalone items via SyncMap · UI tweaks · OBScript enchantments / spells / AI via CS · UE4SS Lua runtime behaviors · Blueprint behavior mods.
Workable
Custom-mesh standalone items · animation replacement & retargeting · custom creatures via blueprint duplication · cloth capes via amulet slot workaround · body part hiding bitmask · MetaHuman facial animation (DNA files) · Wwise External Sources · new NPC creation · custom inventory icons · Blueprint UI mods · hair modding via data tables.
Partial
New interior cells - possible but no NPC pathfinding (NPCs freeze). Every placed object must be SyncMapped. Custom races - non-playable NPCs work; full CharGen sliders still in research. Lip sync on custom races - requires matching ABP_HeadPostProcess and voice type.
Not Yet Possible
UE-side terrain editing · new exterior cells & worldspaces · custom Chaos Cloth Assets (dataflows can’t be packed) · fully custom voiced dialogue (WEM solved, BNK generation is the blocker) · custom magic effects (hardcoded) · multiple enchantments (backend validation blocks it) · navmesh editing · arbitrary body slot expansion (Altar enums fixed).

Field Notes

1.2 Update Mesh Fix Checklist

  1. Set Collision Presets to Custom on all static meshes (primary crash source: BODY SETUP 0: BAD NAME INDEX).
  2. Match Material Slot Names to vanilla values. Extract vanilla mesh properties JSON from FModel, search "MaterialSlotName". Mismatch = BAD NAME INDEX.
  3. Uncheck left checkbox on all Static Switch Parameters in every MIC - checked switches cause grey world or crash.
  4. For blueprint/form crashes after 1.2: extract fresh vanilla BP_ forms from 1.2 PAKs and rebuild. Gauntlets and greaves are most commonly affected; boots often fine; helmets sometimes affected.
  5. Use the 1.2 Updater Tool (mods/5281) to automate collision and slot name fixing.

Material Rules That Save Hours

  • Never change a static switch - even to the same value. Causes grey world-grid rendering.
  • Don’t pak the base material. Make a material instance of the base and pak only the instance.
  • All non-static parameters are fair game to override.
  • FPSClippingFix is the correct parent for any first-person-visible gear (weapons, gloves, cuirass sleeves).

Debug Console Quick Reference

ToggleDebugCamera         ; free camera (replaces old tfc)
SloMo 0                   ; freeze time completely
Altar.StandOutOblivionAsset 1          ; turn legacy assets pink (dev tool)
Altar.Cheat.AllowSetStage 1           ; enable SetStage in console
player.moveto 00000014    ; teleport to player start
ATM.SetWeatherSetup clear ; set clear weather
set timescale to 1        ; restore normal time
tgm                       ; toggle god mode
tai                       ; toggle AI
tdetect                   ; toggle enemy detection

Engine.ini Blueprint Variable Overrides

Set Blueprint variables without Lua by editing: C:\Users\YOU\Documents\My Games\Oblivion Remastered\Saved\Config\Windows\Engine.ini

[/Game/PathToBlueprint/BP_Name.BP_Name_C]
MyVariable=Value
MyStruct=(Prop1=Val1,Prop2=Val2)

Never include Content in the path. Use /Game/ or /PluginName/. [/Script/ is reserved for C++ classes.

Glossary

AltarProprietary translation layer (by Virtuous) bridging Gamebryo FormIDs to UE5 assets. Code is proprietary; extremely unlikely to be released.
Altar ESPsThe remaster’s own plugins (AltarESPMain, AltarDeluxe, AltarESPLocal). Convert all item names to LOC_FN localization keys. Never disable.
AVPairedPawnCentral UE class for player and NPCs. Most character data hangs off its components (WeaponsPairingComponent, OblivionActorStatePairingComponent, PhenotypeData, etc.).
BDPBody-part blueprint (BP_BDP_...) defining which body parts are hidden, mesh references, and material assignments for an armor piece.
CCAChaos Cloth Asset - UE5’s cloth system. Custom ones crash OBR because their dataflows cannot be packed into a pak.
ChunkingSplitting a UE project so only specific assets cook into separate paks. Never chunk skeletons. Blueprint paks go in LogicMods, art paks go in ~mods.
CUE4ParseCommunity C# library for reading UE4/5 assets. Used to extract DNA files which aren’t standard .uasset format.
DNA fileMetaHuman facial rig data file that controls how face bones deform. Not a standard .uasset - requires CUE4Parse to extract.
Dynamic FormIDRuntime-assigned FormID for enchanted or scripted items. Not covered by static TSMI SyncMap entries - causes invisible enchanted custom armor.
ESP / ESMBethesda plugin/master files - records and logic (the brain). Same format as 2006 Oblivion.
External SourcesWwise feature for loading custom .wem files at runtime without rebuilding soundbanks. Requires a packaged static mesh in the mod actor to function.
FPSClippingFixMaterial parent for first-person-visible gear (weapons, gloves, cuirass sleeves). Prevents geometry clipping in first-person view.
GamebryoThe original 2006 engine still running the game’s logic underneath UE5. Handles scripts, quests, stats, AI, saves.
GNDGround model - static mesh (SM_ prefix) shown when an item is dropped on the floor. Must have proper collision or it falls through terrain.
IO StoreUE5’s content packaging format. Three files per mod: .pak (loose), .ucas (content), .utoc (index). All three must be present.
j0.devLocal cloud server required by JAA 1.4.0+ for automatic batch asset imports from FModel JSON exports.
LOC_FNLocalization key prefix used by AltarESPMain to rename all vanilla items. Editing names without forwarding this causes the [nl] artifact.
MagicLoaderTool for new interior cells. Modifies the CellsToMapPath DataTable linking Gamebryo cells to UE map files.
MagickaMultiplierAn actor value accessible via Lua - confirmed by community research. Useful for custom magic scaling systems.
ModActorThe mandatory-named blueprint (must be named exactly ModActor) that UE4SS BPModLoaderMod auto-discovers and injects on game load.
NaniteUE5 virtualized geometry. Handles LODs automatically. FModel exports only the fallback mesh; use simple-nanite-parser for full detail.
NewWorldModelsUAssetGUI field in BP forms that declares which static mesh (GND) to display when an item is dropped.
NIFClassic Oblivion mesh format - dead for rendering in the remaster. BSA loading is attempted for compatibility but actual visuals come from PAK.
NNRMComposite texture format: Normal(RG) + Roughness(B) + Metallic(A). Import as BC7 non-sRGB. Used by most armor, weapon, and character materials.
Notification bridgeTechnique: OBScript sends a message notification → Lua reads via ConsumeNotification hook → hides it → executes UE-side logic. The key to “impossible” mods.
PAKUnreal content package (.utoc + .ucas + .pak). All three files must be present together for a mod to load.
PhysicsProxyLow-poly mesh duplicate used to calculate cloth physics; movements are then interpolated to the actual mesh. Should have no material assigned.
SkeletalVariantsProperty in generic armor BPs that forces CCA files to be mandatory and prevents cloth paint from working. Absent from hair/amulet BPs - why those work.
speech2faceUE procedural function handling mouth sync for voice lines. Runs independently from audio - audio and face animation are not synchronized frame-by-frame.
SyncMapTesSyncMapInjector’s .ini mapping file linking ESP form IDs to UE5 asset paths. Injected at game startup by a Lua mod.
TABPTemplate Animation Blueprint - the main ABP using linked layer instances for locomotion, combat, and idle animation categories.
ubulkUE5 extension for large textures split from the main .uasset. Fix blurry textures by enabling “Never Stream” on the texture in UE Editor.
uint32 limitationFormID values in UE are stored as uint32 (unsigned 32-bit int). Not natively accessible from UE Blueprints - requires C++ or UE4SS Lua.
VAudioHandlersIn-game Wwise audio subsystem. Accessible from Blueprints via BPF_PostEvent to post vanilla Wwise events without full Wwise integration.
_P suffixRequired on pak names placed in ~mods so UE loads them. All three files (.pak, .ucas, .utoc) must carry the _P suffix.
Root LockUE import option that zeroes out root bone offset on an animation. Required for all OBR animations since root motion is fully disabled in the game.
Linked LayerA sub-ABP instance within the TABP that handles a specific animation category (combat, locomotion, idle). Animation replacers must target the correct layer.
ESLLight Plugin format - NOT available in Oblivion Remastered. The 512 plugin slot limit from classic Oblivion applies.
Bendu OloThe default “base NPC” the player references in the CS. Do not delete or modify this record - it is used internally by the game as a reference actor.
Nine Divines ArmorThe vanilla quest most referenced as a scripting example for equipment-triggered scripts (OnEquip/OnUnequip with conditions). Study it in xEdit as a reference pattern.
WBPPairedPawnDebugInfo_CBuilt-in AI debug widget. Use FindFirstOf from Lua to access live NPC AI state data without building a custom debug overlay.
KismetDebuggerUE4SS tool for stepping through custom Blueprint mod logic in real time while the game is running.
TESSync.DynamicFormsA runtime TMap (~246 entries) in Lua that maps dynamic FormIDs to their live TESForm objects. The way to access enchanted or scripted items at runtime.

Reference

Every Tool & Folder

The complete modding workbench: where every file goes, how load order works, and every tool with its purpose.

The File Buckets

Every file you install or create belongs to exactly one destination. Learn to answer “which bucket?” by hand even when a mod manager handles it automatically.

BucketFile TypesLocationLayer
Data.esp .esm...\Content\Dev\ObvData\Data\brain
SyncMap.ini (TSMI mappings)...\ObvData\Data\SyncMap\bridge
Paks ~mods.pak + .ucas + .utoc...\Content\Paks\~mods\visual
LogicModsBlueprint paks...\Content\Paks\LogicMods\<ModName>\visual
UE4SS Mods (Lua)main.lua in Scripts\ subfolder...\Binaries\Win64\ue4ss\Mods\<ModName>\visual
UE4SS BPModLoaderModBlueprint paks (alternative path)...\Binaries\Win64\ue4ss\Mods\LogicMods\visual
OBSE / ASI Plugins.dll / .asi...\Binaries\Win64\extension
GameSettings.ini GMST overrides...\Binaries\Win64\GameSettings\brain

PAK Naming & Load Rules

  • _P suffix in ~mods: all three files must end in _P (e.g. MyMod_P.pak, MyMod_P.ucas, MyMod_P.utoc).
  • Alphabetical priority in Paks folder: placed directly in Paks, a mod sorts alphabetically and must sort below Oblivion.pak to win conflicts - use a prefix like 000_MyMod.pak.
  • Blueprint/logic mods go in the LogicMods subfolder.

ESP Load Order - Canonical Template

Oblivion.esm
DLCBattlehornCastle.esp · DLCFrostcrag.esp · DLCHorseArmor.esp
DLCMehrunesRazor.esp · DLCOrrery.esp · DLCShiveringIsles.esp
DLCSpellTomes.esp · DLCThievesDen.esp · DLCVileLair.esp
Knights.esp
AltarESPMain.esp
  ← YOUR NEW-OBJECT / NEW-CELL PLUGINS GO HERE
AltarDeluxe.esp
AltarESPLocal.esp
The New-Object Load Order RulePlugins that add new objects or new interior cells must sit above AltarDeluxe.esp. Below it, new objects render invisible and entering a new interior crashes - a TesSyncMapInjector ordering requirement.

Key Content Paths in the Game Files

/Game/Art/Character/{Race}/          ← Race textures and meshes
/Game/Art/Equipment/armor/{type}/    ← Armor skeletal meshes
/Game/Art/Equipment/weapons/         ← Weapon static meshes
/Game/Forms/items/armor/             ← Armor form blueprints (BP_BDP_*)
/Game/Forms/items/weapons/           ← Weapon form blueprints
/Game/Forms/actors/npc/              ← NPC blueprints
/Game/Forms/actors/race/             ← Race blueprints
/Game/Dev/Phenotypes/Hair/           ← Hair phenotype data tables
/Game/Materials/                     ← Master materials
/Game/Art/Animation/Humanoid/        ← Character animations
/Game/Art/Animation/Humanoid/Facial  ← Per-voice-line face animations
/Game/Art/UI/Icons/Dynamic_Icons/    ← Inventory icons

Loaders & Bridge Tools

ToolRoleNotes
OBSE64DLL plugin loader (not script extender yet)Steam only; match game build; dll+exe to Binaries\Win64
Address Library (mods/4475)Version-independent OBSE pluginsRequired by almost all OBSE plugins
UE4SS (mods/32, OR build v3.0.1)Lua scripting + Blueprint loader for UE5 sideLaunch once without OBSE to configure; use OR build not GitHub generic
TesSyncMapInjector (mods/1272)Links ESP FormIDs to UE5 assets at runtimeINIs in ObvData\Data\SyncMap; unzip to game root
Smart MapperxEdit script that auto-generates TSMI INI filesOutput lands in xEdit\SyncMap; copy to game Data\SyncMap
MagicLoader (mods/1966)Enables new interior cells via CellsToMapPath DataTableAlso requires MagicPatcher for string table entries
OBRConsole (mods/2205)Lets UE4SS Lua run Oblivion console commands⚠ Crashes if you tab out of the game while active
SML / Simple BP Mod Loader (mods/1172)Alternative Blueprint loader; better input action docsAlso provides SML Dev Resources for console output, UObject storage

ESP / Data Authoring

ToolRoleNotes
xEdit 4.1.5n+ (TES4R build)Primary ESP authoring and conflict detectionGet from xEdit Discord #xedit-builds; launch with -TES4R flag
Construction Set (2006) + CSEVisual editor for cells, containers, world objectsNever load Altar ESPs; saved esp in ObvData\Data\Data (move up one level)
LOOTAutomatic load-order sortingSort, then hand-fix the special cases above
NL-Tag Remover (mods/473)Strips [nl] artifact from new item namesRequired for any mod that adds new named items
Haphestia’s Fix and Port Script (fixmod)Fixes the 2 missing parameters CS doesn’t add for RemasterRun in xEdit with Altar files loaded
Game Settings Loader (mods/833)Load GMST overrides from config fileAlternative to setGS console command each session
Runtime EditorIDs (mods/1331)Surfaces EditorIDs in the in-game consoleQoL for testing; lets you spawn items by EditorID

UE5 Asset Pipeline

ToolRoleNotes
Blender + PSK/PSA pluginModel and animation authoringRename armature to “Armature”; if PSK breaks try Befzz plugin
Unreal Engine 5.3.2Cook art assets into paksUse 5.3.2 specifically. Install to C:/ not Program Files.
FModel + .usmap (mods/47)Browse and extract game assetsSet to GAME_UE5_3; regenerate .usmap after patches via UE4SS Ctrl+Numpad6
OR Mod Tools (mods/3918)retoc + UAssetGUI + oo2core bundleUse Nexus UAssetGUI version (PackageName bug fix)
retoc (github.com/trumank/retoc)Extract and repack IO Store paksto-legacy to extract; to-zen to repack; update for ubulk fix
UAssetGUI (Nexus version)Edit cooked .uasset/.uexp blueprint and form filesSet version to 5.3; needs both .uasset and .uexp in same folder
JsonAsAsset + j0.devImport materials from FModel JSON into UE projectUse C0bra5+Tectors fork; enable Stubs checkbox; MICs need manual parenting
C.A.F.E. (mods/4891)CosmicBoogaloo’s armor/weapon form path toolForm name must match asset path name exactly
NNRM Merge/Split Tool (mods/3051)Split and recombine NNRM channel-packed texturesAlso use c0bra5’s ffmpeg scripts for channel-precise splitting
OR SDK (Kein/Altar)UE project stub with Oblivion superclassesCompile with VS2022 MSVC v143 v14.38.33130; no engine-from-source needed
simple-nanite-parser (c0bra5)Extract full-detail Nanite meshesFModel only exports fallback mesh; this gets the real thing. Python, exports to GLTF.
Alpakit for UE 5.3.2One-click auto-deploy of Blueprint modsOBR-compatible fork by Wikt0r1us
sound2wemConvert audio files to Wwise .wem formatRequired for any audio replacement workflow
wwiserBrowse Wwise .bnk soundbanks to find .wem IDsFind CAkSound node SOURCE IDs for specific voice lines
Visual Studio 2022 (direct link)Compile Altar SDK project and OBSE64 C++ pluginsGame Dev C++ workload + MSVC v143 v14.38.33130 component
1.2 Updater Tool (mods/5281)Automates collision + material slot name fixing for 1.2Run on any mesh mod made before patch 1.2
Body Part Chart (mods/2583)nyyxn’s authoritative body-part bitmask referenceRequired reference for MaleBodySectionHidden values
Community DNA Files (mods/2592)MetaHuman DNA files for all base races and named NPCsRequired for custom head facial animation

Community

Credits & Contributors

This wiki exists because of hundreds of people who spent countless hours reverse-engineering a game with no official documentation, sharing everything they found for free, and building tools for everyone to use. This page is for them.

A Note to Virtuos & Bethesda “No official modding support?” That’s fine - we didn’t need it. In less than a year, the community reverse-engineered your dual-engine architecture, built a complete toolchain from nothing, figured out the Altar translation layer, and shipped thousands of mods. This wiki documents more about the technical internals of Oblivion Remastered than any official documentation ever would have. The community would still appreciate a Construction Kit though. Just saying. - The OBR Modding Community

The Community Hub

Every discovery in this wiki came from the OBR Modding Discord. If you’re making mods, you need to be here. The #research-general, #research-modeling, #research-scripting-ue4ss, #research-animation, and #research-mapping channels are where the real work happens.

Research Contributors

These are the people who actually figured this stuff out. Every name in these lists did something real - wrote a tool, reversed a system, tested a workflow, documented a finding, or shared a crucial insight that saved everyone else hours of work.

Modeling, Textures & Asset Pipeline

c0bra5Nanite parser (simple-nanite-parser), material reverse-engineering (c0bra5 method), NNRM ffmpeg scripts, Nanite mesh extraction workflow. One of the most prolific technical contributors. github.com/C0bra5
deathwrenchPractical armor pipeline workflow, FBX export settings, collision setup, standalone item creation, confirmed Blender export settings. The workflow most people use traces back here.
tenebrisequitemCloth physics deep-dive research - established WHY custom CCA files crash, the dataflow limitation, and the SkeletalVariants culprit.
jack_laMorph targets, vertex colors, material research. Documented the PSK vs UEFormat tradeoffs and vertex color sRGB/linear issues.
qunaiBitmask research, BP form structure, TSMI enchanted item bug documentation. Body part hiding system mapping.
dotaxisPackaging workflow, deferred hook system for Lua mods, general toolchain contributions.
.astralusTexture pipeline research, engine config documentation.
izedev_55749MetaHuman DNA research - discovered the game uses the MetaHuman system for facial animation. Confirmed Audio2Face workflow.
exicideBeast race armor compatibility research.
WSDogExtracted and publicly shared DNA files for all base races and named NPCs. mods/2592
_trungusEye color editing research.
nyyxnBody part vertex color chart - the authoritative reference for MaleBodySectionHidden bitmask values. mods/2583

Animation, Audio & ABP System

krasuepisacLocomotion blendspace TMap replacement discovery - the persistent animation replacement technique. StrideWarping documentation.
kei7855Animation notify research for combo chaining. Shortsword animation replacer (reference implementation). mods/2489
ryanhankAnimation research contributor.
michaelpstanichAnimation research contributor.
lunemodsAnimation research contributor.
strikshawAnimation research contributor.
miken1keCommunity standard Blender rig for OBR animation work. mods/2069
jakealaimoLive motion capture modded into Oblivion Remastered - reference implementation for advanced animation. YouTube
.yeah.nah.yeah.Wwise External Sources bug discovery (the static mesh workaround). Map border open space research. Animation & mapping contributor.

World Editing & Mapping

nafnaf_95New interior cells research, exterior cell limitations, CK–UE terrain height mismatch workaround, Black Marsh expansion proof-of-concept, navmesh limitations documentation.
khameli0nFixed the MagicLoader bug that overwrote FULL name fields. Critical tool fix.
HaphestiaCreated MagicLoader - the tool that makes new interior cells possible. mods/1966
diceneConsumeNotification method discovery (the foundation of the notification bridge). Custom map loading research, mapping research contributor.
cnnrduncan.umap layer structure documentation. Confirmed the layer-per-category architecture.
minutereadyGrass/tree placement system research, Mad’s rapid spray placement method, Blueprint/Lua spawn approach for vegetation.
narm_Mapping research contributor.
wxmichaelConfirmed .umap direct editing via UAssetGUI for object placement.
grimlock_artsHeightmap research contributor. Documented the “TES heightmap is just a flat base layer” finding.
agentlefoxHeightmap Extractor tool research.

Scripting, Lua & Cross-System

MadAborModdingCreated the Levitation mod - the reference implementation of the full OBScript→Lua notification bridge. The breakthrough that unlocked “impossible” mods. mods/3334
PuddlePumpkinKwaNotifications mod (Lua↔Blueprint communication pattern), ObvrWidgetLibrary, open-source BP mod examples. GitHub
zarrastroUE4SS scripting research, hook catalog contributor.
randombombombomUE4SS scripting research, hook catalog contributor.
veter_UE4SS scripting research, hook catalog contributor.
ubawesomeUE4SS scripting research, hook catalog contributor.
faeriemushroomUE4SS scripting research, hook catalog contributor.
crimsonSpellCastType snippet, UE4SS scripting research, hook catalog contributor.

Tools & Frameworks

KeinMaintained the Altar stub UE project (Kein/Altar fork) - the single most important tool for Blueprint modding. Without this, everyone would need a 200GB UE source build. GitHub
nathtestUProjOblivionRemastered - Altar source headers reference. GitHub
alexander_preitCommunity armor/clothing toolkit, JAA quick setup guide. Made getting started dramatically easier.
TripsterPre-compiled Altar project and Materials Pack - removed the VS build requirement for most Blueprint modders.
wikt0r1usAlpakit for UE 5.3.2 - OBR-compatible fork of the packaging tool. GitHub
tommnBloodlust (kill detection + OBScript), Custom Summoning mod (summon via hijacked NPC forms). mods/2321
yerawizardharrehShield on Back - open-source Blueprint uassets example. mods/2077

The Compiler & Curator

CosmicBoogaloo - DreamEater

The author of this wiki. I didn’t discover most of what’s on these pages - this community did. My job was to read every Discord export, every research channel, every tool readme, and every pinned message, then organize it into something a new modder can actually use without drowning in 9,655 messages of raw Discord history.

I also made C.A.F.E. (the armor/weapon form path tool), published 60+ mods with 15,000+ downloads, got covered by PC Gamer, GameSpot, and The Gamer, and received a personal compliment from Markus Persson. I’m building a full indie game engine and studio under the DreamEater name. Oblivion Remastered was the spark that started all of it.

A Note on Missing Credits This wiki was compiled from Discord exports covering hundreds of conversations. Some contributors may be missing from this page, or have outdated/missing Nexus links. If you contributed to any research documented here and want to be properly credited (or have a link added, corrected, or removed), please reach out. Every contribution matters.

Get In Touch

Found an error? Have new research to add? Want to be credited or update your info? Use the form below.

Contact CosmicBoogaloo

Thank You To every person who ever typed something into a Discord channel at midnight because they figured something out and wanted to share it: this is for you. The OBR modding community built something remarkable in under a year with no official support, no source code, and no documentation. That’s genuinely impressive. Keep going.
OBR Mod Creation Wiki - compiled by CosmicBoogaloo / DreamEater Living document · mid-2026 · always verify tool versions