import { DefaultScene } from "../core/defaultScene"; import { Color3, DirectionalLight, MeshBuilder, Observable, StandardMaterial, Vector3 } from "@babylonjs/core"; import type { AudioEngineV2 } from "@babylonjs/core"; import Level from "./level"; import debugLog from '../core/debug'; /** * Minimal test level with just a box and a light for debugging */ export class TestLevel implements Level { private _onReadyObservable: Observable = new Observable(); private _initialized: boolean = false; private _audioEngine: AudioEngineV2; private _boxCreationInterval: number | null = null; private _totalBoxesCreated: number = 0; private _boxesPerIteration: number = 1; constructor(audioEngine: AudioEngineV2) { this._audioEngine = audioEngine; debugLog('[TestLevel] Constructor called'); // Don't call initialize here - let Main call it after registering the observable } getReadyObservable(): Observable { return this._onReadyObservable; } public async play() { debugLog('[TestLevel] play() called - entering XR'); debugLog('[TestLevel] XR available:', !!DefaultScene.XR); debugLog('[TestLevel] XR baseExperience:', !!DefaultScene.XR?.baseExperience); try { // Enter XR mode const xr = await DefaultScene.XR.baseExperience.enterXRAsync('immersive-vr', 'local-floor'); debugLog('[TestLevel] XR mode entered successfully'); debugLog('[TestLevel] XR session:', xr); debugLog('[TestLevel] Camera position:', DefaultScene.XR.baseExperience.camera.position.toString()); this.startBoxCreation(); } catch (error) { console.error('[TestLevel] ERROR entering XR:', error); } } public dispose() { debugLog('[TestLevel] dispose() called'); // Stop box creation timer if (this._boxCreationInterval) { clearInterval(this._boxCreationInterval); this._boxCreationInterval = null; debugLog('[TestLevel] Box creation timer stopped'); } } /** * Create a box at the specified position with the specified color */ private createBox(position: Vector3, color: Color3, name?: string): void { const box = MeshBuilder.CreateBox( name || `box_${this._totalBoxesCreated}`, { size: 0.5 }, DefaultScene.MainScene ); box.position = position; const material = new StandardMaterial(`material_${this._totalBoxesCreated}`, DefaultScene.MainScene); material.diffuseColor = color; material.specularColor = new Color3(0.5, 0.5, 0.5); box.material = material; this._totalBoxesCreated++; } /** * Start the box creation timer that doubles the number of boxes each iteration */ private startBoxCreation(): void { debugLog('[TestLevel] Starting box creation timer...'); const createBatch = () => { const boxesToCreate = Math.min( this._boxesPerIteration, 1000 - this._totalBoxesCreated ); debugLog(`[TestLevel] Creating ${boxesToCreate} boxes (total will be: ${this._totalBoxesCreated + boxesToCreate}/1000)`); for (let i = 0; i < boxesToCreate; i++) { // Random position in a 20x20x20 cube around origin const position = new Vector3( Math.random() * 20 - 10, Math.random() * 20, Math.random() * 20 - 10 ); // Random color const color = new Color3( Math.random(), Math.random(), Math.random() ); this.createBox(position, color); } debugLog(`[TestLevel] Created ${boxesToCreate} boxes. Total: ${this._totalBoxesCreated}/1000`); // Log performance metrics const fps = DefaultScene.MainScene.getEngine().getFps(); // Directly compute triangle count from all meshes const totalIndices = DefaultScene.MainScene.meshes.reduce((sum, mesh) => { if (mesh.isEnabled() && mesh.isVisible) { return sum + mesh.getTotalIndices(); } return sum; }, 0); const triangleCount = Math.floor(totalIndices / 3); debugLog(`[TestLevel] Performance Metrics:`, { fps: fps.toFixed(2), triangleCount: triangleCount, totalIndices: totalIndices, totalMeshes: DefaultScene.MainScene.meshes.length, activeMeshes: DefaultScene.MainScene.meshes.filter(m => m.isEnabled() && m.isVisible).length, totalBoxes: this._totalBoxesCreated }); // Check if we've reached 1000 boxes if (this._totalBoxesCreated >= 1000) { debugLog('[TestLevel] Reached 1000 boxes, stopping timer'); if (this._boxCreationInterval) { clearInterval(this._boxCreationInterval); this._boxCreationInterval = null; } return; } // Double the number for next iteration this._boxesPerIteration *= 2; }; // Create first batch immediately createBatch(); // Set up interval for subsequent batches this._boxCreationInterval = setInterval(createBatch, 5000); } public async initialize() { debugLog('[TestLevel] initialize() called'); debugLog('[TestLevel] Scene info:', { meshCount: DefaultScene.MainScene.meshes.length, lightCount: DefaultScene.MainScene.lights.length }); if (this._initialized) { debugLog('[TestLevel] Already initialized, skipping'); return; } // Create a simple directional light const light = new DirectionalLight( "testLight", new Vector3(-1, -2, 1), DefaultScene.MainScene ); light.intensity = 1.0; debugLog('[TestLevel] Created directional light:', { name: light.name, direction: light.direction.toString(), intensity: light.intensity }); // Create a simple colored box const box = MeshBuilder.CreateBox( "testBox", { size: 2 }, DefaultScene.MainScene ); box.position = new Vector3(0, 1, 5); // In front of camera // Create a simple material const material = new StandardMaterial("testMaterial", DefaultScene.MainScene); material.diffuseColor = new Color3(1, 0, 0); // Red material.specularColor = new Color3(0.5, 0.5, 0.5); box.material = material; debugLog('[TestLevel] Created test box:', { name: box.name, position: box.position.toString(), size: 2, color: 'red' }); // Create a ground plane for reference const ground = MeshBuilder.CreateGround( "testGround", { width: 10, height: 10 }, DefaultScene.MainScene ); ground.position.y = 0; const groundMaterial = new StandardMaterial("groundMaterial", DefaultScene.MainScene); groundMaterial.diffuseColor = new Color3(0.3, 0.3, 0.3); // Grey ground.material = groundMaterial; debugLog('[TestLevel] Created ground plane:', { name: ground.name, dimensions: '10x10', position: ground.position.toString() }); debugLog('[TestLevel] Final scene state:', { totalMeshes: DefaultScene.MainScene.meshes.length, totalLights: DefaultScene.MainScene.lights.length, meshNames: DefaultScene.MainScene.meshes.map(m => m.name) }); this._initialized = true; debugLog('[TestLevel] Initialization complete - scene ready for XR'); // Notify that initialization is complete this._onReadyObservable.notifyObservers(this); } }