// Debug Report Generator // Generates comprehensive debug reports for GLB generation export class DebugReportGenerator { generate(sceneData, sceneGraph, generationLog = [], warnings = [], errors = []) { const report = { timestamp: new Date().toISOString(), sceneName: sceneData.sceneName || "generated_scene", status: errors.length > 0 ? "ERROR" : warnings.length > 0 ? "WARNING" : "SUCCESS", summary: { totalNodes: sceneGraph.nodes?.length || 0, totalMeshes: sceneGraph.meshes?.length || 0, totalMaterials: sceneGraph.materials?.length || 0, totalLights: sceneGraph.lights?.length || 0, totalAnimations: sceneGraph.animations?.length || 0, hasAvatar: sceneData.avatar?.present || false, hasAudio: (sceneData.audio?.ambient?.length || 0) + (sceneData.audio?.spatial?.length || 0) > 0, hasEffects: Object.keys(sceneData.effects || {}).length > 0 }, sceneData: { environment: sceneData.environment || "room", lighting: { type: sceneData.lighting?.type || "day", lightsCount: sceneData.lighting?.lights?.length || 0, ambientStrength: sceneData.lighting?.ambient_strength || 0.3 }, objectsCount: sceneData.objects?.length || 0, avatar: sceneData.avatar?.present ? { name: sceneData.avatar.name || "Avatar", action: sceneData.avatar.action || "stand", position: sceneData.avatar.position || [0, 0, 0] } : null, audio: { ambient: sceneData.audio?.ambient?.length || 0, spatial: sceneData.audio?.spatial?.length || 0 }, effects: Object.keys(sceneData.effects || {}) }, sceneGraph: { nodes: sceneGraph.nodes?.map(n => ({ name: n.name, type: n.extras?.type || "unknown", hasMesh: n.mesh !== null && n.mesh !== undefined, hasLight: n.light !== null && n.light !== undefined, hasCamera: n.camera !== null && n.camera !== undefined, childrenCount: n.children?.length || 0 })) || [], meshes: sceneGraph.meshes?.map(m => ({ name: m.name, primitivesCount: m.primitives?.length || 0, hasMaterial: m.primitives?.[0]?.material !== undefined })) || [], materials: sceneGraph.materials?.map(m => ({ name: m.name, hasPBR: m.pbrMetallicRoughness !== undefined, hasEmission: m.emissiveFactor !== undefined })) || [], lights: sceneGraph.lights?.map(l => ({ name: l.name, type: l.type })) || [] }, generation: { log: generationLog, warnings: warnings, errors: errors, performance: { parseTime: null, graphTime: null, generationTime: null, totalTime: null } }, optimization: { compression: "none", textureFormat: "auto", meshOptimization: false, animationOptimization: false }, compatibility: { threejs: "compatible", babylon: "compatible", unity: "compatible", unreal: "compatible" }, recommendations: [] }; // Generate recommendations this.generateRecommendations(report, sceneData, sceneGraph); // Format as text return this.formatAsText(report); } generateRecommendations(report, sceneData, sceneGraph) { const recommendations = []; // Check for missing elements if (!sceneData.ground) { recommendations.push("WARNING: No ground plane defined. Scene may appear incomplete."); } if (sceneGraph.lights?.length === 0) { recommendations.push("WARNING: No lights defined. Scene will be dark."); } if (sceneGraph.meshes?.length === 0) { recommendations.push("WARNING: No meshes generated. Scene will be empty."); } // Check for optimization opportunities if (sceneGraph.meshes?.length > 50) { recommendations.push("OPTIMIZATION: Consider reducing mesh count or using instancing for better performance."); } if (sceneGraph.materials?.length > 20) { recommendations.push("OPTIMIZATION: Consider reusing materials to reduce draw calls."); } // Check for missing textures const materialsWithTextures = sceneGraph.materials?.filter(m => m.pbrMetallicRoughness?.baseColorTexture !== undefined ).length || 0; if (materialsWithTextures === 0 && sceneGraph.materials?.length > 0) { recommendations.push("INFO: No textures used. Consider adding textures for better visual quality."); } // Check animation if (sceneData.avatar?.present && !sceneData.avatar.action) { recommendations.push("INFO: Avatar present but no animation specified. Consider adding walk/stand animation."); } // Check audio if (sceneData.audio && (sceneData.audio.ambient?.length === 0 && sceneData.audio.spatial?.length === 0)) { recommendations.push("INFO: Audio nodes defined but empty. Consider adding ambient or spatial audio."); } report.recommendations = recommendations; } formatAsText(report) { let text = `GLB Generation Debug Report ${'='.repeat(60)} Generated: ${report.timestamp} Scene: ${report.sceneName} Status: ${report.status} ${'='.repeat(60)} SUMMARY ${'-'.repeat(60)} Total Nodes: ${report.summary.totalNodes} Total Meshes: ${report.summary.totalMeshes} Total Materials: ${report.summary.totalMaterials} Total Lights: ${report.summary.totalLights} Total Animations: ${report.summary.totalAnimations} Has Avatar: ${report.summary.hasAvatar ? 'Yes' : 'No'} Has Audio: ${report.summary.hasAudio ? 'Yes' : 'No'} Has Effects: ${report.summary.hasEffects ? 'Yes' : 'No'} ${'='.repeat(60)} SCENE DATA ${'-'.repeat(60)} Environment: ${report.sceneData.environment} Lighting Type: ${report.sceneData.lighting.type} Lighting Count: ${report.sceneData.lighting.lightsCount} Ambient Strength: ${report.sceneData.lighting.ambientStrength} Objects Count: ${report.sceneData.objectsCount} ${report.sceneData.avatar ? `Avatar: ${report.sceneData.avatar.name} (${report.sceneData.avatar.action})` : 'Avatar: None'} Audio: ${report.sceneData.audio.ambient} ambient, ${report.sceneData.audio.spatial} spatial Effects: ${report.sceneData.effects.join(', ') || 'None'} ${'='.repeat(60)} SCENE GRAPH ${'-'.repeat(60)} Nodes (${report.sceneGraph.nodes.length}): ${report.sceneGraph.nodes.map(n => ` - ${n.name} [${n.type}] (mesh: ${n.hasMesh}, light: ${n.hasLight}, camera: ${n.hasCamera}, children: ${n.childrenCount})` ).join('\n')} Meshes (${report.sceneGraph.meshes.length}): ${report.sceneGraph.meshes.map(m => ` - ${m.name} (${m.primitivesCount} primitives)` ).join('\n')} Materials (${report.sceneGraph.materials.length}): ${report.sceneGraph.materials.map(m => ` - ${m.name} (PBR: ${m.hasPBR}, Emission: ${m.hasEmission})` ).join('\n')} Lights (${report.sceneGraph.lights.length}): ${report.sceneGraph.lights.map(l => ` - ${l.name} [${l.type}]` ).join('\n')} ${'='.repeat(60)} GENERATION LOG ${'-'.repeat(60)} ${report.generation.log.length > 0 ? report.generation.log.join('\n') : 'No log entries'} ${report.generation.warnings.length > 0 ? ` WARNINGS: ${report.generation.warnings.map(w => ` - ${w}`).join('\n')} ` : ''} ${report.generation.errors.length > 0 ? ` ERRORS: ${report.generation.errors.map(e => ` - ${e}`).join('\n')} ` : ''} ${'='.repeat(60)} OPTIMIZATION ${'-'.repeat(60)} Compression: ${report.optimization.compression} Texture Format: ${report.optimization.textureFormat} Mesh Optimization: ${report.optimization.meshOptimization ? 'Enabled' : 'Disabled'} Animation Optimization: ${report.optimization.animationOptimization ? 'Enabled' : 'Disabled'} ${'='.repeat(60)} COMPATIBILITY ${'-'.repeat(60)} Three.js: ${report.compatibility.threejs} Babylon.js: ${report.compatibility.babylon} Unity: ${report.compatibility.unity} Unreal Engine: ${report.compatibility.unreal} ${report.recommendations.length > 0 ? ` ${'='.repeat(60)} RECOMMENDATIONS ${'-'.repeat(60)} ${report.recommendations.map(r => ` - ${r}`).join('\n')} ` : ''} ${'='.repeat(60)} END OF REPORT ${'='.repeat(60)} `; return text; } }