Comprehensive feature roadmap for transforming the Module Designer into a Unity/UE5/Hammer-class level editor for KotOR modules.
Philosophy: Merge the Indoor Builder, Walkmesh Editor, and current Module Designer into a single unified environment where all level editing tasks happen in one canvas with contextual modes.
Scope rule for this doc: Only keep ideas that translate into concrete changes in the existing Module Designer implementation: Tools/HolocronToolset/src/toolset/gui/windows/module_designer.py and Tools/HolocronToolset/src/ui/windows/module_designer.ui.
Current Module Designer baseline (verified): 3D OpenGL viewport (
mainRenderer) + 2D walkmesh renderer (flatRenderer) stacked; left panel hasresourceTreeandlytTree+ room transform spinboxes; right panel is a flatinstanceList; per-type visibility toggles; QUndoStack; Blender integration.
| Capability to Steal | Why It Matters in Our UI | Implement In Module Designer |
|---|---|---|
| Explicit tool/mode state (select / transform / place / walkmesh edit) | Our interaction is currently implicit and hard to reason about | Add a small tool palette/toolbar in the UI; add a state machine in module_designer.py that routes mouse events differently by mode |
| Transform gizmos (translate/rotate/scale + axis lock) | Dragging without gizmos is imprecise; no discoverable affordance | Render gizmos in mainRenderer; connect gizmo drag -> MoveCommand/RotateCommand and update Blender sync |
| Inspector dock (inline property editing for selection) | Right now properties live in separate dialogs; slow for iteration | Add a dock/panel that populates from selected GITInstance (and from selected walkmesh face when in Walkmesh Mode) |
| Hierarchy + search + grouping (rooms -> instances, folders/layers) | instanceList doesn’t scale; no structure; no search |
Replace instanceList with a tree view grouped by room + type; add search filter; add user-defined groups (visgroups/layers) |
| Drag-and-drop placement from resources | Resource tree is browse-only; placement is friction | Enable resourceTree drag; accept drops in mainRenderer; create a ghost preview and place via walkmesh raycast |
| Surface/grid/angle snapping (editor-side helpers) | We place/drag with no snap; alignment is painful | Add snap settings + indicators; apply snapping inside the 3D controls when moving/placing instances |
| Walkmesh edit in the 3D viewport (vertex/edge/face selection) | We can render walkmesh but can’t edit it in-place | Add Walkmesh Mode that switches raycast targets to BWM geometry; add selection highlights + edit operations |
| Walkmesh face properties panel (surfacemat + walk flags) | We don’t expose per-face editing; this is core functionality | Add a walkmeshTab (or inspector section) showing surfacemat + walk/walkCheck/lineOfSight and writes back to BWM |
| VIS editor tab (room-to-room visibility matrix + viz) | VIS is a core KotOR format; current workflow is a hack | Add visTab with a matrix editor and in-viewport visualization of links |
| Camera: orbit + fly + speed + bookmarks | Navigation is a productivity multiplier | Extend the existing controls to support both orbit and fly navigation; expose speed + bookmark slots in UI |
| Multi-view layout option (orthographic helpers) | Precise alignment is hard with a single view | Provide a toggle that swaps the render area into a 4-pane splitter with perspective + orthographic views |
| Validation + build/test loop (errors surfaced early) | We save GIT but don’t help the user ship a playable module | Add “Check for Problems” (broken resrefs, missing models, degenerate faces) and a “Build & Launch” action |
- Creation Kit: navmesh edit mode in-viewport, reference browser/filtering, layers, VIS-like occlusion mindset.
- Dragon Age Toolset: tight 2D (layout) + 3D (preview/placement) integration, trigger region drawing workflow.
- Unity/Unreal: inspector+hierarchy workflow, gizmos, drag/drop placement, snapping, scene organization.
- Hammer/Radiant: multi-view precision layouts, error checking pipeline mindset.
- Blender: edit-mode mental model for vertex/edge/face manipulation.
- Forge: KotOR-specific proof that 3D walkmesh editing + object placement is viable and already implemented elsewhere.
- Layer definitions: User-defined layers (e.g., "Enemies", "Loot", "Scripted")
- Layer assignment: Assign objects to layers
- Layer filtering: Toggle entire layer visibility
- Layer locking: Lock all objects in a layer
- Transform: Position (X/Y/Z), Rotation (Euler/Quaternion), Scale
- Blueprint fields: All UTC/UTD/UTP/etc. GFF fields editable inline
- Scripts dropdown: Assign OnSpawn/OnDeath/etc. scripts via file picker
- Tags/Comments: User metadata for organization
- Preview thumbnail: Show 3D model miniature for selected object
- Resource link: Click to open blueprint in dedicated editor
- Surface Material: Dropdown from surfacemat.2da
- Walkable flags: walk, walkCheck, lineOfSight (checkboxes)
- Adjacent faces: List of neighboring face indices
- Face normal: Display as read-only vector
- Area (m²): Display face surface area
- Model: Resref dropdown with preview
- Position/Rotation: Transform in LYT coordinates
- Linked rooms: VIS visibility list (editable checkboxes)
- Ambient light: Color picker + intensity slider
- Fog settings: Color, near/far distances
- Searchable asset library: All game + mod resources indexed
- Type filters: UTC, UTD, UTP, MDL, TGA, WAV, etc.
- Thumbnail previews: Auto-generate for models/textures
- Favorites: Star frequently-used blueprints
- Recent files: Quick access to last 20 opened resources
- Drag-and-drop: Drag blueprint from browser into 3D viewport to place
- Kit browser: Visual grid of indoor kits with thumbnails
- Prefab groups: Save multi-object arrangements as reusable prefabs
- Prefab variants: Override properties on placed instances
- Grid view: All TGA/TPC textures with previews
- Search/filter: By name, folder, resolution
- Apply to selection: Select faces/objects and apply texture
- Texture alignment tools: Offset, scale, rotation per face
- Shader preview: Show how texture will look in-game
- Tint color overlay
- Transparency/Alpha settings
- Emissive glow for self-lit textures
- Target game: K1/K2 toggle with format conversion warnings
- Compression: Toggle ERF compression
- Validation: Pre-build checks (missing textures, broken scripts, etc.)
- Build log: Detailed step-by-step output window
- Quick test: Build + launch game at module entry point
- Export current area to .mod: Standalone module file
- Export individual resources: LYT, VIS, ARE, GIT, WOK files separately
- Export screenshot: High-res render of current viewport for documentation
- Vertex merge: Weld nearby vertices (for cleaning up walkmeshes)
- Face split: Subdivide selected face into triangles
- Edge flip: Flip shared edge between two triangles
- Extrude: Pull face outward to create new geometry (for walkmesh extensions)
- Bevel: Round edges of selected faces
- Distance ruler: Click-to-measure between two points
- Area calculator: Select faces to see total walkable area
- Angle display: Show angles between edges/faces
- Grid snapping: Snap position to grid increments (configurable 0.1/0.5/1.0 units)
- Vertex snapping: Hold V to snap to nearest vertex (Hammer-style)
- Surface snapping: Drop object onto walkable surface below cursor
- Rotation snapping: Snap rotation to 15°/30°/45°/90° increments
- Animation preview: Play room/object animations in viewport
- Walkmesh collision test: Drop a test sphere to see walkable paths
- Camera path preview: Scrub through camera movements
- Trigger visualization: Show trigger volumes as colored wireframe boxes
- Build + Launch: One-click build .mod and boot game to test
- Warp code generation: Auto-generate cheat codes for testing
- Spawn test NPCs: Place dummy creatures for scale reference
- Double-click object: Opens blueprint in dedicated editor (UTC/UTD/etc.)
- Script editor integration: Right-click script field -> "Edit in NSS Editor"
- Dialog editor link: Right-click creature -> "Edit Dialog"
- 2DA editor link: Right-click dropdown -> "Edit 2DA Entry"
- Live blueprint updates: When blueprint edited externally, refresh in viewport
- Conflict resolution: Warn if external changes conflict with unsaved module edits
- Version control hints: Show Git status icons on modified resources
- Live co-editing: Multiple users edit same module (à la Google Docs)
- Change highlighting: See other users' cursors + selections
- Voice chat integration: Optional VOIP for design discussions
- Version history: Rollback to previous module states
- Face count display: Show triangle count per room/object
- Texture memory usage: Warn if too many high-res textures loaded
- Walkmesh complexity analyzer: Suggest simplification for over-dense meshes
- Vis optimization: Auto-suggest VIS relationships based on occlusion
- LOD system: Distant objects render as bounding boxes (Unity-style)
- Frustum culling: Don't render objects outside camera view
- Lazy loading: Load room models on-demand when visible
- Background processing: Compile/build on separate thread
- Dark mode (default)
- Light mode
- High contrast mode (accessibility)
- Custom color schemes: User-defined workspace colors
- Drag-and-drop panel arrangement: Rearrange Inspector/Hierarchy/Browser
- Save layouts: Workspace presets (e.g., "Walkmesh Editing", "Object Placement")
- Pop-out panels: Undock panels to external windows (multi-monitor)
- Q: Pan tool
- W: Move tool
- E: Rotate tool
- R: Scale tool
- F: Focus on selection
- Delete: Delete selected objects
- Ctrl+D: Duplicate
- Ctrl+Z/Y: Undo/Redo
- Ctrl+S: Save
- Space: Toggle mode (Layout/Object/Walkmesh)
- Alt+LMB: Orbit camera
- Alt+RMB: Zoom camera
- Alt+MMB: Pan camera
- Wireframe overlay: Show polygon edges on shaded models
- Normals display: Show vertex/face normals as directional arrows
- UV map display: Show texture coordinates
- Collision shapes: Highlight DWK/PWK walkmeshes in different colors
- Path visualization: Show PTH connections as bezier curves
- Sound radius: Show UTS max distance as wireframe sphere
- Trigger polygons: Highlight encounter/trigger zones in translucent color
- Missing texture warnings: Red checkerboard on missing materials
- Broken script references: Yellow warning icon on invalid ResRef
- Overlapping geometry: Detect Z-fighting faces
- Degenerate triangles: Warn about zero-area faces in walkmesh
- Unreachable areas: Show walkmesh islands not connected to rest
- Tooltips: Hover over every button/field for explanation
- Mode tutorials: First-time popups explaining each mode
- Video tutorials: Embedded YouTube links for complex workflows
- Shortcuts cheat sheet: Press F1 to show modal with all hotkeys
- Starter templates: Empty module, single-room test, outdoor test
- Example modules: Pre-built demos showing best practices
- Asset pack browser: Download community kits/prefabs
- AI-assisted placement: "Place 5 enemies in this room" -> Auto-distributes
- Procedural generation: Randomize room layouts from seed
- Blender bridge: Live-sync with Blender for advanced modeling
- VR editing: Edit levels in VR headset (à la Unreal VR Mode)
- Cloud rendering: Offload lightmap baking to remote server
- Voice commands: "Add a door here" -> Hands-free editing
Based on the Forge merge plan, these are the critical path items to implement first:
| File | Change | Status |
|---|---|---|
module_designer.ui |
Added mode selector (QLabel + QComboBox "modeSelector" with Object/Layout/Walkmesh + separator) | ✅ Done |
module_designer.ui |
Added layoutTab ("Indoor") with kit/module selectors, component lists, options, build button |
✅ Done |
module_designer.ui |
Added walkmeshTab ("Walkmesh") with paint controls, material list, face properties |
✅ Done |
module_designer.ui |
Added visTab ("VIS") with visMatrix tree widget, set all / clear all buttons |
✅ Done |
module_designer.ui |
Added IndoorMapRenderer to splitter (starts hidden), registered as custom widget |
✅ Done |
module_designer.py |
Added EditorMode enum (OBJECT=0, LAYOUT=1, WALKMESH=2) |
✅ Done |
module_designer.py |
Added indoor builder state to __init__ (IndoorMap, kits, paint state, ModuleKitManager) |
✅ Done |
module_designer.py |
Added _on_mode_changed + _apply_mode_visibility (show/hide renderers + tabs per mode) |
✅ Done |
module_designer.py |
Added _setup_indoor_kits / _setup_indoor_modules / _setup_indoor_signals |
✅ Done |
module_designer.py |
Added _populate_walkmesh_material_list with colored swatches |
✅ Done |
module_designer.py |
Added kit/module selection handlers, paint toggle, build handler | ✅ Done |
module_designer.py |
Added indoor renderer event stubs (mouse moved/pressed/released/scrolled) | ✅ Done |
module_designer.py |
Port room operations: place, delete, duplicate, merge, rotate, flip | ✅ Done |
module_designer.py |
Port clipboard: cut/copy/paste rooms with walkmesh overrides | ✅ Done |
module_designer.py |
Port right-click context menu (room/hook actions, rotate/flip sub-menus) | ✅ Done |
module_designer.py |
Port renderer signals: rooms_moved, rooms_rotated, warp_moved | ✅ Done |
module_designer.py |
Added _initialize_indoor_options (sync UI checkboxes to renderer state) |
✅ Done |
module_designer.py |
Connected customContextMenuRequested signal for indoor context menu |
✅ Done |
module_designer.py |
Port walkmesh paint stroke logic (_begin/_apply/_finish_indoor_paint_stroke, _current_indoor_material) |
✅ Done |
module_designer.py |
Port scroll-wheel handling (zoom, drag rotation, placement rotation) | ✅ Done |
module_designer.py |
Port _cancel_all_indoor_operations (marquee, drag, paint, placement cancel) |
✅ Done |
module_designer.py |
Port Layout mode status bar updates (_update_indoor_status_bar) |
✅ Done |
module_designer.py |
Port view navigation helpers (_indoor_reset_view, _indoor_center_on_selection, _indoor_add_connected_to_selection) |
✅ Done |
module_designer.py |
Port extra keyboard shortcuts (P=paint toggle, Home/Ctrl+0=reset view, F5=refresh, Ctrl+S/N/O=file I/O) | ✅ Done |
module_designer.py |
Port indoor file I/O (_indoor_new, _indoor_open, _indoor_save, _indoor_save_as) |
✅ Done |
module_designer.py |
Moved PaintWalkmeshCommand and ZOOM_WHEEL_SENSITIVITY from TYPE_CHECKING to runtime imports |
✅ Done |
module_designer.py |
Connected sig_mouse_double_clicked in _setup_indoor_signals for connected selection |
✅ Done |
module_designer.py |
Added _on_indoor_mouse_double_clicked handler (double-click room -> add connected rooms to selection) |
✅ Done |
module_designer.ui |
Replaced QListWidget instanceList with QWidget instancePanel -> QVBoxLayout -> QLineEdit instanceSearchEdit + QTreeWidget instanceTree |
✅ Done |
uic/module_designer.py |
Updated UIC Python file to match: instancePanel, instanceSearchEdit (placeholder + clear), instanceTree (headerHidden, ExtendedSelection, CustomContextMenu) |
✅ Done |
module_designer.py |
Rewrote rebuild_instance_list to build hierarchical QTreeWidget grouped by GIT type (Creatures, Placeables, Doors, etc.) with item counts |
✅ Done |
module_designer.py |
Added _filter_instance_tree(text) — live search filter that shows/hides tree items and groups |
✅ Done |
module_designer.py |
Updated select_instance_items_on_list to iterate QTreeWidget (group -> child traversal) |
✅ Done |
module_designer.py |
Updated on_instance_list_single_clicked / double_clicked / get_git_instance_from_highlighted_list_item / on_instance_list_right_clicked for QTreeWidgetItem API |
✅ Done |
module_designer.py |
Updated signal connections: instanceTree.clicked/doubleClicked/customContextMenuRequested + instanceSearchEdit.textChanged |
✅ Done |
module_designer.py |
Updated _apply_mode_visibility to use instancePanel.setVisible |
✅ Done |
designer_controls.py |
Added 2D->3D camera sync: snap_camera_to_selected in 2D controls also snaps 3D mainRenderer camera |
✅ Done |
designer_controls.py |
Added 3D->2D camera sync: move_camera_to_selected in 3D controls also snaps 2D flatRenderer camera |
✅ Done |
| File | Change | Status |
|---|---|---|
module_designer.ui |
Added resourceSearchEdit QLineEdit above resourceTree in resourceTabLayout (DA Toolset-style filter) |
✅ Done |
uic/module_designer.py |
Added matching resourceSearchEdit with placeholder text and clear button |
✅ Done |
module_designer.py |
Connected resourceSearchEdit.textChanged -> _filter_resource_tree; added _filter_resource_tree(text) for live resource tree filtering |
✅ Done |
module_designer.py |
Added EditorTool enum (SELECT=0, MOVE=1, ROTATE=2) after EditorMode |
✅ Done |
module_designer.ui |
Added checkable tool QPushButtons (toolSelectBtn/toolMoveBtn/toolRotateBtn) + toolSeparator in toolbar |
✅ Done |
uic/module_designer.py |
Added matching 3 tool QPushButtons (checkable, Select checked by default) + VLine separator | ✅ Done |
module_designer.py |
Added _setup_tool_buttons() — creates QButtonGroup for mutual exclusivity, connects clicked signals |
✅ Done |
module_designer.py |
Added _set_active_tool(tool) — switches active tool, updates button checked state, shows status message |
✅ Done |
module_designer.py |
Added _active_tool state in __init__, tool visibility toggle in _apply_mode_visibility (hidden in Layout mode) |
✅ Done |
module_designer.py |
Added _handle_object_mode_key_press(e) — handles keyboard shortcuts for Object/Walkmesh modes |
✅ Done |
module_designer.py |
Q/W/E hotkeys -> switch to Select/Move/Rotate tool, F -> focus selected, Z -> cycle viewport shading | ✅ Done |
module_designer.py |
Delete/Backspace -> delete selected instances in Object mode | ✅ Done |
module_designer.py |
Camera bookmarks: Ctrl+1..9 saves, 1..9 recalls (stores x,y,z,pitch,yaw,distance) with 2D sync | ✅ Done |
module_designer.py |
Added _cycle_viewport_shading() — toggles lightmapCheck between Lightmapped/Solid + status message |
✅ Done |
module_designer.py |
Added _save_camera_bookmark(slot) / _recall_camera_bookmark(slot) with 2D flatRenderer sync |
✅ Done |
module_designer.py |
Added persistent status bar (_setup_status_bar) — permanent Mode / Tool / Selection labels, updated on mode/tool/selection changes |
✅ Done |
module_designer.py |
Added duplicate_selected_instances() — deep-copies selected GIT instances with (0.5, 0.5, 0) offset, adds to GIT, syncs Blender |
✅ Done |
module_designer.py |
Added Ctrl+D (duplicate) and Space (cycle editor mode) hotkeys in _handle_object_mode_key_press |
✅ Done |
module_designer.py |
Added camera HUD overlay (_setup_camera_hud) — translucent QLabel on mainRenderer showing real-time X/Y/Z/Pitch/Yaw/Distance, updated every 200ms |
✅ Done |
module_designer.ui |
Added snap controls: snapCheck (QCheckBox), snapSizeSpin (QDoubleSpinBox 0.05-10m), rotSnapCheck, rotSnapDegreeSpin (1-90°), snapSeparator |
✅ Done |
uic/module_designer.py |
Added matching snap controls + separator in toolbar | ✅ Done |
module_designer.py |
Added _snap_to_grid(value) / _snap_rotation(degrees) helpers; wired grid snap into move_selected, rotation snap into rotate_selected |
✅ Done |
module_designer.py |
Added G key toggles grid snap, snap controls hidden in Layout mode via _apply_mode_visibility |
✅ Done |
| File | Change | Status |
|---|---|---|
designer_controls.py |
Modified ModuleDesignerControls3d.on_mouse_moved to gate instance-drag on _active_tool: SELECT tool blocks all instance manipulation; ROTATE tool redirects left-drag to rotation instead of XY move |
✅ Done |
module_designer.ui |
Added propertiesGroup QGroupBox (with QFormLayout) to instancePanel: ResRef/Type read-only labels, X/Y/Z QDoubleSpinBoxes, Bearing QDoubleSpinBox, "Open Blueprint" QPushButton |
✅ Done |
uic/module_designer.py |
Added matching propertiesGroup, propResRefValue, propTypeValue, propXSpin, propYSpin, propZSpin, propBearingSpin, propOpenBlueprintBtn widgets wired into instancePanelLayout |
✅ Done |
module_designer.py |
Added _setup_properties_panel() — wires spinbox valueChanged + button clicked signals, initialises _inspector_updating guard |
✅ Done |
module_designer.py |
Added _update_properties_panel() — refreshes inspector fields from selected_instances[0]: resref, type‑name, XYZ position, bearing in degrees; disables bearing spinbox for non-bearing instance types |
✅ Done |
module_designer.py |
Added _on_inspector_position_changed() — pushes MoveCommand to undo stack when XYZ spinboxes edited, invalidates scene cache |
✅ Done |
module_designer.py |
Added _on_inspector_bearing_changed() — updates instance.bearing (radians) from bearing spinbox, invalidates scene cache |
✅ Done |
module_designer.py |
Added _on_inspector_open_blueprint() — calls edit_instance() for selected instance from inspector "Open Blueprint" button |
✅ Done |
module_designer.py |
Called _update_properties_panel() at end of set_selection() so inspector auto-syncs on every selection change |
✅ Done |
module_designer.py |
Fixed on_instance_list_double_clicked to also call edit_instance(instance), opening the blueprint editor on double-click (previously only focused/selected) |
✅ Done |
module_designer.py |
Added _RESTYPE_TO_GIT_CLASS module-level dict mapping ResourceType.UTC/UTP/UTD/UTW/UTS/UTE/UTT/UTM -> GIT instance classes |
✅ Done |
module_designer.py |
Added _setup_resource_dnd() — enables resourceTree.setDragEnabled(True) + DragOnly, sets mainRenderer.setAcceptDrops(True), installs self as event filter |
✅ Done |
module_designer.py |
Added eventFilter(obj, event) — handles DragEnter/DragMove/Drop/DragLeave events on mainRenderer; accepts drop when _dragged_resource is set |
✅ Done |
module_designer.py |
Added _handle_resource_drop(resource, screen_pos) — maps resource type to GIT class, resolves world position via screen_to_world_from_depth_buffer (with cursor fallback), pre-sets resref, calls add_instance() |
✅ Done |
module_designer.py |
Added QAbstractItemView + QEvent to runtime imports |
✅ Done |
| Area | Behavior |
|---|---|
| Mode combobox | modeSelector is synced with _editor_mode; _apply_mode_visibility(mode) runs on startup and after module load so tabs and renderers match the current mode. Layout -> lytTab + layoutTab, indoorRenderer + _lyt_renderer visible; Walkmesh -> walkmeshTab. |
| Layout tab population | Modules: _module_kit_manager is created when an installation is set; _setup_indoor_modules() populates moduleKitSelect; when no installation, shows "(Select an installation for module kits)". Kits: get_kits_path() points to Tools/HolocronToolset/kits (repo root); _setup_indoor_kits() loads from that path. Create a kits folder with README if missing. |
| Snap scope | Toolbar (Object/Walkmesh only): snapCheck / snapSizeSpin = grid snap for move/place; rotSnapCheck / rotSnapDegreeSpin = rotation snap. Applied in gizmo drag, move_selected, rotate_selected, inspector position/bearing, resource drop, add-at-cursor, duplicate. Layout tab: indoor renderer uses its own snap (snapToGridCheck, gridSizeSpin, rotSnapSpin) for room placement only. |
| Drag-drop | Resources: drag from resource tree -> drop on 3D (mainRenderer) or 2D (flatRenderer); world position from depth/cursor (3D) or flatRenderer.to_world_coords (2D); walkmesh snap for creatures/waypoints. Room pieces: select component in Layout tab -> click on indoor 2D or 3D view to place; placement updates LYT and invalidates rooms; LYT/walkmesh generation on build. |
| Visibility vs pickability | Type toggles (Creatures, Doors, etc.) control visibility only. Pick hidden checkbox (Object mode): when on, 3D picker and 2D _instances_under_mouse include hidden types so they can be selected. When off, only visible types are pickable. |
| Camera / render loop | Single update path: renderer loop() calls _loop_callback(delta_time) (CameraController.update with accumulated mouse deltas) then update(). Delta time from elapsed time; vsync via QSurfaceFormat.setSwapInterval(1) (module designer process). SceneCache.build_cache() runs every frame in the render path; dead-object removal is conditional to reduce allocations when nothing was removed. |
Integrate indoor builder room-layout logic into✅ DONE — EditorMode enum, mode selector combo,module_designer.pyas "Layout Mode"_on_mode_changed,_apply_mode_visibility, indoor state in__init__, kit/module selectors, indoor renderer wired up- Retire standalone
indoor_builder.ui— deferred until all builder.py logic fully ported Add mode selector toolbar (Layout/Object/Walkmesh/Lighting/Terrain/NavMesh)✅ DONE — Mode selector combo in toolbar with Object/Layout/Walkmesh modes; tab visibility toggles per mode- Port 3D room placement with doorhook snapping to Module Designer — partially done: IndoorMapRenderer embedded + signals connected; snap checkboxes wired; full placement flow needs more builder.py migration
- 3D room preview: Render full textured rooms in viewport during placement
- Implement face/vertex/edge raycast picking in
ModuleRenderer - Add vertex drag gizmo (OpenGL translation handles with snap-to-grid)
- Create
walkmeshTabin left panel with material/flag inspector - Port normal arrow visualization (per-face directional indicators)
- Add perimeter edge highlighting (boundary detection)
- Walkmesh toolbar: Face/Vertex/Edge mode switcher (Creation Kit-style)
- Add
visTabwith room-to-room visibility checkbox matrix - Replace
set_all_visible()with granular per-room control - Visualize VIS relationships as colored connection lines in 3D viewport
- Auto-VIS generation: Analyze room adjacency and doorhook connections to suggest optimal VIS
- Occlusion testing: Preview in-game visibility from selected room's perspective
Build hierarchical scene tree (Unity-style Hierarchy panel)✅ DONE — Replaced flatQListWidget instanceListwithQTreeWidget instanceTreegrouped by GIT type (Creatures, Placeables, Doors, etc.) with item counts + search filterQLineEdit- Implement blueprint browser drag-and-drop into 3D viewport
- Add transform gizmos (translate/rotate/scale) with axis locking
- Support all 11 GIT object types with property inspector
- Alignment tools: Snap to other objects, align to center/edges, distribute evenly
- Group selection: Create temporary groups for batch transforms
- Add 4-panel layout (Top/Front/Side/Perspective orthographic views)
- Implement Unity/Creation Kit camera controls (WASD flythrough, Alt+LMB orbit, MMB pan)
Add camera bookmarks (Ctrl+1-9 to save, 1-9 to recall — Creation Kit-style)✅ DONE — Ctrl+1..9 saves, 1..9 recalls camera position/orientation with 2D sync- Walk mode: Constrained camera movement on walkmesh surface for player-perspective testing
- Split-screen preview: Side-by-side comparison views
- Implement full undo/redo stack across all modes
Add keyboard shortcut system (Q/W/E/R for tools, F to focus, Delete to remove)✅ DONE — Q/W/E for Select/Move/Rotate tools, F to focus, Delete/Backspace to remove, Z for shading, camera bookmarks- Create dockable panel system (drag-and-drop rearrangement)
- Add dark mode theme (Unity-inspired)
- Workspace layouts: Save/load panel arrangements (e.g., "Walkmesh Editing", "Object Placement", "Lighting")
The Module Designer will be considered "complete" when:
- ✅ A user can create a new module from scratch (rooms, objects, walkmeshes, VIS) without leaving the editor
- ✅ All Indoor Builder room-assembly features work seamlessly in 3D Layout Mode
- ✅ All Forge walkmesh editing features work in Walkmesh Mode with Creation Kit-level precision
- ✅ The scene hierarchy mirrors Unity's clarity (expand/collapse, search/filter, visibility toggles)
- ✅ 4-panel orthographic view matches Hammer/Radiant workflows for precise 3D alignment
- ✅ Walkmesh vertex editing is as intuitive as Blender's (click vertex -> drag gizmo -> update mesh)
- ✅ Build + Launch compiles a playable
.modand boots the game in < 30 seconds - ✅ Undo/redo works flawlessly for every operation (room placement, object transforms, walkmesh edits, VIS changes)
- ✅ The UI is intuitive enough that a Unity/UE5/Creation Kit user can start editing with zero training
- ✅ No external tools are needed for basic level design (100% self-contained workflow)
Current Module Designer state: 3D OpenGL viewport + 2D walkmesh renderer (stacked), left panel with Resources tab and Layout tab (room position spinboxes + lytTree), right panel with a flat
QListWidgetof instances, visibility checkboxes per GIT type, QUndoStack, Blender export integration, status bar. No transform gizmos, no walkmesh vertex editing, no inspector panel, no hierarchy, no multi-select, no VIS editor, no search/filter, no snap helpers, no right-click context menu in viewport, no face property editor.
Gold standard for 3D RPG cell editing. KotOR room = CK cell, GIT object = CK reference, walkmesh = navmesh.
| What It Does | We Don't Have This | Implement As |
|---|---|---|
| Navmesh Mode toggle — same viewport, different interaction layer | No walkmesh vertex editing at all in 3D view | Walkmesh Mode button in toolbar; switch raycast target from GIT objects to BWM vertices |
| Generate Navmesh — auto-triangulates walkable area from room geometry | on_generate_walkmesh exists but no face editing after |
Right-click walkmesh -> auto-fill gaps, merge islands |
| Finalize Navmesh — computes edge adjacency/portals in one click | Adjacency must be hand-authored or left broken | One-click "Finalize" rebuilds all edge/transition links from current geometry |
| Reference Browser — filterable table of all placed objects (type, position, tag, resref) | instanceList is a flat unsorted list with no search |
Replace with QTreeView grouped by type; add search bar above it |
| Layer system — Architecture/Props/Lighting layers; toggle at layer level | Visibility is global per-type only | Named layers: group instances -> toggle whole layer in/out |
| Camera bookmarks — Ctrl+1–9 saves camera view, 1–9 recalls | None | QAction array; save/restore camera matrix per slot |
| Double-click opens editor — double-click object -> properties dialog | Works (opens resource editor dialog) | ✅ Already have this — extend to show inline inspector instead |
| Audio emitter preview — wireframe sphere showing UTS max distance | Sound objects show as icons, no radius visualization | Draw sphere overlay when UTS selected using maxDistance field |
| Edge portal marking — mark walkmesh edges as doorway transitions | Door transitions are implicit; no visual indication | Highlight transition edges in walkmesh view; allow marking them |
Closest analog to KotOR — same third-person RPG genre, GFF-based data, area = module.
| What It Does | We Don't Have This | Implement As |
|---|---|---|
| Synchronized 2D+3D views — select in 2D map -> 3D view jumps to it | 2D and 3D are independent; selecting in one doesn't update other | Emit selection signal from flatRenderer -> camera/selection sync in mainRenderer |
| Drag blueprint from library into viewport — drag UTC/UTP directly into 3D view | Resource tree is browse-only; no drag-and-drop into viewport | Make resourceTree source of QDrag; mainRenderer accepts drops -> raycast spawn position |
| Waypoint network visualization — lines connecting waypoint objects | Waypoints show as dots, no connections shown | Draw lines between waypoints that share the same tag/patrol route |
| Trigger polygon drawing — draw freeform 2D polygon -> creates trigger/encounter | Triggers placed as single point; polygon shape must be edited in separate dialog | In 2D view: polygon drawing tool for triggers and encounter geometry |
| Resource browser search — type partial name -> instant filter | resourceTree has no search bar |
Add QLineEdit filter above resourceTree; hide non-matching items |
| Area properties panel — music, ambient sound, minimap, fog in one place | ARE properties only accessible through the ARE editor (separate window) | Add an "Area" tab to the left panel showing ARE key fields inline |
| Lighting presets — one-click mood (Interior/Dawn/Dusk) | Lightmap toggle (on/off) only | Preset buttons that set ambient light color + fog to common moods for quick preview |
Baseline for modern 3D editor UX. Every gap here is a standard that users will expect.
| What It Does | We Don't Have This | Implement As |
|---|---|---|
| Transform gizmo (W/E/R) — color-coded XYZ handles for move/rotate/scale | Objects drag freely; no axis-locked handles | Render OpenGL gizmo handles on selected instance; W/E/R keys switch mode |
| Inspector panel — all properties of selection in scrollable side panel (no modal) | Properties only shown in separate dialog windows | Add right-side dock QWidget that populates with GIT struct fields on selection change |
| Hierarchy with search — tree of all scene objects, type icons, search filter | Flat QListWidget, no tree, no search |
Replace instanceList with QTreeWidget grouped by GIT type; add search QLineEdit |
| Multi-select — Ctrl+Click or drag-box -> batch transform, batch property edit | Single selection only | Track selected_instances: list -> already exists; expose to viewport for drag-box selection |
| Vertex snapping (V key) — snap object origin to nearest vertex on any mesh | No snapping at all when dragging | While dragging, if V held: find nearest walkmesh vertex; snap placement to it |
| Surface snapping — drop object onto walkmesh surface below cursor | Objects placed at flat Z; no auto-ground | Raycast downward from cursor on drop/move; set object Z to hit point |
| Focus Selected (F key) — camera frames selection tightly | No focus shortcut | F key -> compute bounding box of selected instances -> set camera position/target |
| Frame All (A key) — camera frames entire scene | No frame-all | A key -> bounding box of all loaded instances -> set camera |
| Gizmo visibility toggles — show/hide sound spheres, trigger boxes, camera frustums | No per-gizmo overlay toggles | Add overlay checkboxes: Show Sound Radii / Show Trigger Shapes / Show Camera FOV |
| Coordinate space (World/Local) — toggle whether gizmo axes align to world or object | Always world-space | Button in toolbar; affects how rotate/scale gizmo axes are oriented |
Best-in-class viewport navigation and scene organization.
| What It Does | We Don't Have This | Implement As |
|---|---|---|
| WASD fly camera with mouse look | Alt+drag orbit only; no WASD flythrough | Right-click + drag in 3D view activates mouse look; WASD moves camera while held |
| Camera speed slider — scroll wheel adjusts fly speed in real-time | Fixed camera speed | Scroll wheel while in fly mode -> adjust camera_move_speed; show current speed in status bar |
| Outliner folders — drag objects into named folders; toggle folder visibility | No grouping at all in instance list | Folders in QTreeWidget: right-click -> "New Group"; drag instances between groups |
| Details panel search filter — type property name -> scroll to matching field | No inspector panel at all | When inspector is added, put a QLineEdit at top that filters/highlights matching fields |
| Right-click context menu in viewport — place object, frame selection, hide, etc. | No context menu on 3D viewport right-click | Connect customContextMenuRequested on mainRenderer -> build context-sensitive QMenu |
| Viewport toolbar mode buttons — visual icon buttons for Select/Move/Rotate/Scale | No mode buttons; interaction mode is implicit | Add toolbar row above 3D viewport: Select / Move / Rotate / Scale icon buttons |
Precision multi-view layout; best for spatial alignment work.
| What It Does | We Don't Have This | Implement As |
|---|---|---|
| 4-panel layout — Top/Front/Side/Perspective simultaneously | One 3D + one 2D stacked vertically; can't see all axes at once | QSplitter with 4 panes: 3D perspective + 3 orthographic (XY top-down, XZ front, YZ side) |
| Maximize active view — double-click panel to fill the window | Fixed split only | Double-click on any sub-renderer -> reparent to fill main area; double-click again to restore |
| Grid size hotkeys ([ and ]) — increase/decrease snap grid in 2 keystrokes | No grid snapping at all | [ / ] keys cycle snap grid size (0.1 / 0.5 / 1.0 / 2.0 units); show current in status bar |
| Visgroups — named groups with per-group visibility toggle | Type-level visibility checkboxes only | Named Visgroups in left panel; drag objects in; one toggle per group |
| Error checker — "Check for Problems" scans for missing textures, broken refs | No validation tools | Menu item -> scan module for broken resrefs, missing models, degenerate walkmesh faces |
| Compile pipeline — one-click Build -> launches game | Save GIT only; no build+launch | Add "Build & Launch" action that calls build_mod_from_indoor_file() -> game spawn warp |
Best walkmesh vertex editing workflow reference — we need Blender's Edit Mode concept.
| What It Does | We Don't Have This | Implement As |
|---|---|---|
| Edit Mode vs Object Mode — Tab key toggles; completely different interaction in same viewport | No walkmesh editing in 3D view at all | Tab or toolbar button: Object Mode (GIT instances) ↔ Walkmesh Mode (BWM vertices/faces) |
| Vertex / Edge / Face select modes (1/2/3) — granular selection within mesh | N/A — no mesh editing | In Walkmesh Mode: 1=vertex select, 2=edge select, 3=face select; highlight accordingly |
| Merge Vertices (M) — weld nearby/overlapping vertices | No geometry cleanup tools | In Walkmesh Mode: M key -> merge dialog (at cursor / at center / by distance) |
| Snap modes (Shift+Tab) — cycle vertex/edge/face/grid snapping | No snap at all | Snap pie menu cycling through modes; sticky state shown in status bar |
| Properties panel — context-sensitive; shows face material when face selected | No per-element property view | When walkmesh face selected -> show surfacemat dropdown + walk/walkCheck/lineOfSight booleans in inspector |
| Viewport shading (Z key) — Wireframe/Solid/Material toggle | Lightmap checkbox (on/off) only | Z key cycles 3D viewport shading: Wireframe / Solid / Lightmapped |
| Box select (B key) — drag rectangle to select multiple vertices/objects | No box select | B key -> drag selection rectangle in 3D viewport; add all intersecting objects/vertices to selection |
Per-face editing workflow that directly maps to surfacemat on walkmesh triangles.
| What It Does | We Don't Have This | Implement As |
|---|---|---|
| Surface Inspector — per-selected-face numeric fields (no dialog close required) | No per-face editing; surfacemat not editable | walkmeshTab dock: when face selected -> show surfacemat dropdown + flags inline |
| Entity key-value editor — raw struct fields visible as editable key-value table | GIT struct editing only via full resource editor window | In inspector panel: show GIT instance fields as editable QTableWidget rows (key / value) |
| Free camera WASD | Orbit-only; WASD not usable for flythrough | Right-click-held activates free look; WASD moves; right-click-release returns to orbit |
| Group system — Ctrl+G groups brushes; move as unit | No grouping | Multi-select -> right-click -> "Group" -> assigned Visgroup |
| Pattern | We Have | Gap |
|---|---|---|
| One window, no modal dialogs for properties | Properties open separate dialog | Inline inspector dock panel |
| Undo everything including view changes | QUndoStack on GIT ops | Viewpoint/walkmesh edits not undoable yet |
| Visual feedback on selection | 3D selection highlight | No gizmo, no bounding box outline |
| Immediate property feedback | Dialog requires Apply / OK | Inspector should write-through to model on field change |
| Keyboard shortcuts for all tools | Ctrl+Z/S only | W/E/R/F/A/B/Tab/Z/[/] all unassigned |
| Status bar showing active mode + snap state | Mouse coords + selected instance | Need: current mode, current snap size, coordinate space |