Object.assign(pc, function () {
var id = 0;
var _tmpAabb = new pc.BoundingBox();
/**
* @constructor
* @name pc.Mesh
* @classdesc A graphical primitive. The mesh is defined by a {@link pc.VertexBuffer} and an optional
* {@link pc.IndexBuffer}. It also contains a primitive definition which controls the type of the
* primitive and the portion of the vertex or index buffer to use.
* @description Create a new mesh.
* @property {pc.VertexBuffer} vertexBuffer The vertex buffer holding the vertex data of the mesh.
* @property {pc.IndexBuffer[]} indexBuffer An array of index buffers. For unindexed meshes, this array can
* be empty. The first index buffer in the array is used by {@link pc.MeshInstance}s with a renderStyle
* property set to pc.RENDERSTYLE_SOLID. The second index buffer in the array is used if renderStyle is
* set to pc.RENDERSTYLE_WIREFRAME.
* @property {Object[]} primitive Array of primitive objects defining how vertex (and index) data in the
* mesh should be interpreted by the graphics device. For details on the primitive object, see
* {@link pc.GraphicsDevice#draw}. The primitive is ordered based on render style like the indexBuffer property.
* @property {pc.BoundingBox} aabb The axis-aligned bounding box for the object space vertices of this mesh.
*/
var Mesh = function () {
this._refCount = 0;
this.id = id++;
this.vertexBuffer = null;
this.indexBuffer = [null];
this.primitive = [{
type: 0,
base: 0,
count: 0
}];
this.skin = null;
this.morph = null;
// AABB for object space mesh vertices
this._aabb = new pc.BoundingBox();
// Array of object space AABBs of vertices affected by each bone
this.boneAabb = null;
};
Object.defineProperty(Mesh.prototype, 'aabb', {
get: function () {
return this.morph ? this.morph.aabb : this._aabb;
},
set: function (aabb) {
if (this.morph) {
this._aabb = this.morph._baseAabb = aabb;
this.morph._calculateAabb();
} else {
this._aabb = aabb;
}
}
});
/**
* @constructor
* @name pc.MeshInstance
* @classdesc An instance of a {@link pc.Mesh}. A single mesh can be referenced by many
* mesh instances that can have different transforms and materials.
* @description Create a new mesh instance.
* @param {pc.GraphNode} node The graph node defining the transform for this instance.
* @param {pc.Mesh} mesh The graphics mesh being instanced.
* @param {pc.Material} material The material used to render this instance.
* @example
* // Create a mesh instance pointing to a 1x1x1 'cube' mesh
* var mesh = pc.createBox(graphicsDevice);
* var material = new pc.StandardMaterial();
* var node = new pc.GraphNode();
* var meshInstance = new pc.MeshInstance(node, mesh, material);
* @property {pc.BoundingBox} aabb The world space axis-aligned bounding box for this
* mesh instance.
* @property {Boolean} castShadow Controls whether the mesh instance casts shadows.
* Defaults to false.
* @property {Boolean} visible Enable rendering for this mesh instance. Use visible property to enable/disable rendering without overhead of removing from scene.
* But note that the mesh instance is still in the hierarchy and still in the draw call list.
* @property {pc.Material} material The material used by this mesh instance.
* @property {Number} renderStyle The render style of the mesh instance. Can be:
* <ul>
* <li>pc.RENDERSTYLE_SOLID</li>
* <li>pc.RENDERSTYLE_WIREFRAME</li>
* <li>pc.RENDERSTYLE_POINTS</li>
* </ul>
* Defaults to pc.RENDERSTYLE_SOLID.
* @property {Boolean} cull Controls whether the mesh instance can be culled by with frustum culling ({@link pc.CameraComponent#frustumCulling}).
* @property {Number} drawOrder Use this value to affect rendering order of mesh instances.
* Only used when mesh instances are added to a {@link pc.Layer} with {@link pc.Layer#opaqueSortMode} or {@link pc.Layer#transparentSortMode} (depending on the material) set to {@link pc.SORTMODE_MANUAL}.
* @property {Boolean} visibleThisFrame Read this value in {@link pc.Layer#onPostCull} to determine if the object is actually going to be rendered.
*/
var MeshInstance = function MeshInstance(node, mesh, material) {
this._key = [0, 0];
this._shader = [null, null, null];
this.isStatic = false;
this._staticLightList = null;
this._staticSource = null;
this.node = node; // The node that defines the transform of the mesh instance
this._mesh = mesh; // The mesh that this instance renders
mesh._refCount++;
this.material = material; // The material with which to render this instance
this._shaderDefs = pc.MASK_DYNAMIC << 16; // 2 byte toggles, 2 bytes light mask; Default value is no toggles and mask = pc.MASK_DYNAMIC
this._shaderDefs |= mesh.vertexBuffer.format.hasUv0 ? pc.SHADERDEF_UV0 : 0;
this._shaderDefs |= mesh.vertexBuffer.format.hasUv1 ? pc.SHADERDEF_UV1 : 0;
this._shaderDefs |= mesh.vertexBuffer.format.hasColor ? pc.SHADERDEF_VCOLOR : 0;
this._shaderDefs |= mesh.vertexBuffer.format.hasTangents ? pc.SHADERDEF_TANGENTS : 0;
this._lightHash = 0;
// Render options
this.visible = true;
this.layer = pc.LAYER_WORLD; // legacy
this.renderStyle = pc.RENDERSTYLE_SOLID;
this.castShadow = false;
this._receiveShadow = true;
this._screenSpace = false;
this._noDepthDrawGl1 = false;
this.cull = true;
this.pick = true;
this._updateAabb = true;
this._updateAabbFunc = null;
// 64-bit integer key that defines render order of this mesh instance
this.updateKey();
this._skinInstance = null;
this.morphInstance = null;
this.instancingData = null;
// World space AABB
this.aabb = new pc.BoundingBox();
this._boneAabb = null;
this._aabbVer = -1;
this.drawOrder = 0;
this.visibleThisFrame = 0;
// custom function used to customize culling (e.g. for 2D UI elements)
this.isVisibleFunc = null;
this.parameters = {};
this.stencilFront = null;
this.stencilBack = null;
// Negative scale batching support
this.flipFaces = false;
};
Object.defineProperty(MeshInstance.prototype, 'mesh', {
get: function () {
return this._mesh;
},
set: function (mesh) {
if (this._mesh) this._mesh._refCount--;
this._mesh = mesh;
if (mesh) mesh._refCount++;
}
});
Object.defineProperty(MeshInstance.prototype, 'aabb', {
get: function () {
var aabb;
if (!this._updateAabb) return this._aabb;
if (this._updateAabbFunc) {
return this._updateAabbFunc(this._aabb);
}
if (this.skinInstance) {
var numBones = this.mesh.skin.boneNames.length;
var boneUsed, i;
// Initialize local bone AABBs if needed
if (!this.mesh.boneAabb) {
this.mesh.boneAabb = [];
this.mesh.boneUsed = [];
var elems = this.mesh.vertexBuffer.format.elements;
var numVerts = this.mesh.vertexBuffer.numVertices;
var vertSize = this.mesh.vertexBuffer.format.size;
var index;
var offsetP, offsetI, offsetW;
var j, k, l;
for (i = 0; i < elems.length; i++) {
if (elems[i].name === pc.SEMANTIC_POSITION) {
offsetP = elems[i].offset;
} else if (elems[i].name === pc.SEMANTIC_BLENDINDICES) {
offsetI = elems[i].offset;
} else if (elems[i].name === pc.SEMANTIC_BLENDWEIGHT) {
offsetW = elems[i].offset;
}
}
var data8 = new Uint8Array(this.mesh.vertexBuffer.storage);
var dataF = new Float32Array(this.mesh.vertexBuffer.storage);
var offsetPF = offsetP / 4;
var offsetWF = offsetW / 4;
var vertSizeF = vertSize / 4;
var bMax, bMin;
var x, y, z;
var boneMin = [];
var boneMax = [];
boneUsed = this.mesh.boneUsed;
for (i = 0; i < numBones; i++) {
boneMin[i] = new pc.Vec3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
boneMax[i] = new pc.Vec3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
}
// Find bone AABBs by attached vertices
for (j = 0; j < numVerts; j++) {
for (k = 0; k < 4; k++) {
if (dataF[j * vertSizeF + offsetWF + k] > 0) {
index = data8[j * vertSize + offsetI + k];
// Vertex j is affected by bone index
x = dataF[j * vertSizeF + offsetPF];
y = dataF[j * vertSizeF + offsetPF + 1];
z = dataF[j * vertSizeF + offsetPF + 2];
bMax = boneMax[index];
bMin = boneMin[index];
if (bMin.x > x) bMin.x = x;
if (bMin.y > y) bMin.y = y;
if (bMin.z > z) bMin.z = z;
if (bMax.x < x) bMax.x = x;
if (bMax.y < y) bMax.y = y;
if (bMax.z < z) bMax.z = z;
boneUsed[index] = true;
}
}
}
// Apply morphing to bone AABBs
if (this.morphInstance) {
var vertIndex;
var targets = this.morphInstance.morph._targets;
// Find min/max morphed vertex positions
var minMorphedPos = new Float32Array(numVerts * 3);
var maxMorphedPos = new Float32Array(numVerts * 3);
var m, dx, dy, dz;
var target, mtIndices, mtIndicesLength, deltaPos;
for (j = 0; j < numVerts; j++) {
minMorphedPos[j * 3] = maxMorphedPos[j * 3] = dataF[j * vertSizeF + offsetPF];
minMorphedPos[j * 3 + 1] = maxMorphedPos[j * 3 + 1] = dataF[j * vertSizeF + offsetPF + 1];
minMorphedPos[j * 3 + 2] = maxMorphedPos[j * 3 + 2] = dataF[j * vertSizeF + offsetPF + 2];
}
for (l = 0; l < targets.length; l++) {
target = targets[l];
mtIndices = target.indices;
mtIndicesLength = mtIndices.length;
deltaPos = target.deltaPositions;
for (k = 0; k < mtIndicesLength; k++) {
vertIndex = mtIndices[k];
dx = deltaPos[k * 3];
dy = deltaPos[k * 3 + 1];
dz = deltaPos[k * 3 + 2];
if (dx < 0) {
minMorphedPos[vertIndex * 3] += dx;
} else {
maxMorphedPos[vertIndex * 3] += dx;
}
if (dy < 0) {
minMorphedPos[vertIndex * 3 + 1] += dy;
} else {
maxMorphedPos[vertIndex * 3 + 1] += dy;
}
if (dz < 0) {
minMorphedPos[vertIndex * 3 + 2] += dz;
} else {
maxMorphedPos[vertIndex * 3 + 2] += dz;
}
}
}
// Re-evaluate bone AABBs against min/max morphed positions
for (l = 0; l < targets.length; l++) {
target = targets[l];
mtIndices = target.indices;
mtIndicesLength = mtIndices.length;
deltaPos = target.deltaPositions;
for (k = 0; k < mtIndicesLength; k++) {
vertIndex = mtIndices[k];
for (m = 0; m < 4; m++) {
if (dataF[vertIndex * vertSizeF + offsetWF + m] > 0) {
index = data8[vertIndex * vertSize + offsetI + m];
// Vertex vertIndex is affected by bone index
bMax = boneMax[index];
bMin = boneMin[index];
x = minMorphedPos[vertIndex * 3];
y = minMorphedPos[vertIndex * 3 + 1];
z = minMorphedPos[vertIndex * 3 + 2];
if (bMin.x > x) bMin.x = x;
if (bMin.y > y) bMin.y = y;
if (bMin.z > z) bMin.z = z;
x = maxMorphedPos[vertIndex * 3];
y = maxMorphedPos[vertIndex * 3 + 1];
z = maxMorphedPos[vertIndex * 3 + 2];
if (bMax.x < x) bMax.x = x;
if (bMax.y < y) bMax.y = y;
if (bMax.z < z) bMax.z = z;
}
}
}
}
}
for (i = 0; i < numBones; i++) {
aabb = new pc.BoundingBox();
aabb.setMinMax(boneMin[i], boneMax[i]);
this.mesh.boneAabb.push(aabb);
}
}
// Initialize per-instance AABBs if needed
if (!this._boneAabb) {
this._boneAabb = [];
for (i = 0; i < this.mesh.boneAabb.length; i++) {
this._boneAabb[i] = new pc.BoundingBox();
}
}
boneUsed = this.mesh.boneUsed;
// Update per-instance bone AABBs
for (i = 0; i < this.mesh.boneAabb.length; i++) {
if (!boneUsed[i]) continue;
this._boneAabb[i].setFromTransformedAabb(this.mesh.boneAabb[i], this.skinInstance.matrices[i]);
}
// Update full instance AABB
var rootNodeTransform = this.node.getWorldTransform();
var first = true;
for (i = 0; i < this.mesh.boneAabb.length; i++) {
if (!boneUsed[i]) continue;
if (first) {
_tmpAabb.center.copy(this._boneAabb[i].center);
_tmpAabb.halfExtents.copy(this._boneAabb[i].halfExtents);
first = false;
} else {
_tmpAabb.add(this._boneAabb[i]);
}
}
this._aabb.setFromTransformedAabb(_tmpAabb, rootNodeTransform);
} else if (this.node._aabbVer !== this._aabbVer) {
// if there is no mesh then reset aabb
aabb = this.mesh ? this.mesh.aabb : this._aabb;
if (!this.mesh) {
aabb.center.set(0, 0, 0);
aabb.halfExtents.set(0, 0, 0);
}
this._aabb.setFromTransformedAabb(aabb, this.node.getWorldTransform());
this._aabbVer = this.node._aabbVer;
}
return this._aabb;
},
set: function (aabb) {
this._aabb = aabb;
}
});
Object.defineProperty(MeshInstance.prototype, 'material', {
get: function () {
return this._material;
},
set: function (material) {
var i;
for (i = 0; i < this._shader.length; i++) {
this._shader[i] = null;
}
// Remove the material's reference to this mesh instance
if (this._material) {
var meshInstances = this._material.meshInstances;
i = meshInstances.indexOf(this);
if (i !== -1) {
meshInstances.splice(i, 1);
}
}
var prevBlend = this._material ? (this._material.blendType !== pc.BLEND_NONE) : false;
var prevMat = this._material;
this._material = material;
if (this._material) {
// Record that the material is referenced by this mesh instance
this._material.meshInstances.push(this);
this.updateKey();
}
if (material) {
if ((material.blendType !== pc.BLEND_NONE) !== prevBlend) {
var scene = material._scene;
if (!scene && prevMat && prevMat._scene) scene = prevMat._scene;
if (scene) {
scene.layers._dirtyBlend = true;
} else {
material._dirtyBlend = true;
}
}
}
}
});
Object.defineProperty(MeshInstance.prototype, 'layer', {
get: function () {
return this._layer;
},
set: function (layer) {
this._layer = layer;
this.updateKey();
}
});
Object.defineProperty(MeshInstance.prototype, 'receiveShadow', {
get: function () {
return this._receiveShadow;
},
set: function (val) {
this._receiveShadow = val;
this._shaderDefs = val ? (this._shaderDefs & ~pc.SHADERDEF_NOSHADOW) : (this._shaderDefs | pc.SHADERDEF_NOSHADOW);
this._shader[pc.SHADER_FORWARD] = null;
this._shader[pc.SHADER_FORWARDHDR] = null;
}
});
Object.defineProperty(MeshInstance.prototype, 'skinInstance', {
get: function () {
return this._skinInstance;
},
set: function (val) {
this._skinInstance = val;
this._shaderDefs = val ? (this._shaderDefs | pc.SHADERDEF_SKIN) : (this._shaderDefs & ~pc.SHADERDEF_SKIN);
for (var i = 0; i < this._shader.length; i++) {
this._shader[i] = null;
}
}
});
Object.defineProperty(MeshInstance.prototype, 'screenSpace', {
get: function () {
return this._screenSpace;
},
set: function (val) {
this._screenSpace = val;
this._shaderDefs = val ? (this._shaderDefs | pc.SHADERDEF_SCREENSPACE) : (this._shaderDefs & ~pc.SHADERDEF_SCREENSPACE);
this._shader[pc.SHADER_FORWARD] = null;
}
});
Object.defineProperty(MeshInstance.prototype, 'key', {
get: function () {
return this._key[pc.SORTKEY_FORWARD];
},
set: function (val) {
this._key[pc.SORTKEY_FORWARD] = val;
}
});
/**
* @name pc.MeshInstance#mask
* @type Number
* @description Mask controlling which {@link pc.LightComponent}s light this mesh instance, which {@link pc.CameraComponent} sees it and in which {@link pc.Layer} it is rendered.
* Defaults to 1.
*/
Object.defineProperty(MeshInstance.prototype, 'mask', {
get: function () {
return this._shaderDefs >> 16;
},
set: function (val) {
var toggles = this._shaderDefs & 0x0000FFFF;
this._shaderDefs = toggles | (val << 16);
this._shader[pc.SHADER_FORWARD] = null;
this._shader[pc.SHADER_FORWARDHDR] = null;
}
});
Object.assign(MeshInstance.prototype, {
syncAabb: function () {
// Deprecated
},
updateKey: function () {
var material = this.material;
this._key[pc.SORTKEY_FORWARD] = getKey(this.layer,
(material.alphaToCoverage || material.alphaTest) ? pc.BLEND_NORMAL : material.blendType, // render alphatest/atoc after opaque
false, material.id);
},
setParameter: pc.Material.prototype.setParameter,
setParameters: pc.Material.prototype.setParameters,
deleteParameter: pc.Material.prototype.deleteParameter,
getParameter: pc.Material.prototype.getParameter,
getParameters: pc.Material.prototype.getParameters,
clearParameters: pc.Material.prototype.clearParameters
});
var Command = function (layer, blendType, command) {
this._key = [];
this._key[pc.SORTKEY_FORWARD] = getKey(layer, blendType, true, 0);
this.command = command;
};
Object.defineProperty(Command.prototype, 'key', {
get: function () {
return this._key[pc.SORTKEY_FORWARD];
},
set: function (val) {
this._key[pc.SORTKEY_FORWARD] = val;
}
});
var InstancingData = function (numObjects, dynamic, instanceSize) {
instanceSize = instanceSize || 16;
this.buffer = new Float32Array(numObjects * instanceSize);
this.count = numObjects;
this.offset = 0;
this.usage = dynamic ? pc.BUFFER_DYNAMIC : pc.BUFFER_STATIC;
this._buffer = null;
};
Object.assign(InstancingData.prototype, {
update: function () {
if (this._buffer) {
this._buffer.setData(this.buffer);
}
}
});
function getKey(layer, blendType, isCommand, materialId) {
// Key definition:
// Bit
// 31 : sign bit (leave)
// 27 - 30 : layer
// 26 : translucency type (opaque/transparent)
// 25 : Command bit (1: this key is for a command, 0: it's a mesh instance)
// 0 - 24 : Material ID (if oqaque) or 0 (if transparent - will be depth)
return ((layer & 0x0f) << 27) |
((blendType === pc.BLEND_NONE ? 1 : 0) << 26) |
((isCommand ? 1 : 0) << 25) |
((materialId & 0x1ffffff) << 0);
}
return {
Command: Command,
Mesh: Mesh,
MeshInstance: MeshInstance,
InstancingData: InstancingData,
_getDrawcallSortKey: getKey
};
}());