Object.assign(pc, function () {
/**
* @constructor
* @name pc.Model
* @classdesc A model is a graphical object that can be added to or removed from a scene.
* It contains a hierarchy and any number of mesh instances.
* @description Creates a new model.
* @example
* // Create a new model
* var model = new pc.Model();
* @property {pc.GraphNode} graph The root node of the model's graph node hierarchy.
* @property {pc.MeshInstance[]} meshInstances An array of meshInstances contained in this model.
*/
var Model = function Model() {
this.graph = null;
this.meshInstances = [];
this.skinInstances = [];
this.morphInstances = [];
this.cameras = [];
this.lights = [];
this._shadersVersion = 0;
};
Object.assign(Model.prototype, {
getGraph: function () {
return this.graph;
},
setGraph: function (graph) {
this.graph = graph;
},
getCameras: function () {
return this.cameras;
},
setCameras: function (cameras) {
this.cameras = cameras;
},
getLights: function () {
return this.lights;
},
setLights: function (lights) {
this.lights = lights;
},
getMaterials: function () {
var i;
var materials = [];
for (i = 0; i < this.meshInstances.length; i++) {
var meshInstance = this.meshInstances[i];
if (materials.indexOf(meshInstance.material) === -1) {
materials.push(meshInstance.material);
}
}
return materials;
},
/**
* @function
* @name pc.Model#clone
* @description Clones a model. The returned model has a newly created hierarchy
* and mesh instances, but meshes are shared between the clone and the specified
* model.
* @returns {pc.Model} A clone of the specified model.
* @example
* var clonedModel = model.clone();
*/
clone: function () {
var i, j;
// Duplicate the node hierarchy
var srcNodes = [];
var cloneNodes = [];
var _duplicate = function (node) {
var newNode = node.clone();
srcNodes.push(node);
cloneNodes.push(newNode);
for (var idx = 0; idx < node._children.length; idx++) {
newNode.addChild(_duplicate(node._children[idx]));
}
return newNode;
};
var cloneGraph = _duplicate(this.graph);
var cloneMeshInstances = [];
var cloneSkinInstances = [];
var cloneMorphInstances = [];
// Clone the skin instances
for (i = 0; i < this.skinInstances.length; i++) {
var skin = this.skinInstances[i].skin;
var cloneSkinInstance = new pc.SkinInstance(skin);
// Resolve bone IDs to actual graph nodes
var bones = [];
for (j = 0; j < skin.boneNames.length; j++) {
var boneName = skin.boneNames[j];
var bone = cloneGraph.findByName(boneName);
bones.push(bone);
}
cloneSkinInstance.bones = bones;
cloneSkinInstances.push(cloneSkinInstance);
}
// Clone the morph instances
for (i = 0; i < this.morphInstances.length; i++) {
var morph = this.morphInstances[i].morph;
var cloneMorphInstance = new pc.MorphInstance(morph);
cloneMorphInstances.push(cloneMorphInstance);
}
// Clone the mesh instances
for (i = 0; i < this.meshInstances.length; i++) {
var meshInstance = this.meshInstances[i];
var nodeIndex = srcNodes.indexOf(meshInstance.node);
var cloneMeshInstance = new pc.MeshInstance(cloneNodes[nodeIndex], meshInstance.mesh, meshInstance.material);
if (meshInstance.skinInstance) {
var skinInstanceIndex = this.skinInstances.indexOf(meshInstance.skinInstance);
cloneMeshInstance.skinInstance = cloneSkinInstances[skinInstanceIndex];
}
if (meshInstance.morphInstance) {
var morphInstanceIndex = this.morphInstances.indexOf(meshInstance.morphInstance);
cloneMeshInstance.morphInstance = cloneMorphInstances[morphInstanceIndex];
}
cloneMeshInstances.push(cloneMeshInstance);
}
var clone = new pc.Model();
clone.graph = cloneGraph;
clone.meshInstances = cloneMeshInstances;
clone.skinInstances = cloneSkinInstances;
clone.morphInstances = cloneMorphInstances;
clone.getGraph().syncHierarchy();
return clone;
},
/**
* @function
* @name pc.Model#destroy
* @description destroys skinning texture and possibly deletes vertex/index buffers of a model.
* Mesh is reference-counted, so buffers are only deleted if all models with referencing mesh instances were deleted.
* That means all in-scene models + the "base" one (asset.resource) which is created when the model is parsed.
* It is recommended to use asset.unload() instead, which will also remove the model from the scene.
*/
destroy: function () {
var meshInstances = this.meshInstances;
var meshInstance, mesh, skin, morph, ib, boneTex, j;
var device;
for (var i = 0; i < meshInstances.length; i++) {
meshInstance = meshInstances[i];
mesh = meshInstance.mesh;
if (mesh) {
mesh._refCount--;
if (mesh._refCount < 1) {
if (mesh.vertexBuffer) {
device = device || mesh.vertexBuffer.device;
mesh.vertexBuffer.destroy();
mesh.vertexBuffer = null;
}
for (j = 0; j < mesh.indexBuffer.length; j++) {
device = device || mesh.indexBuffer.device;
ib = mesh.indexBuffer[j];
if (!ib) continue;
ib.destroy();
}
mesh.indexBuffer.length = 0;
}
}
skin = meshInstance.skinInstance;
if (skin) {
boneTex = skin.boneTexture;
if (boneTex) {
boneTex.destroy();
}
}
meshInstance.skinInstance = null;
morph = meshInstance.morphInstance;
if (morph) {
morph.destroy();
}
meshInstance.morphInstance = null;
meshInstance.material = null; // make sure instance and material clear references
}
},
/**
* @function
* @name pc.Model#generateWireframe
* @description Generates the necessary internal data for a model to be
* renderable as wireframe. Once this function has been called, any mesh
* instance in the model can have its renderStyle property set to
* pc.RENDERSTYLE_WIREFRAME
* @example
* model.generateWireframe();
* for (var i = 0; i < model.meshInstances.length; i++) {
* model.meshInstances[i].renderStyle = pc.RENDERSTYLE_WIREFRAME;
* }
*/
generateWireframe: function () {
var i, j, k;
var i1, i2;
var mesh, base, count, indexBuffer, wireBuffer;
var srcIndices, dstIndices;
// Build an array of unique meshes in this model
var meshes = [];
for (i = 0; i < this.meshInstances.length; i++) {
mesh = this.meshInstances[i].mesh;
if (meshes.indexOf(mesh) === -1) {
meshes.push(mesh);
}
}
var offsets = [[0, 1], [1, 2], [2, 0]];
for (i = 0; i < meshes.length; i++) {
mesh = meshes[i];
base = mesh.primitive[pc.RENDERSTYLE_SOLID].base;
count = mesh.primitive[pc.RENDERSTYLE_SOLID].count;
indexBuffer = mesh.indexBuffer[pc.RENDERSTYLE_SOLID];
srcIndices = new Uint16Array(indexBuffer.lock());
var uniqueLineIndices = {};
var lines = [];
for (j = base; j < base + count; j += 3) {
for (k = 0; k < 3; k++) {
i1 = srcIndices[j + offsets[k][0]];
i2 = srcIndices[j + offsets[k][1]];
var line = (i1 > i2) ? ((i2 << 16) | i1) : ((i1 << 16) | i2);
if (uniqueLineIndices[line] === undefined) {
uniqueLineIndices[line] = 0;
lines.push(i1, i2);
}
}
}
indexBuffer.unlock();
wireBuffer = new pc.IndexBuffer(indexBuffer.device, pc.INDEXFORMAT_UINT16, lines.length);
dstIndices = new Uint16Array(wireBuffer.lock());
dstIndices.set(lines);
wireBuffer.unlock();
mesh.primitive[pc.RENDERSTYLE_WIREFRAME] = {
type: pc.PRIMITIVE_LINES,
base: 0,
count: lines.length,
indexed: true
};
mesh.indexBuffer[pc.RENDERSTYLE_WIREFRAME] = wireBuffer;
}
}
});
return {
Model: Model
};
}());