import Mesh from './Mesh';
import Cache from './core/Cache';
import BoundingBox from './math/BoundingBox';
var tmpBoundingBox = new BoundingBox();
/**
* @constructor clay.InstancedMesh
* @extends clay.Mesh
*/
var InstancedMesh = Mesh.extend(function () {
return /** @lends clay.InstancedMesh# */ {
/**
* Instances array. Each object in array must have node property
* @type Array
* @example
* var node = new clay.Node()
* instancedMesh.instances.push({
* node: node
* });
*/
instances: [],
instancedAttributes: {},
_attributesSymbols: []
};
}, function () {
this._cache = new Cache();
this.createInstancedAttribute('instanceMat1', 'float', 4, 1);
this.createInstancedAttribute('instanceMat2', 'float', 4, 1);
this.createInstancedAttribute('instanceMat3', 'float', 4, 1);
}, {
isInstancedMesh: function () { return true; },
getInstanceCount: function () {
return this.instances.length;
},
removeAttribute: function (symbol) {
var idx = this._attributesSymbols.indexOf(symbol);
if (idx >= 0) {
this._attributesSymbols.splice(idx, 1);
}
delete this.instancedAttributes[symbol];
},
createInstancedAttribute: function (symbol, type, size, divisor) {
if (this.instancedAttributes[symbol]) {
return;
}
this.instancedAttributes[symbol] = {
symbol: symbol,
type: type,
size: size,
divisor: divisor == null ? 1 : divisor,
value: null
};
this._attributesSymbols.push(symbol);
},
getInstancedAttributesBuffers: function (renderer) {
var cache = this._cache;
cache.use(renderer.__uid__);
var buffers = cache.get('buffers') || [];
if (cache.isDirty('dirty')) {
var gl = renderer.gl;
for (var i = 0; i < this._attributesSymbols.length; i++) {
var attr = this.instancedAttributes[this._attributesSymbols[i]];
var bufferObj = buffers[i];
if (!bufferObj) {
bufferObj = {
buffer: gl.createBuffer()
};
buffers[i] = bufferObj;
}
bufferObj.symbol = attr.symbol;
bufferObj.divisor = attr.divisor;
bufferObj.size = attr.size;
bufferObj.type = attr.type;
gl.bindBuffer(gl.ARRAY_BUFFER, bufferObj.buffer);
gl.bufferData(gl.ARRAY_BUFFER, attr.value, gl.DYNAMIC_DRAW);
}
cache.fresh('dirty');
cache.put('buffers', buffers);
}
return buffers;
},
update: function (forceUpdateWorld) {
Mesh.prototype.update.call(this, forceUpdateWorld);
var arraySize = this.getInstanceCount() * 4;
var instancedAttributes = this.instancedAttributes;
var instanceMat1 = instancedAttributes.instanceMat1.value;
var instanceMat2 = instancedAttributes.instanceMat2.value;
var instanceMat3 = instancedAttributes.instanceMat3.value;
if (!instanceMat1 || instanceMat1.length !== arraySize) {
instanceMat1 = instancedAttributes.instanceMat1.value = new Float32Array(arraySize);
instanceMat2 = instancedAttributes.instanceMat2.value = new Float32Array(arraySize);
instanceMat3 = instancedAttributes.instanceMat3.value = new Float32Array(arraySize);
}
var sourceBoundingBox = (this.skeleton && this.skeleton.boundingBox) || this.geometry.boundingBox;
var needUpdateBoundingBox = sourceBoundingBox != null && (this.castShadow || this.frustumCulling);
if (needUpdateBoundingBox && this.instances.length > 0) {
this.boundingBox = this.boundingBox || new BoundingBox();
this.boundingBox.min.set(Infinity, Infinity, Infinity);
this.boundingBox.max.set(-Infinity, -Infinity, -Infinity);
}
else {
this.boundingBox = null;
}
for (var i = 0; i < this.instances.length; i++) {
var instance = this.instances[i];
var node = instance.node;
if (!node) {
throw new Error('Instance must include node');
}
var transform = node.worldTransform.array;
var i4 = i * 4;
instanceMat1[i4] = transform[0];
instanceMat1[i4 + 1] = transform[1];
instanceMat1[i4 + 2] = transform[2];
instanceMat1[i4 + 3] = transform[12];
instanceMat2[i4] = transform[4];
instanceMat2[i4 + 1] = transform[5];
instanceMat2[i4 + 2] = transform[6];
instanceMat2[i4 + 3] = transform[13];
instanceMat3[i4] = transform[8];
instanceMat3[i4 + 1] = transform[9];
instanceMat3[i4 + 2] = transform[10];
instanceMat3[i4 + 3] = transform[14];
// Update bounding box
if (needUpdateBoundingBox) {
tmpBoundingBox.transformFrom(sourceBoundingBox, node.worldTransform);
this.boundingBox.union(tmpBoundingBox, this.boundingBox);
}
}
this._cache.dirty('dirty');
}
});
export default InstancedMesh;