Set a unique AO map per model (or template) while keeping the base material fully shared across all models/templates?
Use a script attribute (e.g. this.aoMap as texture asset) on the model/template root to apply it to all meshInstances within that model, without material-cloning or per-instance changes?
Open for any suggested workflow to share materials and not create multiple model-specific ones. Updating a stainless-material would require a lot of headache if using model-specific materials.
Any update to meshInstance.material would update everything on any object with the same material. And as far as I understand, you cannot set aoMap directly on the meshInstance. Or can I?
That would be exactly what I’m looking for. But it doesn’t work. Code below:
var AoMapComponent = pc.createScript('aoMapComponent');
// Attribute for per-model AO map (exposed in Inspector)
AoMapComponent.attributes.add('aoMap', {
type: 'asset',
assetType: 'texture',
title: 'AO Map',
description: 'Model-specific AO texture (overrides per meshInstance, keeps shared material)'
});
AoMapComponent.prototype.initialize = function() {
console.log('AO Component: Initializing for entity', this.entity.name);
// Check Render component
if (!this.entity.render || !this.entity.render.meshInstances.length) {
console.warn('AO Component: Entity lacks Render component or meshes.');
return;
}
console.log('AO Component: Render OK with', this.entity.render.meshInstances.length, 'meshes');
// Function for AO application (order: set useAo + parameter BEFORE update)
var applyAo = function() {
// Skip if no AO set (keep shared base material)
if (!this.aoMap || !this.aoMap.resource) {
console.log('AO Component: No AO asset or resource set; retaining shared base material (e.g., stainless)');
return;
}
console.log('AO Component: Applying AO via texture_aoMap uniform – resource ready!');
this.entity.render.meshInstances.forEach((meshInstance) => {
// Order: Enable AO first, set uniform, THEN update (ensures uniform binds in compilation)
meshInstance.material.useAo = true; // Enable AO shader
meshInstance.setParameter('texture_aoMap', this.aoMap.resource); // Set uniform BEFORE update
meshInstance.material.aoStrength = 1.5; // Stronger effect
meshInstance.material.update(); // Update shader/bindings AFTER uniform set
// Debug logs
console.log('AO Component Debug: Uniform set?', meshInstance.parameters.texture_aoMap ? 'Yes' : 'No');
console.log('AO Component Debug: Use AO active?', meshInstance.material.useAo);
});
console.log('AO Component: AO set via texture_aoMap on', this.entity.render.meshInstances.length, 'meshInstances – shared material preserved!');
}.bind(this);
// Apply directly if ready
applyAo();
// Load if not ready
if (this.aoMap && !this.aoMap.resource) {
console.log('AO Component: Loading AO asset (ID:', this.aoMap.id, ') for model...');
this.app.assets.load(this.aoMap);
this.aoMap.on('load', applyAo);
this.aoMap.on('error', function(err) { console.error('AO Component: Load error:', err); });
} else if (!this.aoMap) {
console.log('AO Component: No AO asset; using shared base material');
}
};
// Optional debug update (remove for production)
AoMapComponent.prototype.update = function(dt) {
if (this.app.time % 5 < dt) console.log('AO Component: Active in runtime');
};
Maybe try and apply ao map on the material as well … just a single white pixel texture should be ok, shared by all materials. I think the shader generator only looks at the material to see what shader it needs to build. If there is no AO map there → shader does not use it, and so mesh instance override value is not used either.