@series-inc/rundot-3d-engine 0.6.18 → 0.6.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1930,4 +1930,28 @@ declare class RenderBatcher {
1930
1930
  static reset(): void;
1931
1931
  }
1932
1932
 
1933
- export { AssetManager, CapacitorPlatform, Component, ComponentUpdater, GameObject, InstancedMeshManager, InstancedRenderer, type InstancedRendererOptions, InteractionZone, type InteractionZoneOptions, MeshRenderer, MovementController, Platform, type PlatformAds, type PlatformAnalytics, type PlatformCDN, type PlatformCache, type PlatformContext, type PlatformIAP, type PlatformLifecycles, type PlatformNotifications, type PlatformPreloader, type PlatformService, type PlatformStorage, type PlatformType, PlayerControllerThree, RenderBatcher, SkeletalRenderer, SkeletonCache, VenusGame, type VenusGameConfig, type VirtualJoystickOptions, VirtualJoystickThree, didYouMean, engineError, engineThrow, engineWarn, fuzzyMatch, getPlatform, initializePlatform, initializePlatformAsync, isPlatformInitialized, listAvailable, resetPlatform };
1933
+ interface StaticBatcherJSON extends ComponentJSON {
1934
+ type: "static_batcher";
1935
+ }
1936
+ /**
1937
+ * Merges all child meshes by material into single draw calls.
1938
+ *
1939
+ * Place on a root GameObject whose children are static scenery.
1940
+ * After all child MeshRenderers finish loading, their geometries are
1941
+ * merged per-material into combined meshes. The originals are removed.
1942
+ *
1943
+ * This is classic static batching — one draw call per unique material,
1944
+ * zero per-frame matrix work. Use for geometry that never moves.
1945
+ */
1946
+ declare class StaticBatcher extends Component {
1947
+ static fromPrefabJSON(_json: StaticBatcherJSON, _node: PrefabNode): StaticBatcher;
1948
+ private mergedMeshes;
1949
+ private _isBatched;
1950
+ get isBatched(): boolean;
1951
+ protected onCreate(): void;
1952
+ private pollUntilReady;
1953
+ private batchAll;
1954
+ protected onCleanup(): void;
1955
+ }
1956
+
1957
+ export { AssetManager, CapacitorPlatform, Component, ComponentUpdater, GameObject, InstancedMeshManager, InstancedRenderer, type InstancedRendererOptions, InteractionZone, type InteractionZoneOptions, MeshRenderer, MovementController, Platform, type PlatformAds, type PlatformAnalytics, type PlatformCDN, type PlatformCache, type PlatformContext, type PlatformIAP, type PlatformLifecycles, type PlatformNotifications, type PlatformPreloader, type PlatformService, type PlatformStorage, type PlatformType, PlayerControllerThree, RenderBatcher, SkeletalRenderer, SkeletonCache, StaticBatcher, VenusGame, type VenusGameConfig, type VirtualJoystickOptions, VirtualJoystickThree, didYouMean, engineError, engineThrow, engineWarn, fuzzyMatch, getPlatform, initializePlatform, initializePlatformAsync, isPlatformInitialized, listAvailable, resetPlatform };
package/dist/index.js CHANGED
@@ -1581,6 +1581,114 @@ var MeshRenderer = class extends Component {
1581
1581
  MeshRenderer = __decorateClass([
1582
1582
  PrefabComponent("stow_mesh")
1583
1583
  ], MeshRenderer);
1584
+
1585
+ // src/engine/render/StaticBatcher.ts
1586
+ import * as THREE7 from "three";
1587
+ import { mergeGeometries } from "three/addons/utils/BufferGeometryUtils.js";
1588
+ var StaticBatcher = class extends Component {
1589
+ static fromPrefabJSON(_json, _node) {
1590
+ return new StaticBatcher();
1591
+ }
1592
+ mergedMeshes = null;
1593
+ _isBatched = false;
1594
+ get isBatched() {
1595
+ return this._isBatched;
1596
+ }
1597
+ onCreate() {
1598
+ this.pollUntilReady();
1599
+ }
1600
+ pollUntilReady() {
1601
+ const renderers = this.gameObject.getComponentsInChildren(MeshRenderer);
1602
+ if (renderers.length === 0) return;
1603
+ const allLoaded = renderers.every((r) => r.isLoaded());
1604
+ if (allLoaded) {
1605
+ requestAnimationFrame(() => this.batchAll(renderers));
1606
+ } else {
1607
+ requestAnimationFrame(() => this.pollUntilReady());
1608
+ }
1609
+ }
1610
+ batchAll(renderers) {
1611
+ if (this._isBatched) return;
1612
+ this.gameObject.updateMatrixWorld(true);
1613
+ const groups = /* @__PURE__ */ new Map();
1614
+ let totalBefore = 0;
1615
+ for (const renderer of renderers) {
1616
+ const mesh = renderer.getMesh();
1617
+ if (!mesh) continue;
1618
+ mesh.traverse((child) => {
1619
+ if (!child.isMesh) return;
1620
+ const m = child;
1621
+ const mats = Array.isArray(m.material) ? m.material : [m.material];
1622
+ for (const mat of mats) {
1623
+ let list = groups.get(mat);
1624
+ if (!list) {
1625
+ list = [];
1626
+ groups.set(mat, list);
1627
+ }
1628
+ list.push({
1629
+ geometry: m.geometry,
1630
+ matrix: m.matrixWorld.clone()
1631
+ });
1632
+ totalBefore++;
1633
+ }
1634
+ });
1635
+ }
1636
+ if (totalBefore === 0) return;
1637
+ const batcher = RenderBatcher.getInstance();
1638
+ for (const renderer of renderers) {
1639
+ batcher.unregister(renderer);
1640
+ }
1641
+ this.mergedMeshes = [];
1642
+ const rootInverse = new THREE7.Matrix4().copy(this.gameObject.matrixWorld).invert();
1643
+ const tempMatrix = new THREE7.Matrix4();
1644
+ for (const [material, entries] of groups) {
1645
+ const clones = [];
1646
+ for (const entry of entries) {
1647
+ const clone = entry.geometry.clone();
1648
+ tempMatrix.multiplyMatrices(rootInverse, entry.matrix);
1649
+ clone.applyMatrix4(tempMatrix);
1650
+ clones.push(clone);
1651
+ }
1652
+ const merged = mergeGeometries(clones, false);
1653
+ if (!merged) {
1654
+ for (const c of clones) c.dispose();
1655
+ continue;
1656
+ }
1657
+ for (const c of clones) c.dispose();
1658
+ const mesh = new THREE7.Mesh(merged, material);
1659
+ mesh.name = `staticBatch_${material.uuid.slice(0, 8)}`;
1660
+ mesh.castShadow = true;
1661
+ mesh.receiveShadow = true;
1662
+ mesh.frustumCulled = true;
1663
+ mesh.matrixAutoUpdate = false;
1664
+ this.gameObject.add(mesh);
1665
+ this.mergedMeshes.push(mesh);
1666
+ }
1667
+ for (const renderer of renderers) {
1668
+ const go = renderer.getGameObject();
1669
+ if (go !== this.gameObject) {
1670
+ go.removeComponent(MeshRenderer);
1671
+ }
1672
+ }
1673
+ this._isBatched = true;
1674
+ console.log(
1675
+ `[StaticBatcher] "${this.gameObject.name}": ${totalBefore} meshes \u2192 ${this.mergedMeshes.length} draw calls`
1676
+ );
1677
+ }
1678
+ onCleanup() {
1679
+ if (this.mergedMeshes) {
1680
+ for (const mesh of this.mergedMeshes) {
1681
+ this.gameObject.remove(mesh);
1682
+ mesh.geometry.dispose();
1683
+ }
1684
+ this.mergedMeshes = null;
1685
+ }
1686
+ this._isBatched = false;
1687
+ }
1688
+ };
1689
+ StaticBatcher = __decorateClass([
1690
+ PrefabComponent("static_batcher")
1691
+ ], StaticBatcher);
1584
1692
  export {
1585
1693
  AssetManager,
1586
1694
  CapacitorPlatform,
@@ -1597,6 +1705,7 @@ export {
1597
1705
  RenderBatcher,
1598
1706
  SkeletalRenderer,
1599
1707
  SkeletonCache,
1708
+ StaticBatcher,
1600
1709
  VenusGame,
1601
1710
  VirtualJoystickThree,
1602
1711
  didYouMean,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/engine/interaction/InteractionZone.ts","../src/engine/mobile/VirtualJoystickThree.ts","../src/engine/movement/MovementController.ts","../src/engine/player/PlayerControllerThree.ts","../src/engine/render/SkeletalRenderer.ts","../src/engine/render/InstancedRenderer.ts","../src/engine/render/MeshRenderer.ts"],"sourcesContent":["import * as THREE from \"three\"\nimport { Component, GameObject } from \"@engine/core\"\nimport {\n RigidBodyComponentThree,\n RigidBodyType,\n ColliderShape,\n} from \"@systems/physics/RigidBodyComponentThree.ts\"\n\n/**\n * Simple interaction zone using Rapier physics triggers\n * Replaces the complex raycasting system with simple physics triggers\n */\nexport class InteractionZone extends Component {\n public readonly id: string\n\n private active: boolean = true\n private onEnterCallback?: (other: GameObject) => void\n private onExitCallback?: (other: GameObject) => void\n private entitiesInZone: Set<GameObject> = new Set()\n\n private rigidBody: RigidBodyComponentThree | null = null\n private visualMesh: THREE.Mesh | null = null\n private options: InteractionZoneOptions\n\n constructor(\n onEnter?: (other: GameObject) => void,\n onExit?: (other: GameObject) => void,\n options: InteractionZoneOptions = {}\n ) {\n super()\n this.id = `interaction_${Math.random().toString(36).substr(2, 9)}`\n this.onEnterCallback = onEnter\n this.onExitCallback = onExit\n this.options = {\n width: 2,\n depth: 2,\n active: true,\n show: true,\n ...options,\n }\n this.active = this.options.active ?? true\n }\n\n /**\n * Set interaction callbacks\n */\n public setCallbacks(callbacks: {\n onEnter?: (other: GameObject) => void\n onExit?: (other: GameObject) => void\n }): void {\n this.onEnterCallback = callbacks.onEnter\n this.onExitCallback = callbacks.onExit\n }\n\n /**\n * Called when component is attached to GameObject\n */\n protected onCreate(): void {\n if (this.options.show) {\n this.createVisualMesh()\n }\n this.createTriggerCollider()\n this.setActive(this.active)\n }\n\n public onEnabled(): void {\n // Trigger registration handled by explicit registerOnTriggerEnter/Exit() calls\n // No additional logic needed here\n }\n\n /**\n * Create the visual mesh for the interaction zone\n */\n private createVisualMesh(): void {\n const width = this.options.width!\n const height = 0.1 // Fixed height for top-down view\n const depth = this.options.depth!\n\n // Create box geometry\n const geometry = new THREE.BoxGeometry(width, height, depth)\n\n // Create semi-transparent material\n const material = new THREE.MeshBasicMaterial({\n color: 0x000000, // Black like the original\n transparent: true,\n opacity: 0.15, // Original opacity\n side: THREE.DoubleSide,\n })\n\n this.visualMesh = new THREE.Mesh(geometry, material)\n\n // Add to the GameObject\n this.gameObject.add(this.visualMesh)\n this.visualMesh.position.y += 0.1\n\n // Visual mesh created\n }\n\n /**\n * Create the physics trigger collider\n */\n private createTriggerCollider(): void {\n this.rigidBody = new RigidBodyComponentThree({\n type: RigidBodyType.STATIC,\n shape: ColliderShape.BOX,\n size: new THREE.Vector3(this.options.width!, 0.1, this.options.depth!), // Fixed height of 0.1\n isSensor: true, // This makes it a trigger collider\n // No collision groups = default behavior (can detect anything that wants to hit it)\n })\n\n // Apply center offset if specified (only X and Z since it's Vector2)\n if (this.options.centerOffset) {\n this.gameObject.position.x += this.options.centerOffset.x\n this.gameObject.position.z += this.options.centerOffset.y // Vector2.y maps to world Z\n }\n\n this.gameObject.addComponent(this.rigidBody)\n\n // Register methods directly - just like C# actions!\n this.rigidBody.registerOnTriggerEnter(this.onTriggerEnter.bind(this))\n this.rigidBody.registerOnTriggerExit(this.onTriggerExit.bind(this))\n }\n\n /**\n * Handle trigger enter event (to be called by physics system)\n */\n public onTriggerEnter(other: GameObject): void {\n // console.log(`🎯 ENTER: ${other.name} → InteractionZone ${this.id}`); // Reduced spam\n\n if (!this.active) {\n console.warn(`🎯 InteractionZone ${this.id}: Ignoring enter event - zone is inactive`)\n return\n }\n\n // Check if parent GameObject is enabled\n if (!this.gameObject.isEnabled()) {\n // console.log(`🎯 InteractionZone ${this.id}: Ignoring enter event - parent GameObject is disabled`);\n return\n }\n\n if (!this.entitiesInZone.has(other)) {\n this.entitiesInZone.add(other)\n // console.log(`🎯 Added ${other.name} to zone (total: ${this.entitiesInZone.size})`); // Reduced spam\n\n if (this.onEnterCallback) {\n this.onEnterCallback(other)\n }\n }\n }\n\n /**\n * Handle trigger exit event (to be called by physics system)\n */\n public onTriggerExit(other: GameObject): void {\n // console.log(`🎯 EXIT: ${other.name} ← InteractionZone ${this.id}`); // Reduced spam\n\n if (!this.active) {\n // Zone inactive, ignoring exit\n return\n }\n\n // Check if parent GameObject is enabled\n if (!this.gameObject.isEnabled()) {\n // console.log(`🎯 InteractionZone ${this.id}: Ignoring exit event - parent GameObject is disabled`);\n return\n }\n\n if (this.entitiesInZone.has(other)) {\n this.entitiesInZone.delete(other)\n // console.log(`🎯 Removed ${other.name} from zone (total: ${this.entitiesInZone.size})`); // Reduced spam\n\n if (this.onExitCallback) {\n this.onExitCallback(other)\n }\n }\n }\n\n /**\n * Get all entities currently in the zone\n */\n public getEntitiesInZone(): GameObject[] {\n return Array.from(this.entitiesInZone)\n }\n\n /**\n * Check if a specific entity is in the zone\n */\n public hasEntity(entity: GameObject): boolean {\n return this.entitiesInZone.has(entity)\n }\n\n /**\n * Set active state\n */\n public setActive(active: boolean): void {\n this.active = active\n\n // Show/hide visual mesh (only if visual mesh was created)\n if (this.visualMesh) {\n this.visualMesh.visible = active\n }\n\n if (this.rigidBody) {\n // TODO: Enable/disable the rigid body when that feature is available\n }\n\n if (!active) {\n // Clear all entities when deactivated\n this.entitiesInZone.clear()\n }\n }\n\n /**\n * Check if the interaction zone is active\n */\n public isActive(): boolean {\n return this.active\n }\n\n /**\n * Get the visual mesh\n */\n public getVisualMesh(): THREE.Mesh | null {\n return this.visualMesh\n }\n\n /**\n * Get the collider component\n */\n public getCollider(): RigidBodyComponentThree | null {\n return this.rigidBody\n }\n\n /**\n * Get the GameObject this zone is attached to\n */\n public getGameObject(): GameObject {\n return this.gameObject\n }\n\n /**\n * Component cleanup\n */\n protected onCleanup(): void {\n this.entitiesInZone.clear()\n\n // Trigger cleanup handled automatically by RigidBodyComponentThree!\n // The registered callbacks will be cleaned up when the RigidBody is destroyed\n\n // Clean up visual mesh\n if (this.visualMesh) {\n this.visualMesh.geometry.dispose()\n if (this.visualMesh.material instanceof THREE.Material) {\n this.visualMesh.material.dispose()\n }\n this.gameObject.remove(this.visualMesh)\n this.visualMesh = null\n }\n\n // RigidBodyComponentThree will be cleaned up automatically by the GameObject\n }\n}\n\n// Interface for options\nexport interface InteractionZoneOptions {\n width?: number\n depth?: number\n active?: boolean\n centerOffset?: THREE.Vector2\n show?: boolean\n}\n","import * as THREE from \"three\"\nimport { Component } from \"@engine/core\"\n\nexport interface VirtualJoystickOptions {\n size?: number // Size of the joystick base\n knobSize?: number // Size of the joystick knob\n deadZone?: number // Dead zone radius (0-1)\n maxDistance?: number // Maximum distance for knob movement\n color?: string // Color of the joystick\n visible?: boolean // Whether to show joystick visuals (still functional when hidden)\n opacity?: number // Opacity of joystick visuals (0-1)\n}\n\n/**\n * Virtual joystick component for mobile/touch input - Three.js version\n * Shows on pointer down, hides on pointer up\n * Provides normalized direction vector for movement\n * Uses HTML/CSS for UI elements\n */\nexport class VirtualJoystickThree extends Component {\n // Configuration\n private options: Required<VirtualJoystickOptions>\n\n // Mobile detection\n private static isMobileDevice(): boolean {\n return (\n /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||\n \"ontouchstart\" in window ||\n navigator.maxTouchPoints > 0\n )\n }\n\n // UI Elements\n private joystickContainer: HTMLElement | null = null\n private joystickBase: HTMLElement | null = null\n private joystickKnob: HTMLElement | null = null\n private mobileHint: HTMLElement | null = null\n\n // State\n private isActive: boolean = false\n private startPosition: THREE.Vector2 = new THREE.Vector2()\n private currentPosition: THREE.Vector2 = new THREE.Vector2()\n private direction: THREE.Vector2 = new THREE.Vector2()\n private magnitude: number = 0\n\n // Input tracking\n private joystickPointerId: number | null = null\n private isDragging: boolean = false\n private joystickRadius: number = 0\n\n // Event handlers (need to bind them for proper cleanup)\n private boundPointerDown = this.onPointerDown.bind(this)\n private boundPointerMove = this.onPointerMove.bind(this)\n private boundPointerUp = this.onPointerUp.bind(this)\n private boundTouchStart = this.onTouchStart.bind(this)\n private boundTouchMove = this.onTouchMove.bind(this)\n private boundTouchEnd = this.onTouchEnd.bind(this)\n\n constructor(options: VirtualJoystickOptions = {}) {\n super()\n\n this.options = {\n size: options.size ?? 120,\n knobSize: options.knobSize ?? 40,\n deadZone: options.deadZone ?? 0.15,\n maxDistance: options.maxDistance ?? 50,\n color: options.color ?? \"white\",\n visible: options.visible ?? true,\n opacity: options.opacity ?? 0.2,\n }\n\n this.joystickRadius = this.options.maxDistance\n }\n\n protected onCreate(): void {\n this.createJoystickUI()\n this.createMobileHint()\n this.setupInputHandlers()\n }\n\n protected onCleanup(): void {\n this.cleanupUI()\n this.removeInputHandlers()\n }\n\n /**\n * Create the joystick UI elements using HTML/CSS\n */\n private createJoystickUI(): void {\n // Create container for joystick (initially hidden)\n this.joystickContainer = document.createElement(\"div\")\n this.joystickContainer.id = \"virtual-joystick-container\"\n this.joystickContainer.style.cssText = `\n position: fixed;\n width: ${this.options.size + this.options.knobSize}px;\n height: ${this.options.size + this.options.knobSize}px;\n display: none;\n pointer-events: none;\n z-index: 1000;\n user-select: none;\n touch-action: none;\n `\n\n // Create joystick base (outer circle)\n this.joystickBase = document.createElement(\"div\")\n this.joystickBase.style.cssText = `\n position: absolute;\n width: ${this.options.size}px;\n height: ${this.options.size}px;\n border: 4px solid ${this.options.color};\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.2);\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n box-sizing: border-box;\n opacity: ${this.options.opacity};\n `\n\n // Create joystick knob (inner circle)\n this.joystickKnob = document.createElement(\"div\")\n this.joystickKnob.style.cssText = `\n position: absolute;\n width: ${this.options.knobSize}px;\n height: ${this.options.knobSize}px;\n border: 2px solid ${this.options.color};\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.8);\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n transition: none;\n box-sizing: border-box;\n opacity: ${this.options.opacity};\n `\n\n // Add elements to container\n this.joystickContainer.appendChild(this.joystickBase)\n this.joystickContainer.appendChild(this.joystickKnob)\n\n // Add to document body\n document.body.appendChild(this.joystickContainer)\n }\n\n /**\n * Create mobile hint for touch controls (only shows on mobile devices)\n */\n private createMobileHint(): void {\n // Only show hint on mobile devices\n if (!VirtualJoystickThree.isMobileDevice()) {\n return\n }\n\n this.mobileHint = document.createElement(\"div\")\n this.mobileHint.id = \"mobile-joystick-hint\"\n this.mobileHint.textContent = \"Touch & drag to move\"\n this.mobileHint.style.cssText = `\n position: fixed;\n bottom: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.8);\n color: white;\n padding: 10px 20px;\n border-radius: 20px;\n font-family: Arial, sans-serif;\n font-size: 14px;\n z-index: 999;\n pointer-events: none;\n user-select: none;\n border: 2px solid rgba(255, 255, 255, 0.3);\n animation: fadeInOut 4s ease-in-out;\n `\n\n // Add CSS animation\n if (!document.querySelector(\"#mobile-hint-styles\")) {\n const style = document.createElement(\"style\")\n style.id = \"mobile-hint-styles\"\n style.textContent = `\n @keyframes fadeInOut {\n 0% { opacity: 0; transform: translateX(-50%) translateY(20px); }\n 20% { opacity: 1; transform: translateX(-50%) translateY(0px); }\n 80% { opacity: 1; transform: translateX(-50%) translateY(0px); }\n 100% { opacity: 0; transform: translateX(-50%) translateY(-20px); }\n }\n `\n document.head.appendChild(style)\n }\n\n document.body.appendChild(this.mobileHint)\n\n // Remove hint after animation completes\n setTimeout(() => {\n if (this.mobileHint && this.mobileHint.parentNode) {\n this.mobileHint.parentNode.removeChild(this.mobileHint)\n this.mobileHint = null\n }\n }, 4000)\n }\n\n /**\n * Setup input handlers for pointer and touch events\n */\n private setupInputHandlers(): void {\n // Add both pointer and touch event listeners for maximum compatibility\n document.addEventListener(\"pointerdown\", this.boundPointerDown, {\n passive: false,\n })\n document.addEventListener(\"pointermove\", this.boundPointerMove, {\n passive: false,\n })\n document.addEventListener(\"pointerup\", this.boundPointerUp, {\n passive: false,\n })\n\n // Touch events as fallback\n document.addEventListener(\"touchstart\", this.boundTouchStart, {\n passive: false,\n })\n document.addEventListener(\"touchmove\", this.boundTouchMove, {\n passive: false,\n })\n document.addEventListener(\"touchend\", this.boundTouchEnd, {\n passive: false,\n })\n }\n\n /**\n * Remove input handlers\n */\n private removeInputHandlers(): void {\n document.removeEventListener(\"pointerdown\", this.boundPointerDown)\n document.removeEventListener(\"pointermove\", this.boundPointerMove)\n document.removeEventListener(\"pointerup\", this.boundPointerUp)\n\n document.removeEventListener(\"touchstart\", this.boundTouchStart)\n document.removeEventListener(\"touchmove\", this.boundTouchMove)\n document.removeEventListener(\"touchend\", this.boundTouchEnd)\n }\n\n /**\n * Handle pointer down event\n */\n private onPointerDown(event: PointerEvent): void {\n // Only respond to primary pointer (first touch/click)\n if (this.isActive || !event.isPrimary) return\n\n this.startJoystick(event.clientX, event.clientY, event.pointerId)\n event.preventDefault()\n }\n\n /**\n * Handle touch start event (fallback)\n */\n private onTouchStart(event: TouchEvent): void {\n if (this.isActive || event.touches.length === 0) return\n\n const touch = event.touches[0]\n this.startJoystick(touch.clientX, touch.clientY, touch.identifier)\n event.preventDefault()\n }\n\n /**\n * Start the joystick at the given position\n */\n private startJoystick(x: number, y: number, pointerId: number): void {\n this.isActive = true\n this.isDragging = true\n this.joystickPointerId = pointerId\n\n // Hide mobile hint when joystick is first used\n if (this.mobileHint && this.mobileHint.parentNode) {\n this.mobileHint.parentNode.removeChild(this.mobileHint)\n this.mobileHint = null\n }\n\n // Set positions\n this.startPosition.set(x, y)\n this.currentPosition.set(x, y)\n\n // Show and position the joystick (only if visible is true)\n if (this.joystickContainer) {\n this.joystickContainer.style.display = this.options.visible ? \"block\" : \"none\"\n this.joystickContainer.style.left = `${x - (this.options.size + this.options.knobSize) / 2}px`\n this.joystickContainer.style.top = `${y - (this.options.size + this.options.knobSize) / 2}px`\n }\n\n // Reset knob to center\n if (this.joystickKnob) {\n this.joystickKnob.style.transform = \"translate(-50%, -50%)\"\n }\n\n this.updateDirection()\n }\n\n /**\n * Handle pointer move event\n */\n private onPointerMove(event: PointerEvent): void {\n if (!this.isActive || !this.isDragging || event.pointerId !== this.joystickPointerId) {\n return\n }\n\n this.updateJoystick(event.clientX, event.clientY)\n event.preventDefault()\n }\n\n /**\n * Handle touch move event (fallback)\n */\n private onTouchMove(event: TouchEvent): void {\n if (!this.isActive || !this.isDragging) return\n\n // Find the touch with our ID\n for (let i = 0; i < event.touches.length; i++) {\n const touch = event.touches[i]\n if (touch.identifier === this.joystickPointerId) {\n this.updateJoystick(touch.clientX, touch.clientY)\n event.preventDefault()\n break\n }\n }\n }\n\n /**\n * Update joystick position and direction\n */\n private updateJoystick(x: number, y: number): void {\n this.currentPosition.set(x, y)\n\n // Update knob position and direction\n this.updateKnobPosition()\n this.updateDirection()\n }\n\n /**\n * Handle pointer up event\n */\n private onPointerUp(event: PointerEvent): void {\n if (!this.isActive || event.pointerId !== this.joystickPointerId) {\n return\n }\n\n this.endJoystick()\n event.preventDefault()\n }\n\n /**\n * Handle touch end event (fallback)\n */\n private onTouchEnd(event: TouchEvent): void {\n if (!this.isActive) return\n\n // Check if our touch ended\n let touchEnded = true\n for (let i = 0; i < event.touches.length; i++) {\n if (event.touches[i].identifier === this.joystickPointerId) {\n touchEnded = false\n break\n }\n }\n\n if (touchEnded) {\n this.endJoystick()\n event.preventDefault()\n }\n }\n\n /**\n * End the joystick interaction\n */\n private endJoystick(): void {\n this.isActive = false\n this.isDragging = false\n this.joystickPointerId = null\n\n // Hide the joystick\n if (this.joystickContainer) {\n this.joystickContainer.style.display = \"none\"\n }\n\n // Reset direction\n this.direction.set(0, 0)\n this.magnitude = 0\n }\n\n /**\n * Update knob position based on current pointer position\n */\n private updateKnobPosition(): void {\n if (!this.joystickKnob) return\n\n // Calculate offset from joystick center\n const offset = this.currentPosition.clone().sub(this.startPosition)\n\n // Clamp to max distance\n const distance = offset.length()\n if (distance > this.joystickRadius) {\n offset.normalize().multiplyScalar(this.joystickRadius)\n }\n\n // Update knob position\n this.joystickKnob.style.transform = `translate(calc(-50% + ${offset.x}px), calc(-50% + ${offset.y}px))`\n }\n\n /**\n * Update direction vector based on knob position\n */\n private updateDirection(): void {\n // Calculate offset from center\n const offset = this.currentPosition.clone().sub(this.startPosition)\n const distance = offset.length()\n\n // Apply dead zone\n if (distance < this.options.deadZone * this.joystickRadius) {\n this.direction.set(0, 0)\n this.magnitude = 0\n return\n }\n\n // Calculate normalized direction\n const normalizedDistance = Math.min(distance / this.joystickRadius, 1.0)\n this.direction = offset.normalize()\n this.magnitude = normalizedDistance\n }\n\n /**\n * Get the current input direction as a Vector3 (Y=0 for movement)\n */\n public getDirection(): THREE.Vector3 | null {\n if (!this.isActive || this.magnitude === 0) {\n return null\n }\n\n // Convert 2D joystick input to 3D movement direction\n // X maps to X (left/right), Y maps to Z (forward/back)\n // Screen: drag up = negative Y, drag down = positive Y\n // World: forward = positive Z, back = negative Z\n // So: worldZ = -screenY (drag up = move forward)\n return new THREE.Vector3(\n this.direction.x * this.magnitude,\n 0,\n -this.direction.y * this.magnitude\n )\n }\n\n /**\n * Get the current input magnitude (0-1)\n */\n public getMagnitude(): number {\n return this.magnitude\n }\n\n /**\n * Check if the joystick is currently active\n */\n public isActiveJoystick(): boolean {\n return this.isActive\n }\n\n /**\n * Toggle joystick visual visibility (joystick remains functional)\n */\n public setVisible(visible: boolean): void {\n this.options.visible = visible\n if (this.joystickContainer && this.isActive) {\n this.joystickContainer.style.display = visible ? \"block\" : \"none\"\n }\n }\n\n /**\n * Get current visibility state\n */\n public isVisible(): boolean {\n return this.options.visible\n }\n\n /**\n * Set joystick opacity (0-1)\n */\n public setOpacity(opacity: number): void {\n this.options.opacity = Math.max(0, Math.min(1, opacity))\n if (this.joystickBase) {\n this.joystickBase.style.opacity = `${this.options.opacity}`\n }\n if (this.joystickKnob) {\n this.joystickKnob.style.opacity = `${this.options.opacity}`\n }\n }\n\n /**\n * Get current opacity\n */\n public getOpacity(): number {\n return this.options.opacity\n }\n\n /**\n * Clean up UI resources\n */\n private cleanupUI(): void {\n if (this.joystickContainer && this.joystickContainer.parentNode) {\n this.joystickContainer.parentNode.removeChild(this.joystickContainer)\n }\n\n if (this.mobileHint && this.mobileHint.parentNode) {\n this.mobileHint.parentNode.removeChild(this.mobileHint)\n }\n\n this.joystickContainer = null\n this.joystickBase = null\n this.joystickKnob = null\n this.mobileHint = null\n }\n}\n","import * as THREE from \"three\"\nimport { Component } from \"@engine/core\"\nimport { RigidBodyComponentThree } from \"@systems/physics/RigidBodyComponentThree.ts\"\n\n/**\n * Three.js Movement controller component that handles physics-based movement and rotation\n * Uses Rapier physics with rotation locking for smooth, controlled movement\n * Can be used for player input, AI navigation, or any entity that needs to move\n */\nexport class MovementController extends Component {\n // Public configuration properties\n public maxMoveSpeed: number = 8.0\n public acceleration: number = 40\n public turnSpeed: number = 12 // How fast to rotate (radians per second)\n\n private rigidBodyComponent: RigidBodyComponentThree | null = null\n private targetRotationY: number = 0\n private currentRotationY: number = 0\n\n // Pre-allocated vector for velocity queries to avoid GC pressure\n private _currentVelocity = new THREE.Vector3()\n\n /**\n * Called when the component is created and attached to a GameObject\n */\n protected onCreate(): void {\n // Find the rigid body component on this GameObject\n this.findRigidBodyComponentThree()\n\n // Initialize rotation tracking\n this.currentRotationY = this.gameObject.rotation.y\n this.targetRotationY = this.currentRotationY\n }\n\n /**\n * Find the rigid body component on this GameObject\n */\n private findRigidBodyComponentThree(): void {\n this.rigidBodyComponent = this.gameObject.getComponent(RigidBodyComponentThree) || null\n if (!this.rigidBodyComponent) {\n console.warn(\"MovementController: No RigidBodyComponentThree found on GameObject\")\n }\n }\n\n /**\n * Set the rigid body component this controller should manage\n */\n public setRigidBodyComponentThree(rigidBodyComponent: RigidBodyComponentThree): void {\n this.rigidBodyComponent = rigidBodyComponent\n }\n\n /**\n * Move the entity based on input direction\n * @param inputDirection Normalized direction vector (or null for no movement)\n * @param deltaTime Time since last frame in seconds\n */\n public move(inputDirection: THREE.Vector3 | null, deltaTime: number): void {\n if (!this.rigidBodyComponent) return\n\n const targetVelocity = this.calculateTargetVelocity(inputDirection)\n const smoothedVelocity = this.smoothVelocity(targetVelocity, deltaTime)\n\n // Apply velocity to physics body (Rapier handles rotation locking)\n this.rigidBodyComponent.setVelocity(smoothedVelocity)\n\n // Handle Y rotation separately and smoothly\n this.updateRotation(inputDirection, deltaTime)\n }\n\n /**\n * Calculate target velocity based on input direction\n */\n private calculateTargetVelocity(inputDirection: THREE.Vector3 | null): THREE.Vector3 {\n const targetVelocity = new THREE.Vector3(0, 0, 0)\n\n if (inputDirection && inputDirection.length() > 0.01) {\n targetVelocity.x = inputDirection.x * this.maxMoveSpeed\n targetVelocity.z = inputDirection.z * this.maxMoveSpeed\n }\n\n // Y velocity is always 0 (grounded movement)\n targetVelocity.y = 0\n\n return targetVelocity\n }\n\n /**\n * Smooth velocity towards target using acceleration\n */\n private smoothVelocity(targetVelocity: THREE.Vector3, deltaTime: number): THREE.Vector3 {\n if (!this.rigidBodyComponent) return targetVelocity\n\n this.rigidBodyComponent.getVelocity(this._currentVelocity)\n const maxDelta = this.acceleration * deltaTime\n\n // Smooth X and Z velocities\n const smoothedVelocity = new THREE.Vector3()\n smoothedVelocity.x = this.moveTowards(this._currentVelocity.x, targetVelocity.x, maxDelta)\n smoothedVelocity.z = this.moveTowards(this._currentVelocity.z, targetVelocity.z, maxDelta)\n smoothedVelocity.y = 0 // Keep grounded\n\n return smoothedVelocity\n }\n\n /**\n * Update rotation smoothly towards movement direction using quaternion slerp\n */\n private updateRotation(inputDirection: THREE.Vector3 | null, deltaTime: number): void {\n // Always clear angular velocity first to prevent unwanted spinning\n if (this.rigidBodyComponent) {\n const rigidBody = this.rigidBodyComponent.getRigidBody()\n if (rigidBody) {\n // Stop any existing angular rotation to prevent spinning\n this.rigidBodyComponent.setAngularVelocity(new THREE.Vector3(0, 0, 0))\n }\n }\n\n // If no input direction, don't update rotation - just ensure rotation is stopped\n if (!inputDirection || inputDirection.length() < 0.01) {\n // We've already cleared angular velocity above, so just return\n return\n }\n\n // Calculate target rotation based on movement direction\n const targetRotationY = Math.atan2(inputDirection.x, inputDirection.z)\n\n // Create target quaternion\n const targetQuaternion = new THREE.Quaternion()\n targetQuaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), targetRotationY)\n\n // Get current quaternion\n const currentQuaternion = new THREE.Quaternion()\n currentQuaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.currentRotationY)\n\n // Calculate slerp factor based on turn speed\n const slerpFactor = Math.min(1.0, this.turnSpeed * deltaTime)\n\n // Slerp between current and target rotation\n const resultQuaternion = new THREE.Quaternion()\n resultQuaternion.slerpQuaternions(currentQuaternion, targetQuaternion, slerpFactor)\n\n // Update current rotation Y for tracking\n const euler = new THREE.Euler()\n euler.setFromQuaternion(resultQuaternion, \"YXZ\")\n this.currentRotationY = euler.y\n\n // Apply rotation through physics body if available, otherwise directly to GameObject\n if (this.rigidBodyComponent) {\n const rigidBody = this.rigidBodyComponent.getRigidBody()\n if (rigidBody) {\n // Apply to physics body (this will sync back to GameObject automatically)\n const rapierQuat = {\n x: resultQuaternion.x,\n y: resultQuaternion.y,\n z: resultQuaternion.z,\n w: resultQuaternion.w,\n }\n rigidBody.setRotation(rapierQuat, true)\n } else {\n // Fallback: apply rotation directly to GameObject\n this.gameObject.rotation.y = this.currentRotationY\n }\n } else {\n // Fallback: apply rotation directly to GameObject\n this.gameObject.rotation.y = this.currentRotationY\n }\n }\n\n /**\n * Utility function to move a value towards a target at a given rate\n */\n private moveTowards(current: number, target: number, maxDelta: number): number {\n const delta = target - current\n if (Math.abs(delta) <= maxDelta) {\n return target\n }\n return current + Math.sign(delta) * maxDelta\n }\n\n /**\n * Get current movement state for debugging\n */\n public getMovementState(): any {\n return {\n maxMoveSpeed: this.maxMoveSpeed,\n acceleration: this.acceleration,\n turnSpeed: this.turnSpeed,\n currentRotationY: this.currentRotationY,\n targetRotationY: this.targetRotationY,\n hasRigidBody: !!this.rigidBodyComponent,\n }\n }\n\n /**\n * Clean up resources when the component is removed\n */\n protected onCleanup(): void {\n this.rigidBodyComponent = null\n }\n}\n","import * as THREE from \"three\"\nimport { Component } from \"@engine/core\"\n\nexport interface PlayerControls {\n forward: string\n backward: string\n left: string\n right: string\n run: string\n interact: string\n}\n\n/**\n * Three.js Player Controller Component\n * Handles WASD movement, mouse look, and basic interactions\n */\nexport class PlayerControllerThree extends Component {\n // Movement parameters\n public moveSpeed: number = 5.0\n public runSpeed: number = 8.0\n public rotationSpeed: number = 2.0\n\n // Controls configuration\n private controls: PlayerControls = {\n forward: \"KeyW\",\n backward: \"KeyS\",\n left: \"KeyA\",\n right: \"KeyD\",\n run: \"ShiftLeft\",\n interact: \"KeyE\",\n }\n\n // Input state\n private keys: Set<string> = new Set()\n private mouseX: number = 0\n private mouseY: number = 0\n private mouseSensitivity: number = 0.002\n private isPointerLocked: boolean = false\n\n // Camera reference\n private camera: THREE.PerspectiveCamera\n private cameraHeight: number = 1.7 // Eye level height\n\n // Movement state\n private velocity: THREE.Vector3 = new THREE.Vector3()\n private direction: THREE.Vector3 = new THREE.Vector3()\n\n constructor(camera: THREE.PerspectiveCamera) {\n super()\n this.camera = camera\n }\n\n protected onCreate(): void {\n this.setupEventListeners()\n this.setupPointerLock()\n\n // Position camera at player eye level\n this.updateCameraPosition()\n\n console.log(\"🎮 Player controller initialized\")\n console.log(\"📋 Controls: WASD to move, Shift to run, E to interact, Click to look around\")\n }\n\n /**\n * Set up keyboard and mouse event listeners\n */\n private setupEventListeners(): void {\n // Keyboard events\n document.addEventListener(\"keydown\", this.onKeyDown.bind(this))\n document.addEventListener(\"keyup\", this.onKeyUp.bind(this))\n\n // Mouse events\n document.addEventListener(\"mousemove\", this.onMouseMove.bind(this))\n document.addEventListener(\"click\", this.onClick.bind(this))\n\n // Pointer lock events\n document.addEventListener(\"pointerlockchange\", this.onPointerLockChange.bind(this))\n }\n\n /**\n * Set up pointer lock for mouse look\n */\n private setupPointerLock(): void {\n const canvas = document.getElementById(\"renderCanvas\") as HTMLCanvasElement\n if (canvas) {\n canvas.addEventListener(\"click\", () => {\n if (!this.isPointerLocked) {\n canvas.requestPointerLock()\n }\n })\n }\n }\n\n /**\n * Handle key down events\n */\n private onKeyDown(event: KeyboardEvent): void {\n this.keys.add(event.code)\n\n // Handle special keys\n if (event.code === this.controls.interact) {\n this.onInteract()\n }\n }\n\n /**\n * Handle key up events\n */\n private onKeyUp(event: KeyboardEvent): void {\n this.keys.delete(event.code)\n }\n\n /**\n * Handle mouse movement for looking around\n */\n private onMouseMove(event: MouseEvent): void {\n if (!this.isPointerLocked) return\n\n this.mouseX += event.movementX * this.mouseSensitivity\n this.mouseY += event.movementY * this.mouseSensitivity\n\n // Clamp vertical rotation\n this.mouseY = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, this.mouseY))\n\n // Apply rotation to camera\n this.camera.rotation.order = \"YXZ\"\n this.camera.rotation.y = -this.mouseX\n this.camera.rotation.x = -this.mouseY\n }\n\n /**\n * Handle canvas clicks for pointer lock\n */\n private onClick(event: MouseEvent): void {\n const canvas = document.getElementById(\"renderCanvas\") as HTMLCanvasElement\n if (event.target === canvas && !this.isPointerLocked) {\n canvas.requestPointerLock()\n }\n }\n\n /**\n * Handle pointer lock state changes\n */\n private onPointerLockChange(): void {\n this.isPointerLocked = document.pointerLockElement !== null\n\n if (this.isPointerLocked) {\n console.log(\"🖱️ Mouse locked - look around!\")\n } else {\n console.log(\"🖱️ Mouse unlocked - click canvas to lock again\")\n }\n }\n\n /**\n * Handle interaction key press\n */\n private onInteract(): void {\n console.log(\"🤝 Player trying to interact...\")\n // TODO: Implement interaction system\n }\n\n /**\n * Update method - called every frame\n */\n public update(deltaTime: number): void {\n this.updateMovement(deltaTime)\n this.updateCameraPosition()\n }\n\n /**\n * Update player movement based on input\n */\n private updateMovement(deltaTime: number): void {\n // Reset direction\n this.direction.set(0, 0, 0)\n\n // Get current movement speed\n const isRunning = this.keys.has(this.controls.run)\n const currentSpeed = isRunning ? this.runSpeed : this.moveSpeed\n\n // Calculate movement direction based on camera orientation\n const forward = new THREE.Vector3()\n const right = new THREE.Vector3()\n\n // Get camera's forward direction (ignoring Y rotation for ground movement)\n this.camera.getWorldDirection(forward)\n forward.y = 0\n forward.normalize()\n\n // Get camera's right direction\n right.crossVectors(forward, this.camera.up).normalize()\n\n // Apply movement inputs\n if (this.keys.has(this.controls.forward)) {\n this.direction.add(forward)\n }\n if (this.keys.has(this.controls.backward)) {\n this.direction.sub(forward)\n }\n if (this.keys.has(this.controls.right)) {\n this.direction.add(right)\n }\n if (this.keys.has(this.controls.left)) {\n this.direction.sub(right)\n }\n\n // Normalize direction to prevent faster diagonal movement\n if (this.direction.length() > 0) {\n this.direction.normalize()\n\n // Apply movement to player position\n this.velocity.copy(this.direction).multiplyScalar(currentSpeed * deltaTime)\n this.gameObject.position.add(this.velocity)\n }\n\n // Keep player on ground level (no Y drift)\n this.gameObject.position.y = 0\n }\n\n /**\n * Update camera position to follow player\n */\n private updateCameraPosition(): void {\n // Position camera at player position + eye height\n this.camera.position.copy(this.gameObject.position)\n this.camera.position.y += this.cameraHeight\n }\n\n /**\n * Get current movement state for debugging\n */\n public getMovementState(): {\n position: THREE.Vector3\n isMoving: boolean\n isRunning: boolean\n lookDirection: THREE.Vector3\n } {\n const lookDirection = new THREE.Vector3()\n this.camera.getWorldDirection(lookDirection)\n\n return {\n position: this.gameObject.position.clone(),\n isMoving: this.direction.length() > 0,\n isRunning: this.keys.has(this.controls.run),\n lookDirection: lookDirection,\n }\n }\n\n /**\n * Set player position\n */\n public setPosition(position: THREE.Vector3): void {\n this.gameObject.position.copy(position)\n this.updateCameraPosition()\n }\n\n /**\n * Clean up event listeners\n */\n protected onCleanup(): void {\n document.removeEventListener(\"keydown\", this.onKeyDown.bind(this))\n document.removeEventListener(\"keyup\", this.onKeyUp.bind(this))\n document.removeEventListener(\"mousemove\", this.onMouseMove.bind(this))\n document.removeEventListener(\"click\", this.onClick.bind(this))\n document.removeEventListener(\"pointerlockchange\", this.onPointerLockChange.bind(this))\n\n // Exit pointer lock if active\n if (this.isPointerLocked) {\n document.exitPointerLock()\n }\n\n console.log(\"🎮 Player controller cleaned up\")\n }\n}\n","import * as THREE from \"three\"\nimport { engineThrow } from \"@engine/core/EngineError\"\nimport { Component } from \"@engine/core\"\nimport { AssetManager } from \"@engine/assets\"\n\n/**\n * Three.js Skeletal renderer for animated FBX models\n * - Uses AssetManager for preloading coordination\n * - Uses SkeletonCache for proper skeletal cloning\n * - Specifically designed for animated characters\n * - Always cloned, always has shadows, never static\n */\nexport class SkeletalRenderer extends Component {\n private _group: THREE.Group | null = null\n private _assetPath: string\n private _material: THREE.Material | null\n private _skeletalModel: THREE.Object3D | null = null\n\n constructor(assetPath: string, material?: THREE.Material) {\n super()\n this._assetPath = assetPath\n this._material = material || null\n }\n\n protected onCreate(): void {\n this.createSkeletalMesh()\n }\n\n private createSkeletalMesh(): void {\n if (!this._assetPath) {\n engineThrow(\"SkeletalRenderer\", \"No asset path specified.\", \"Pass the FBX asset path to the constructor: new SkeletalRenderer('path/to/character.fbx')\")\n }\n\n // Create wrapper group for this instance\n this._group = new THREE.Group()\n this._group.name = `skeletal_${this._assetPath.split(\"/\").pop()}`\n this.gameObject.add(this._group)\n\n // Get properly cloned skeletal model from AssetManager's skeleton cache\n this._skeletalModel = AssetManager.getSkeletalClone(this._assetPath)\n\n if (!this._skeletalModel) {\n engineThrow(\"SkeletalRenderer\", `No skeletal model found for \"${this._assetPath}\".`, \"Make sure to preload with AssetManager.preloadSkeletalModel() before creating the SkeletalRenderer. The asset must be loaded and cached first.\")\n }\n\n // Apply custom material if provided\n if (this._material) {\n this._skeletalModel.traverse((child: THREE.Object3D) => {\n if (child instanceof THREE.Mesh) {\n child.material = this._material!\n child.frustumCulled = false\n }\n })\n } else {\n this._skeletalModel.traverse((child: THREE.Object3D) => {\n if (child instanceof THREE.Mesh) {\n child.frustumCulled = false\n }\n })\n }\n\n this._group.add(this._skeletalModel)\n\n // Always apply shadows for characters\n this.applyShadowsToGroup(this._group)\n }\n\n private applyShadowsToGroup(group: THREE.Group): void {\n group.traverse((child: THREE.Object3D) => {\n if (child instanceof THREE.Mesh) {\n child.castShadow = true\n child.receiveShadow = true\n }\n })\n }\n\n // ========== Public API ==========\n\n /**\n * Get the wrapper group (attached to GameObject)\n */\n public getGroup(): THREE.Group | null {\n return this._group\n }\n\n /**\n * Get the skeletal model (for animation setup)\n */\n public getSkeletalModel(): THREE.Object3D | null {\n return this._skeletalModel\n }\n\n /**\n * Get the asset path being rendered\n */\n public getAssetPath(): string {\n return this._assetPath\n }\n\n /**\n * Enable or disable visibility\n */\n public setVisible(visible: boolean): void {\n if (this._group) {\n this._group.visible = visible\n }\n }\n\n /**\n * Get visibility state\n */\n public isVisible(): boolean {\n return this._group?.visible ?? false\n }\n\n // ========== Component Lifecycle ==========\n\n protected onCleanup(): void {\n if (this._group) {\n this.gameObject.remove(this._group)\n }\n // Note: Don't track instance destruction since AssetManager doesn't track SkeletonCache instances\n }\n\n public onEnabled(): void {\n this.setVisible(true)\n }\n\n public onDisabled(): void {\n this.setVisible(false)\n }\n}\n","import { Component } from \"@engine/core\"\nimport { engineError } from \"@engine/core/EngineError\"\nimport { InstancedMeshManager } from \"./InstancedMeshManager\"\n\n/**\n * Options for InstancedRenderer\n */\nexport interface InstancedRendererOptions {\n /** If true (default), matrix updates every frame. If false, only updates when markDirty() is called. */\n isDynamic?: boolean\n /** Whether instances cast shadows (default: false). Only used if batch is auto-created. */\n castShadow?: boolean\n /** Whether instances receive shadows (default: false). Only used if batch is auto-created. */\n receiveShadow?: boolean\n /** Initial batch capacity (default: 16, grows automatically). Only used if batch is auto-created. */\n initialCapacity?: number\n}\n\n/**\n * Component for rendering objects using GPU instancing.\n *\n * Transform is controlled by the GameObject - the manager syncs matrices every frame.\n * If no batch exists for the key, one is created automatically from the GameObject's mesh.\n *\n * Performance modes:\n * - Dynamic (default): Matrix updates every frame. Use for moving objects.\n * - Static: Matrix only updates when markDirty() is called. Use for stationary objects.\n *\n * Usage:\n * ```typescript\n * // Simple - batch auto-creates from GameObject's mesh\n * gameObject.addComponent(new InstancedRenderer(\"burger\"))\n *\n * // With options\n * gameObject.addComponent(new InstancedRenderer(\"burger\", {\n * isDynamic: false,\n * castShadow: true\n * }))\n *\n * // For stationary objects, switch to static mode\n * renderer.setDynamic(false)\n *\n * // When a static object moves, mark it dirty\n * renderer.markDirty()\n * ```\n *\n * Key differences from mesh-based renderers (ObjRenderer, etc.):\n * - No getMesh() - instances don't have their own THREE.Object3D\n * - No getBounds() - can query the batch geometry if needed\n * - All instances share the same geometry and material\n */\nexport class InstancedRenderer extends Component {\n private readonly batchKey: string\n private readonly options: InstancedRendererOptions\n private instanceId: string | null = null\n\n /**\n * Create an InstancedRenderer\n * @param batchKey The batch key to register with. If no batch exists, one is auto-created.\n * @param options Configuration options (or just pass `true`/`false` for isDynamic)\n */\n constructor(batchKey: string, options: InstancedRendererOptions | boolean = {}) {\n super()\n this.batchKey = batchKey\n // Support legacy boolean parameter for isDynamic\n this.options = typeof options === \"boolean\" ? { isDynamic: options } : options\n }\n\n /**\n * Register with the batch when the component is created.\n * If no batch exists, one will be created automatically from this GameObject's mesh.\n */\n protected onCreate(): void {\n const manager = InstancedMeshManager.getInstance()\n\n if (!manager.isReady()) {\n engineError(\"InstancedRenderer\", \"InstancedMeshManager not initialized.\", \"Call InstancedMeshManager.getInstance().initialize(scene) during game setup. If you're using VenusGame, this happens automatically in create().\")\n return\n }\n\n // addInstance now auto-creates batch from GameObject if needed\n this.instanceId = manager.addInstance(this.batchKey, this.gameObject, {\n isDynamic: this.options.isDynamic ?? true,\n castShadow: this.options.castShadow ?? false,\n receiveShadow: this.options.receiveShadow ?? false,\n initialCapacity: this.options.initialCapacity,\n })\n\n if (!this.instanceId) {\n engineError(\"InstancedRenderer\", `Failed to add instance to batch \"${this.batchKey}\".`, \"The batch could not be created. Make sure the GameObject has a loaded mesh, or pre-register the batch with StowKitSystem.registerMeshForInstancing().\")\n }\n }\n\n /**\n * Set the visibility of this instance\n */\n public setVisible(visible: boolean): void {\n if (this.instanceId) {\n InstancedMeshManager.getInstance().setInstanceVisible(this.batchKey, this.instanceId, visible)\n }\n }\n\n /**\n * Get the visibility of this instance\n */\n public getVisible(): boolean {\n if (this.instanceId) {\n return InstancedMeshManager.getInstance().getInstanceVisible(this.batchKey, this.instanceId)\n }\n return false\n }\n\n /**\n * Show the instance (convenience method)\n */\n public show(): void {\n this.setVisible(true)\n }\n\n /**\n * Hide the instance (convenience method)\n */\n public hide(): void {\n this.setVisible(false)\n }\n\n /**\n * Get the batch key this renderer is registered with\n */\n public getBatchKey(): string {\n return this.batchKey\n }\n\n /**\n * Check if this renderer is successfully registered with a batch\n */\n public isRegistered(): boolean {\n return this.instanceId !== null\n }\n\n /**\n * Get the instance ID (for debugging)\n */\n public getInstanceId(): string | null {\n return this.instanceId\n }\n\n /**\n * Mark this instance as needing a matrix update.\n * Only relevant for static instances - dynamic instances update every frame anyway.\n * Call this when the transform of a static instance changes.\n */\n public markDirty(): void {\n if (this.instanceId) {\n InstancedMeshManager.getInstance().markInstanceDirty(this.batchKey, this.instanceId)\n }\n }\n\n /**\n * Set whether this instance is dynamic (updates every frame) or static (only when marked dirty).\n * Use this to optimize performance when items transition between moving and stationary states.\n * @param isDynamic If true, updates every frame. If false, only updates when markDirty() is called.\n */\n public setDynamic(isDynamic: boolean): void {\n if (this.instanceId) {\n InstancedMeshManager.getInstance().setInstanceDynamic(\n this.batchKey,\n this.instanceId,\n isDynamic\n )\n }\n }\n\n /**\n * Called when the GameObject becomes enabled\n */\n public onEnabled(): void {\n this.setVisible(true)\n }\n\n /**\n * Called when the GameObject becomes disabled\n */\n public onDisabled(): void {\n this.setVisible(false)\n }\n\n /**\n * Unregister from the batch when the component is cleaned up\n */\n protected onCleanup(): void {\n if (this.instanceId) {\n InstancedMeshManager.getInstance().removeInstance(this.batchKey, this.instanceId)\n this.instanceId = null\n }\n }\n}\n","import * as THREE from \"three\"\nimport { engineError, engineWarn } from \"@engine/core/EngineError\"\nimport { GameObject, Component } from \"@engine/core\"\nimport {\n PrefabComponent,\n PrefabInstance,\n ComponentRegistry,\n type ComponentJSON,\n type PrefabNode,\n} from \"@systems/prefabs\"\nimport { StowKitSystem } from \"@systems/stowkit\"\nimport { RenderBatcher } from \"./RenderBatcher\"\n\ninterface NodeOverrideJSON {\n position?: [number, number, number]\n rotation?: [number, number, number]\n scale?: [number, number, number]\n components?: ComponentJSON[]\n}\n\ninterface StowMeshJSON extends ComponentJSON {\n type: \"stow_mesh\"\n mesh: {\n pack: string\n assetId: string\n }\n /** When set, only the named group's meshes are rendered (sub-mesh mode). */\n subMesh?: string\n castShadow?: boolean\n receiveShadow?: boolean\n nodeOverrides?: Record<string, NodeOverrideJSON>\n}\n\n/**\n * MeshRenderer - Component for rendering a mesh from StowKitSystem.\n *\n * This is a non-instanced renderer - each instance is a separate draw call.\n * Use InstancedRenderer for many instances of the same mesh (better performance).\n *\n * When a mesh has named child groups (from preserveHierarchy), MeshRenderer\n * promotes them to GameObjects so they can have components and be accessed\n * via getMeshChild(). Transform overrides and components from nodeOverrides\n * are applied automatically.\n *\n * Usage:\n * ```typescript\n * const env = new GameObject(\"Environment\")\n * env.addComponent(new MeshRenderer(\"restaurant_display_common\"))\n * ```\n */\n@PrefabComponent(\"stow_mesh\")\nexport class MeshRenderer extends Component {\n static fromPrefabJSON(json: StowMeshJSON, _node: PrefabNode): MeshRenderer {\n if (!json.mesh?.assetId) {\n engineError(\"MeshRenderer\", \"stow_mesh component missing mesh.assetId.\", \"Every stow_mesh component needs a mesh.assetId property pointing to the mesh name in a StowKit pack. Check your prefab data.\")\n return new MeshRenderer(\"unknown\")\n }\n // Priority: JSON property > context options > default (true)\n const options = PrefabInstance.currentOptions\n const castShadow = json.castShadow ?? options?.castShadow ?? true\n const receiveShadow = json.receiveShadow ?? options?.receiveShadow ?? true\n const renderer = new MeshRenderer(json.mesh.assetId, castShadow, receiveShadow)\n renderer.subMeshName = json.subMesh ?? null\n if (json.nodeOverrides) {\n renderer.nodeOverrides = json.nodeOverrides\n }\n return renderer\n }\n\n private mesh: THREE.Group | null = null\n private readonly meshName: string\n private readonly castShadow: boolean\n private readonly receiveShadow: boolean\n private _isStatic: boolean\n private isMeshLoaded: boolean = false\n private materialOverride: THREE.Material | null = null\n private loadedCallbacks: (() => void)[] | null = null\n private nodeOverrides: Record<string, NodeOverrideJSON> | null = null\n private meshChildMap: Map<string, GameObject> | null = null\n /** When set, only render the named group from the asset (sub-mesh mode). */\n private subMeshName: string | null = null\n /** Deferred batch registration — waits one frame so other components can modify materials */\n private _pendingBatchRegister: boolean = false\n\n /**\n * @param meshName The name of the mesh in the StowKit pack\n * @param castShadow Whether meshes should cast shadows (default: true)\n * @param receiveShadow Whether meshes should receive shadows (default: true)\n * @param isStatic Whether this mesh is static (default: false). Static meshes have matrixAutoUpdate disabled for better performance.\n * @param materialOverride Optional material to use instead of the default StowKit material\n */\n constructor(\n meshName: string,\n castShadow: boolean = true,\n receiveShadow: boolean = true,\n isStatic: boolean = false,\n materialOverride: THREE.Material | null = null\n ) {\n super()\n this.meshName = meshName\n this.castShadow = castShadow\n this.receiveShadow = receiveShadow\n this._isStatic = isStatic\n this.materialOverride = materialOverride\n }\n\n protected onCreate(): void {\n const stowkit = StowKitSystem.getInstance()\n\n // Check if mesh is already cached\n const cachedMesh = stowkit.getMeshSync(this.meshName)\n if (cachedMesh) {\n this.addMesh(cachedMesh)\n } else {\n // Start async load - will add mesh in update when ready\n stowkit.getMesh(this.meshName)\n }\n }\n\n public update(_deltaTime: number): void {\n // Deferred batch registration (one frame after mesh loads)\n if (this._pendingBatchRegister) {\n this._pendingBatchRegister = false\n if (!this.materialOverride && !this.gameObject.hasAutoInstancingBlocker()) {\n RenderBatcher.getInstance().register(this)\n }\n }\n\n if (this.isMeshLoaded) return\n\n const stowkit = StowKitSystem.getInstance()\n const cachedMesh = stowkit.getMeshSync(this.meshName)\n if (cachedMesh) {\n this.addMesh(cachedMesh)\n }\n }\n\n private addMesh(original: THREE.Group): void {\n this.isMeshLoaded = true\n\n // Clone mesh with material conversion\n const cloned = StowKitSystem.getInstance().cloneMeshSync(\n original,\n this.castShadow,\n this.receiveShadow\n )\n\n if (this.subMeshName) {\n // Sub-mesh mode: find the named group and attach only its meshes\n const found = MeshRenderer.findGroupByName(cloned, this.subMeshName)\n if (found) {\n // Create a container group for the sub-mesh content\n this.mesh = new THREE.Group()\n this.mesh.name = this.subMeshName\n // Take direct mesh children from the found group\n // (only direct children — nested groups belong to other sub-mesh components)\n for (const child of [...found.children]) {\n if ((child as any).isMesh) {\n found.remove(child)\n this.mesh.add(child)\n }\n }\n } else {\n engineWarn(\"MeshRenderer\", `Sub-mesh group \"${this.subMeshName}\" not found in \"${this.meshName}\".`, \"The mesh may not have a group with this name. Sub-mesh names come from your 3D modeling tool's hierarchy. Falling back to full mesh.\")\n this.mesh = cloned\n }\n // Dispose unused parts of the clone\n if (this.mesh !== cloned) {\n MeshRenderer.disposeUnused(cloned, this.mesh)\n }\n } else {\n // Full mesh mode (backwards compatible)\n this.mesh = cloned\n }\n\n // Ensure shadow settings are applied to final mesh hierarchy\n // (defensive: cloneMeshSync sets these, but sub-mesh extraction creates a new Group)\n this.mesh.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n child.castShadow = this.castShadow\n child.receiveShadow = this.receiveShadow\n }\n })\n\n // Apply material override if set\n if (this.materialOverride) {\n this.applyMaterialOverride()\n }\n\n this.gameObject.add(this.mesh)\n\n // Promote named child groups to GameObjects for component support\n // (only relevant for full-mesh mode; sub-mesh mode has no child groups)\n if (!this.subMeshName) {\n this.promoteMeshChildren(this.mesh)\n }\n\n // For static meshes, disable matrix auto-update to save CPU\n if (this._isStatic) {\n this.setStatic(true)\n }\n\n // Fire onLoaded callbacks\n if (this.loadedCallbacks) {\n for (const cb of this.loadedCallbacks) cb()\n this.loadedCallbacks = null\n }\n\n // Defer batch registration to next frame so other components (e.g. CharacterShader)\n // have time to modify materials before the batch template is captured\n if (!this.materialOverride) {\n this._pendingBatchRegister = true\n }\n }\n\n /** Depth-first search for a named Group in a mesh hierarchy. */\n private static findGroupByName(root: THREE.Object3D, name: string): THREE.Group | null {\n for (const child of root.children) {\n if (child.type === \"Group\" && child.name === name) return child as THREE.Group\n const found = MeshRenderer.findGroupByName(child, name)\n if (found) return found\n }\n return null\n }\n\n /** Dispose geometries/materials from a mesh tree, preserving those used by `keep`. */\n private static disposeUnused(root: THREE.Object3D, keep: THREE.Object3D): void {\n const keepSet = new Set<THREE.BufferGeometry | THREE.Material>()\n keep.traverse((child) => {\n if (child instanceof THREE.Mesh) {\n if (child.geometry) keepSet.add(child.geometry)\n if (child.material) {\n const mats = Array.isArray(child.material) ? child.material : [child.material]\n for (const m of mats) keepSet.add(m)\n }\n }\n })\n root.traverse((child) => {\n if (child instanceof THREE.Mesh) {\n if (child.geometry && !keepSet.has(child.geometry)) child.geometry.dispose()\n if (child.material) {\n const mats = Array.isArray(child.material) ? child.material : [child.material]\n for (const m of mats) {\n if (!keepSet.has(m)) {\n if ((m as any).map) (m as any).map.dispose()\n m.dispose()\n }\n }\n }\n }\n })\n }\n\n /**\n * Walk the loaded mesh hierarchy and replace named child Groups with\n * GameObjects so they can receive components. Applies nodeOverrides\n * (transform + components) from the prefab data.\n */\n private promoteMeshChildren(group: THREE.Object3D): void {\n for (const child of [...group.children]) {\n if (child.type !== \"Group\" || !child.name) continue\n\n // Create a GameObject to replace this group\n const childGO = new GameObject(child.name)\n childGO.position.copy(child.position)\n childGO.quaternion.copy(child.quaternion)\n childGO.scale.copy(child.scale)\n\n // Apply nodeOverride transforms if present\n const override = this.nodeOverrides?.[child.name]\n if (override?.position) childGO.position.fromArray(override.position)\n if (override?.rotation) {\n childGO.rotation.set(\n override.rotation[0],\n override.rotation[1],\n override.rotation[2]\n )\n }\n if (override?.scale) childGO.scale.fromArray(override.scale)\n\n // Move all children from the group into the GameObject\n for (const grandchild of [...child.children]) {\n child.remove(grandchild)\n childGO.add(grandchild)\n }\n\n // Replace group with GameObject in parent\n group.remove(child)\n group.add(childGO)\n\n // Create override components\n if (override?.components) {\n for (const compJSON of override.components) {\n const factory = ComponentRegistry.get(compJSON.type)\n if (factory) {\n try {\n const comp = factory.fromPrefabJSON(compJSON, null as unknown as PrefabNode)\n if (comp) childGO.addComponent(comp)\n } catch (e) {\n engineError(\"MeshRenderer\", `Failed to create component \"${compJSON.type}\" on mesh child \"${child.name}\".`, \"Check the component's fromPrefabJSON() method and the nodeOverrides data in your prefab.\", e)\n }\n }\n }\n }\n\n // Index for getMeshChild()\n if (!this.meshChildMap) this.meshChildMap = new Map()\n this.meshChildMap.set(child.name, childGO)\n\n // Recurse for nested groups\n this.promoteMeshChildren(childGO)\n }\n }\n\n /**\n * Get a named child group from the loaded mesh as a GameObject.\n * Returns null if the mesh hasn't loaded yet or if no child with that name exists.\n *\n * ```typescript\n * renderer.onLoaded(() => {\n * const key = renderer.getMeshChild(\"Key\")\n * key?.addComponent(new Spinner(\"z\"))\n * })\n * ```\n */\n public getMeshChild(name: string): GameObject | null {\n return this.meshChildMap?.get(name) ?? null\n }\n\n /**\n * Get all named child groups from the loaded mesh.\n * Returns null if the mesh hasn't loaded yet.\n */\n public getMeshChildren(): ReadonlyMap<string, GameObject> | null {\n return this.meshChildMap ?? null\n }\n\n /**\n * Apply the material override to all meshes\n */\n private applyMaterialOverride(): void {\n if (!this.mesh || !this.materialOverride) return\n\n this.mesh.traverse((child) => {\n if (child instanceof THREE.Mesh) {\n child.material = this.materialOverride!\n }\n })\n }\n\n /**\n * Set a material override for all meshes in this renderer.\n * Call this after the mesh is loaded, or pass it in the constructor.\n */\n public setMaterial(material: THREE.Material): void {\n this.materialOverride = material\n if (this.mesh) {\n this.applyMaterialOverride()\n }\n // Material no longer matches batch template — unregister\n RenderBatcher.getInstance().unregister(this)\n }\n\n /**\n * Check if this mesh is currently static (no automatic matrix updates)\n */\n public get isStatic(): boolean {\n return this._isStatic\n }\n\n /**\n * Set whether this mesh is static (no automatic matrix updates).\n * Static meshes save CPU by not recalculating transforms every frame.\n * Call forceMatrixUpdate() after moving a static mesh.\n */\n public setStatic(isStatic: boolean): void {\n this._isStatic = isStatic\n if (!this.mesh) return\n\n if (isStatic) {\n // Update matrices one final time before freezing\n this.forceMatrixUpdate()\n\n // Disable auto-update on mesh and all descendants\n this.mesh.matrixAutoUpdate = false\n this.mesh.traverse((child) => {\n child.matrixAutoUpdate = false\n })\n this.gameObject.matrixAutoUpdate = false\n } else {\n // Enable auto-update\n this.mesh.matrixAutoUpdate = true\n this.mesh.traverse((child) => {\n child.matrixAutoUpdate = true\n })\n this.gameObject.matrixAutoUpdate = true\n }\n }\n\n /**\n * Force a one-time matrix update. Call this after moving a static mesh.\n * Does not change the static/dynamic state.\n */\n public forceMatrixUpdate(): void {\n if (this.mesh) {\n this.mesh.updateMatrix()\n this.mesh.updateMatrixWorld(true)\n }\n this.gameObject.updateMatrix()\n this.gameObject.updateMatrixWorld(true)\n }\n\n /**\n * Get the mesh group (null if not yet loaded)\n */\n public getMesh(): THREE.Group | null {\n return this.mesh\n }\n\n /**\n * Get the name of the mesh this component is managing\n */\n public getMeshName(): string {\n return this.meshName\n }\n\n /**\n * Get the sub-mesh name (null if rendering the full mesh)\n */\n public getSubMeshName(): string | null {\n return this.subMeshName\n }\n\n /**\n * Get shadow settings\n */\n public getCastShadow(): boolean {\n return this.castShadow\n }\n\n public getReceiveShadow(): boolean {\n return this.receiveShadow\n }\n\n /**\n * Check if the mesh was successfully loaded\n */\n public isLoaded(): boolean {\n return this.mesh !== null\n }\n\n /**\n * Register a callback that fires when the mesh finishes loading,\n * or immediately if already loaded.\n */\n public onLoaded(callback: () => void): void {\n if (this.mesh) {\n callback()\n return\n }\n if (!this.loadedCallbacks) this.loadedCallbacks = []\n this.loadedCallbacks.push(callback)\n }\n\n /**\n * Get the first material from the loaded mesh, or null if not yet loaded.\n */\n public getMaterial(): THREE.Material | null {\n if (!this.mesh) return null\n let result: THREE.Material | null = null\n this.mesh.traverse((child) => {\n if (!result && child instanceof THREE.Mesh && child.material) {\n result = Array.isArray(child.material) ? child.material[0] : child.material\n }\n })\n return result\n }\n\n /**\n * Set the visibility of the mesh\n */\n public setVisible(visible: boolean): void {\n if (this.mesh) {\n this.mesh.visible = visible\n }\n }\n\n /**\n * Get bounds of the mesh (useful for physics)\n */\n public getBounds(): THREE.Vector3 | null {\n if (!this.mesh) {\n return null\n }\n return StowKitSystem.getInstance().getBounds(this.mesh)\n }\n\n /**\n * Cleanup - remove mesh from scene and dispose of resources\n */\n protected onCleanup(): void {\n this._pendingBatchRegister = false\n // Unregister from RenderBatcher before disposing mesh\n RenderBatcher.getInstance().unregister(this)\n\n if (this.mesh) {\n // Remove from GameObject/scene\n this.gameObject.remove(this.mesh)\n\n // Traverse and dispose of geometries and materials\n this.mesh.traverse((child) => {\n if (child instanceof THREE.Mesh) {\n if (child.geometry) {\n child.geometry.dispose()\n }\n\n if (child.material) {\n if (Array.isArray(child.material)) {\n child.material.forEach((m) => {\n if (m.map) m.map.dispose()\n m.dispose()\n })\n } else {\n if (child.material.map) child.material.map.dispose()\n child.material.dispose()\n }\n }\n }\n })\n\n this.mesh = null\n }\n\n this.meshChildMap = null\n }\n}\n"],"mappings":"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","names":["THREE","THREE","THREE","THREE","THREE"]}
1
+ {"version":3,"sources":["../src/engine/interaction/InteractionZone.ts","../src/engine/mobile/VirtualJoystickThree.ts","../src/engine/movement/MovementController.ts","../src/engine/player/PlayerControllerThree.ts","../src/engine/render/SkeletalRenderer.ts","../src/engine/render/InstancedRenderer.ts","../src/engine/render/MeshRenderer.ts","../src/engine/render/StaticBatcher.ts"],"sourcesContent":["import * as THREE from \"three\"\nimport { Component, GameObject } from \"@engine/core\"\nimport {\n RigidBodyComponentThree,\n RigidBodyType,\n ColliderShape,\n} from \"@systems/physics/RigidBodyComponentThree.ts\"\n\n/**\n * Simple interaction zone using Rapier physics triggers\n * Replaces the complex raycasting system with simple physics triggers\n */\nexport class InteractionZone extends Component {\n public readonly id: string\n\n private active: boolean = true\n private onEnterCallback?: (other: GameObject) => void\n private onExitCallback?: (other: GameObject) => void\n private entitiesInZone: Set<GameObject> = new Set()\n\n private rigidBody: RigidBodyComponentThree | null = null\n private visualMesh: THREE.Mesh | null = null\n private options: InteractionZoneOptions\n\n constructor(\n onEnter?: (other: GameObject) => void,\n onExit?: (other: GameObject) => void,\n options: InteractionZoneOptions = {}\n ) {\n super()\n this.id = `interaction_${Math.random().toString(36).substr(2, 9)}`\n this.onEnterCallback = onEnter\n this.onExitCallback = onExit\n this.options = {\n width: 2,\n depth: 2,\n active: true,\n show: true,\n ...options,\n }\n this.active = this.options.active ?? true\n }\n\n /**\n * Set interaction callbacks\n */\n public setCallbacks(callbacks: {\n onEnter?: (other: GameObject) => void\n onExit?: (other: GameObject) => void\n }): void {\n this.onEnterCallback = callbacks.onEnter\n this.onExitCallback = callbacks.onExit\n }\n\n /**\n * Called when component is attached to GameObject\n */\n protected onCreate(): void {\n if (this.options.show) {\n this.createVisualMesh()\n }\n this.createTriggerCollider()\n this.setActive(this.active)\n }\n\n public onEnabled(): void {\n // Trigger registration handled by explicit registerOnTriggerEnter/Exit() calls\n // No additional logic needed here\n }\n\n /**\n * Create the visual mesh for the interaction zone\n */\n private createVisualMesh(): void {\n const width = this.options.width!\n const height = 0.1 // Fixed height for top-down view\n const depth = this.options.depth!\n\n // Create box geometry\n const geometry = new THREE.BoxGeometry(width, height, depth)\n\n // Create semi-transparent material\n const material = new THREE.MeshBasicMaterial({\n color: 0x000000, // Black like the original\n transparent: true,\n opacity: 0.15, // Original opacity\n side: THREE.DoubleSide,\n })\n\n this.visualMesh = new THREE.Mesh(geometry, material)\n\n // Add to the GameObject\n this.gameObject.add(this.visualMesh)\n this.visualMesh.position.y += 0.1\n\n // Visual mesh created\n }\n\n /**\n * Create the physics trigger collider\n */\n private createTriggerCollider(): void {\n this.rigidBody = new RigidBodyComponentThree({\n type: RigidBodyType.STATIC,\n shape: ColliderShape.BOX,\n size: new THREE.Vector3(this.options.width!, 0.1, this.options.depth!), // Fixed height of 0.1\n isSensor: true, // This makes it a trigger collider\n // No collision groups = default behavior (can detect anything that wants to hit it)\n })\n\n // Apply center offset if specified (only X and Z since it's Vector2)\n if (this.options.centerOffset) {\n this.gameObject.position.x += this.options.centerOffset.x\n this.gameObject.position.z += this.options.centerOffset.y // Vector2.y maps to world Z\n }\n\n this.gameObject.addComponent(this.rigidBody)\n\n // Register methods directly - just like C# actions!\n this.rigidBody.registerOnTriggerEnter(this.onTriggerEnter.bind(this))\n this.rigidBody.registerOnTriggerExit(this.onTriggerExit.bind(this))\n }\n\n /**\n * Handle trigger enter event (to be called by physics system)\n */\n public onTriggerEnter(other: GameObject): void {\n // console.log(`🎯 ENTER: ${other.name} → InteractionZone ${this.id}`); // Reduced spam\n\n if (!this.active) {\n console.warn(`🎯 InteractionZone ${this.id}: Ignoring enter event - zone is inactive`)\n return\n }\n\n // Check if parent GameObject is enabled\n if (!this.gameObject.isEnabled()) {\n // console.log(`🎯 InteractionZone ${this.id}: Ignoring enter event - parent GameObject is disabled`);\n return\n }\n\n if (!this.entitiesInZone.has(other)) {\n this.entitiesInZone.add(other)\n // console.log(`🎯 Added ${other.name} to zone (total: ${this.entitiesInZone.size})`); // Reduced spam\n\n if (this.onEnterCallback) {\n this.onEnterCallback(other)\n }\n }\n }\n\n /**\n * Handle trigger exit event (to be called by physics system)\n */\n public onTriggerExit(other: GameObject): void {\n // console.log(`🎯 EXIT: ${other.name} ← InteractionZone ${this.id}`); // Reduced spam\n\n if (!this.active) {\n // Zone inactive, ignoring exit\n return\n }\n\n // Check if parent GameObject is enabled\n if (!this.gameObject.isEnabled()) {\n // console.log(`🎯 InteractionZone ${this.id}: Ignoring exit event - parent GameObject is disabled`);\n return\n }\n\n if (this.entitiesInZone.has(other)) {\n this.entitiesInZone.delete(other)\n // console.log(`🎯 Removed ${other.name} from zone (total: ${this.entitiesInZone.size})`); // Reduced spam\n\n if (this.onExitCallback) {\n this.onExitCallback(other)\n }\n }\n }\n\n /**\n * Get all entities currently in the zone\n */\n public getEntitiesInZone(): GameObject[] {\n return Array.from(this.entitiesInZone)\n }\n\n /**\n * Check if a specific entity is in the zone\n */\n public hasEntity(entity: GameObject): boolean {\n return this.entitiesInZone.has(entity)\n }\n\n /**\n * Set active state\n */\n public setActive(active: boolean): void {\n this.active = active\n\n // Show/hide visual mesh (only if visual mesh was created)\n if (this.visualMesh) {\n this.visualMesh.visible = active\n }\n\n if (this.rigidBody) {\n // TODO: Enable/disable the rigid body when that feature is available\n }\n\n if (!active) {\n // Clear all entities when deactivated\n this.entitiesInZone.clear()\n }\n }\n\n /**\n * Check if the interaction zone is active\n */\n public isActive(): boolean {\n return this.active\n }\n\n /**\n * Get the visual mesh\n */\n public getVisualMesh(): THREE.Mesh | null {\n return this.visualMesh\n }\n\n /**\n * Get the collider component\n */\n public getCollider(): RigidBodyComponentThree | null {\n return this.rigidBody\n }\n\n /**\n * Get the GameObject this zone is attached to\n */\n public getGameObject(): GameObject {\n return this.gameObject\n }\n\n /**\n * Component cleanup\n */\n protected onCleanup(): void {\n this.entitiesInZone.clear()\n\n // Trigger cleanup handled automatically by RigidBodyComponentThree!\n // The registered callbacks will be cleaned up when the RigidBody is destroyed\n\n // Clean up visual mesh\n if (this.visualMesh) {\n this.visualMesh.geometry.dispose()\n if (this.visualMesh.material instanceof THREE.Material) {\n this.visualMesh.material.dispose()\n }\n this.gameObject.remove(this.visualMesh)\n this.visualMesh = null\n }\n\n // RigidBodyComponentThree will be cleaned up automatically by the GameObject\n }\n}\n\n// Interface for options\nexport interface InteractionZoneOptions {\n width?: number\n depth?: number\n active?: boolean\n centerOffset?: THREE.Vector2\n show?: boolean\n}\n","import * as THREE from \"three\"\nimport { Component } from \"@engine/core\"\n\nexport interface VirtualJoystickOptions {\n size?: number // Size of the joystick base\n knobSize?: number // Size of the joystick knob\n deadZone?: number // Dead zone radius (0-1)\n maxDistance?: number // Maximum distance for knob movement\n color?: string // Color of the joystick\n visible?: boolean // Whether to show joystick visuals (still functional when hidden)\n opacity?: number // Opacity of joystick visuals (0-1)\n}\n\n/**\n * Virtual joystick component for mobile/touch input - Three.js version\n * Shows on pointer down, hides on pointer up\n * Provides normalized direction vector for movement\n * Uses HTML/CSS for UI elements\n */\nexport class VirtualJoystickThree extends Component {\n // Configuration\n private options: Required<VirtualJoystickOptions>\n\n // Mobile detection\n private static isMobileDevice(): boolean {\n return (\n /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||\n \"ontouchstart\" in window ||\n navigator.maxTouchPoints > 0\n )\n }\n\n // UI Elements\n private joystickContainer: HTMLElement | null = null\n private joystickBase: HTMLElement | null = null\n private joystickKnob: HTMLElement | null = null\n private mobileHint: HTMLElement | null = null\n\n // State\n private isActive: boolean = false\n private startPosition: THREE.Vector2 = new THREE.Vector2()\n private currentPosition: THREE.Vector2 = new THREE.Vector2()\n private direction: THREE.Vector2 = new THREE.Vector2()\n private magnitude: number = 0\n\n // Input tracking\n private joystickPointerId: number | null = null\n private isDragging: boolean = false\n private joystickRadius: number = 0\n\n // Event handlers (need to bind them for proper cleanup)\n private boundPointerDown = this.onPointerDown.bind(this)\n private boundPointerMove = this.onPointerMove.bind(this)\n private boundPointerUp = this.onPointerUp.bind(this)\n private boundTouchStart = this.onTouchStart.bind(this)\n private boundTouchMove = this.onTouchMove.bind(this)\n private boundTouchEnd = this.onTouchEnd.bind(this)\n\n constructor(options: VirtualJoystickOptions = {}) {\n super()\n\n this.options = {\n size: options.size ?? 120,\n knobSize: options.knobSize ?? 40,\n deadZone: options.deadZone ?? 0.15,\n maxDistance: options.maxDistance ?? 50,\n color: options.color ?? \"white\",\n visible: options.visible ?? true,\n opacity: options.opacity ?? 0.2,\n }\n\n this.joystickRadius = this.options.maxDistance\n }\n\n protected onCreate(): void {\n this.createJoystickUI()\n this.createMobileHint()\n this.setupInputHandlers()\n }\n\n protected onCleanup(): void {\n this.cleanupUI()\n this.removeInputHandlers()\n }\n\n /**\n * Create the joystick UI elements using HTML/CSS\n */\n private createJoystickUI(): void {\n // Create container for joystick (initially hidden)\n this.joystickContainer = document.createElement(\"div\")\n this.joystickContainer.id = \"virtual-joystick-container\"\n this.joystickContainer.style.cssText = `\n position: fixed;\n width: ${this.options.size + this.options.knobSize}px;\n height: ${this.options.size + this.options.knobSize}px;\n display: none;\n pointer-events: none;\n z-index: 1000;\n user-select: none;\n touch-action: none;\n `\n\n // Create joystick base (outer circle)\n this.joystickBase = document.createElement(\"div\")\n this.joystickBase.style.cssText = `\n position: absolute;\n width: ${this.options.size}px;\n height: ${this.options.size}px;\n border: 4px solid ${this.options.color};\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.2);\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n box-sizing: border-box;\n opacity: ${this.options.opacity};\n `\n\n // Create joystick knob (inner circle)\n this.joystickKnob = document.createElement(\"div\")\n this.joystickKnob.style.cssText = `\n position: absolute;\n width: ${this.options.knobSize}px;\n height: ${this.options.knobSize}px;\n border: 2px solid ${this.options.color};\n border-radius: 50%;\n background: rgba(255, 255, 255, 0.8);\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n transition: none;\n box-sizing: border-box;\n opacity: ${this.options.opacity};\n `\n\n // Add elements to container\n this.joystickContainer.appendChild(this.joystickBase)\n this.joystickContainer.appendChild(this.joystickKnob)\n\n // Add to document body\n document.body.appendChild(this.joystickContainer)\n }\n\n /**\n * Create mobile hint for touch controls (only shows on mobile devices)\n */\n private createMobileHint(): void {\n // Only show hint on mobile devices\n if (!VirtualJoystickThree.isMobileDevice()) {\n return\n }\n\n this.mobileHint = document.createElement(\"div\")\n this.mobileHint.id = \"mobile-joystick-hint\"\n this.mobileHint.textContent = \"Touch & drag to move\"\n this.mobileHint.style.cssText = `\n position: fixed;\n bottom: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(0, 0, 0, 0.8);\n color: white;\n padding: 10px 20px;\n border-radius: 20px;\n font-family: Arial, sans-serif;\n font-size: 14px;\n z-index: 999;\n pointer-events: none;\n user-select: none;\n border: 2px solid rgba(255, 255, 255, 0.3);\n animation: fadeInOut 4s ease-in-out;\n `\n\n // Add CSS animation\n if (!document.querySelector(\"#mobile-hint-styles\")) {\n const style = document.createElement(\"style\")\n style.id = \"mobile-hint-styles\"\n style.textContent = `\n @keyframes fadeInOut {\n 0% { opacity: 0; transform: translateX(-50%) translateY(20px); }\n 20% { opacity: 1; transform: translateX(-50%) translateY(0px); }\n 80% { opacity: 1; transform: translateX(-50%) translateY(0px); }\n 100% { opacity: 0; transform: translateX(-50%) translateY(-20px); }\n }\n `\n document.head.appendChild(style)\n }\n\n document.body.appendChild(this.mobileHint)\n\n // Remove hint after animation completes\n setTimeout(() => {\n if (this.mobileHint && this.mobileHint.parentNode) {\n this.mobileHint.parentNode.removeChild(this.mobileHint)\n this.mobileHint = null\n }\n }, 4000)\n }\n\n /**\n * Setup input handlers for pointer and touch events\n */\n private setupInputHandlers(): void {\n // Add both pointer and touch event listeners for maximum compatibility\n document.addEventListener(\"pointerdown\", this.boundPointerDown, {\n passive: false,\n })\n document.addEventListener(\"pointermove\", this.boundPointerMove, {\n passive: false,\n })\n document.addEventListener(\"pointerup\", this.boundPointerUp, {\n passive: false,\n })\n\n // Touch events as fallback\n document.addEventListener(\"touchstart\", this.boundTouchStart, {\n passive: false,\n })\n document.addEventListener(\"touchmove\", this.boundTouchMove, {\n passive: false,\n })\n document.addEventListener(\"touchend\", this.boundTouchEnd, {\n passive: false,\n })\n }\n\n /**\n * Remove input handlers\n */\n private removeInputHandlers(): void {\n document.removeEventListener(\"pointerdown\", this.boundPointerDown)\n document.removeEventListener(\"pointermove\", this.boundPointerMove)\n document.removeEventListener(\"pointerup\", this.boundPointerUp)\n\n document.removeEventListener(\"touchstart\", this.boundTouchStart)\n document.removeEventListener(\"touchmove\", this.boundTouchMove)\n document.removeEventListener(\"touchend\", this.boundTouchEnd)\n }\n\n /**\n * Handle pointer down event\n */\n private onPointerDown(event: PointerEvent): void {\n // Only respond to primary pointer (first touch/click)\n if (this.isActive || !event.isPrimary) return\n\n this.startJoystick(event.clientX, event.clientY, event.pointerId)\n event.preventDefault()\n }\n\n /**\n * Handle touch start event (fallback)\n */\n private onTouchStart(event: TouchEvent): void {\n if (this.isActive || event.touches.length === 0) return\n\n const touch = event.touches[0]\n this.startJoystick(touch.clientX, touch.clientY, touch.identifier)\n event.preventDefault()\n }\n\n /**\n * Start the joystick at the given position\n */\n private startJoystick(x: number, y: number, pointerId: number): void {\n this.isActive = true\n this.isDragging = true\n this.joystickPointerId = pointerId\n\n // Hide mobile hint when joystick is first used\n if (this.mobileHint && this.mobileHint.parentNode) {\n this.mobileHint.parentNode.removeChild(this.mobileHint)\n this.mobileHint = null\n }\n\n // Set positions\n this.startPosition.set(x, y)\n this.currentPosition.set(x, y)\n\n // Show and position the joystick (only if visible is true)\n if (this.joystickContainer) {\n this.joystickContainer.style.display = this.options.visible ? \"block\" : \"none\"\n this.joystickContainer.style.left = `${x - (this.options.size + this.options.knobSize) / 2}px`\n this.joystickContainer.style.top = `${y - (this.options.size + this.options.knobSize) / 2}px`\n }\n\n // Reset knob to center\n if (this.joystickKnob) {\n this.joystickKnob.style.transform = \"translate(-50%, -50%)\"\n }\n\n this.updateDirection()\n }\n\n /**\n * Handle pointer move event\n */\n private onPointerMove(event: PointerEvent): void {\n if (!this.isActive || !this.isDragging || event.pointerId !== this.joystickPointerId) {\n return\n }\n\n this.updateJoystick(event.clientX, event.clientY)\n event.preventDefault()\n }\n\n /**\n * Handle touch move event (fallback)\n */\n private onTouchMove(event: TouchEvent): void {\n if (!this.isActive || !this.isDragging) return\n\n // Find the touch with our ID\n for (let i = 0; i < event.touches.length; i++) {\n const touch = event.touches[i]\n if (touch.identifier === this.joystickPointerId) {\n this.updateJoystick(touch.clientX, touch.clientY)\n event.preventDefault()\n break\n }\n }\n }\n\n /**\n * Update joystick position and direction\n */\n private updateJoystick(x: number, y: number): void {\n this.currentPosition.set(x, y)\n\n // Update knob position and direction\n this.updateKnobPosition()\n this.updateDirection()\n }\n\n /**\n * Handle pointer up event\n */\n private onPointerUp(event: PointerEvent): void {\n if (!this.isActive || event.pointerId !== this.joystickPointerId) {\n return\n }\n\n this.endJoystick()\n event.preventDefault()\n }\n\n /**\n * Handle touch end event (fallback)\n */\n private onTouchEnd(event: TouchEvent): void {\n if (!this.isActive) return\n\n // Check if our touch ended\n let touchEnded = true\n for (let i = 0; i < event.touches.length; i++) {\n if (event.touches[i].identifier === this.joystickPointerId) {\n touchEnded = false\n break\n }\n }\n\n if (touchEnded) {\n this.endJoystick()\n event.preventDefault()\n }\n }\n\n /**\n * End the joystick interaction\n */\n private endJoystick(): void {\n this.isActive = false\n this.isDragging = false\n this.joystickPointerId = null\n\n // Hide the joystick\n if (this.joystickContainer) {\n this.joystickContainer.style.display = \"none\"\n }\n\n // Reset direction\n this.direction.set(0, 0)\n this.magnitude = 0\n }\n\n /**\n * Update knob position based on current pointer position\n */\n private updateKnobPosition(): void {\n if (!this.joystickKnob) return\n\n // Calculate offset from joystick center\n const offset = this.currentPosition.clone().sub(this.startPosition)\n\n // Clamp to max distance\n const distance = offset.length()\n if (distance > this.joystickRadius) {\n offset.normalize().multiplyScalar(this.joystickRadius)\n }\n\n // Update knob position\n this.joystickKnob.style.transform = `translate(calc(-50% + ${offset.x}px), calc(-50% + ${offset.y}px))`\n }\n\n /**\n * Update direction vector based on knob position\n */\n private updateDirection(): void {\n // Calculate offset from center\n const offset = this.currentPosition.clone().sub(this.startPosition)\n const distance = offset.length()\n\n // Apply dead zone\n if (distance < this.options.deadZone * this.joystickRadius) {\n this.direction.set(0, 0)\n this.magnitude = 0\n return\n }\n\n // Calculate normalized direction\n const normalizedDistance = Math.min(distance / this.joystickRadius, 1.0)\n this.direction = offset.normalize()\n this.magnitude = normalizedDistance\n }\n\n /**\n * Get the current input direction as a Vector3 (Y=0 for movement)\n */\n public getDirection(): THREE.Vector3 | null {\n if (!this.isActive || this.magnitude === 0) {\n return null\n }\n\n // Convert 2D joystick input to 3D movement direction\n // X maps to X (left/right), Y maps to Z (forward/back)\n // Screen: drag up = negative Y, drag down = positive Y\n // World: forward = positive Z, back = negative Z\n // So: worldZ = -screenY (drag up = move forward)\n return new THREE.Vector3(\n this.direction.x * this.magnitude,\n 0,\n -this.direction.y * this.magnitude\n )\n }\n\n /**\n * Get the current input magnitude (0-1)\n */\n public getMagnitude(): number {\n return this.magnitude\n }\n\n /**\n * Check if the joystick is currently active\n */\n public isActiveJoystick(): boolean {\n return this.isActive\n }\n\n /**\n * Toggle joystick visual visibility (joystick remains functional)\n */\n public setVisible(visible: boolean): void {\n this.options.visible = visible\n if (this.joystickContainer && this.isActive) {\n this.joystickContainer.style.display = visible ? \"block\" : \"none\"\n }\n }\n\n /**\n * Get current visibility state\n */\n public isVisible(): boolean {\n return this.options.visible\n }\n\n /**\n * Set joystick opacity (0-1)\n */\n public setOpacity(opacity: number): void {\n this.options.opacity = Math.max(0, Math.min(1, opacity))\n if (this.joystickBase) {\n this.joystickBase.style.opacity = `${this.options.opacity}`\n }\n if (this.joystickKnob) {\n this.joystickKnob.style.opacity = `${this.options.opacity}`\n }\n }\n\n /**\n * Get current opacity\n */\n public getOpacity(): number {\n return this.options.opacity\n }\n\n /**\n * Clean up UI resources\n */\n private cleanupUI(): void {\n if (this.joystickContainer && this.joystickContainer.parentNode) {\n this.joystickContainer.parentNode.removeChild(this.joystickContainer)\n }\n\n if (this.mobileHint && this.mobileHint.parentNode) {\n this.mobileHint.parentNode.removeChild(this.mobileHint)\n }\n\n this.joystickContainer = null\n this.joystickBase = null\n this.joystickKnob = null\n this.mobileHint = null\n }\n}\n","import * as THREE from \"three\"\nimport { Component } from \"@engine/core\"\nimport { RigidBodyComponentThree } from \"@systems/physics/RigidBodyComponentThree.ts\"\n\n/**\n * Three.js Movement controller component that handles physics-based movement and rotation\n * Uses Rapier physics with rotation locking for smooth, controlled movement\n * Can be used for player input, AI navigation, or any entity that needs to move\n */\nexport class MovementController extends Component {\n // Public configuration properties\n public maxMoveSpeed: number = 8.0\n public acceleration: number = 40\n public turnSpeed: number = 12 // How fast to rotate (radians per second)\n\n private rigidBodyComponent: RigidBodyComponentThree | null = null\n private targetRotationY: number = 0\n private currentRotationY: number = 0\n\n // Pre-allocated vector for velocity queries to avoid GC pressure\n private _currentVelocity = new THREE.Vector3()\n\n /**\n * Called when the component is created and attached to a GameObject\n */\n protected onCreate(): void {\n // Find the rigid body component on this GameObject\n this.findRigidBodyComponentThree()\n\n // Initialize rotation tracking\n this.currentRotationY = this.gameObject.rotation.y\n this.targetRotationY = this.currentRotationY\n }\n\n /**\n * Find the rigid body component on this GameObject\n */\n private findRigidBodyComponentThree(): void {\n this.rigidBodyComponent = this.gameObject.getComponent(RigidBodyComponentThree) || null\n if (!this.rigidBodyComponent) {\n console.warn(\"MovementController: No RigidBodyComponentThree found on GameObject\")\n }\n }\n\n /**\n * Set the rigid body component this controller should manage\n */\n public setRigidBodyComponentThree(rigidBodyComponent: RigidBodyComponentThree): void {\n this.rigidBodyComponent = rigidBodyComponent\n }\n\n /**\n * Move the entity based on input direction\n * @param inputDirection Normalized direction vector (or null for no movement)\n * @param deltaTime Time since last frame in seconds\n */\n public move(inputDirection: THREE.Vector3 | null, deltaTime: number): void {\n if (!this.rigidBodyComponent) return\n\n const targetVelocity = this.calculateTargetVelocity(inputDirection)\n const smoothedVelocity = this.smoothVelocity(targetVelocity, deltaTime)\n\n // Apply velocity to physics body (Rapier handles rotation locking)\n this.rigidBodyComponent.setVelocity(smoothedVelocity)\n\n // Handle Y rotation separately and smoothly\n this.updateRotation(inputDirection, deltaTime)\n }\n\n /**\n * Calculate target velocity based on input direction\n */\n private calculateTargetVelocity(inputDirection: THREE.Vector3 | null): THREE.Vector3 {\n const targetVelocity = new THREE.Vector3(0, 0, 0)\n\n if (inputDirection && inputDirection.length() > 0.01) {\n targetVelocity.x = inputDirection.x * this.maxMoveSpeed\n targetVelocity.z = inputDirection.z * this.maxMoveSpeed\n }\n\n // Y velocity is always 0 (grounded movement)\n targetVelocity.y = 0\n\n return targetVelocity\n }\n\n /**\n * Smooth velocity towards target using acceleration\n */\n private smoothVelocity(targetVelocity: THREE.Vector3, deltaTime: number): THREE.Vector3 {\n if (!this.rigidBodyComponent) return targetVelocity\n\n this.rigidBodyComponent.getVelocity(this._currentVelocity)\n const maxDelta = this.acceleration * deltaTime\n\n // Smooth X and Z velocities\n const smoothedVelocity = new THREE.Vector3()\n smoothedVelocity.x = this.moveTowards(this._currentVelocity.x, targetVelocity.x, maxDelta)\n smoothedVelocity.z = this.moveTowards(this._currentVelocity.z, targetVelocity.z, maxDelta)\n smoothedVelocity.y = 0 // Keep grounded\n\n return smoothedVelocity\n }\n\n /**\n * Update rotation smoothly towards movement direction using quaternion slerp\n */\n private updateRotation(inputDirection: THREE.Vector3 | null, deltaTime: number): void {\n // Always clear angular velocity first to prevent unwanted spinning\n if (this.rigidBodyComponent) {\n const rigidBody = this.rigidBodyComponent.getRigidBody()\n if (rigidBody) {\n // Stop any existing angular rotation to prevent spinning\n this.rigidBodyComponent.setAngularVelocity(new THREE.Vector3(0, 0, 0))\n }\n }\n\n // If no input direction, don't update rotation - just ensure rotation is stopped\n if (!inputDirection || inputDirection.length() < 0.01) {\n // We've already cleared angular velocity above, so just return\n return\n }\n\n // Calculate target rotation based on movement direction\n const targetRotationY = Math.atan2(inputDirection.x, inputDirection.z)\n\n // Create target quaternion\n const targetQuaternion = new THREE.Quaternion()\n targetQuaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), targetRotationY)\n\n // Get current quaternion\n const currentQuaternion = new THREE.Quaternion()\n currentQuaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.currentRotationY)\n\n // Calculate slerp factor based on turn speed\n const slerpFactor = Math.min(1.0, this.turnSpeed * deltaTime)\n\n // Slerp between current and target rotation\n const resultQuaternion = new THREE.Quaternion()\n resultQuaternion.slerpQuaternions(currentQuaternion, targetQuaternion, slerpFactor)\n\n // Update current rotation Y for tracking\n const euler = new THREE.Euler()\n euler.setFromQuaternion(resultQuaternion, \"YXZ\")\n this.currentRotationY = euler.y\n\n // Apply rotation through physics body if available, otherwise directly to GameObject\n if (this.rigidBodyComponent) {\n const rigidBody = this.rigidBodyComponent.getRigidBody()\n if (rigidBody) {\n // Apply to physics body (this will sync back to GameObject automatically)\n const rapierQuat = {\n x: resultQuaternion.x,\n y: resultQuaternion.y,\n z: resultQuaternion.z,\n w: resultQuaternion.w,\n }\n rigidBody.setRotation(rapierQuat, true)\n } else {\n // Fallback: apply rotation directly to GameObject\n this.gameObject.rotation.y = this.currentRotationY\n }\n } else {\n // Fallback: apply rotation directly to GameObject\n this.gameObject.rotation.y = this.currentRotationY\n }\n }\n\n /**\n * Utility function to move a value towards a target at a given rate\n */\n private moveTowards(current: number, target: number, maxDelta: number): number {\n const delta = target - current\n if (Math.abs(delta) <= maxDelta) {\n return target\n }\n return current + Math.sign(delta) * maxDelta\n }\n\n /**\n * Get current movement state for debugging\n */\n public getMovementState(): any {\n return {\n maxMoveSpeed: this.maxMoveSpeed,\n acceleration: this.acceleration,\n turnSpeed: this.turnSpeed,\n currentRotationY: this.currentRotationY,\n targetRotationY: this.targetRotationY,\n hasRigidBody: !!this.rigidBodyComponent,\n }\n }\n\n /**\n * Clean up resources when the component is removed\n */\n protected onCleanup(): void {\n this.rigidBodyComponent = null\n }\n}\n","import * as THREE from \"three\"\nimport { Component } from \"@engine/core\"\n\nexport interface PlayerControls {\n forward: string\n backward: string\n left: string\n right: string\n run: string\n interact: string\n}\n\n/**\n * Three.js Player Controller Component\n * Handles WASD movement, mouse look, and basic interactions\n */\nexport class PlayerControllerThree extends Component {\n // Movement parameters\n public moveSpeed: number = 5.0\n public runSpeed: number = 8.0\n public rotationSpeed: number = 2.0\n\n // Controls configuration\n private controls: PlayerControls = {\n forward: \"KeyW\",\n backward: \"KeyS\",\n left: \"KeyA\",\n right: \"KeyD\",\n run: \"ShiftLeft\",\n interact: \"KeyE\",\n }\n\n // Input state\n private keys: Set<string> = new Set()\n private mouseX: number = 0\n private mouseY: number = 0\n private mouseSensitivity: number = 0.002\n private isPointerLocked: boolean = false\n\n // Camera reference\n private camera: THREE.PerspectiveCamera\n private cameraHeight: number = 1.7 // Eye level height\n\n // Movement state\n private velocity: THREE.Vector3 = new THREE.Vector3()\n private direction: THREE.Vector3 = new THREE.Vector3()\n\n constructor(camera: THREE.PerspectiveCamera) {\n super()\n this.camera = camera\n }\n\n protected onCreate(): void {\n this.setupEventListeners()\n this.setupPointerLock()\n\n // Position camera at player eye level\n this.updateCameraPosition()\n\n console.log(\"🎮 Player controller initialized\")\n console.log(\"📋 Controls: WASD to move, Shift to run, E to interact, Click to look around\")\n }\n\n /**\n * Set up keyboard and mouse event listeners\n */\n private setupEventListeners(): void {\n // Keyboard events\n document.addEventListener(\"keydown\", this.onKeyDown.bind(this))\n document.addEventListener(\"keyup\", this.onKeyUp.bind(this))\n\n // Mouse events\n document.addEventListener(\"mousemove\", this.onMouseMove.bind(this))\n document.addEventListener(\"click\", this.onClick.bind(this))\n\n // Pointer lock events\n document.addEventListener(\"pointerlockchange\", this.onPointerLockChange.bind(this))\n }\n\n /**\n * Set up pointer lock for mouse look\n */\n private setupPointerLock(): void {\n const canvas = document.getElementById(\"renderCanvas\") as HTMLCanvasElement\n if (canvas) {\n canvas.addEventListener(\"click\", () => {\n if (!this.isPointerLocked) {\n canvas.requestPointerLock()\n }\n })\n }\n }\n\n /**\n * Handle key down events\n */\n private onKeyDown(event: KeyboardEvent): void {\n this.keys.add(event.code)\n\n // Handle special keys\n if (event.code === this.controls.interact) {\n this.onInteract()\n }\n }\n\n /**\n * Handle key up events\n */\n private onKeyUp(event: KeyboardEvent): void {\n this.keys.delete(event.code)\n }\n\n /**\n * Handle mouse movement for looking around\n */\n private onMouseMove(event: MouseEvent): void {\n if (!this.isPointerLocked) return\n\n this.mouseX += event.movementX * this.mouseSensitivity\n this.mouseY += event.movementY * this.mouseSensitivity\n\n // Clamp vertical rotation\n this.mouseY = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, this.mouseY))\n\n // Apply rotation to camera\n this.camera.rotation.order = \"YXZ\"\n this.camera.rotation.y = -this.mouseX\n this.camera.rotation.x = -this.mouseY\n }\n\n /**\n * Handle canvas clicks for pointer lock\n */\n private onClick(event: MouseEvent): void {\n const canvas = document.getElementById(\"renderCanvas\") as HTMLCanvasElement\n if (event.target === canvas && !this.isPointerLocked) {\n canvas.requestPointerLock()\n }\n }\n\n /**\n * Handle pointer lock state changes\n */\n private onPointerLockChange(): void {\n this.isPointerLocked = document.pointerLockElement !== null\n\n if (this.isPointerLocked) {\n console.log(\"🖱️ Mouse locked - look around!\")\n } else {\n console.log(\"🖱️ Mouse unlocked - click canvas to lock again\")\n }\n }\n\n /**\n * Handle interaction key press\n */\n private onInteract(): void {\n console.log(\"🤝 Player trying to interact...\")\n // TODO: Implement interaction system\n }\n\n /**\n * Update method - called every frame\n */\n public update(deltaTime: number): void {\n this.updateMovement(deltaTime)\n this.updateCameraPosition()\n }\n\n /**\n * Update player movement based on input\n */\n private updateMovement(deltaTime: number): void {\n // Reset direction\n this.direction.set(0, 0, 0)\n\n // Get current movement speed\n const isRunning = this.keys.has(this.controls.run)\n const currentSpeed = isRunning ? this.runSpeed : this.moveSpeed\n\n // Calculate movement direction based on camera orientation\n const forward = new THREE.Vector3()\n const right = new THREE.Vector3()\n\n // Get camera's forward direction (ignoring Y rotation for ground movement)\n this.camera.getWorldDirection(forward)\n forward.y = 0\n forward.normalize()\n\n // Get camera's right direction\n right.crossVectors(forward, this.camera.up).normalize()\n\n // Apply movement inputs\n if (this.keys.has(this.controls.forward)) {\n this.direction.add(forward)\n }\n if (this.keys.has(this.controls.backward)) {\n this.direction.sub(forward)\n }\n if (this.keys.has(this.controls.right)) {\n this.direction.add(right)\n }\n if (this.keys.has(this.controls.left)) {\n this.direction.sub(right)\n }\n\n // Normalize direction to prevent faster diagonal movement\n if (this.direction.length() > 0) {\n this.direction.normalize()\n\n // Apply movement to player position\n this.velocity.copy(this.direction).multiplyScalar(currentSpeed * deltaTime)\n this.gameObject.position.add(this.velocity)\n }\n\n // Keep player on ground level (no Y drift)\n this.gameObject.position.y = 0\n }\n\n /**\n * Update camera position to follow player\n */\n private updateCameraPosition(): void {\n // Position camera at player position + eye height\n this.camera.position.copy(this.gameObject.position)\n this.camera.position.y += this.cameraHeight\n }\n\n /**\n * Get current movement state for debugging\n */\n public getMovementState(): {\n position: THREE.Vector3\n isMoving: boolean\n isRunning: boolean\n lookDirection: THREE.Vector3\n } {\n const lookDirection = new THREE.Vector3()\n this.camera.getWorldDirection(lookDirection)\n\n return {\n position: this.gameObject.position.clone(),\n isMoving: this.direction.length() > 0,\n isRunning: this.keys.has(this.controls.run),\n lookDirection: lookDirection,\n }\n }\n\n /**\n * Set player position\n */\n public setPosition(position: THREE.Vector3): void {\n this.gameObject.position.copy(position)\n this.updateCameraPosition()\n }\n\n /**\n * Clean up event listeners\n */\n protected onCleanup(): void {\n document.removeEventListener(\"keydown\", this.onKeyDown.bind(this))\n document.removeEventListener(\"keyup\", this.onKeyUp.bind(this))\n document.removeEventListener(\"mousemove\", this.onMouseMove.bind(this))\n document.removeEventListener(\"click\", this.onClick.bind(this))\n document.removeEventListener(\"pointerlockchange\", this.onPointerLockChange.bind(this))\n\n // Exit pointer lock if active\n if (this.isPointerLocked) {\n document.exitPointerLock()\n }\n\n console.log(\"🎮 Player controller cleaned up\")\n }\n}\n","import * as THREE from \"three\"\nimport { engineThrow } from \"@engine/core/EngineError\"\nimport { Component } from \"@engine/core\"\nimport { AssetManager } from \"@engine/assets\"\n\n/**\n * Three.js Skeletal renderer for animated FBX models\n * - Uses AssetManager for preloading coordination\n * - Uses SkeletonCache for proper skeletal cloning\n * - Specifically designed for animated characters\n * - Always cloned, always has shadows, never static\n */\nexport class SkeletalRenderer extends Component {\n private _group: THREE.Group | null = null\n private _assetPath: string\n private _material: THREE.Material | null\n private _skeletalModel: THREE.Object3D | null = null\n\n constructor(assetPath: string, material?: THREE.Material) {\n super()\n this._assetPath = assetPath\n this._material = material || null\n }\n\n protected onCreate(): void {\n this.createSkeletalMesh()\n }\n\n private createSkeletalMesh(): void {\n if (!this._assetPath) {\n engineThrow(\"SkeletalRenderer\", \"No asset path specified.\", \"Pass the FBX asset path to the constructor: new SkeletalRenderer('path/to/character.fbx')\")\n }\n\n // Create wrapper group for this instance\n this._group = new THREE.Group()\n this._group.name = `skeletal_${this._assetPath.split(\"/\").pop()}`\n this.gameObject.add(this._group)\n\n // Get properly cloned skeletal model from AssetManager's skeleton cache\n this._skeletalModel = AssetManager.getSkeletalClone(this._assetPath)\n\n if (!this._skeletalModel) {\n engineThrow(\"SkeletalRenderer\", `No skeletal model found for \"${this._assetPath}\".`, \"Make sure to preload with AssetManager.preloadSkeletalModel() before creating the SkeletalRenderer. The asset must be loaded and cached first.\")\n }\n\n // Apply custom material if provided\n if (this._material) {\n this._skeletalModel.traverse((child: THREE.Object3D) => {\n if (child instanceof THREE.Mesh) {\n child.material = this._material!\n child.frustumCulled = false\n }\n })\n } else {\n this._skeletalModel.traverse((child: THREE.Object3D) => {\n if (child instanceof THREE.Mesh) {\n child.frustumCulled = false\n }\n })\n }\n\n this._group.add(this._skeletalModel)\n\n // Always apply shadows for characters\n this.applyShadowsToGroup(this._group)\n }\n\n private applyShadowsToGroup(group: THREE.Group): void {\n group.traverse((child: THREE.Object3D) => {\n if (child instanceof THREE.Mesh) {\n child.castShadow = true\n child.receiveShadow = true\n }\n })\n }\n\n // ========== Public API ==========\n\n /**\n * Get the wrapper group (attached to GameObject)\n */\n public getGroup(): THREE.Group | null {\n return this._group\n }\n\n /**\n * Get the skeletal model (for animation setup)\n */\n public getSkeletalModel(): THREE.Object3D | null {\n return this._skeletalModel\n }\n\n /**\n * Get the asset path being rendered\n */\n public getAssetPath(): string {\n return this._assetPath\n }\n\n /**\n * Enable or disable visibility\n */\n public setVisible(visible: boolean): void {\n if (this._group) {\n this._group.visible = visible\n }\n }\n\n /**\n * Get visibility state\n */\n public isVisible(): boolean {\n return this._group?.visible ?? false\n }\n\n // ========== Component Lifecycle ==========\n\n protected onCleanup(): void {\n if (this._group) {\n this.gameObject.remove(this._group)\n }\n // Note: Don't track instance destruction since AssetManager doesn't track SkeletonCache instances\n }\n\n public onEnabled(): void {\n this.setVisible(true)\n }\n\n public onDisabled(): void {\n this.setVisible(false)\n }\n}\n","import { Component } from \"@engine/core\"\nimport { engineError } from \"@engine/core/EngineError\"\nimport { InstancedMeshManager } from \"./InstancedMeshManager\"\n\n/**\n * Options for InstancedRenderer\n */\nexport interface InstancedRendererOptions {\n /** If true (default), matrix updates every frame. If false, only updates when markDirty() is called. */\n isDynamic?: boolean\n /** Whether instances cast shadows (default: false). Only used if batch is auto-created. */\n castShadow?: boolean\n /** Whether instances receive shadows (default: false). Only used if batch is auto-created. */\n receiveShadow?: boolean\n /** Initial batch capacity (default: 16, grows automatically). Only used if batch is auto-created. */\n initialCapacity?: number\n}\n\n/**\n * Component for rendering objects using GPU instancing.\n *\n * Transform is controlled by the GameObject - the manager syncs matrices every frame.\n * If no batch exists for the key, one is created automatically from the GameObject's mesh.\n *\n * Performance modes:\n * - Dynamic (default): Matrix updates every frame. Use for moving objects.\n * - Static: Matrix only updates when markDirty() is called. Use for stationary objects.\n *\n * Usage:\n * ```typescript\n * // Simple - batch auto-creates from GameObject's mesh\n * gameObject.addComponent(new InstancedRenderer(\"burger\"))\n *\n * // With options\n * gameObject.addComponent(new InstancedRenderer(\"burger\", {\n * isDynamic: false,\n * castShadow: true\n * }))\n *\n * // For stationary objects, switch to static mode\n * renderer.setDynamic(false)\n *\n * // When a static object moves, mark it dirty\n * renderer.markDirty()\n * ```\n *\n * Key differences from mesh-based renderers (ObjRenderer, etc.):\n * - No getMesh() - instances don't have their own THREE.Object3D\n * - No getBounds() - can query the batch geometry if needed\n * - All instances share the same geometry and material\n */\nexport class InstancedRenderer extends Component {\n private readonly batchKey: string\n private readonly options: InstancedRendererOptions\n private instanceId: string | null = null\n\n /**\n * Create an InstancedRenderer\n * @param batchKey The batch key to register with. If no batch exists, one is auto-created.\n * @param options Configuration options (or just pass `true`/`false` for isDynamic)\n */\n constructor(batchKey: string, options: InstancedRendererOptions | boolean = {}) {\n super()\n this.batchKey = batchKey\n // Support legacy boolean parameter for isDynamic\n this.options = typeof options === \"boolean\" ? { isDynamic: options } : options\n }\n\n /**\n * Register with the batch when the component is created.\n * If no batch exists, one will be created automatically from this GameObject's mesh.\n */\n protected onCreate(): void {\n const manager = InstancedMeshManager.getInstance()\n\n if (!manager.isReady()) {\n engineError(\"InstancedRenderer\", \"InstancedMeshManager not initialized.\", \"Call InstancedMeshManager.getInstance().initialize(scene) during game setup. If you're using VenusGame, this happens automatically in create().\")\n return\n }\n\n // addInstance now auto-creates batch from GameObject if needed\n this.instanceId = manager.addInstance(this.batchKey, this.gameObject, {\n isDynamic: this.options.isDynamic ?? true,\n castShadow: this.options.castShadow ?? false,\n receiveShadow: this.options.receiveShadow ?? false,\n initialCapacity: this.options.initialCapacity,\n })\n\n if (!this.instanceId) {\n engineError(\"InstancedRenderer\", `Failed to add instance to batch \"${this.batchKey}\".`, \"The batch could not be created. Make sure the GameObject has a loaded mesh, or pre-register the batch with StowKitSystem.registerMeshForInstancing().\")\n }\n }\n\n /**\n * Set the visibility of this instance\n */\n public setVisible(visible: boolean): void {\n if (this.instanceId) {\n InstancedMeshManager.getInstance().setInstanceVisible(this.batchKey, this.instanceId, visible)\n }\n }\n\n /**\n * Get the visibility of this instance\n */\n public getVisible(): boolean {\n if (this.instanceId) {\n return InstancedMeshManager.getInstance().getInstanceVisible(this.batchKey, this.instanceId)\n }\n return false\n }\n\n /**\n * Show the instance (convenience method)\n */\n public show(): void {\n this.setVisible(true)\n }\n\n /**\n * Hide the instance (convenience method)\n */\n public hide(): void {\n this.setVisible(false)\n }\n\n /**\n * Get the batch key this renderer is registered with\n */\n public getBatchKey(): string {\n return this.batchKey\n }\n\n /**\n * Check if this renderer is successfully registered with a batch\n */\n public isRegistered(): boolean {\n return this.instanceId !== null\n }\n\n /**\n * Get the instance ID (for debugging)\n */\n public getInstanceId(): string | null {\n return this.instanceId\n }\n\n /**\n * Mark this instance as needing a matrix update.\n * Only relevant for static instances - dynamic instances update every frame anyway.\n * Call this when the transform of a static instance changes.\n */\n public markDirty(): void {\n if (this.instanceId) {\n InstancedMeshManager.getInstance().markInstanceDirty(this.batchKey, this.instanceId)\n }\n }\n\n /**\n * Set whether this instance is dynamic (updates every frame) or static (only when marked dirty).\n * Use this to optimize performance when items transition between moving and stationary states.\n * @param isDynamic If true, updates every frame. If false, only updates when markDirty() is called.\n */\n public setDynamic(isDynamic: boolean): void {\n if (this.instanceId) {\n InstancedMeshManager.getInstance().setInstanceDynamic(\n this.batchKey,\n this.instanceId,\n isDynamic\n )\n }\n }\n\n /**\n * Called when the GameObject becomes enabled\n */\n public onEnabled(): void {\n this.setVisible(true)\n }\n\n /**\n * Called when the GameObject becomes disabled\n */\n public onDisabled(): void {\n this.setVisible(false)\n }\n\n /**\n * Unregister from the batch when the component is cleaned up\n */\n protected onCleanup(): void {\n if (this.instanceId) {\n InstancedMeshManager.getInstance().removeInstance(this.batchKey, this.instanceId)\n this.instanceId = null\n }\n }\n}\n","import * as THREE from \"three\"\nimport { engineError, engineWarn } from \"@engine/core/EngineError\"\nimport { GameObject, Component } from \"@engine/core\"\nimport {\n PrefabComponent,\n PrefabInstance,\n ComponentRegistry,\n type ComponentJSON,\n type PrefabNode,\n} from \"@systems/prefabs\"\nimport { StowKitSystem } from \"@systems/stowkit\"\nimport { RenderBatcher } from \"./RenderBatcher\"\n\ninterface NodeOverrideJSON {\n position?: [number, number, number]\n rotation?: [number, number, number]\n scale?: [number, number, number]\n components?: ComponentJSON[]\n}\n\ninterface StowMeshJSON extends ComponentJSON {\n type: \"stow_mesh\"\n mesh: {\n pack: string\n assetId: string\n }\n /** When set, only the named group's meshes are rendered (sub-mesh mode). */\n subMesh?: string\n castShadow?: boolean\n receiveShadow?: boolean\n nodeOverrides?: Record<string, NodeOverrideJSON>\n}\n\n/**\n * MeshRenderer - Component for rendering a mesh from StowKitSystem.\n *\n * This is a non-instanced renderer - each instance is a separate draw call.\n * Use InstancedRenderer for many instances of the same mesh (better performance).\n *\n * When a mesh has named child groups (from preserveHierarchy), MeshRenderer\n * promotes them to GameObjects so they can have components and be accessed\n * via getMeshChild(). Transform overrides and components from nodeOverrides\n * are applied automatically.\n *\n * Usage:\n * ```typescript\n * const env = new GameObject(\"Environment\")\n * env.addComponent(new MeshRenderer(\"restaurant_display_common\"))\n * ```\n */\n@PrefabComponent(\"stow_mesh\")\nexport class MeshRenderer extends Component {\n static fromPrefabJSON(json: StowMeshJSON, _node: PrefabNode): MeshRenderer {\n if (!json.mesh?.assetId) {\n engineError(\"MeshRenderer\", \"stow_mesh component missing mesh.assetId.\", \"Every stow_mesh component needs a mesh.assetId property pointing to the mesh name in a StowKit pack. Check your prefab data.\")\n return new MeshRenderer(\"unknown\")\n }\n // Priority: JSON property > context options > default (true)\n const options = PrefabInstance.currentOptions\n const castShadow = json.castShadow ?? options?.castShadow ?? true\n const receiveShadow = json.receiveShadow ?? options?.receiveShadow ?? true\n const renderer = new MeshRenderer(json.mesh.assetId, castShadow, receiveShadow)\n renderer.subMeshName = json.subMesh ?? null\n if (json.nodeOverrides) {\n renderer.nodeOverrides = json.nodeOverrides\n }\n return renderer\n }\n\n private mesh: THREE.Group | null = null\n private readonly meshName: string\n private readonly castShadow: boolean\n private readonly receiveShadow: boolean\n private _isStatic: boolean\n private isMeshLoaded: boolean = false\n private materialOverride: THREE.Material | null = null\n private loadedCallbacks: (() => void)[] | null = null\n private nodeOverrides: Record<string, NodeOverrideJSON> | null = null\n private meshChildMap: Map<string, GameObject> | null = null\n /** When set, only render the named group from the asset (sub-mesh mode). */\n private subMeshName: string | null = null\n /** Deferred batch registration — waits one frame so other components can modify materials */\n private _pendingBatchRegister: boolean = false\n\n /**\n * @param meshName The name of the mesh in the StowKit pack\n * @param castShadow Whether meshes should cast shadows (default: true)\n * @param receiveShadow Whether meshes should receive shadows (default: true)\n * @param isStatic Whether this mesh is static (default: false). Static meshes have matrixAutoUpdate disabled for better performance.\n * @param materialOverride Optional material to use instead of the default StowKit material\n */\n constructor(\n meshName: string,\n castShadow: boolean = true,\n receiveShadow: boolean = true,\n isStatic: boolean = false,\n materialOverride: THREE.Material | null = null\n ) {\n super()\n this.meshName = meshName\n this.castShadow = castShadow\n this.receiveShadow = receiveShadow\n this._isStatic = isStatic\n this.materialOverride = materialOverride\n }\n\n protected onCreate(): void {\n const stowkit = StowKitSystem.getInstance()\n\n // Check if mesh is already cached\n const cachedMesh = stowkit.getMeshSync(this.meshName)\n if (cachedMesh) {\n this.addMesh(cachedMesh)\n } else {\n // Start async load - will add mesh in update when ready\n stowkit.getMesh(this.meshName)\n }\n }\n\n public update(_deltaTime: number): void {\n // Deferred batch registration (one frame after mesh loads)\n if (this._pendingBatchRegister) {\n this._pendingBatchRegister = false\n if (!this.materialOverride && !this.gameObject.hasAutoInstancingBlocker()) {\n RenderBatcher.getInstance().register(this)\n }\n }\n\n if (this.isMeshLoaded) return\n\n const stowkit = StowKitSystem.getInstance()\n const cachedMesh = stowkit.getMeshSync(this.meshName)\n if (cachedMesh) {\n this.addMesh(cachedMesh)\n }\n }\n\n private addMesh(original: THREE.Group): void {\n this.isMeshLoaded = true\n\n // Clone mesh with material conversion\n const cloned = StowKitSystem.getInstance().cloneMeshSync(\n original,\n this.castShadow,\n this.receiveShadow\n )\n\n if (this.subMeshName) {\n // Sub-mesh mode: find the named group and attach only its meshes\n const found = MeshRenderer.findGroupByName(cloned, this.subMeshName)\n if (found) {\n // Create a container group for the sub-mesh content\n this.mesh = new THREE.Group()\n this.mesh.name = this.subMeshName\n // Take direct mesh children from the found group\n // (only direct children — nested groups belong to other sub-mesh components)\n for (const child of [...found.children]) {\n if ((child as any).isMesh) {\n found.remove(child)\n this.mesh.add(child)\n }\n }\n } else {\n engineWarn(\"MeshRenderer\", `Sub-mesh group \"${this.subMeshName}\" not found in \"${this.meshName}\".`, \"The mesh may not have a group with this name. Sub-mesh names come from your 3D modeling tool's hierarchy. Falling back to full mesh.\")\n this.mesh = cloned\n }\n // Dispose unused parts of the clone\n if (this.mesh !== cloned) {\n MeshRenderer.disposeUnused(cloned, this.mesh)\n }\n } else {\n // Full mesh mode (backwards compatible)\n this.mesh = cloned\n }\n\n // Ensure shadow settings are applied to final mesh hierarchy\n // (defensive: cloneMeshSync sets these, but sub-mesh extraction creates a new Group)\n this.mesh.traverse((child) => {\n if ((child as THREE.Mesh).isMesh) {\n child.castShadow = this.castShadow\n child.receiveShadow = this.receiveShadow\n }\n })\n\n // Apply material override if set\n if (this.materialOverride) {\n this.applyMaterialOverride()\n }\n\n this.gameObject.add(this.mesh)\n\n // Promote named child groups to GameObjects for component support\n // (only relevant for full-mesh mode; sub-mesh mode has no child groups)\n if (!this.subMeshName) {\n this.promoteMeshChildren(this.mesh)\n }\n\n // For static meshes, disable matrix auto-update to save CPU\n if (this._isStatic) {\n this.setStatic(true)\n }\n\n // Fire onLoaded callbacks\n if (this.loadedCallbacks) {\n for (const cb of this.loadedCallbacks) cb()\n this.loadedCallbacks = null\n }\n\n // Defer batch registration to next frame so other components (e.g. CharacterShader)\n // have time to modify materials before the batch template is captured\n if (!this.materialOverride) {\n this._pendingBatchRegister = true\n }\n }\n\n /** Depth-first search for a named Group in a mesh hierarchy. */\n private static findGroupByName(root: THREE.Object3D, name: string): THREE.Group | null {\n for (const child of root.children) {\n if (child.type === \"Group\" && child.name === name) return child as THREE.Group\n const found = MeshRenderer.findGroupByName(child, name)\n if (found) return found\n }\n return null\n }\n\n /** Dispose geometries/materials from a mesh tree, preserving those used by `keep`. */\n private static disposeUnused(root: THREE.Object3D, keep: THREE.Object3D): void {\n const keepSet = new Set<THREE.BufferGeometry | THREE.Material>()\n keep.traverse((child) => {\n if (child instanceof THREE.Mesh) {\n if (child.geometry) keepSet.add(child.geometry)\n if (child.material) {\n const mats = Array.isArray(child.material) ? child.material : [child.material]\n for (const m of mats) keepSet.add(m)\n }\n }\n })\n root.traverse((child) => {\n if (child instanceof THREE.Mesh) {\n if (child.geometry && !keepSet.has(child.geometry)) child.geometry.dispose()\n if (child.material) {\n const mats = Array.isArray(child.material) ? child.material : [child.material]\n for (const m of mats) {\n if (!keepSet.has(m)) {\n if ((m as any).map) (m as any).map.dispose()\n m.dispose()\n }\n }\n }\n }\n })\n }\n\n /**\n * Walk the loaded mesh hierarchy and replace named child Groups with\n * GameObjects so they can receive components. Applies nodeOverrides\n * (transform + components) from the prefab data.\n */\n private promoteMeshChildren(group: THREE.Object3D): void {\n for (const child of [...group.children]) {\n if (child.type !== \"Group\" || !child.name) continue\n\n // Create a GameObject to replace this group\n const childGO = new GameObject(child.name)\n childGO.position.copy(child.position)\n childGO.quaternion.copy(child.quaternion)\n childGO.scale.copy(child.scale)\n\n // Apply nodeOverride transforms if present\n const override = this.nodeOverrides?.[child.name]\n if (override?.position) childGO.position.fromArray(override.position)\n if (override?.rotation) {\n childGO.rotation.set(\n override.rotation[0],\n override.rotation[1],\n override.rotation[2]\n )\n }\n if (override?.scale) childGO.scale.fromArray(override.scale)\n\n // Move all children from the group into the GameObject\n for (const grandchild of [...child.children]) {\n child.remove(grandchild)\n childGO.add(grandchild)\n }\n\n // Replace group with GameObject in parent\n group.remove(child)\n group.add(childGO)\n\n // Create override components\n if (override?.components) {\n for (const compJSON of override.components) {\n const factory = ComponentRegistry.get(compJSON.type)\n if (factory) {\n try {\n const comp = factory.fromPrefabJSON(compJSON, null as unknown as PrefabNode)\n if (comp) childGO.addComponent(comp)\n } catch (e) {\n engineError(\"MeshRenderer\", `Failed to create component \"${compJSON.type}\" on mesh child \"${child.name}\".`, \"Check the component's fromPrefabJSON() method and the nodeOverrides data in your prefab.\", e)\n }\n }\n }\n }\n\n // Index for getMeshChild()\n if (!this.meshChildMap) this.meshChildMap = new Map()\n this.meshChildMap.set(child.name, childGO)\n\n // Recurse for nested groups\n this.promoteMeshChildren(childGO)\n }\n }\n\n /**\n * Get a named child group from the loaded mesh as a GameObject.\n * Returns null if the mesh hasn't loaded yet or if no child with that name exists.\n *\n * ```typescript\n * renderer.onLoaded(() => {\n * const key = renderer.getMeshChild(\"Key\")\n * key?.addComponent(new Spinner(\"z\"))\n * })\n * ```\n */\n public getMeshChild(name: string): GameObject | null {\n return this.meshChildMap?.get(name) ?? null\n }\n\n /**\n * Get all named child groups from the loaded mesh.\n * Returns null if the mesh hasn't loaded yet.\n */\n public getMeshChildren(): ReadonlyMap<string, GameObject> | null {\n return this.meshChildMap ?? null\n }\n\n /**\n * Apply the material override to all meshes\n */\n private applyMaterialOverride(): void {\n if (!this.mesh || !this.materialOverride) return\n\n this.mesh.traverse((child) => {\n if (child instanceof THREE.Mesh) {\n child.material = this.materialOverride!\n }\n })\n }\n\n /**\n * Set a material override for all meshes in this renderer.\n * Call this after the mesh is loaded, or pass it in the constructor.\n */\n public setMaterial(material: THREE.Material): void {\n this.materialOverride = material\n if (this.mesh) {\n this.applyMaterialOverride()\n }\n // Material no longer matches batch template — unregister\n RenderBatcher.getInstance().unregister(this)\n }\n\n /**\n * Check if this mesh is currently static (no automatic matrix updates)\n */\n public get isStatic(): boolean {\n return this._isStatic\n }\n\n /**\n * Set whether this mesh is static (no automatic matrix updates).\n * Static meshes save CPU by not recalculating transforms every frame.\n * Call forceMatrixUpdate() after moving a static mesh.\n */\n public setStatic(isStatic: boolean): void {\n this._isStatic = isStatic\n if (!this.mesh) return\n\n if (isStatic) {\n // Update matrices one final time before freezing\n this.forceMatrixUpdate()\n\n // Disable auto-update on mesh and all descendants\n this.mesh.matrixAutoUpdate = false\n this.mesh.traverse((child) => {\n child.matrixAutoUpdate = false\n })\n this.gameObject.matrixAutoUpdate = false\n } else {\n // Enable auto-update\n this.mesh.matrixAutoUpdate = true\n this.mesh.traverse((child) => {\n child.matrixAutoUpdate = true\n })\n this.gameObject.matrixAutoUpdate = true\n }\n }\n\n /**\n * Force a one-time matrix update. Call this after moving a static mesh.\n * Does not change the static/dynamic state.\n */\n public forceMatrixUpdate(): void {\n if (this.mesh) {\n this.mesh.updateMatrix()\n this.mesh.updateMatrixWorld(true)\n }\n this.gameObject.updateMatrix()\n this.gameObject.updateMatrixWorld(true)\n }\n\n /**\n * Get the mesh group (null if not yet loaded)\n */\n public getMesh(): THREE.Group | null {\n return this.mesh\n }\n\n /**\n * Get the name of the mesh this component is managing\n */\n public getMeshName(): string {\n return this.meshName\n }\n\n /**\n * Get the sub-mesh name (null if rendering the full mesh)\n */\n public getSubMeshName(): string | null {\n return this.subMeshName\n }\n\n /**\n * Get shadow settings\n */\n public getCastShadow(): boolean {\n return this.castShadow\n }\n\n public getReceiveShadow(): boolean {\n return this.receiveShadow\n }\n\n /**\n * Check if the mesh was successfully loaded\n */\n public isLoaded(): boolean {\n return this.mesh !== null\n }\n\n /**\n * Register a callback that fires when the mesh finishes loading,\n * or immediately if already loaded.\n */\n public onLoaded(callback: () => void): void {\n if (this.mesh) {\n callback()\n return\n }\n if (!this.loadedCallbacks) this.loadedCallbacks = []\n this.loadedCallbacks.push(callback)\n }\n\n /**\n * Get the first material from the loaded mesh, or null if not yet loaded.\n */\n public getMaterial(): THREE.Material | null {\n if (!this.mesh) return null\n let result: THREE.Material | null = null\n this.mesh.traverse((child) => {\n if (!result && child instanceof THREE.Mesh && child.material) {\n result = Array.isArray(child.material) ? child.material[0] : child.material\n }\n })\n return result\n }\n\n /**\n * Set the visibility of the mesh\n */\n public setVisible(visible: boolean): void {\n if (this.mesh) {\n this.mesh.visible = visible\n }\n }\n\n /**\n * Get bounds of the mesh (useful for physics)\n */\n public getBounds(): THREE.Vector3 | null {\n if (!this.mesh) {\n return null\n }\n return StowKitSystem.getInstance().getBounds(this.mesh)\n }\n\n /**\n * Cleanup - remove mesh from scene and dispose of resources\n */\n protected onCleanup(): void {\n this._pendingBatchRegister = false\n // Unregister from RenderBatcher before disposing mesh\n RenderBatcher.getInstance().unregister(this)\n\n if (this.mesh) {\n // Remove from GameObject/scene\n this.gameObject.remove(this.mesh)\n\n // Traverse and dispose of geometries and materials\n this.mesh.traverse((child) => {\n if (child instanceof THREE.Mesh) {\n if (child.geometry) {\n child.geometry.dispose()\n }\n\n if (child.material) {\n if (Array.isArray(child.material)) {\n child.material.forEach((m) => {\n if (m.map) m.map.dispose()\n m.dispose()\n })\n } else {\n if (child.material.map) child.material.map.dispose()\n child.material.dispose()\n }\n }\n }\n })\n\n this.mesh = null\n }\n\n this.meshChildMap = null\n }\n}\n","import * as THREE from \"three\"\nimport { mergeGeometries } from \"three/addons/utils/BufferGeometryUtils.js\"\nimport { Component, GameObject } from \"@engine/core\"\nimport {\n PrefabComponent,\n type ComponentJSON,\n type PrefabNode,\n} from \"@systems/prefabs\"\nimport { MeshRenderer } from \"./MeshRenderer\"\nimport { RenderBatcher } from \"./RenderBatcher\"\n\ninterface StaticBatcherJSON extends ComponentJSON {\n type: \"static_batcher\"\n}\n\n/**\n * Merges all child meshes by material into single draw calls.\n *\n * Place on a root GameObject whose children are static scenery.\n * After all child MeshRenderers finish loading, their geometries are\n * merged per-material into combined meshes. The originals are removed.\n *\n * This is classic static batching — one draw call per unique material,\n * zero per-frame matrix work. Use for geometry that never moves.\n */\n@PrefabComponent(\"static_batcher\")\nexport class StaticBatcher extends Component {\n static fromPrefabJSON(_json: StaticBatcherJSON, _node: PrefabNode): StaticBatcher {\n return new StaticBatcher()\n }\n\n private mergedMeshes: THREE.Mesh[] | null = null\n private _isBatched = false\n\n public get isBatched(): boolean {\n return this._isBatched\n }\n\n protected onCreate(): void {\n // Wait one frame so child MeshRenderers register first,\n // then wait for all of them to load.\n this.pollUntilReady()\n }\n\n private pollUntilReady(): void {\n const renderers = this.gameObject.getComponentsInChildren(MeshRenderer)\n if (renderers.length === 0) return\n\n const allLoaded = renderers.every((r) => r.isLoaded())\n if (allLoaded) {\n // One more frame deferral so RenderBatcher registration + custom\n // shader components have applied (matches MeshRenderer's deferral).\n requestAnimationFrame(() => this.batchAll(renderers))\n } else {\n requestAnimationFrame(() => this.pollUntilReady())\n }\n }\n\n private batchAll(renderers: MeshRenderer[]): void {\n if (this._isBatched) return\n\n // Ensure world matrices are current\n this.gameObject.updateMatrixWorld(true)\n\n // Collect all mesh nodes grouped by material reference\n const groups = new Map<\n THREE.Material,\n { geometry: THREE.BufferGeometry; matrix: THREE.Matrix4 }[]\n >()\n\n let totalBefore = 0\n\n for (const renderer of renderers) {\n const mesh = renderer.getMesh()\n if (!mesh) continue\n\n mesh.traverse((child) => {\n if (!(child as THREE.Mesh).isMesh) return\n const m = child as THREE.Mesh\n const mats = Array.isArray(m.material) ? m.material : [m.material]\n\n for (const mat of mats) {\n let list = groups.get(mat)\n if (!list) {\n list = []\n groups.set(mat, list)\n }\n list.push({\n geometry: m.geometry,\n matrix: m.matrixWorld.clone(),\n })\n totalBefore++\n }\n })\n }\n\n if (totalBefore === 0) return\n\n // Unregister all child renderers from RenderBatcher before removing them\n const batcher = RenderBatcher.getInstance()\n for (const renderer of renderers) {\n batcher.unregister(renderer)\n }\n\n // Merge each material group into one mesh\n this.mergedMeshes = []\n\n // Inverse of root world matrix — merged geometry lives in root-local space\n const rootInverse = new THREE.Matrix4().copy(this.gameObject.matrixWorld).invert()\n const tempMatrix = new THREE.Matrix4()\n\n for (const [material, entries] of groups) {\n // Clone each geometry and bake its world transform (relative to root)\n const clones: THREE.BufferGeometry[] = []\n for (const entry of entries) {\n const clone = entry.geometry.clone()\n // localMatrix = rootInverse * meshWorldMatrix\n tempMatrix.multiplyMatrices(rootInverse, entry.matrix)\n clone.applyMatrix4(tempMatrix)\n clones.push(clone)\n }\n\n const merged = mergeGeometries(clones, false)\n if (!merged) {\n // Dispose clones on failure\n for (const c of clones) c.dispose()\n continue\n }\n\n // Dispose temp clones (merged owns its own buffers)\n for (const c of clones) c.dispose()\n\n const mesh = new THREE.Mesh(merged, material)\n mesh.name = `staticBatch_${material.uuid.slice(0, 8)}`\n mesh.castShadow = true\n mesh.receiveShadow = true\n mesh.frustumCulled = true\n mesh.matrixAutoUpdate = false\n\n this.gameObject.add(mesh)\n this.mergedMeshes.push(mesh)\n }\n\n // Remove original meshes — dispose geometry + hide from scene\n for (const renderer of renderers) {\n const go = renderer.getGameObject()\n if (go !== this.gameObject) {\n // Remove the child renderer's mesh from its gameObject\n go.removeComponent(MeshRenderer)\n }\n }\n\n this._isBatched = true\n\n console.log(\n `[StaticBatcher] \"${this.gameObject.name}\": ${totalBefore} meshes → ${this.mergedMeshes.length} draw calls`\n )\n }\n\n protected onCleanup(): void {\n if (this.mergedMeshes) {\n for (const mesh of this.mergedMeshes) {\n this.gameObject.remove(mesh)\n mesh.geometry.dispose()\n }\n this.mergedMeshes = null\n }\n this._isBatched = false\n }\n}\n"],"mappings":"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","names":["THREE","THREE","THREE","THREE","THREE","THREE"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@series-inc/rundot-3d-engine",
3
- "version": "0.6.18",
3
+ "version": "0.6.19",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "scripts": {