immersive2/VRCONFIGPLAN.md
Michael Mainguy aa41895675 Add VR configuration panel implementation plan and Phase 1 foundation
Create VRCONFIGPLAN.md with comprehensive 10-phase implementation guide for building an immersive WebXR configuration panel using AdvancedDynamicTexture.

Implement Phase 1: Core panel setup
- Create VRConfigPanel class following Handle pattern for grabbability
- Set up 2m x 1.5m plane mesh with high-resolution ADT (2048x2048)
- Initialize main StackPanel container with title
- Add show/hide/dispose methods for panel lifecycle
- Integrate with appConfigInstance observable for config changes
- Auto-parent to platform for world movement tracking

The panel starts hidden and provides foundation for adding configuration controls in subsequent phases (location snap, rotation snap, fly mode, snap turn, label rendering mode).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 11:53:47 -06:00

298 lines
10 KiB
Markdown

# VR Configuration Panel Implementation Plan
## Overview
Create an immersive WebXR configuration panel that mirrors the 2D ConfigModal functionality using BabylonJS AdvancedDynamicTexture (ADT). The panel will allow users to adjust all application settings directly in VR.
## Recommended Approach: AdvancedDynamicTexture (ADT)
**Why ADT?**
- Most common approach for WebXR UI in BabylonJS
- Existing pattern in codebase (see `src/menus/configMenu.ts`)
- Good balance of simplicity and functionality
- Native support for text, buttons, sliders, and dropdowns
- Easy integration with existing Handle pattern
**Estimated Effort**: 150-200 lines of code, 4-8 hours implementation time
## File Structure
```
src/menus/
├── vrConfigPanel.ts (NEW - main implementation)
└── configMenu.ts (REFERENCE - existing VR config example)
src/diagram/
└── diagramMenuManager.ts (MODIFY - add toolbox button)
src/util/
└── appConfig.ts (USE - singleton for config management)
```
## Implementation Phases
### Phase 1: Core Panel Setup
- [ ] Create `src/menus/vrConfigPanel.ts` file
- [ ] Implement class structure following Handle pattern:
```typescript
export class VRConfigPanel {
private _scene: Scene;
private _handleMesh: Mesh;
private _advancedTexture: AdvancedDynamicTexture;
private _configObserver: Observer<AppConfigType>;
constructor(scene: Scene) {
// Initialize panel
}
public get handleMesh(): Mesh {
return this._handleMesh;
}
public show(): void {
this._handleMesh.setEnabled(true);
}
public hide(): void {
this._handleMesh.setEnabled(false);
}
public dispose(): void {
// Cleanup
}
}
```
- [ ] Create base mesh (plane) for panel backing
- [ ] Set up AdvancedDynamicTexture with appropriate resolution (1024x1024 or 2048x2048)
- [ ] Position panel at comfortable viewing distance (0.5-0.7m from camera)
- [ ] Make panel grabbable via Handle pattern
**Reference Files**:
- `src/menus/inputTextView.ts` - Handle pattern implementation
- `src/menus/configMenu.ts` - ADT usage example
### Phase 2: UI Layout Structure
- [ ] Create main container (StackPanel for vertical layout)
- [ ] Add title text at top ("Configuration")
- [ ] Create 5 section containers (one for each config group):
1. Location Snap
2. Rotation Snap
3. Fly Mode
4. Snap Turn
5. Label Rendering Mode
- [ ] Style containers with padding and spacing
- [ ] Add visual separators between sections
**ADT Components to Use**:
- `StackPanel` - Main vertical container
- `TextBlock` - Labels and section titles
- `Rectangle` - Containers and separators
**Reference**: `src/menus/configMenu.ts:44-89` for existing layout patterns
### Phase 3: Location Snap Section
- [ ] Add "Location Snap" label
- [ ] Create enable/disable toggle button
- Shows "Enabled" or "Disabled"
- Updates `appConfigInstance` on click
- [ ] Add RadioGroup for snap values:
- Options: 1cm (.01), 5cm (.05), 10cm (.1), 50cm (.5), 1m (1)
- Default: 10cm (.1)
- Disable when snap is off
- [ ] Wire up to `appConfigInstance.setGridSnap(value)`
- [ ] Subscribe to config changes to update UI
**ADT Components**:
- `Button` - Toggle switch
- `RadioButton` + `TextBlock` - Value selection
- Color coding: enabled (green/myColor), disabled (gray)
**Reference ConfigModal**: `src/react/pages/configModal.tsx:83-94`
### Phase 4: Rotation Snap Section
- [ ] Add "Rotation Snap" label
- [ ] Create enable/disable toggle button
- [ ] Add RadioGroup for rotation values:
- Options: 22.5°, 45°, 90°, 180°, 360°
- Default: 90°
- Disable when snap is off
- [ ] Wire up to `appConfigInstance.setRotateSnap(value)`
- [ ] Subscribe to config changes to update UI
**Reference ConfigModal**: `src/react/pages/configModal.tsx:96-108`
### Phase 5: Fly Mode Section
- [ ] Add "Fly Mode" label
- [ ] Create toggle button
- Shows "Fly Mode Enabled" or "Fly Mode Disabled"
- [ ] Wire up to `appConfigInstance.setFlyMode(value)`
- [ ] Subscribe to config changes to update UI
**Reference ConfigModal**: `src/react/pages/configModal.tsx:109-112`
### Phase 6: Snap Turn Section
- [ ] Add "Snap Turn" label
- [ ] Create enable/disable toggle button
- [ ] Add RadioGroup for snap turn angles:
- Options: 22.5°, 45°, 90°, 180°, 360°
- Default: 45°
- Disable when snap is off
- [ ] Wire up to `appConfigInstance.setTurnSnap(value)`
- [ ] Subscribe to config changes to update UI
**Reference ConfigModal**: `src/react/pages/configModal.tsx:113-125`
### Phase 7: Label Rendering Mode Section
- [ ] Add "Label Rendering Mode" label
- [ ] Create RadioGroup for rendering modes:
- Fixed
- Billboard (Always Face Camera)
- Dynamic (Coming Soon) - disabled
- Distance-based (Coming Soon) - disabled
- [ ] Wire up to `appConfigInstance.setLabelRenderingMode(value)`
- [ ] Subscribe to config changes to update UI
- [ ] Style disabled options with gray text
**Reference ConfigModal**: `src/react/pages/configModal.tsx:126-135`
### Phase 8: Integration with Toolbox
- [ ] Modify `src/diagram/diagramMenuManager.ts` to instantiate VRConfigPanel
- [ ] Add "Config" button to toolbox (similar to "Exit VR" button pattern)
- [ ] Wire up button click to show/hide panel
- [ ] Position panel relative to camera when shown (see `positionComponentsRelativeToCamera`)
- [ ] Add parent relationship to platform for movement tracking
**Reference**:
- `src/diagram/diagramMenuManager.ts:85-97` - Exit button creation
- `src/util/functions/groundMeshObserver.ts:127-222` - Component positioning
### Phase 9: Observable Integration
- [ ] Subscribe to `appConfigInstance.onConfigChangedObservable` in constructor
- [ ] Update all UI elements when config changes externally
- [ ] Ensure Observable cleanup in dispose() method
- [ ] Test config changes from both VR panel and 2D ConfigModal
**Pattern**:
```typescript
this._configObserver = appConfigInstance.onConfigChangedObservable.add((config) => {
// Update UI elements to reflect new config
this.updateLocationSnapUI(config.locationSnap);
this.updateRotationSnapUI(config.rotateSnap);
// ... etc
});
```
### Phase 10: Testing & Polish
- [ ] Test all toggle switches update config correctly
- [ ] Test all radio button selections update config correctly
- [ ] Verify config changes propagate to DiagramObjects (label mode, snap behavior)
- [ ] Test panel positioning in VR (comfortable viewing distance)
- [ ] Test panel grabbability via Handle
- [ ] Verify panel follows platform movement
- [ ] Test config persistence (localStorage)
- [ ] Test config synchronization between VR panel and 2D ConfigModal
- [ ] Add visual feedback for button clicks (color changes, animations)
- [ ] Ensure proper cleanup on panel disposal
- [ ] Test in both WebXR and desktop modes
## Code Patterns to Follow
### 1. Toggle Button Pattern
```typescript
const toggleButton = Button.CreateSimpleButton("toggle", "Enabled");
toggleButton.width = "200px";
toggleButton.height = "40px";
toggleButton.color = "white";
toggleButton.background = "green";
toggleButton.onPointerClickObservable.add(() => {
const newValue = !currentValue;
toggleButton.textBlock.text = newValue ? "Enabled" : "Disabled";
toggleButton.background = newValue ? "green" : "gray";
appConfigInstance.setSomeSetting(newValue);
});
```
### 2. RadioGroup Pattern
```typescript
const radioGroup = new SelectionPanel("snapValues");
const options = [
{ value: 0.01, label: "1cm" },
{ value: 0.1, label: "10cm" },
// ... more options
];
options.forEach(option => {
const radio = new RadioButton();
radio.width = "20px";
radio.height = "20px";
radio.isChecked = (option.value === currentValue);
radio.onIsCheckedChangedObservable.add((checked) => {
if (checked) {
appConfigInstance.setGridSnap(option.value);
}
});
// Add label next to radio button
});
```
### 3. Config Observer Pattern
```typescript
this._configObserver = appConfigInstance.onConfigChangedObservable.add((config) => {
this.updateUIFromConfig(config);
});
// In dispose():
if (this._configObserver) {
appConfigInstance.onConfigChangedObservable.remove(this._configObserver);
}
```
## Key Integration Points
### AppConfig Singleton
- Import: `import {appConfigInstance} from "../util/appConfig";`
- Read: `appConfigInstance.current.locationSnap`
- Write: `appConfigInstance.setGridSnap(0.1)`
- Subscribe: `appConfigInstance.onConfigChangedObservable.add(callback)`
### DiagramMenuManager
- Instantiate panel: `this._vrConfigPanel = new VRConfigPanel(this._scene);`
- Add button to toolbox: Follow exit button pattern in `setupExitButton()`
- Show panel: `this._vrConfigPanel.show();`
- Position panel: Follow pattern in `groundMeshObserver.ts:127-222`
### Handle Pattern
- Make panel grabbable by controllers
- Parent to platform for world movement
- Use `_handleMesh` as root for entire panel UI
## Reference Files
1. **src/menus/configMenu.ts** - Existing VR config implementation with ADT
2. **src/menus/inputTextView.ts** - Handle pattern and ADT setup
3. **src/react/pages/configModal.tsx** - UI structure and config sections
4. **src/util/appConfig.ts** - Config singleton and setter methods
5. **src/diagram/diagramMenuManager.ts** - Toolbox button creation
6. **src/util/functions/groundMeshObserver.ts** - Component positioning
## Success Criteria
- [ ] All 5 config sections implemented and functional
- [ ] Config changes in VR panel update appConfigInstance
- [ ] Config changes propagate to all DiagramObjects
- [ ] Panel is grabbable and repositionable
- [ ] Panel follows platform movement
- [ ] Config persists to localStorage
- [ ] Synchronized with 2D ConfigModal
- [ ] Comfortable viewing experience in VR
- [ ] No memory leaks (proper Observable cleanup)
## Notes
- Start hidden (only show when user clicks toolbox button)
- Position at ~0.5m in front of camera when opened
- Use Y-axis billboard mode to keep panel upright but allow rotation
- Consider adding "Close" button at bottom of panel
- Match color scheme with existing UI (myColor theme)
- Test with both left and right controller grabbing