Add comprehensive material sharing validation and diagnostics
Implemented extensive logging and validation to diagnose material sharing issues and prevent unnecessary material creation: **Validation Added:** - Pre-creation check: Verify tool meshes have materials before creating instances - Early exit if tool mesh lacks material to prevent bad instances - Post-creation validation in buildColor.ts to catch tool creation issues **Enhanced Diagnostics:** - Detailed debug logging for tool mesh lookup and instance creation - Error logging with full context when material sharing fails - Source mesh material validation for InstancedMesh - Lists available tool meshes when lookup fails **Statistics Tracking:** - Tracks instances created vs materials shared - Counts fallback material creations - Logs sharing rate every 10 instances (target: 100%) - Helps identify material sharing failures in production **Expected Outcome:** - 100% material sharing rate for tool-based entities - Zero fallback material creations - All instances inherit materials from tool templates - Better draw call batching (same material = batched rendering) This diagnostic infrastructure will identify: 1. Timing issues (tools not ready when entities created) 2. Tool mesh creation failures 3. BabylonJS InstancedMesh material inheritance issues 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
bda0735c7f
commit
02c08b35f2
@ -20,6 +20,13 @@ import {xyztovec} from "./vectorConversion";
|
||||
import {AnimatedLineTexture} from "../../util/animatedLineTexture";
|
||||
import {LightmapGenerator} from "../../util/lightmapGenerator";
|
||||
|
||||
// Material sharing statistics
|
||||
let materialStats = {
|
||||
instancesCreated: 0,
|
||||
materialsShared: 0,
|
||||
materialsFallback: 0
|
||||
};
|
||||
|
||||
export function buildMeshFromDiagramEntity(entity: DiagramEntity, scene: Scene): AbstractMesh {
|
||||
const logger = log.getLogger('buildMeshFromDiagramEntity');
|
||||
if (!entity) {
|
||||
@ -81,12 +88,38 @@ function createNewInstanceIfNecessary(entity: DiagramEntity, scene: Scene): Abst
|
||||
case DiagramTemplates.CONE:
|
||||
case DiagramTemplates.PLANE:
|
||||
case DiagramTemplates.PERSON:
|
||||
const toolMesh = scene.getMeshById("tool-" + entity.template + "-" + entity.color);
|
||||
const toolMeshId = "tool-" + entity.template + "-" + entity.color;
|
||||
const toolMesh = scene.getMeshById(toolMeshId);
|
||||
if (toolMesh && !oldMesh) {
|
||||
// Verify tool mesh has material before creating instance
|
||||
if (!toolMesh.material) {
|
||||
logger.error(`Tool mesh ${toolMeshId} found but has no material! This should never happen.`);
|
||||
logger.error(`Tool mesh state: enabled=${toolMesh.isEnabled()}, parent=${toolMesh.parent?.name}`);
|
||||
// Don't create instance without material
|
||||
break;
|
||||
}
|
||||
|
||||
logger.debug(`Found tool mesh: ${toolMeshId}, material: ${toolMesh.material.id}`);
|
||||
newMesh = new InstancedMesh(entity.id, (toolMesh as Mesh));
|
||||
// InstancedMesh.material property delegates to sourceMesh.material automatically
|
||||
logger.debug(`Created instance ${entity.id}, inherited material: ${newMesh.material?.id}`);
|
||||
|
||||
// Track material sharing statistics
|
||||
materialStats.instancesCreated++;
|
||||
if (newMesh.material) {
|
||||
materialStats.materialsShared++;
|
||||
}
|
||||
|
||||
// newMesh.metadata = {template: entity.template, exportable: true, tool: false};
|
||||
} else {
|
||||
logger.warn('no tool mesh found for ' + entity.template + "-" + entity.color);
|
||||
if (!toolMesh) {
|
||||
logger.warn(`No tool mesh found for ${toolMeshId}. Available tool meshes: ${
|
||||
scene.meshes.filter(m => m.id.startsWith('tool-')).map(m => m.id).slice(0, 5).join(', ')
|
||||
}...`);
|
||||
}
|
||||
if (oldMesh) {
|
||||
logger.debug(`Skipping instance creation, mesh ${entity.id} already exists`);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -162,9 +195,31 @@ function mapMetadata(entity: DiagramEntity, newMesh: AbstractMesh, scene: Scene)
|
||||
/*if (entity.scale) {
|
||||
newMesh.scaling = xyztovec(entity.scale);
|
||||
}*/
|
||||
// Material validation - InstancedMesh should automatically inherit material from source mesh
|
||||
if (!newMesh.material && newMesh?.metadata?.template != "#object-template") {
|
||||
logger.warn("new material created, this shouldn't happen");
|
||||
logger.error(`MATERIAL SHARING FAILURE for mesh ${newMesh.id}:`);
|
||||
logger.error(` Template: ${newMesh.metadata?.template}`);
|
||||
logger.error(` Color: ${entity.color}`);
|
||||
logger.error(` Is InstancedMesh: ${newMesh instanceof InstancedMesh}`);
|
||||
|
||||
if (newMesh instanceof InstancedMesh) {
|
||||
logger.error(` Source mesh: ${newMesh.sourceMesh?.id}`);
|
||||
logger.error(` Source mesh material: ${newMesh.sourceMesh?.material?.id || 'MISSING'}`);
|
||||
logger.error(` This indicates tool mesh was created without material!`);
|
||||
}
|
||||
|
||||
// Create fallback material as last resort to prevent crashes
|
||||
logger.warn(`Creating fallback material to prevent crash - this impacts performance!`);
|
||||
newMesh.material = buildMissingMaterial("material-" + entity.id, scene, entity.color);
|
||||
|
||||
// Track fallback material creation
|
||||
materialStats.materialsFallback++;
|
||||
}
|
||||
|
||||
// Log material sharing statistics periodically
|
||||
if (materialStats.instancesCreated > 0 && materialStats.instancesCreated % 10 === 0) {
|
||||
const sharingRate = (materialStats.materialsShared / materialStats.instancesCreated * 100).toFixed(1);
|
||||
logger.info(`Material Sharing Stats: ${materialStats.materialsShared}/${materialStats.instancesCreated} (${sharingRate}%), Fallbacks: ${materialStats.materialsFallback}`);
|
||||
}
|
||||
if (entity.text) {
|
||||
newMesh.metadata.text = entity.text;
|
||||
|
||||
@ -51,6 +51,14 @@ export async function buildColor(color: Color3, scene: Scene, parent: TransformN
|
||||
newItem.position = new Vector3(calculatePosition(++i), .1, 0);
|
||||
tools.push(newItem.id);
|
||||
toolMap.set(newItem.id, newItem);
|
||||
|
||||
// Validate that tool instance has proper material inheritance
|
||||
if (!newItem.material || !newItem.sourceMesh?.material) {
|
||||
console.error(`Tool creation validation FAILED for ${newItem.id}:`);
|
||||
console.error(` Tool material: ${!!newItem.material}`);
|
||||
console.error(` Source mesh: ${newItem.sourceMesh?.id}`);
|
||||
console.error(` Source material: ${!!newItem.sourceMesh?.material}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (colorBoxMesh.metadata) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user