/** * Mesh Superset - The apiMesh properties get moved into the Mesh object itself, and then the instance is created * @param graphics GameLib.D3.Graphics * @param apiMesh GameLib.D3.API.Mesh * @constructor */ GameLib.D3.Mesh = function ( graphics, apiMesh ) { this.graphics = graphics; this.graphics.isNotThreeThrow(); if (GameLib.Utils.UndefinedOrNull(apiMesh)) { apiMesh = {}; } if (apiMesh instanceof GameLib.D3.Mesh) { return apiMesh; } GameLib.D3.API.Mesh.call( this, apiMesh.id, apiMesh.meshType, apiMesh.name, apiMesh.vertices, apiMesh.faces, apiMesh.faceVertexUvs, apiMesh.materials, apiMesh.parentMesh, apiMesh.parentScene, apiMesh.skeleton, apiMesh.skinIndices, apiMesh.skinWeights, apiMesh.position, apiMesh.quaternion, apiMesh.scale, apiMesh.localPosition, apiMesh.localRotation, apiMesh.localScale, apiMesh.up, apiMesh.modelMatrix, apiMesh.parentEntity, apiMesh.renderOrder ); this.materials = this.materials.map( function(material) { if (material instanceof GameLib.D3.API.Material) { return new GameLib.D3.Material( this.graphics, material ) } else { return material; } }.bind(this) ); if (this.skeleton) { this.skeleton = new GameLib.D3.Skeleton( this.graphics, this.skeleton ); } this.vertices = this.vertices.map( function (apiVertex) { return new GameLib.D3.Vertex( this.graphics, apiVertex ); }.bind(this) ); this.position = new GameLib.Vector3( this.graphics, this.position, this ); this.scale = new GameLib.Vector3( this.graphics, this.scale, this ); this.up = new GameLib.Vector3( this.graphics, this.up, this ); this.quaternion = new GameLib.Quaternion( this.graphics, this.quaternion, this ); this.localPosition = new GameLib.Vector3( this.graphics, this.localPosition, this ); this.localRotation = new GameLib.Vector3( this.graphics, this.localRotation, this ); this.localScale = new GameLib.Vector3( this.graphics, this.localScale, this ); this.modelMatrix = new GameLib.Matrix4( this.graphics, this.modelMatrix, this ); this.buildIdToObject(); this.instance = this.createInstance(); this.instance.geometry.computeBoundingBox(); this.width = this.instance.geometry.boundingBox.max.x - this.instance.geometry.boundingBox.min.x; this.height = this.instance.geometry.boundingBox.max.y - this.instance.geometry.boundingBox.min.y; this.depth = this.instance.geometry.boundingBox.max.z - this.instance.geometry.boundingBox.min.z; }; GameLib.D3.Mesh.prototype = Object.create(GameLib.D3.API.Mesh.prototype); GameLib.D3.Mesh.prototype.constructor = GameLib.D3.Mesh; /** * Mesh Type * @type {number} */ GameLib.D3.Mesh.TYPE_NORMAL = 0x0; GameLib.D3.Mesh.TYPE_SKINNED = 0x1; GameLib.D3.Mesh.TYPE_CURVE = 0x2; /** * Mesh Shape (predefined or custom) * @type {number} */ GameLib.D3.Mesh.SHAPE_CUSTOM = 0x1; GameLib.D3.Mesh.SHAPE_SPHERE = 0x2; /** * Creates custom geometry * @returns {THREE.Geometry} */ GameLib.D3.Mesh.prototype.customGeometry = function(){ var geometry = new THREE.Geometry(); /** * Setup vertices */ for (var v = 0; v < this.vertices.length; v++) { geometry.vertices.push( new this.graphics.instance.Vector3( this.vertices[v].position.x, this.vertices[v].position.y, this.vertices[v].position.z ) ) } /** * Setup faces */ for (var f = 0; f < this.faces.length; f++) { var face = new this.graphics.instance.Face3( this.faces[f].v0, this.faces[f].v1, this.faces[f].v2, new this.graphics.instance.Vector3( this.faces[f].normal.x, this.faces[f].normal.y, this.faces[f].normal.z ), new this.graphics.instance.Color( this.faces[f].color.r, this.faces[f].color.g, this.faces[f].color.b ), this.faces[f].materialIndex ); face.vertexColors = [ new this.graphics.instance.Color( this.faces[f].vertexColors[0].r, this.faces[f].vertexColors[0].g, this.faces[f].vertexColors[0].b ), new this.graphics.instance.Color( this.faces[f].vertexColors[1].r, this.faces[f].vertexColors[1].g, this.faces[f].vertexColors[1].b ), new this.graphics.instance.Color( this.faces[f].vertexColors[2].r, this.faces[f].vertexColors[2].g, this.faces[f].vertexColors[2].b ) ]; face.normal = new this.graphics.instance.Vector3( this.faces[f].normal.x, this.faces[f].normal.y, this.faces[f].normal.z ); face.vertexNormals = [ new this.graphics.instance.Vector3( this.faces[f].vertexNormals[0].x, this.faces[f].vertexNormals[0].y, this.faces[f].vertexNormals[0].z ), new this.graphics.instance.Vector3( this.faces[f].vertexNormals[1].x, this.faces[f].vertexNormals[1].y, this.faces[f].vertexNormals[1].z ), new this.graphics.instance.Vector3( this.faces[f].vertexNormals[2].x, this.faces[f].vertexNormals[2].y, this.faces[f].vertexNormals[2].z ) ]; geometry.faces.push(face); } geometry.faceVertexUvs = []; /** * Setup face UVs */ for (var fm = 0; fm < this.faceVertexUvs.length; fm++) { var faceMaterialVertexUvs = this.faceVertexUvs[fm]; geometry.faceVertexUvs[fm] = []; for (var fuv = 0; fuv < faceMaterialVertexUvs.length; fuv++) { geometry.faceVertexUvs[fm][fuv] = []; geometry.faceVertexUvs[fm][fuv].push( new this.graphics.instance.Vector2( faceMaterialVertexUvs[fuv][0].x, faceMaterialVertexUvs[fuv][0].y ), new this.graphics.instance.Vector2( faceMaterialVertexUvs[fuv][1].x, faceMaterialVertexUvs[fuv][1].y ), new this.graphics.instance.Vector2( faceMaterialVertexUvs[fuv][2].x, faceMaterialVertexUvs[fuv][2].y ) ); } } return geometry; }; /** * Creates a mesh instance or updates it */ GameLib.D3.Mesh.prototype.createInstance = function(update) { if (update) { if (this.parentMesh && this.parentMesh.loaded) { this.instance.parent = this.parentMesh.instance; this.instance.position.x = this.localPosition.x; this.instance.position.y = this.localPosition.y; this.instance.position.z = this.localPosition.z; this.instance.rotation.x = this.localRotation.x; this.instance.rotation.y = this.localRotation.y; this.instance.rotation.z = this.localRotation.z; this.instance.scale.x = this.localScale.x; this.instance.scale.y = this.localScale.y; this.instance.scale.z = this.localScale.z; } else { this.instance.quaternion.x = this.quaternion.x; this.instance.quaternion.y = this.quaternion.y; this.instance.quaternion.z = this.quaternion.z; this.instance.quaternion.w = this.quaternion.w; this.instance.position.x = this.position.x + this.localPosition.x; this.instance.position.y = this.position.y + this.localPosition.y; this.instance.position.z = this.position.z + this.localPosition.z; this.instance.scale.x = this.scale.x * this.localScale.x; this.instance.scale.y = this.scale.y * this.localScale.y; this.instance.scale.z = this.scale.z * this.localScale.z; this.instance.up.x = this.up.x; this.instance.up.y = this.up.y; this.instance.up.z = this.up.z; this.instance.rotateX(this.localRotation.x); this.instance.rotateY(this.localRotation.y); this.instance.rotateZ(this.localRotation.z); } this.instance.name = this.name; if (this.materials.length === 1 && this.materials[0].instance) { this.instance.material = this.materials[0].instance; } this.instance.renderOrder = this.renderOrder; } else { var instance = null; var geometry = null; if (this instanceof GameLib.D3.Mesh.Sphere) { geometry = this.sphereGeometry(); } else { geometry = this.customGeometry(); } geometry.computeFaceNormals(); geometry.computeVertexNormals(); if (this.meshType === GameLib.D3.Mesh.TYPE_NORMAL) { instance = new THREE.Mesh(geometry); } else if (this.meshType === GameLib.D3.Mesh.TYPE_CURVE) { instance = new THREE.Points(geometry); } else if (this.meshType === GameLib.D3.Mesh.TYPE_SKINNED) { /** * Setup bones (indexes) */ for (var si = 0; si < this.skinIndices.length; si++) { geometry.skinIndices.push( new THREE.Vector4( this.skinIndices[si].x, this.skinIndices[si].y, this.skinIndices[si].z, this.skinIndices[si].w ) ); } /** * Setup bones (weights) */ for (var sw = 0; sw < this.skinWeights.length; sw++) { geometry.skinWeights.push( new THREE.Vector4( this.skinWeights[sw].x, this.skinWeights[sw].y, this.skinWeights[sw].z, this.skinWeights[sw].w ) ); } instance = new THREE.SkinnedMesh(geometry); instance.add(this.skeleton.rootBoneInstance); instance.bind(this.skeleton.instance); } else { console.log('cannot handle meshes of type ' + this.meshType + ' yet.'); } instance.name = this.name; if (this.parentMesh && this.parentMesh.loaded) { instance.parent = this.parentMesh.instance; instance.position.x = this.localPosition.x; instance.position.y = this.localPosition.y; instance.position.z = this.localPosition.z; instance.rotation.x = this.localRotation.x; instance.rotation.y = this.localRotation.y; instance.rotation.z = this.localRotation.z; instance.scale.x = this.localScale.x; instance.scale.y = this.localScale.y; instance.scale.z = this.localScale.z; } else { instance.quaternion.x = this.quaternion.x; instance.quaternion.y = this.quaternion.y; instance.quaternion.z = this.quaternion.z; instance.quaternion.w = this.quaternion.w; instance.position.x = this.position.x + this.localPosition.x; instance.position.y = this.position.y + this.localPosition.y; instance.position.z = this.position.z + this.localPosition.z; instance.scale.x = this.scale.x * this.localScale.x; instance.scale.y = this.scale.y * this.localScale.y; instance.scale.z = this.scale.z * this.localScale.z; instance.up.x = this.up.x; instance.up.y = this.up.y; instance.up.z = this.up.z; instance.rotateX(this.localRotation.x); instance.rotateY(this.localRotation.y); instance.rotateZ(this.localRotation.z); } if (this.materials.length === 1 && this.materials[0].instance) { instance.material = this.materials[0].instance; } instance.renderOrder = this.renderOrder; this.subscribe( GameLib.Event.MATERIAL_LOADED, function(data) { if (this.instance.material === data.material.instance) { return; } if (this.materials[0] === data.material) { this.instance.material = data.material.instance; this.instance.geometry.uvsNeedUpdate = true; } } ); this.subscribe( GameLib.Event.MATERIAL_TYPE_CHANGED, function(data) { if (this.materials[0].id === data.material.id) { this.instance.material = data.material.instance; this.instance.geometry.uvsNeedUpdate = true; } } ); return instance; } }; /** * Updates the mesh instance */ GameLib.D3.Mesh.prototype.updateInstance = function() { this.createInstance(true); }; /** * Converts a GameLib.D3.Mesh to a GameLib.D3.API.Mesh * @returns {GameLib.D3.API.Mesh} */ GameLib.D3.Mesh.prototype.toApiObject = function(save) { var apiSkeleton = null; if (this.skeleton) { if (save) { this.skeleton.save(); } apiSkeleton = this.skeleton.id; } var apiMaterials = null; if (this.materials) { apiMaterials = this.materials.map( function(material) { if (save) { material.save(); } return material.id; } ) } return new GameLib.D3.API.Mesh( this.id, this.meshType, this.name, this.vertices.map( function (vertex) { return vertex.toApiObject(); } ), this.faces, this.faceVertexUvs, apiMaterials, GameLib.Utils.IdOrNull(this.parentMesh), GameLib.Utils.IdOrNull(this.parentScene), apiSkeleton, this.skinIndices, this.skinWeights, this.position.toApiObject(), this.quaternion.toApiObject(), this.scale.toApiObject(), this.localPosition.toApiObject(), this.localRotation.toApiObject(), this.localScale.toApiObject(), this.up.toApiObject(), this.modelMatrix.toApiObject(), GameLib.Utils.IdOrNull(this.parentEntity), this.renderOrder ); }; /** * Converts a standard object mesh to a GameLib.D3.Mesh * @param graphics GameLib.D3.Graphics * @param objectMesh {Object} * @constructor */ GameLib.D3.Mesh.FromObjectMesh = function(graphics, objectMesh) { var apiMesh = GameLib.D3.API.Mesh.FromObjectMesh(objectMesh); return new GameLib.D3.Mesh( graphics, apiMesh ); }; /** * Centers the mesh around origin */ GameLib.D3.Mesh.prototype.centerAroundOrigin = function() { var localPosition = this.instance.geometry.center(); this.instance.position.set(0,0,0); this.instance.updateMatrix(); this.position.x = this.instance.position.x; this.position.y = this.instance.position.y; this.position.z = this.instance.position.z; for (var v = 0; v < this.instance.geometry.vertices.length; v++) { this.vertices[v].position.x = this.instance.geometry.vertices[v].x; this.vertices[v].position.y = this.instance.geometry.vertices[v].y; this.vertices[v].position.z = this.instance.geometry.vertices[v].z; } this.localPosition.x = -localPosition.x; this.localPosition.y = -localPosition.y; this.localPosition.z = -localPosition.z; this.updateInstance(); };