diff --git a/build/game-lib-min.js b/build/game-lib-min.js new file mode 100644 index 0000000..1595b67 --- /dev/null +++ b/build/game-lib-min.js @@ -0,0 +1,2 @@ +function GameLib(){}"undefined"==typeof GameLib.D3&&(GameLib.D3=function(){}),GameLib.D3.BoneWeight=function(e,i){this.boneIndex=e,this.weight=i},GameLib.D3.Bone=function(e,i,t,n,s,o,a,r,h,l){this.id=e,this.name=t,this.boneId=i,"undefined"==typeof n&&(n=[]),this.childBoneIds=n,"undefined"==typeof s&&(s=null),this.parentBoneId=s,"undefined"==typeof o&&(o=new GameLib.D3.Vector4),this.quaternion=o,"undefined"==typeof a&&(a=new GameLib.D3.Vector3(0,0,0)),this.position=a,"undefined"==typeof r&&(r=new GameLib.D3.Vector3(0,0,0)),this.rotation=r,"undefined"==typeof h&&(h=new GameLib.D3.Vector3(1,1,1)),this.scale=h,"undefined"==typeof l&&(l=new GameLib.D3.Vector3(0,1,0)),this.up=l},GameLib.D3.Broadphase=function(e,i,t,n,s){this.id=e,"undefined"==typeof i&&(i="broadphase-"+t),this.name=i,"undefined"==typeof t&&(t=GameLib.D3.Broadphase.BROADPHASE_TYPE_NAIVE),this.broadphaseType=t,"undefined"==typeof n&&(n=null),this.engine=n,this.instance=null,s&&this.createInstance()},GameLib.D3.Broadphase.prototype.createInstance=function(){if(!(this.engine instanceof GameLib.D3.Engine))throw console.warn("No Engine"),new Error("No Engine");this.engine.isNotCannonThrow();var e=null;if(this.broadphaseType==GameLib.D3.Broadphase.BROADPHASE_TYPE_NAIVE)e=new this.engine.instance.NaiveBroadphase;else if(this.broadphaseType==GameLib.D3.Broadphase.BROADPHASE_TYPE_GRID)e=new this.engine.instance.GridBroadphase;else{if(this.broadphaseType!=GameLib.D3.Broadphase.BROADPHASE_TYPE_SAP)throw console.warn("Unsupported broadphase type: "+this.broadphaseType),new Error("Unsupported broadphase type: "+this.broadphaseType);e=new this.engine.instance.SAPBroardphase}return this.instance=e,e},GameLib.D3.Broadphase.BROADPHASE_TYPE_NAIVE=1,GameLib.D3.Broadphase.BROADPHASE_TYPE_GRID=2,GameLib.D3.Broadphase.BROADPHASE_TYPE_SAP=3,GameLib.D3.Color=function(e,i,t,n){this.r=e,this.g=i,this.b=t,this.a=n},GameLib.D3.Engine=function(e,i){this.engineType=e,this.instance=i},GameLib.D3.Engine.prototype.isCannon=function(){return this.engineType==GameLib.D3.Engine.ENGINE_TYPE_CANNON},GameLib.D3.Engine.prototype.isNotCannonThrow=function(){if(this.engineType!=GameLib.D3.Engine.ENGINE_TYPE_CANNON)throw console.warn("Only CANNON supported for this function"),new Error("Only CANNON supported for this function")},GameLib.D3.Engine.prototype.isAmmo=function(){return this.engineType==GameLib.D3.Engine.ENGINE_TYPE_AMMO},GameLib.D3.Engine.prototype.isGoblin=function(){return this.engineType==GameLib.D3.Engine.ENGINE_TYPE_GOBLIN},GameLib.D3.Engine.ENGINE_TYPE_CANNON=1,GameLib.D3.Engine.ENGINE_TYPE_AMMO=2,GameLib.D3.Engine.ENGINE_TYPE_GOBLIN=3,GameLib.D3.FlyControls=function(e,i,t){this.flySpeed=100,this.canvas=t,this.THREE=i,this.yaw=0,this.pitch=0,this.canRotate=!1,this.moveForward=!1,this.moveBackward=!1,this.moveLeft=!1,this.moveRight=!1,this.moveUp=!1,this.moveDown=!1,this.mouseUpCallback=this.onMouseUp.bind(this),this.mouseDownCallback=this.onMouseDown.bind(this),this.mouseMoveCallback=this.onMouseMove.bind(this),this.mouseWheelCallback=this.onMouseWheel.bind(this),this.keyDownCallback=this.onKeyDown.bind(this),this.keyUpCallback=this.onKeyUp.bind(this),this.camera=e,this.canvas.addEventListener("keydown",this.keyDownCallback,!1),this.canvas.addEventListener("keyup",this.keyUpCallback,!1),this.canvas.addEventListener("mousedown",this.mouseDownCallback,!1),this.canvas.addEventListener("mouseup",this.mouseUpCallback,!1),this.canvas.addEventListener("mousewheel",this.mouseWheelCallback,!1),this.havePointerLock="pointerLockElement"in document||"mozPointerLockElement"in document||"webkitPointerLockElement"in document,this.element=document.body,this.havePointerLock&&(this.element.requestPointerLock=this.element.requestPointerLock||this.element.mozRequestPointerLock||this.element.webkitRequestPointerLock,document.exitPointerLock=document.exitPointerLock||document.mozExitPointerLock||document.webkitExitPointerLock)},GameLib.D3.FlyControls.prototype.onMouseWheel=function(e){this.moveForward=!0,this.applyTranslation(.001*e.wheelDelta),e.preventDefault(),this.moveForward=!1},GameLib.D3.FlyControls.prototype.onMouseDown=function(e){1==e.button&&(this.canRotate=!0,this.canvas.addEventListener("mousemove",this.mouseMoveCallback,!1))},GameLib.D3.FlyControls.prototype.onMouseUp=function(e){1==e.button&&(this.canRotate=!1,this.canvas.removeEventListener("mousemove",this.mouseMoveCallback))},GameLib.D3.FlyControls.prototype.applyRotation=function(){this.camera.rotation.set(this.pitch,this.yaw,0,"YXZ")},GameLib.D3.FlyControls.prototype.applyTranslation=function(e){var i=new this.THREE.Vector3(0,0,-1),t=new this.THREE.Euler(0,0,0,"YXZ");t.set(this.pitch,this.yaw,0,"YXZ"),i=i.applyEuler(t);var n=i.normalize(),s=n.cross(new this.THREE.Vector3(0,1,0));this.moveForward?(this.camera.position.x+=n.x*(e*this.flySpeed),this.camera.position.y+=n.y*(e*this.flySpeed),this.camera.position.z+=n.z*(e*this.flySpeed)):this.moveBackward&&(this.camera.position.x-=n.x*(e*this.flySpeed),this.camera.position.y-=n.y*(e*this.flySpeed),this.camera.position.z-=n.z*(e*this.flySpeed)),this.moveLeft?(this.camera.position.x-=s.x*(e*this.flySpeed),this.camera.position.y-=s.y*(e*this.flySpeed),this.camera.position.z-=s.z*(e*this.flySpeed)):this.moveRight&&(this.camera.position.x+=s.x*(e*this.flySpeed),this.camera.position.y+=s.y*(e*this.flySpeed),this.camera.position.z+=s.z*(e*this.flySpeed)),this.moveUp?this.camera.position.y+=e*this.flySpeed:this.moveDown&&(this.camera.position.y-=e*this.flySpeed)},GameLib.D3.FlyControls.prototype.update=function(e){this.applyRotation(),this.applyTranslation(e)},GameLib.D3.FlyControls.prototype.onMouseMove=function(e){if(this.canRotate){var i=e.movementX||e.mozMovementX||e.webkitMovementX||0,t=e.movementY||e.mozMovementY||e.webkitMovementY||0;this.yaw-=.002*i,this.pitch-=.002*t}},GameLib.D3.FlyControls.prototype.onKeyDown=function(e){switch(e.keyCode){case 87:this.moveForward=!0;break;case 65:this.moveLeft=!0;break;case 83:this.moveBackward=!0;break;case 68:this.moveRight=!0;break;case 104:this.moveUp=!0;break;case 98:this.moveDown=!0}},GameLib.D3.FlyControls.prototype.onKeyUp=function(e){switch(e.keyCode){case 38:case 87:this.moveForward=!1;break;case 37:case 65:this.moveLeft=!1;break;case 40:case 83:this.moveBackward=!1;break;case 39:case 68:this.moveRight=!1;break;case 104:this.moveUp=!1;break;case 98:this.moveDown=!1}},GameLib.D3.Game=function(){this.scenes={},this.physicsWorlds=[],this.sceneToPhysicsWorldsMap={}},GameLib.D3.Game.prototype.AddScene=function(e){this.scenes[e.name]=e},GameLib.D3.Game.prototype.AddPhysicsWorld=function(e){this.physicsWorlds.push(e)},GameLib.D3.Game.prototype.LinkPhysicsWorldToScene=function(e,i){this.sceneToPhysicsWorldsMap[i.name]=this.sceneToPhysicsWorldsMap[i.name]||[],this.sceneToPhysicsWorldsMap[i.name].push(e)},GameLib.D3.Game.prototype.GetPhysicsWorldsForScene=function(e){return this.sceneToPhysicsWorldsMap[e.name]},GameLib.D3.Game.prototype.ProcessPhysics=function(e){for(var i in this.sceneToPhysicsWorldsMap){var t=this.sceneToPhysicsWorldsMap[i],n=this.scenes[i];if(n&&t)for(var s=0,o=t.length;s0){var s=this.loadMaps(e,n,t);Q.all(s).then(function(){i.resolve(t)}).catch(function(e){console.log(e),i.reject(e)})}else i.resolve(t);return i.promise},GameLib.D3.Matrix3=function(e,i,t){this.identity(),e&&(this.rows[0]=e),i&&(this.rows[1]=i),t&&(this.rows[2]=t)},GameLib.D3.Matrix3.prototype.identity=function(){this.rows=[new GameLib.D3.Vector4(1,0,0),new GameLib.D3.Vector4(0,1,0),new GameLib.D3.Vector4(0,0,1)]},GameLib.D3.Matrix4=function(e,i,t,n){this.identity(),e&&(this.rows[0]=e),i&&(this.rows[1]=i),t&&(this.rows[2]=t),n&&(this.rows[3]=n)},GameLib.D3.Matrix4.prototype.rotationMatrixX=function(e){return this.identity(),this.rows[1]=new GameLib.D3.Vector4(0,Math.cos(e),-1*Math.sin(e),0),this.rows[2]=new GameLib.D3.Vector4(0,Math.sin(e),Math.cos(e),0),this},GameLib.D3.Matrix4.prototype.rotationMatrixY=function(e){return this.identity(),this.rows[0]=new GameLib.D3.Vector4(Math.cos(e),0,Math.sin(e),0),this.rows[2]=new GameLib.D3.Vector4(-1*Math.sin(e),0,Math.cos(e),0),this},GameLib.D3.Matrix4.prototype.rotationMatrixZ=function(e){return this.identity(),this.rows[0]=new GameLib.D3.Vector4(Math.cos(e),-1*Math.sin(e),0,0),this.rows[1]=new GameLib.D3.Vector4(Math.sin(e),Math.cos(e),0,0),this},GameLib.D3.Matrix4.prototype.rotateX=function(e,i){return this.identity(),this.rotationMatrixX(e),this.multiply(i)},GameLib.D3.Matrix4.prototype.rotateY=function(e,i){return this.identity(),this.rotationMatrixY(e),this.multiply(i)},GameLib.D3.Matrix4.prototype.rotateZ=function(e,i){return this.identity(),this.rotationMatrixZ(e),this.multiply(i)},GameLib.D3.Matrix4.prototype.multiply=function(e){return e instanceof GameLib.D3.Vector4?new GameLib.D3.Vector4(this.rows[0].x*e.x+this.rows[0].y*e.y+this.rows[0].z*e.z+this.rows[0].w*e.w,this.rows[1].x*e.x+this.rows[1].y*e.y+this.rows[1].z*e.z+this.rows[1].w*e.w,this.rows[2].x*e.x+this.rows[2].y*e.y+this.rows[2].z*e.z+this.rows[2].w*e.w,this.rows[3].x*e.x+this.rows[3].y*e.y+this.rows[3].z*e.z+this.rows[3].w*e.w):e instanceof GameLib.D3.Vector3?new GameLib.D3.Vector3(this.rows[0].x*e.x+this.rows[0].y*e.y+this.rows[0].z*e.z,this.rows[1].x*e.x+this.rows[1].y*e.y+this.rows[1].z*e.z,this.rows[2].x*e.x+this.rows[2].y*e.y+this.rows[2].z*e.z):void 0},GameLib.D3.Matrix4.prototype.identity=function(){this.rows=[new GameLib.D3.Vector4(1,0,0,0),new GameLib.D3.Vector4(0,1,0,0),new GameLib.D3.Vector4(0,0,1,0),new GameLib.D3.Vector4(0,0,0,1)]},GameLib.D3.Matrix4.prototype.lookAt=function(e,i,t){var n=new GameLib.D3.Vector3(e.x,e.y,e.z),s=n.subtract(i).normalize();0===s.squared()&&(s.z=1);var o=t.cross(s).normalize();0===o.squared()&&(s.x+=1e-4,o=t.cross(s).normalize());var a=s.cross(o);return this.rows[0].x=o.x,this.rows[0].y=o.y,this.rows[0].z=o.z,this.rows[1].x=a.x,this.rows[1].y=a.y,this.rows[1].z=a.z,this.rows[2].x=s.x,this.rows[2].y=s.y,this.rows[2].z=s.z,this},GameLib.D3.Mesh=function(e,i,t,n,s,o,a,r,h,l,c,p,m,d,y,u,f,b,L,v){this.id=e,this.path=i,this.name=t,this.meshType=n,this.vertices=s,this.faces=o,"undefined"==typeof a&&(a=null),this.skeleton=a,"undefined"==typeof r&&(r=[]),this.faceVertexUvs=r,"undefined"==typeof h&&(h=[]),this.skinIndices=h,"undefined"==typeof l&&(l=[]),this.skinWeights=l,"undefined"==typeof c&&(c=[]),this.materials=c,"undefined"==typeof p&&(p=new GameLib.D3.Vector3(0,0,0)),this.position=p,"undefined"==typeof m&&new GameLib.D3.Vector4,this.quaternion=m,"undefined"==typeof d&&(d=new GameLib.D3.Vector3(0,0,0)),this.rotation=d,"undefined"==typeof y&&(y=new GameLib.D3.Vector3(1,1,1)),this.scale=y,"undefined"==typeof u&&(u=new GameLib.D3.Vector3(0,1,0)),this.up=u,this.physics=f,this.parentMeshId=b,this.parentSceneId=L,this.rawData=null},GameLib.D3.Mesh.TYPE_NORMAL=0,GameLib.D3.Mesh.TYPE_SKINNED=1,GameLib.D3.prototype.createThreeMesh=function(e,i,t){var n=null;if(e.meshType==GameLib.D3.Mesh.TYPE_NORMAL&&(n=new this.THREE.Mesh(i,t)),e.meshType==GameLib.D3.Mesh.TYPE_SKINNED){for(var s=e.skeleton.bones,o=e.skinIndices,a=e.skinWeights,r=[],h=0;h0;){var a=s.pop();if(a.triangle.v0==a.edge.x&&a.triangle.v1==a.edge.y||a.triangle.v1==a.edge.x&&a.triangle.v2==a.edge.y||a.triangle.v2==a.edge.x&&a.triangle.v0==a.edge.y){var r=a.triangle.v1;a.triangle.v1=a.triangle.v2,a.triangle.v2=r;var h=a.triangle.v1uv;a.triangle.v1uv=a.triangle.v2uv,a.triangle.v2uv=h}o.push(a);for(var l=[new GameLib.D3.Vector2(a.triangle.v0,a.triangle.v1),new GameLib.D3.Vector2(a.triangle.v1,a.triangle.v2),new GameLib.D3.Vector2(a.triangle.v2,a.triangle.v0)],c=0;c9||console.log("The vertices are not in the right length : "+e.length);for(var t=[],n=new GameLib.D3.Vector4.Points,s=0;s0)for(var a=0;a0},GameLib.D3.Vector3.normal=function(e,i,t){var n=i.copy(),s=t.copy();return n.subtract(e).cross(s.subtract(e))},GameLib.D3.Vector3.prototype.lookAt=function(e,i){var t=GameLib.D3.Matrix4.lookAt(this,e,i);this.multiply(t)},GameLib.D3.Vector3.prototype.translate=function(e){return this.x+=e.x,this.y+=e.y,this.z+=e.z,this},GameLib.D3.Vector3.prototype.squared=function(){return this.x*this.x+this.y*this.y+this.z*this.z},GameLib.D3.Vector3.prototype.copy=function(){return new GameLib.D3.Vector3(this.x,this.y,this.z)},GameLib.D3.Vector3.prototype.multiply=function(e){if(e instanceof GameLib.D3.Vector3)this.x*=e.x,this.y*=e.y,this.z*=e.z;else{if(!(e instanceof GameLib.D3.Matrix4))throw console.log("functionality not implemented - please do this"),new Error("not implemented");var i=e.rows[0].x*this.x+e.rows[0].y*this.y+e.rows[0].z*this.z,t=e.rows[1].x*this.x+e.rows[1].y*this.y+e.rows[1].z*this.z,n=e.rows[2].x*this.x+e.rows[2].y*this.y+e.rows[2].z*this.z;this.x=i,this.y=t,this.z=n}return this},GameLib.D3.Vector3.prototype.dot=function(e){return this.x*e.x+this.y*e.y+this.z*e.z},GameLib.D3.Vector3.prototype.normalize=function(){var e=1e-6,i=this.squared();if(io&&(o=h.x,n=i*e)}this.vectors=s;for(var l=(new GameLib.D3.Matrix4).rotationMatrixY(n),c=0;co&&(o=h.y,n=i*e)}this.vectors=s;for(var l=(new GameLib.D3.Matrix4).rotationMatrixX(n),c=0;cn&&(n=this.vectors[a].x),this.vectors[a].y>s&&(s=this.vectors[a].y),this.vectors[a].z>o&&(o=this.vectors[a].z);return new GameLib.D3.Vector3(Math.abs(n-e),Math.abs(s-i),Math.abs(s-t))},GameLib.D3.Vector4.Points.prototype.average=function(){for(var e=0,i=0,t=0,n=0;n 0) { + var textureMaps = this.loadMaps(blenderMaterial, blenderMaps, threeMaterial); + Q.all(textureMaps).then( + function(){ + defer.resolve(threeMaterial); + } + ).catch( + function(error){ + console.log(error); + defer.reject(error); + } + ) + } else { + defer.resolve(threeMaterial); + } + + return defer.promise; +}; +/** + * Matrix 3 Maths + * @param row0 GameLib.D3.Vector3 + * @param row1 GameLib.D3.Vector3 + * @param row2 GameLib.D3.Vector3 + * @constructor + */ +GameLib.D3.Matrix3 = function( + row0, + row1, + row2 +) { + this.identity(); + + if (row0) { + this.rows[0] = row0; + } + + if (row1) { + this.rows[1] = row1; + } + + if (row2) { + this.rows[2] = row2; + } +}; + +/** + * Set matrix to identity + */ +GameLib.D3.Matrix3.prototype.identity = function () { + this.rows = [ + new GameLib.D3.Vector4(1, 0, 0), + new GameLib.D3.Vector4(0, 1, 0), + new GameLib.D3.Vector4(0, 0, 1) + ]; +}; +GameLib.D3.Matrix4 = function( + row0, + row1, + row2, + row3 +) { + + this.identity(); + + if (row0) { + this.rows[0] = row0; + } + + if (row1) { + this.rows[1] = row1; + } + + if (row2) { + this.rows[2] = row2; + } + + if (row3) { + this.rows[3] = row3; + } +}; + +GameLib.D3.Matrix4.prototype.rotationMatrixX = function (radians) { + this.identity(); + this.rows[1] = new GameLib.D3.Vector4(0, Math.cos(radians), -1 * Math.sin(radians), 0); + this.rows[2] = new GameLib.D3.Vector4(0, Math.sin(radians), Math.cos(radians), 0); + return this; +}; + +GameLib.D3.Matrix4.prototype.rotationMatrixY = function (radians) { + this.identity(); + this.rows[0] = new GameLib.D3.Vector4( + Math.cos(radians), + 0, + Math.sin(radians), + 0 + ); + this.rows[2] = new GameLib.D3.Vector4( + -1 * Math.sin(radians), + 0, + Math.cos(radians), + 0 + ); + return this; +}; + +GameLib.D3.Matrix4.prototype.rotationMatrixZ = function (radians) { + this.identity(); + this.rows[0] = new GameLib.D3.Vector4(Math.cos(radians), -1 * Math.sin(radians), 0, 0); + this.rows[1] = new GameLib.D3.Vector4(Math.sin(radians), Math.cos(radians), 0, 0); + return this; +}; + +GameLib.D3.Matrix4.prototype.rotateX = function (radians, point) { + this.identity(); + this.rotationMatrixX(radians); + return this.multiply(point); +}; + +GameLib.D3.Matrix4.prototype.rotateY = function (radians, point) { + this.identity(); + this.rotationMatrixY(radians); + return this.multiply(point); +}; + +GameLib.D3.Matrix4.prototype.rotateZ = function (radians, point) { + this.identity(); + this.rotationMatrixZ(radians); + return this.multiply(point); +}; + +GameLib.D3.Matrix4.prototype.multiply = function (mvp) { + if (mvp instanceof GameLib.D3.Vector4) { + return new GameLib.D3.Vector4( + this.rows[0].x * mvp.x + this.rows[0].y * mvp.y + this.rows[0].z * mvp.z + this.rows[0].w * mvp.w, + this.rows[1].x * mvp.x + this.rows[1].y * mvp.y + this.rows[1].z * mvp.z + this.rows[1].w * mvp.w, + this.rows[2].x * mvp.x + this.rows[2].y * mvp.y + this.rows[2].z * mvp.z + this.rows[2].w * mvp.w, + this.rows[3].x * mvp.x + this.rows[3].y * mvp.y + this.rows[3].z * mvp.z + this.rows[3].w * mvp.w + ); + } else if (mvp instanceof GameLib.D3.Vector3) { + return new GameLib.D3.Vector3( + this.rows[0].x * mvp.x + this.rows[0].y * mvp.y + this.rows[0].z * mvp.z, + this.rows[1].x * mvp.x + this.rows[1].y * mvp.y + this.rows[1].z * mvp.z, + this.rows[2].x * mvp.x + this.rows[2].y * mvp.y + this.rows[2].z * mvp.z + ); + } +}; + +GameLib.D3.Matrix4.prototype.identity = function () { + this.rows = [ + new GameLib.D3.Vector4(1, 0, 0, 0), + new GameLib.D3.Vector4(0, 1, 0, 0), + new GameLib.D3.Vector4(0, 0, 1, 0), + new GameLib.D3.Vector4(0, 0, 0, 1) + ]; +}; + +GameLib.D3.Matrix4.prototype.lookAt = function (position, target, up) { + + var pv = new GameLib.D3.Vector3(position.x, position.y, position.z); + + var z = pv.subtract(target).normalize(); + + if (z.squared() === 0) { + z.z = 1; + } + + var x = up.cross(z).normalize(); + + if (x.squared() === 0) { + z.x += 0.0001; + x = up.cross(z).normalize(); + } + + var y = z.cross(x); + + this.rows[0].x = x.x; + this.rows[0].y = x.y; + this.rows[0].z = x.z; + + this.rows[1].x = y.x; + this.rows[1].y = y.y; + this.rows[1].z = y.z; + + this.rows[2].x = z.x; + this.rows[2].y = z.y; + this.rows[2].z = z.z; + + return this; + + // te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; + // te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; + // te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; + + + // var matrix4 = new Matrix4(); + // + // matrix4.rows[0] = side.negative(); + // matrix4.rows[1] = _up; + // matrix4.rows[2] = forward; + + // + // matrix4.setColumn(0, side.negative()); + // matrix4.setColumn(1, _up); + // matrix4.setColumn(2, forward); + + //return matrix4; + + // return new Matrix4( + // new Vector4( + // side.x, + // side.y, + // side.z, + // side.negative().dot(position) + // ), + // new Vector4( + // _up.x, + // _up.y, + // _up.z, + // _up.negative().dot(position) + // ), + // new Vector4( + // forward.negative().x, + // forward.negative().y, + // forward.negative().z, + // forward.dot(position) + // ) + // ) +}; + +/** + * Mesh Superset + * @param id + * @param path + * @param name + * @param meshType + * @param vertices + * @param faces + * @param skeleton + * @param faceVertexUvs + * @param skinIndices + * @param skinWeights + * @param materials + * @param position + * @param quaternion + * @param rotation + * @param scale + * @param up + * @param physics + * @param parentMeshId + * @param parentSceneId + * @param rawData + * @constructor + */ +GameLib.D3.Mesh = function( + id, + path, + name, + meshType, + vertices, + faces, + skeleton, + faceVertexUvs, + skinIndices, + skinWeights, + materials, + position, + quaternion, + rotation, + scale, + up, + physics, + parentMeshId, + parentSceneId, + rawData +) { + this.id = id; + this.path = path; + this.name = name; + this.meshType = meshType; + this.vertices = vertices; + this.faces = faces; + + if (typeof skeleton == 'undefined') { + skeleton = null; + } + this.skeleton = skeleton; + + if (typeof faceVertexUvs == 'undefined') { + faceVertexUvs = []; + } + this.faceVertexUvs = faceVertexUvs; + + if (typeof skinIndices == 'undefined') { + skinIndices = []; + } + this.skinIndices = skinIndices; + + if (typeof skinWeights == 'undefined') { + skinWeights = []; + } + this.skinWeights = skinWeights; + + if (typeof materials == 'undefined') { + materials = []; + } + this.materials = materials; + + if (typeof position == 'undefined') { + position = new GameLib.D3.Vector3(0,0,0); + } + this.position = position; + + if (typeof quaternion == 'undefined') { + new GameLib.D3.Vector4(); + } + this.quaternion = quaternion; + + if (typeof rotation == 'undefined') { + rotation = new GameLib.D3.Vector3(0,0,0); + } + this.rotation = rotation; + + if (typeof scale == 'undefined') { + scale = new GameLib.D3.Vector3(1,1,1); + } + this.scale = scale; + + if (typeof up == 'undefined') { + up = new GameLib.D3.Vector3(0,1,0); + } + this.up = up; + + this.physics = physics; + + this.parentMeshId = parentMeshId; + + this.parentSceneId = parentSceneId; + + this.rawData = null;// rawData; +}; + + +/** + * Mesh Type + * @type {number} + */ +GameLib.D3.Mesh.TYPE_NORMAL = 0; +GameLib.D3.Mesh.TYPE_SKINNED = 1; + + +/** + * Creates a THREE Mesh from GameLib.D3.Mesh + * @param gameLibMesh + * @param threeGeometry + * @param threeMaterial + * @returns {*} + */ +GameLib.D3.prototype.createThreeMesh = function(gameLibMesh, threeGeometry, threeMaterial) { + + var threeMesh = null; + + if (gameLibMesh.meshType == GameLib.D3.Mesh.TYPE_NORMAL) { + threeMesh = new this.THREE.Mesh(threeGeometry, threeMaterial); + } + + if (gameLibMesh.meshType == GameLib.D3.Mesh.TYPE_SKINNED) { + + var bones = gameLibMesh.skeleton.bones; + + var skinIndices = gameLibMesh.skinIndices; + + var skinWeights = gameLibMesh.skinWeights; + + var threeBones = []; + + for (var bi = 0; bi < bones.length; bi++) { + + var bone = new this.THREE.Bone(); + + bone.name = bones[bi].name; + + bone.position.x = bones[bi].position.x; + bone.position.y = bones[bi].position.y; + bone.position.z = bones[bi].position.z; + + bone.rotation.x = bones[bi].rotation.x; + bone.rotation.y = bones[bi].rotation.y; + bone.rotation.z = bones[bi].rotation.z; + + bone.quaternion.x = bones[bi].quaternion.x; + bone.quaternion.y = bones[bi].quaternion.y; + bone.quaternion.z = bones[bi].quaternion.z; + bone.quaternion.w = bones[bi].quaternion.w; + + bone.scale.x = bones[bi].scale.x; + bone.scale.y = bones[bi].scale.y; + bone.scale.z = bones[bi].scale.z; + + bone.up.x = bones[bi].up.x; + bone.up.y = bones[bi].up.y; + bone.up.z = bones[bi].up.z; + + threeBones.push(bone); + } + + /** + * Setup the bone relationships + */ + for (var br = 0; br < bones.length; br++) { + for (var cbi = 0; cbi < bones[br].childBoneIds.length; cbi++) { + threeBones[br].add(threeBones[bones[br].childBoneIds[cbi]]); + } + } + + /** + * Setup bones (indexes) + */ + for (var si = 0; si < skinIndices.length; si++) { + threeGeometry.skinIndices.push( + new this.THREE.Vector4( + skinIndices[si].x, + skinIndices[si].y, + skinIndices[si].z, + skinIndices[si].w + ) + ); + } + + /** + * Setup bones (weights) + */ + for (var sw = 0; sw < skinWeights.length; sw++) { + threeGeometry.skinWeights.push( + new this.THREE.Vector4( + skinWeights[sw].x, + skinWeights[sw].y, + skinWeights[sw].z, + skinWeights[sw].w + ) + ); + } + + threeMesh = new this.THREE.SkinnedMesh(threeGeometry, threeMaterial); + + var skeleton = new this.THREE.Skeleton(threeBones); + + skeleton.useVertexTexture = gameLibMesh.skeleton.useVertexTexture; + + for (var i = 0; i < bones.length; i++) { + if (bones[i].parentBoneId === null) { + threeMesh.add(threeBones[i]); + break; + } + } + + threeMesh.bind(skeleton); + + threeMesh.pose(); + + threeMesh.skeleton.skeletonHelper = new this.THREE.SkeletonHelper(threeMesh); + threeMesh.skeleton.skeletonHelper.material.linewidth = 5; + } + + if (threeMesh == null) { + console.log('cannot handle meshes of type ' + gameLibMesh.meshType + ' yet.'); + } + + gameLibMesh.threeMeshId = threeMesh.id; + + return threeMesh; +}; + +GameLib.D3.prototype.invertWindingOrder = function(triangles) { + + for (var i = 0; i < triangles.length; i++) { + var v1 = triangles[i].v1; + triangles[i].v1 = triangles[i].v2; + triangles[i].v2 = v1; + + var backupUV = triangles[i].triangle.v1uv; + triangles[i].triangle.v1uv = triangles[i].triangle.v2uv; + triangles[i].triangle.v2uv = backupUV; + } + + return triangles; +}; + +/** + * This function resets a the winding order of a mesh from a reference point V (the average center of the mesh) + */ +GameLib.D3.prototype.resetWindingOrder = function(faces, vertices) { + + var vertexList = new GameLib.D3.Vector3.Points(); + + for (var v = 0; v < vertices.length; v++) { + vertexList.add(new GameLib.D3.Vector3( + vertices[v].position.x, + vertices[v].position.y, + vertices[v].position.z + )); + } + + var V = vertexList.average(); + + var triangles = []; + + for (var s = 0; s < faces.length; s += 3) { + + var v0 = faces[s]; + var v1 = faces[s+1]; + var v2 = faces[s+2]; + + triangles.push( + { + v0 : v0, + v1 : v1, + v2 : v2, + edges : [ + {v0: v0, v1: v1}, + {v0: v1, v1: v2}, + {v0: v2, v1: v0} + ], + winding : 0, + edgeIndex : -1, + processed : false + } + ); + } + + for (var i = 0; i < triangles.length; i++) { + if ( + GameLib.D3.Vector3.clockwise( + vertices[triangles[i].v0].position, + vertices[triangles[i].v1].position, + vertices[triangles[i].v2].position, + V + ) + ) { + console.log('clockwise'); + var bv1 = triangles[i].v1; + triangles[i].v1 = triangles[i].v2; + triangles[i].v2 = bv1; + } else { + console.log('not clockwise'); + } + } + + return triangles; +}; + +/** + * This function resets the winding order for triangles in faces, given an initial triangle and orientation edge + * used pseudocode from + * http://stackoverflow.com/questions/17036970/how-to-correct-winding-of-triangles-to-counter-clockwise-direction-of-a-3d-mesh + * We need to use a graph traversal algorithm, + * lets assume we have method that returns neighbor of triangle on given edge + * + * neighbor_on_egde( next_tria, edge ) + * + * to_process = set of pairs triangle and orientation edge, initial state is one good oriented triangle with any edge on it + * processed = set of processed triangles; initial empty + * + * while to_process is not empty: + * next_tria, orientation_edge = to_process.pop() + * add next_tria in processed + * if next_tria is not opposite oriented than orientation_edge: + * change next_tria (ABC) orientation (B<->C) + * for each edge (AB) in next_tria: + * neighbor_tria = neighbor_on_egde( next_tria, edge ) + * if neighbor_tria exists and neighbor_tria not in processed: + * to_process add (neighbor_tria, edge opposite oriented (BA)) + * @param faces GameLib.D3.TriangleFace[] + * @param orientationEdge GameLib.D3.Vector2 + * @returns {Array} + */ +GameLib.D3.fixWindingOrder = function(faces, orientationEdge) { + + /** + * Checks if a TriangleFace belonging to a TriangleEdge has already been processed + * @param processed TriangleEdge[] + * @param triangle TriangleFace + * @returns {boolean} + */ + function inProcessed(processed, triangle) { + + for (var i = 0; i < processed.length; i++) { + if (processed[i].triangle.equals(triangle)) { + return true; + } + } + + return false; + } + + /** + * Returns a neighbouring triangle on a specific edge - preserving the edge orientation + * @param edge GameLib.D3.Vector2 + * @param faces GameLib.D3.TriangleFace[] + * @param currentTriangle + * @returns {*} + */ + function neighbourOnEdge(edge, faces, currentTriangle) { + + for (var i = 0; i < faces.length; i++) { + if ( + (faces[i].v0 == edge.x && faces[i].v1 == edge.y) || + (faces[i].v1 == edge.x && faces[i].v2 == edge.y) || + (faces[i].v2 == edge.x && faces[i].v0 == edge.y) || + (faces[i].v0 == edge.y && faces[i].v1 == edge.x) || + (faces[i].v1 == edge.y && faces[i].v2 == edge.x) || + (faces[i].v2 == edge.y && faces[i].v0 == edge.x) + ) { + + var triangle = new GameLib.D3.TriangleFace( + faces[i].v0, + faces[i].v1, + faces[i].v2, + faces[i].materialIndex, + faces[i].v0uv, + faces[i].v1uv, + faces[i].v2uv + ); + + if (triangle.equals(currentTriangle)) { + continue; + } + + return new GameLib.D3.TriangleEdge( + triangle, + edge + ); + } + } + + return null; + } + + var toProcess = [ + new GameLib.D3.TriangleEdge( + new GameLib.D3.TriangleFace( + faces[0].v0, + faces[0].v1, + faces[0].v2, + faces[0].materialIndex, + faces[0].v0uv, + faces[0].v1uv, + faces[0].v2uv + ), + orientationEdge + ) + ]; + + var processed = []; + + while (toProcess.length > 0) { + + var triangleEdge = toProcess.pop(); + + /** + * If edge is the same orientation (i.e. the edge order is the same as the given triangle edge) it needs to be reversed + * to have the same winding order) + */ + if ( + (triangleEdge.triangle.v0 == triangleEdge.edge.x && + triangleEdge.triangle.v1 == triangleEdge.edge.y) || + (triangleEdge.triangle.v1 == triangleEdge.edge.x && + triangleEdge.triangle.v2 == triangleEdge.edge.y) || + (triangleEdge.triangle.v2 == triangleEdge.edge.x && + triangleEdge.triangle.v0 == triangleEdge.edge.y) + ) { + var backupV = triangleEdge.triangle.v1; + triangleEdge.triangle.v1 = triangleEdge.triangle.v2; + triangleEdge.triangle.v2 = backupV; + + var backupUV = triangleEdge.triangle.v1uv; + triangleEdge.triangle.v1uv = triangleEdge.triangle.v2uv; + triangleEdge.triangle.v2uv = backupUV; + } + + processed.push(triangleEdge); + + var edges = [ + new GameLib.D3.Vector2( + triangleEdge.triangle.v0, + triangleEdge.triangle.v1 + ), + new GameLib.D3.Vector2( + triangleEdge.triangle.v1, + triangleEdge.triangle.v2 + ), + new GameLib.D3.Vector2( + triangleEdge.triangle.v2, + triangleEdge.triangle.v0 + ) + ]; + + for (var j = 0; j < edges.length; j++) { + var neighbour = neighbourOnEdge(edges[j], faces, triangleEdge.triangle); + if (neighbour && !inProcessed(processed, neighbour.triangle)) { + toProcess.push(neighbour); + } + } + } + + /** + * In processed - we will have some duplicates - only add the unique ones + * @type {Array} + */ + var triangles = []; + for (var i = 0; i < processed.length; i++) { + var found = false; + for (var k = 0; k < triangles.length; k++) { + if (triangles[k].equals(processed[i].triangle)){ + found = true; + break; + } + } + if (!found) { + triangles.push(processed[i].triangle); + } + } + + return triangles; +}; + +/** + * This is a work-around function to fix polys which don't triangulate because + * they could lie on Z-plane (XZ or YZ)) - we translate the poly to the origin, systematically rotate the poly around + * Z then Y axis + * @param verticesFlat [] + * @param grain is the amount to systematically rotate the poly by - a finer grain means a more accurate maximum XY + * @return [] + */ +GameLib.D3.fixPolyZPlane = function(verticesFlat, grain) { + + if ((verticesFlat.length % 3) != 0 && !(verticesFlat.length > 9)) { + console.log("The vertices are not in the right length : " + verticesFlat.length); + } + + var vertices = []; + + var points = new GameLib.D3.Vector4.Points(); + + for (var i = 0; i < verticesFlat.length; i += 3) { + points.add(new GameLib.D3.Vector3( + verticesFlat[i], + verticesFlat[i + 1], + verticesFlat[i + 2] + )); + } + + points.toOrigin(); + + points.maximizeXDistance(grain); + + points.maximizeYDistance(grain); + + for (i = 0; i < points.vectors.length; i++) { + vertices.push( + [ + points.vectors[i].x, + points.vectors[i].y + ] + ); + } + + return vertices; +}; +/** + * Physics SuperSet Namespace Object + * @param id + * @param name + * @param engine GameLib.D3.Engine + * @param worlds + * @returns {{World: World}} + * @constructor + */ +GameLib.D3.Physics = function( + id, + name, + engine, + worlds +) { + this.id = id; + this.name = name; + this.engine = engine; + + if (typeof worlds == 'undefined') { + worlds = []; + } + this.worlds = worlds; +}; + + +/** + * Solver Types + * @type {number} + */ +GameLib.D3.Physics.SPLIT_SOLVER = 0x1; +GameLib.D3.Physics.GS_SOLVER = 0x2; +/** + * Contains a Poly vertex data structure + * @param localIndex + * @param mvertIndex + * @param uv GameLib.D3.Vector2 + * @param materialIndex + * @param edgeIndex + * @constructor + */ +GameLib.D3.PolyVertex = function( + localIndex, + mvertIndex, + uv, + materialIndex, + edgeIndex +) { + this.localIndex = localIndex; + this.mvertIndex = mvertIndex; + this.uv = uv; + this.materialIndex = materialIndex; + this.edgeIndex = edgeIndex; +}; + +/** + * Clone a PolyVertex + * @returns {GameLib.D3.PolyVertex} + */ +GameLib.D3.PolyVertex.prototype.clone = function() { + return new GameLib.D3.PolyVertex( + this.localIndex, + this.mvertIndex, + this.uv.copy(), + this.materialIndex, + this.edgeIndex + ) +}; +/** + * Physics Raycast Vehicle Superset + * TODO: body + wheels[] + * @constructor + */ +GameLib.D3.Physics.RaycastVehicle = function( +) { + this.vehicleObject = null; +}; + + +/** + * Physics Rigid Body Vehicle Superset + * TODO: body + wheels[] + * @constructor + */ +GameLib.D3.Physics.RigidVehicle = function( +) { + this.vehicleObject = null; +}; + +/** + * RigidBody Superset + * @param mass + * @param friction + * @param position + * @param quaternion + * @param velocity + * @param angularVelocity + * @param linearDamping + * @param angularDamping + * @param allowSleep + * @param sleepSpeedLimit + * @param sleepTimeLimit + * @param collisionFilterGroup + * @param collisionFilterMask + * @param fixedRotation + * @param shape + * @returns {GameLib.D3.Physics.RigidBody} + * @constructor + */ +GameLib.D3.Physics.RigidBody = function( + mass, + friction, + position, + quaternion, + velocity, + angularVelocity, + linearDamping, + angularDamping, + allowSleep, + sleepSpeedLimit, + sleepTimeLimit, + collisionFilterGroup, + collisionFilterMask, + fixedRotation, + shape +) { + + this.position = position || new GameLib.D3.Vector3(); + this.velocity = velocity || new GameLib.D3.Vector3(); + this.angularVelocity = angularVelocity || new GameLib.D3.Vector3(); + this.quaternion = quaternion || new GameLib.D3.Vector4(0, 0, 0, 1); + this.mass = typeof mass == "undefined" ? 0 : mass; + this.friction = typeof friction == "undefined" ? 5 : friction; + this.linearDamping = typeof linearDamping == "undefined" ? 0.01 : linearDamping; + this.angularDamping = typeof angularDamping == "undefined" ? 0.01 : angularDamping; + this.allowSleep = typeof allowSleep == "undefined" ? true : allowSleep; + this.sleepSpeedLimit = typeof sleepSpeedLimit == "undefined" ? 0.1 : sleepSpeedLimit; + this.sleepTimeLimit = typeof sleepTimeLimit == "undefined" ? 1.0 : sleepTimeLimit; + this.collisionFilterGroup = typeof collisionFilterGroup == "undefined" ? 1 : collisionFilterGroup; + this.collisionFilterMask = typeof collisionFilterMask == "undefined" ? 1 : collisionFilterMask; + this.fixedRotation = typeof fixedRotation == "undefined" ? false : fixedRotation; + this.shape = typeof shape == "undefined" ? null : shape; + + this.rigidBodyInstance = this.createRigidBodyInstance(); +}; + +/** + * + * @returns {*} + */ +GameLib.D3.Physics.World.RigidBody.prototype.createRigidBodyInstance = function() { + + var rigidBody = null; + + // Create the bodyObject + if(this.physics.engineType == GameLib.D3.Physics.TYPE_CANNON) { + rigidBody = new this.physics.CANNON.Body( + { + mass: mass, + friction: friction, + position: new this.physics.CANNON.Vec3(position.x, position.y, position.z), + velocity: new this.physics.CANNON.Vec3(velocity.x, velocity.y, velocity.z), + quaternion: new this.physics.CANNON.Quaternion(quaternion.x, quaternion.y, quaternion.z, quaternion.w), + angularVelocity: new this.physics.CANNON.Vec3(angularVelocity.x, angularVelocity.y, angularVelocity.z), + linearDamping: linearDamping, + angularDamping: angularDamping, + allowSleep: allowSleep, + sleepSpeedLimit: sleepSpeedLimit, + sleepTimeLimit: sleepTimeLimit, + collisionFilterGroup: collisionFilterGroup, + collisionFilterMask: collisionFilterMask, + fixedRotation: fixedRotation, + shape: shape + } + ); + + } + + return rigidBody; +}; + + +/** + * Scenes are objects putting meshes into 'world space' + * @param id + * @param path String + * @param name String + * @param meshes GameLib.D3.Mesh[] + * @param quaternion + * @param position + * @param rotation + * @param scale + * @param parentSceneId + * @param lights + * @constructor + */ +GameLib.D3.Scene = function( + id, + path, + name, + meshes, + quaternion, + position, + rotation, + scale, + parentSceneId, + lights, + physics +) { + this.id = id; + this.path = path; + this.name = name; + if (this.name.trim() == "") { + this.name = 'unnamed'; + } + this.meshes = meshes; + + if (typeof quaternion == 'undefined') { + quaternion = new GameLib.D3.Vector4(); + } + this.quaternion = quaternion; + + if (typeof position == 'undefined') { + position = new GameLib.D3.Vector3(0,0,0); + } + this.position = position; + + if (typeof rotation == 'undefined') { + rotation = new GameLib.D3.Vector3(0,0,0); + } + this.rotation = rotation; + + if (typeof scale == 'undefined') { + scale = new GameLib.D3.Vector3(1,1,1); + } + this.scale = scale; + + if (typeof parentSceneId == 'undefined') { + parentSceneId = null; + } + this.parentSceneId = parentSceneId; + + if (typeof lights == 'undefined') { + lights = []; + } + this.lights = lights; + + if (typeof physics == 'undefined') { + physics = []; + } + this.physics = physics; +}; + +/** + * Loads a scene directly from the API + * @param sceneName + * @param onLoaded callback + */ +GameLib.D3.prototype.loadSceneFromApi = function(scene, onLoaded) { + + /** + * First check if this is a client or server side request + */ + if (typeof XMLHttpRequest == 'undefined') { + console.warn('implement server side loading from API here'); + return onLoaded(null, new Error('not implemented')); + } + + var xhr = new XMLHttpRequest(); + xhr.open( + 'GET', + this.apiUrl + '/scene/load' + scene.path + '/' + scene.name + ); + + xhr.onreadystatechange = function(xhr, gameLibD3) { + return function() { + if (xhr.readyState == 4) { + + var response = JSON.parse(xhr.responseText); + + if (!response.scene || response.scene.length == 0) { + return onLoaded(null, null, new Error('Could not load scene')); + } + + var scene = response.scene[0]; + + var physics3ds = []; + + if (scene.physics && scene.physics.length > 0) { + + for (var p = 0; p < scene.physics.length; p++) { + + var physics = scene.physics[p]; + + var physics3d = new GameLib.D3.Physics( + physics.id, + physics.name, + physics.engineType, + gameLibD3.CANNON, + null, + null + ); + + var worlds3d = []; + + for (var w = 0; w < physics.worlds.length; w++) { + + var world = physics.worlds[w]; + + var broadphase = world.broadphase; + + var broadphase3d = new GameLib.D3.Physics.Broadphase( + broadphase.id, + broadphase.name, + broadphase.broadphaseType + ); + + var solver = world.solver; + + var solver3d = new GameLib.D3.Physics.Solver( + solver.id, + solver.name, + solver.solverType, + solver.iterations, + solver.tolerance + ); + + var bodies = world.rigidBodies; + + var bodies3d = []; + + for (var b = 0; b < bodies.length; b++) { + + var body = bodies[b]; + + //TODO: add all body properties here + var body3d = new GameLib.D3.Physics.RigidBody( + body.id, + body.name + ); + + bodies3d.push(body3d); + } + + var world3d = new GameLib.D3.Physics.World( + null, + world.name, + physics3d, + new GameLib.D3.Vector3( + world.gravity.x, + world.gravity.y, + world.gravity.z + ), + broadphase3d, + solver3d, + bodies3d + ); + + worlds3d.push(world3d); + } + + physics3ds.push(physics3d); + } + } + + var lights3d = []; + + for (var l = 0; l < scene.lights.length; l++) { + + var light = scene.lights[l]; + + var light3d = new GameLib.D3.Light( + light.id, + light.lightType, + light.name, + new GameLib.D3.Color( + light.color.r, + light.color.g, + light.color.b, + light.color.a + ), + light.intensity, + new GameLib.D3.Vector3( + light.position.x, + light.position.y, + light.position.z + ), + new GameLib.D3.Vector3( + light.targetPosition.x, + light.targetPosition.y, + light.targetPosition.z + ), + new GameLib.D3.Vector4( + light.quaternion.x, + light.quaternion.y, + light.quaternion.z, + light.quaternion.w + ), + new GameLib.D3.Vector3( + light.rotation.x, + light.rotation.y, + light.rotation.z + ), + new GameLib.D3.Vector3( + light.scale.x, + light.scale.y, + light.scale.z + ), + light.distance, + light.decay, + light.power, + light.angle, + light.penumbra + ); + + lights3d.push(light3d); + }; + + var scene3d = new GameLib.D3.Scene( + scene._id || scene.id, + scene.path, + scene.name, + scene.meshes, + new GameLib.D3.Vector4( + scene.quaternion.x, + scene.quaternion.y, + scene.quaternion.z, + scene.quaternion.w + ), + new GameLib.D3.Vector3( + scene.position.x, + scene.position.y, + scene.position.z + ), + new GameLib.D3.Vector3( + scene.rotation.x, + scene.rotation.y, + scene.rotation.z + ), + new GameLib.D3.Vector3( + scene.scale.x, + scene.scale.y, + scene.scale.z + ), + scene.parentSceneId, + lights3d, + physics3ds + ); + + gameLibD3.loadScene(scene3d, onLoaded, false); + } + } + }(xhr, this); + + xhr.send(); +}; + + +/** + * Loads a GameLib.D3.Scene object into a ThreeJS Scene object + * @param gameLibScene GameLib.D3.Scene + * @param onLoaded callback when all meshes have loaded + * @param computeNormals set to true to compute new face and vertex normals during load + */ +GameLib.D3.prototype.loadScene = function(gameLibScene, onLoaded, computeNormals) { + + console.log("loading scene " + gameLibScene.name); + + this.path = gameLibScene.path; + + var meshQ = []; + + for (var m = 0; m < gameLibScene.meshes.length; m++) { + + var mesh = gameLibScene.meshes[m]; + + console.log("loading mesh " + mesh.name); + + var geometry = new this.THREE.Geometry(); + + var vertices = mesh.vertices; + + var faces = mesh.faces; + + var faceVertexUvs = mesh.faceVertexUvs; + + var materials = mesh.materials; + + /** + * Setup vertices + */ + for (var v = 0; v < vertices.length; v++) { + geometry.vertices.push( + new this.THREE.Vector3( + vertices[v].position.x, + vertices[v].position.y, + vertices[v].position.z + ) + ) + } + + /** + * Setup faces + */ + for (var f = 0; f < faces.length; f++) { + + var face = new this.THREE.Face3( + faces[f].v0, + faces[f].v1, + faces[f].v2, + new this.THREE.Vector3( + faces[f].normal.x, + faces[f].normal.y, + faces[f].normal.z + ), + new this.THREE.Color( + faces[f].color.r, + faces[f].color.g, + faces[f].color.b + ), + faces[f].materialIndex + ); + + face.vertexColors = [ + new this.THREE.Color( + faces[f].vertexColors[0].r, + faces[f].vertexColors[0].g, + faces[f].vertexColors[0].b + ), + new this.THREE.Color( + faces[f].vertexColors[1].r, + faces[f].vertexColors[1].g, + faces[f].vertexColors[1].b + ), + new this.THREE.Color( + faces[f].vertexColors[2].r, + faces[f].vertexColors[2].g, + faces[f].vertexColors[2].b + ) + ]; + + face.normal = new this.THREE.Vector3( + faces[f].normal.x, + faces[f].normal.y, + faces[f].normal.z + ); + + face.vertexNormals = [ + new this.THREE.Vector3( + faces[f].vertexNormals[0].x, + faces[f].vertexNormals[0].y, + faces[f].vertexNormals[0].z + ), + new this.THREE.Vector3( + faces[f].vertexNormals[1].x, + faces[f].vertexNormals[1].y, + faces[f].vertexNormals[1].z + ), + new this.THREE.Vector3( + faces[f].vertexNormals[2].x, + faces[f].vertexNormals[2].y, + faces[f].vertexNormals[2].z + ) + ]; + + geometry.faces.push(face); + } + + geometry.faceVertexUvs = []; + + /** + * Setup face UVs + */ + for (var fm = 0; fm < faceVertexUvs.length; fm++) { + + var faceMaterialVertexUvs = faceVertexUvs[fm]; + + geometry.faceVertexUvs[fm] = []; + + for (var fuv = 0; fuv < faceMaterialVertexUvs.length; fuv++) { + geometry.faceVertexUvs[fm][fuv] = []; + geometry.faceVertexUvs[fm][fuv].push( + new this.THREE.Vector2( + faceMaterialVertexUvs[fuv][0].x, + faceMaterialVertexUvs[fuv][0].y + ), + new this.THREE.Vector2( + faceMaterialVertexUvs[fuv][1].x, + faceMaterialVertexUvs[fuv][1].y + ), + new this.THREE.Vector2( + faceMaterialVertexUvs[fuv][2].x, + faceMaterialVertexUvs[fuv][2].y + ) + ); + } + } + + /** + * Re-calculate normals (if we have to) + * @type {Array} + */ + if (computeNormals) { + geometry.computeFaceNormals(); + geometry.computeVertexNormals(); + } + + var threeMaterialLoaders = []; + + /** + * Setup materials + */ + for (var mi = 0; mi < materials.length; mi++) { + threeMaterialLoaders.push(this.createThreeMaterial(materials[mi])); + } + + var result = this.Q.all(threeMaterialLoaders).then( + function(gl3d, mesh, geometry) { + return function(materials) { + + console.log("loaded material : " + materials[0].name); + + /** + * We don't support MultiMaterial atm - it doesn't work with raycasting + */ + var material = materials[0]; + + var threeMesh = gl3d.createThreeMesh(mesh, geometry, material); + threeMesh.name = mesh.name; + + threeMesh.position.x = mesh.position.x; + threeMesh.position.y = mesh.position.y; + threeMesh.position.z = mesh.position.z; + + threeMesh.rotation.x = mesh.rotation.x; + threeMesh.rotation.y = mesh.rotation.y; + threeMesh.rotation.z = mesh.rotation.z; + + threeMesh.scale.x = mesh.scale.x; + threeMesh.scale.y = mesh.scale.y; + threeMesh.scale.z = mesh.scale.z; + + threeMesh.quaternion.x = mesh.quaternion.x; + threeMesh.quaternion.y = mesh.quaternion.y; + threeMesh.quaternion.z = mesh.quaternion.z; + threeMesh.quaternion.w = mesh.quaternion.w; + + return threeMesh; + }; + }(this, mesh, geometry) + ).catch(function(error){ + console.log(error); + }); + + meshQ.push(result); + } + + this.Q.all(meshQ).then(function(threeMeshes){ + console.log("all meshes have loaded"); + if (typeof onLoaded != 'undefined') { + + var threeLights = []; + + for (var sli = 0; sli < gameLibScene.lights.length; sli++) { + + var blenderLight = gameLibScene.lights[sli]; + + var light = null; + + if (blenderLight.lightType == 'AmbientLight') { + light = new this.THREE.AmbientLight(blenderLight.color, blenderLight.intensity); + } + + if (blenderLight.lightType == 'DirectionalLight') { + light = new this.THREE.DirectionalLight(blenderLight.color, blenderLight.intensity); + } + + if (blenderLight.lightType == 'PointLight') { + light = new this.THREE.PointLight(blenderLight.color, blenderLight.intensity); + light.distance = blenderLight.distance; + light.decay = blenderLight.decay; + } + + if (blenderLight.lightType == 'SpotLight') { + light = new this.THREE.SpotLight(blenderLight.color, blenderLight.intensity); + light.distance = blenderLight.distance; + light.angle = blenderLight.angle; + light.penumbra = blenderLight.penumbra; + light.decay = blenderLight.decay; + } + + light.position.x = blenderLight.position.x; + light.position.y = blenderLight.position.y; + light.position.z = blenderLight.position.z; + + light.rotation.x = blenderLight.rotation.x; + light.rotation.y = blenderLight.rotation.y; + light.rotation.z = blenderLight.rotation.z; + + // light.scale.x = blenderLight.scale.x; + // light.scale.y = blenderLight.scale.y; + // light.scale.z = blenderLight.scale.z; + + if (light == null) { + console.warn('Does not support lights of type :' + blenderLight.lightType + ', not imported'); + } else { + light.name = blenderLight.name; + threeLights.push(light); + } + } + + var threeScene = new this.THREE.Scene(); + + threeScene.name = gameLibScene.name; + + threeScene.position.x = gameLibScene.position.x; + threeScene.position.y = gameLibScene.position.y; + threeScene.position.z = gameLibScene.position.z; + + threeScene.rotation.x = gameLibScene.rotation.x; + threeScene.rotation.y = gameLibScene.rotation.y; + threeScene.rotation.z = gameLibScene.rotation.z; + + threeScene.scale.x = gameLibScene.scale.x; + threeScene.scale.y = gameLibScene.scale.y; + threeScene.scale.z = gameLibScene.scale.z; + + threeScene.quaternion.x = gameLibScene.quaternion.x; + threeScene.quaternion.y = gameLibScene.quaternion.y; + threeScene.quaternion.z = gameLibScene.quaternion.z; + threeScene.quaternion.w = gameLibScene.quaternion.w; + + for (var m = 0; m < threeMeshes.length; m++) { + threeScene.add(threeMeshes[m]); + } + + for (var l = 0; l < threeLights.length; l++) { + threeScene.add(threeLights[l]); + } + + onLoaded( + gameLibScene, + { + scene: threeScene, + lights: threeLights, + meshes: threeMeshes + } + ); + } + }.bind(this)).catch(function(error){ + console.log(error); + }); +}; +/** + * Physics Shape Superset + * @constructor + */ +GameLib.D3.Physics.Shape = function( + shapeObject, // Physics engine specific + shapeType +) { + this.shapeObject = shapeObject; + this.shapeType = shapeType; + this.scale = new GameLib.D3.Vector3(1, 1, 1); +}; + +GameLib.D3.Physics.SHAPE_TYPE_SPHERE = 1; +GameLib.D3.Physics.SHAPE_TYPE_BOX = 2; +GameLib.D3.Physics.SHAPE_TYPE_TRIMESH = 3; +GameLib.D3.Physics.SHAPE_TYPE_CYLINDER = 4; + + +GameLib.D3.Physics.Shape.prototype.Update = function() { + if(this.physics.engineType === GameLib.D3.Physics.TYPE_CANNON) { + if(this.shapeType === GameLib.D3.Physics.SHAPE_TYPE_TRIMESH) { + this.shapeObject.setScale( + new this.physics.CANNON.Vec3( + this.scale.x, + this.scale.y, + this.scale.z + ) + ); + this.shapeObject.updateAABB(); + this.shapeObject.updateNormals(); + this.shapeObject.updateEdges(); + this.shapeObject.updateBoundingSphereRadius(); + this.shapeObject.updateTree(); + } + } +}; + +/** + * Skeleton Superset + * @param id + * @param bones GameLib.D3.Bone + * @param boneInverses + * @param useVertexTexture + * @param boneTextureWidth + * @param boneTextureHeight + * @param boneMatrices + * @param boneTexture + * @constructor + */ +GameLib.D3.Skeleton = function( + id, + bones, + boneInverses, + useVertexTexture, + boneTextureWidth, + boneTextureHeight, + boneMatrices, + boneTexture +) { + this.id = id; + + this.bones = bones; + + /** + * An array of Matrix4s that represent the inverse of the matrixWorld of the individual bones. + * @type GameLib.D3.Matrix4[] + */ + if (typeof boneInverses == 'undefined') { + boneInverses = []; + } + this.boneInverses = boneInverses; + + /** + * Use a vertex texture in the shader - allows for more than 4 bones per vertex, not supported by all devices + * @type {boolean} + */ + if (typeof useVertexTexture == 'undefined') { + useVertexTexture = false; + } + this.useVertexTexture = useVertexTexture; + + if (this.useVertexTexture == true) { + console.warn('support for vertex texture bones is not supported yet - something could break somewhere'); + } + + if (typeof boneTextureWidth == 'undefined') { + boneTextureWidth = 0; + } + this.boneTextureWidth = boneTextureWidth; + + if (typeof boneTextureHeight == 'undefined') { + boneTextureHeight = 0; + } + this.boneTextureHeight = boneTextureHeight; + + if (typeof boneMatrices == 'undefined') { + boneMatrices = []; + } + this.boneMatrices = boneMatrices; + + if (typeof boneTexture == 'undefined') { + boneTexture = []; + } + this.boneTexture = boneTexture; +}; + + +GameLib.D3.SkyBox = function ( + +) { + this.id = null; + this.texturesFolder = null; +}; + +GameLib.D3.SkyBox.prototype.Load = function ( + texturesFolder +) { + this.texturesFolder = texturesFolder; + this.textures = []; + this.materials = []; + this.mesh = {}; + this.scene = new THREE.Scene(); + this.textureCube = null; + + var textureLoader = new THREE.TextureLoader(); + + // this textures are used to display the skybox + this.textures.push(textureLoader.load(this.texturesFolder + "px.png")); + this.textures.push(textureLoader.load(this.texturesFolder + "nx.png")); + this.textures.push(textureLoader.load(this.texturesFolder + "py.png")); + this.textures.push(textureLoader.load(this.texturesFolder + "ny.png")); + this.textures.push(textureLoader.load(this.texturesFolder + "pz.png")); + this.textures.push(textureLoader.load(this.texturesFolder + "nz.png")); + + // assign textures to each cube face + for (var i = 0; i < 6; i ++) { + this.materials.push(new THREE.MeshBasicMaterial({ map: this.textures[i] })); + } + + // create cube geometry + this.mesh = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), new THREE.MeshFaceMaterial(this.materials)); + this.mesh.applyMatrix(new THREE.Matrix4().makeScale(1, 1, -1)); + this.scene.add(this.mesh); + + // Load env textureCube + // this is used for reflections on meshes + // mesh.material.envMap = this.textureCube; + this.textureCube = new THREE.CubeTextureLoader().load([ + this.texturesFolder + "px.png", this.texturesFolder + "nx.png", + this.texturesFolder + "py.png", this.texturesFolder + "ny.png", + this.texturesFolder + "pz.png", this.texturesFolder + "nz.png" + ]); +}; + +GameLib.D3.SkyBox.prototype.Render = function ( + threeRenderer, + threeCamera +) { + var cameraPosition = new THREE.Vector3(threeCamera.position.x, threeCamera.position.y, threeCamera.position.z); + + threeCamera.position.set(0, 0, 0); + + var gl = threeRenderer.context; + + gl.disable(gl.DEPTH_TEST); + + threeRenderer.render(this.scene, threeCamera); + + gl.enable(gl.DEPTH_TEST); + + threeCamera.position.copy(cameraPosition); +}; + +/** + * Physics Solver Superset + * @param id + * @param name + * @param solverType + * @param iterations + * @param tolerance + * @constructor + */ +GameLib.D3.Physics.Solver = function( + id, + name, + solverType, + iterations, + tolerance +) { + this.id = id; + if (typeof name == 'undefined') { + if (solverType == GameLib.D3.Physics.SPLIT_SOLVER) { + name = 'split solver'; + } else if (solverType == GameLib.D3.Physics.GS_SOLVER) { + name = 'gs solver'; + } else { + name = 'unknown solver'; + } + } + this.name = name; + this.solverType = solverType; + this.iterations = iterations; + this.tolerance = tolerance; +}; + +/** + * Solver Types + * @type {number} + */ +GameLib.D3.Physics.SPLIT_SOLVER = 0x1; +GameLib.D3.Physics.GS_SOLVER = 0x2; + +/** + * Texture Superset + * @param id + * @param path + * @param name + * @param image + * @param wrapS + * @param wrapT + * @param repeat + * @param data + * @param format + * @param mapping + * @param magFilter + * @param minFilter + * @param textureType + * @param anisotropy + * @param offset + * @param generateMipmaps + * @param flipY + * @param mipmaps + * @param unpackAlignment + * @param premultiplyAlpha + * @param encoding + * @constructor + */ +GameLib.D3.Texture = function( + id, + name, + image, + wrapS, + wrapT, + repeat, + data, + format, + mapping, + magFilter, + minFilter, + textureType, + anisotropy, + offset, + generateMipmaps, + flipY, + mipmaps, + unpackAlignment, + premultiplyAlpha, + encoding +) { + this.id = id; + this.name = name; + this.image = image; + + if (typeof wrapS == 'undefined') { + wrapS = GameLib.D3.Texture.TYPE_REPEAT_WRAPPING; + } + this.wrapS = wrapS; + + if (typeof wrapT == 'undefined') { + wrapT = GameLib.D3.Texture.TYPE_REPEAT_WRAPPING; + } + this.wrapT = wrapT; + + if (typeof repeat == 'undefined') { + repeat = new GameLib.D3.Vector2(1, 1); + } + this.repeat = repeat; + + if (typeof data == 'undefined') { + data = null; + } + this.data = data; + + if (typeof format == 'undefined') { + format = GameLib.D3.Texture.TYPE_RGBA_FORMAT; + } + this.format = format; + + if (typeof mapping == 'undefined') { + mapping = GameLib.D3.Texture.TYPE_UV_MAPPING; + } + this.mapping = mapping; + + if (typeof magFilter == 'undefined') { + magFilter = GameLib.D3.Texture.TYPE_LINEAR_FILTER; + } + this.magFilter = magFilter; + + if (typeof minFilter == 'undefined') { + minFilter = GameLib.D3.Texture.TYPE_LINEAR_MIPMAP_LINEAR_FILTER; + } + this.minFilter = minFilter; + + if (typeof textureType == 'undefined') { + textureType = GameLib.D3.Texture.TYPE_UNSIGNED_BYTE; + } + this.textureType = textureType; + + if (typeof anisotropy == 'undefined') { + anisotropy = 1; + } + this.anisotropy = anisotropy; + + if (typeof offset == 'undefined') { + offset = new GameLib.D3.Vector2(0, 0); + } + this.offset = offset; + + if (typeof generateMipmaps == 'undefined') { + generateMipmaps = true; + } + this.generateMipmaps = generateMipmaps; + + if (typeof flipY == 'undefined') { + flipY = true; + } + this.flipY = flipY; + + if (typeof mipmaps == 'undefined') { + mipmaps = []; + } + this.mipmaps = mipmaps; + + if (typeof unpackAlignment == 'undefined') { + unpackAlignment = 4; + } + this.unpackAlignment = unpackAlignment; + + if (typeof premultiplyAlpha == 'undefined') { + premultiplyAlpha = false; + } + this.premultiplyAlpha = premultiplyAlpha; + + if (typeof encoding == 'undefined') { + encoding = GameLib.D3.Texture.TYPE_LINEAR_ENCODING; + } + this.encoding = encoding; +}; + +/** + * Texture Formats + * @type {number} + */ +GameLib.D3.Texture.TYPE_ALPHA_FORMAT = 1019; +GameLib.D3.Texture.TYPE_RGB_FORMAT = 1020; +GameLib.D3.Texture.TYPE_RGBA_FORMAT = 1021; +GameLib.D3.Texture.TYPE_LUMINANCE_FORMAT = 1022; +GameLib.D3.Texture.TYPE_LUMINANCE_ALPHA_FORMAT = 1023; +GameLib.D3.Texture.TYPE_DEPTH_FORMAT = 1026; + +/** + * Mapping modes + * @type {number} + */ +GameLib.D3.Texture.TYPE_UV_MAPPING = 300; +GameLib.D3.Texture.TYPE_CUBE_REFLECTION_MAPPING = 301; +GameLib.D3.Texture.TYPE_CUBE_REFRACTION_MAPPING = 302; +GameLib.D3.Texture.TYPE_EQUI_RECTANGULAR_REFLECTION_MAPPING = 303; +GameLib.D3.Texture.TYPE_EQUI_RECTANGULAR_REFRACTION_MAPPING = 304; +GameLib.D3.Texture.TYPE_SPHERICAL_REFLECTION_MAPPING = 305; +GameLib.D3.Texture.TYPE_CUBE_UV_REFLECTION_MAPPING = 306; +GameLib.D3.Texture.TYPE_CUBE_UV_REFRACTION_MAPPING = 307; + +/** + * Wrapping Modes + * @type {number} + */ +GameLib.D3.Texture.TYPE_REPEAT_WRAPPING = 1000; +GameLib.D3.Texture.TYPE_CLAMP_TO_EDGE_WRAPPING = 1001; +GameLib.D3.Texture.TYPE_MIRRORED_REPEAT_WRAPPING = 1002; + +/** + * Mipmap Filters + * @type {number} + */ +GameLib.D3.Texture.TYPE_NEAREST_FILTER = 1003; +GameLib.D3.Texture.TYPE_NEAREST_MIPMAP_NEAREST_FILTER = 1004; +GameLib.D3.Texture.TYPE_NEAREST_MIPMAP_LINEAR_FILTER = 1005; +GameLib.D3.Texture.TYPE_LINEAR_FILTER = 1006; +GameLib.D3.Texture.TYPE_LINEAR_MIPMAP_NEAREST_FILTER = 1007; +GameLib.D3.Texture.TYPE_LINEAR_MIPMAP_LINEAR_FILTER = 1008; + +/** + * Texture Data Types + * @type {number} + */ +GameLib.D3.Texture.TYPE_UNSIGNED_BYTE = 1009; +GameLib.D3.Texture.TYPE_BYTE = 1010; +GameLib.D3.Texture.TYPE_SHORT = 1011; +GameLib.D3.Texture.TYPE_UNSIGNED_SHORT = 1012; +GameLib.D3.Texture.TYPE_INT = 1013; +GameLib.D3.Texture.TYPE_UNSIGNED_INT = 1014; +GameLib.D3.Texture.TYPE_FLOAT = 1015; +GameLib.D3.Texture.TYPE_HALF_FLOAT = 1025; + +/** + * Encoding Modes + * @type {number} + */ +GameLib.D3.Texture.TYPE_LINEAR_ENCODING = 3000; // NO ENCODING AT ALL. +GameLib.D3.Texture.TYPE_SRGB_ENCODING = 3001; +GameLib.D3.Texture.TYPE_GAMMA_ENCODING = 3007; // USES GAMMA_FACTOR, FOR BACKWARDS COMPATIBILITY WITH WEBGLRENDERER.GAMMAINPUT/GAMMAOUTPUT +GameLib.D3.Texture.TYPE_RGBE_ENCODING = 3002; // AKA RADIANCE. +GameLib.D3.Texture.TYPE_LOG_LUV_ENCODING = 3003; +GameLib.D3.Texture.TYPE_RGBM7_ENCODING = 3004; +GameLib.D3.Texture.TYPE_RGBM16_ENCODING = 3005; +GameLib.D3.Texture.TYPE_RGBD_ENCODING = 3006; // MAXRANGE IS 256. + + + +/** + * Defers loading of an image and resolves once image is loaded + * @param gameLibTexture + * @param threeMaterial + * @param threeMaterialMapType + * @returns {Promise} + */ +GameLib.D3.prototype.loadMap = function(gameLibTexture, threeMaterial, threeMaterialMapType) { + + var q = this.Q.defer(); + + var imagePath = null; + + if (gameLibTexture && gameLibTexture.image && gameLibTexture.image.filename) { + /** + * Else, load from upload source + */ + imagePath = this.editorUrl + '/uploads' + this.path + '/' + gameLibTexture.image.filename; + } + + if (imagePath) { + + this.textureLoader.crossOrigin = ''; + + this.textureLoader.load( + imagePath, + function(texture) { + /** + * onLoad + */ + threeMaterial[threeMaterialMapType] = texture; + threeMaterial[threeMaterialMapType].name = gameLibTexture.name; + threeMaterial[threeMaterialMapType].anisotropy = gameLibTexture.anisotropy; + threeMaterial[threeMaterialMapType].encoding = gameLibTexture.encoding; + threeMaterial[threeMaterialMapType].flipY = gameLibTexture.flipY; + /** + * We don't restore the format since this changing from OS to OS and breaks the implementation sometimes + */ + threeMaterial[threeMaterialMapType].generateMipmaps = gameLibTexture.generateMipmaps; + threeMaterial[threeMaterialMapType].magFilter = gameLibTexture.magFilter; + threeMaterial[threeMaterialMapType].minFilter = gameLibTexture.minFilter; + threeMaterial[threeMaterialMapType].mapping = gameLibTexture.mapping; + threeMaterial[threeMaterialMapType].mipmaps = gameLibTexture.mipmaps; + threeMaterial[threeMaterialMapType].offset = new this.THREE.Vector2( + gameLibTexture.offset.x, + gameLibTexture.offset.y + ); + threeMaterial[threeMaterialMapType].premultiplyAlpha = gameLibTexture.premultiplyAlpha; + threeMaterial[threeMaterialMapType].textureType = gameLibTexture.textureType; + threeMaterial[threeMaterialMapType].wrapS = gameLibTexture.wrapS; + threeMaterial[threeMaterialMapType].wrapT = gameLibTexture.wrapT; + threeMaterial[threeMaterialMapType].unpackAlignment = gameLibTexture.unpackAlignment; + threeMaterial.needsUpdate = true; + q.resolve(true); + }, + function(xhr) { + /** + * onProgress + */ + if (this.editor) { + this.editor.setServerStatus(Math.round(xhr.loaded / xhr.total * 100) + '% complete', 'success'); + } + }, + function(error) { + /** + * onError + */ + console.log("an error occurred while trying to load the image : " + imagePath); + q.resolve(null); + } + ); + + } else { + q.resolve(null); + } + + return q.promise; +}; + + +/** + * Returns an array of image loading Promises + * @param blenderMaterial + * @param blenderMaps + * @param threeMaterial + * @returns Q[] + */ +GameLib.D3.prototype.loadMaps = function(blenderMaterial, blenderMaps, threeMaterial) { + + var textureMaps = []; + + for (var ti = 0; ti < blenderMaps.length; ti++) { + + var map = blenderMaps[ti]; + + if (blenderMaterial.maps.hasOwnProperty(map)) { + + var blenderTexture = blenderMaterial.maps[map]; + + if ( + blenderTexture && + blenderTexture.image && + blenderTexture.image.filename + ) { + + var threeMap = null; + + if (map == 'alpha') { + threeMap = 'alhpaMap'; + } + + if (map == 'ao') { + threeMap = 'aoMap'; + } + + if (map == 'bump') { + threeMap = 'bumpMap'; + } + + if (map == 'displacement') { + threeMap = 'displacementMap'; + } + + if (map == 'emissive') { + threeMap = 'emissiveMap'; + } + + if (map == 'environment') { + threeMap = 'envMap'; + } + + if (map == 'light') { + threeMap = 'lightMap'; + } + + if (map == 'specular') { + threeMap = 'specularMap'; + } + + if (map == 'diffuse') { + threeMap = 'map'; + } + + if (map == 'roughness') { + threeMap = 'roughnessMap'; + } + + if (map == 'metalness') { + threeMap = 'metalnessMap'; + } + + if (threeMap == null) { + console.warn("unsupported map type : " + map); + } + + textureMaps.push(this.loadMap(blenderMaterial.maps[map], threeMaterial, threeMap)); + } + } + } + + return textureMaps; +}; + +/** + * TriangleEdge + * @param triangle + * @param edge + * @constructor + */ +GameLib.D3.TriangleEdge = function( + triangle, + edge +) { + this.triangle = triangle; + this.edge = edge; +}; +/** + * TriangleFace + * @param v0 + * @param v1 + * @param v2 + * @param materialIndex + * @param v0uv + * @param v1uv + * @param v2uv + * @param color + * @param vertexColors + * @param vertexNormals + * @param normal + * @constructor + */ +GameLib.D3.TriangleFace = function( + v0, + v1, + v2, + materialIndex, + v0uv, + v1uv, + v2uv, + color, + vertexColors, + vertexNormals, + normal +) { + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.materialIndex = materialIndex; + this.v0uv = v0uv; + this.v1uv = v1uv; + this.v2uv = v2uv; + if (!color) { + color = new GameLib.D3.Color(0xff, 0xff, 0xff, 0xff); + } + this.color = color; + + if (!vertexColors) { + vertexColors = [ + new GameLib.D3.Color(0xff, 0xff, 0xff, 0xff), + new GameLib.D3.Color(0xff, 0xff, 0xff, 0xff), + new GameLib.D3.Color(0xff, 0xff, 0xff, 0xff) + ]; + } + this.vertexColors = vertexColors; + + if (!vertexNormals) { + vertexNormals = [ + new GameLib.D3.Vector3(), + new GameLib.D3.Vector3(), + new GameLib.D3.Vector3() + ] + } + this.vertexNormals = vertexNormals; + + if (!normal) { + normal = new GameLib.D3.Vector3(0); + } + + this.normal = normal; +}; + +/** + * Clone a TriangleFace + * @returns {GameLib.D3.TriangleFace} + */ +GameLib.D3.TriangleFace.prototype.clone = function(){ + return new GameLib.D3.TriangleFace( + this.v0, + this.v1, + this.v2, + this.materialIndex, + this.v0uv.copy(), + this.v1uv.copy(), + this.v2uv.copy() + ); +}; + +/** + * Returns true if two triangles are equal (their vertex indices match in some order) + * @param triangle + * @returns {boolean} + */ +GameLib.D3.TriangleFace.prototype.equals = function(triangle) { + return !!( + ( + (this.v0 == triangle.v0) && + (this.v1 == triangle.v1) && + (this.v2 == triangle.v2) + ) + || + ( + (this.v0 == triangle.v0) && + (this.v1 == triangle.v2) && + (this.v2 == triangle.v1) + ) + || + ( + (this.v0 == triangle.v1) && + (this.v1 == triangle.v0) && + (this.v2 == triangle.v2) + ) + || + ( + (this.v0 == triangle.v1) && + (this.v1 == triangle.v2) && + (this.v2 == triangle.v0) + ) + || + ( + (this.v0 == triangle.v2) && + (this.v1 == triangle.v0) && + (this.v2 == triangle.v1) + ) + || + ( + (this.v0 == triangle.v2) && + (this.v1 == triangle.v1) && + (this.v2 == triangle.v0) + )); +}; + + +GameLib.D3.Vector2 = function(x, y) { + + this.x = 0; + this.y = 0; + + if (x) { + this.x = x; + } + + if (y) { + this.y = y; + } +}; + +GameLib.D3.Vector2.prototype.copy = function() { + return new GameLib.D3.Vector2( + this.x, + this.y + ); +}; + +GameLib.D3.Vector2.prototype.equals = function(v) { + return !!(((this.x == v.x) && + (this.y == v.y)) || + ((this.y == v.x) && + (this.x == v.y))); +}; + +GameLib.D3.Vector3 = function(x, y, z) { + + this.x = 0; + this.y = 0; + this.z = 0; + + if (x) { + this.x = x; + } + + if (y) { + this.y = y; + } + + if (z) { + this.z = z; + } +}; + +GameLib.D3.Vector3.prototype.subtract = function (v) { + + if (v instanceof GameLib.D3.Vector3) { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + } + + if (v instanceof GameLib.D3.Vector4) { + console.warn("trying to subtract vector of bigger length (4 vs 3))"); + } + + return this; +}; + +GameLib.D3.Vector3.prototype.cross = function (v) { + return new GameLib.D3.Vector3( + this.y * v.z - this.z * v.y, + this.z * v.x - this.x * v.z, + this.x * v.y - this.y * v.x + ); +}; + +GameLib.D3.Vector3.prototype.negative = function () { + this.x *= -1; + this.y *= -1; + this.z *= -1; + return this; +}; + +GameLib.D3.Vector3.clockwise = function (u, v, w, viewPoint) { + + var normal = GameLib.D3.Vector3.normal(u, v, w); + + var uv = u.copy(); + + var winding = normal.dot(uv.subtract(viewPoint)); + + return (winding > 0); +}; + +GameLib.D3.Vector3.normal = function (u, v, w) { + var vv = v.copy(); + var wv = w.copy(); + return vv.subtract(u).cross(wv.subtract(u)); +}; + +GameLib.D3.Vector3.prototype.lookAt = function (at, up) { + + var lookAtMatrix = GameLib.D3.Matrix4.lookAt(this, at, up); + + this.multiply(lookAtMatrix); +}; + +GameLib.D3.Vector3.prototype.translate = function (v) { + this.x += v.x; + this.y += v.y; + this.z += v.z; + return this; +}; + +GameLib.D3.Vector3.prototype.squared = function () { + return this.x * this.x + this.y * this.y + this.z * this.z; +}; + +GameLib.D3.Vector3.prototype.copy = function () { + return new GameLib.D3.Vector3( + this.x, + this.y, + this.z + ); +}; + +GameLib.D3.Vector3.prototype.multiply = function (s) { + if (s instanceof GameLib.D3.Vector3) { + this.x *= s.x; + this.y *= s.y; + this.z *= s.z; + } else if (s instanceof GameLib.D3.Matrix4) { + var x = s.rows[0].x * this.x + s.rows[0].y * this.y + s.rows[0].z * this.z; + var y = s.rows[1].x * this.x + s.rows[1].y * this.y + s.rows[1].z * this.z; + var z = s.rows[2].x * this.x + s.rows[2].y * this.y + s.rows[2].z * this.z; + this.x = x; + this.y = y; + this.z = z; + } else { + console.log("functionality not implemented - please do this"); + throw new Error("not implemented"); + } + return this; +}; + + +GameLib.D3.Vector3.prototype.dot = function (v) { + return (this.x * v.x) + (this.y * v.y) + (this.z * v.z); +}; + +GameLib.D3.Vector3.prototype.normalize = function () { + + var EPSILON = 0.000001; + + var v2 = this.squared(); + + if (v2 < EPSILON) { + return this; //do nothing for zero vector + } + + var invLength = 1 / Math.sqrt(v2); + + this.x *= invLength; + this.y *= invLength; + this.z *= invLength; + + return this; +}; + +GameLib.D3.Vector4.Points = function () { + this.vectors = []; +}; + +GameLib.D3.Vector4.Points.prototype.add = function (vector) { + + if (vector instanceof GameLib.D3.Vector3) { + vector = new GameLib.D3.Vector4( + vector.x, + vector.y, + vector.z, + 1 + ) + } + + if (!vector instanceof GameLib.D3.Vector4) { + console.warn("Vector needs to be of type Vector4"); + throw new Error("Vector needs to be of type Vector4"); + } + + this.vectors.push(vector); + + return this; +}; + +GameLib.D3.Vector4.Points.prototype.copy = function () { + + var vectors = []; + + for (var i = 0; i < this.vectors.length; i++) { + vectors.push(this.vectors[i].copy()); + } + + return vectors; +}; + +GameLib.D3.Vector4.Points.prototype.maximizeXDistance = function (grain) { + +// console.log("vectors (before): " + JSON.stringify(this.vectors, null, 2)); + + var multiplier = 0; + + var rotationMatrixY = new GameLib.D3.Matrix4().rotationMatrixY(grain); + + var totalRadians = 0; + + var backupVectors = this.copy(); + + var maxXDistance = 0; + + for (var i = 0; i < Math.PI * 2; i += grain) { + + multiplier++; + + for (var j = 0; j < this.vectors.length; j++) { + this.vectors[j] = rotationMatrixY.multiply(this.vectors[j]); + } + + var distances = this.distances(); + + if (distances.x > maxXDistance) { + + maxXDistance = distances.x; + totalRadians = multiplier * grain; + } + } + + this.vectors = backupVectors; + +// console.log("distance: " + maxXDistance + " radians : " + totalRadians); + + var maxRotationMatrix = new GameLib.D3.Matrix4().rotationMatrixY(totalRadians); + + for (var k = 0; k < this.vectors.length; k++) { + this.vectors[k] = maxRotationMatrix.multiply(this.vectors[k]); + } + +// console.log("vectors (after): " + JSON.stringify(this.vectors, null, 2)); + +}; + +GameLib.D3.Vector4.Points.prototype.maximizeYDistance = function (grain) { + +// console.log("vectors (before): " + JSON.stringify(this.vectors, null, 2)); + + var multiplier = 0; + + var rotationMatrixX = new GameLib.D3.Matrix4().rotationMatrixX(grain); + + var totalRadians = 0; + + var backupVectors = this.copy(); + + var maxYDistance = 0; + + for (var i = 0; i < Math.PI * 2; i += grain) { + + multiplier++; + + for (var j = 0; j < this.vectors.length; j++) { + this.vectors[j] = rotationMatrixX.multiply(this.vectors[j]); + } + + var distances = this.distances(); + + if (distances.y > maxYDistance) { + maxYDistance = distances.y; + totalRadians = multiplier * grain; + } + } + + this.vectors = backupVectors; + +// console.log("distance: " + maxYDistance + " radians : " + totalRadians); + + var maxRotationMatrix = new GameLib.D3.Matrix4().rotationMatrixX(totalRadians); + + for (var k = 0; k < this.vectors.length; k++) { + this.vectors[k] = maxRotationMatrix.multiply(this.vectors[k]); + } + +// console.log("vectors (after): " + JSON.stringify(this.vectors, null, 2)); + +}; + + +GameLib.D3.Vector4.Points.prototype.lookAt = function (at, up) { + + var polyCenter = this.average(); + + console.log("poly center : " + JSON.stringify(polyCenter)); + + var lookAtMatrix = new GameLib.D3.Matrix4().lookAt(polyCenter, at, up); + + lookAtMatrix.rows[0] = new GameLib.D3.Vector4(1, 0, 0, 0); + lookAtMatrix.rows[1] = new GameLib.D3.Vector4(0, 0, 1, 0); + lookAtMatrix.rows[2] = new GameLib.D3.Vector4(0, 1, 0, 0); + + console.log("look at matrix : " + JSON.stringify(lookAtMatrix, null, 2)); + + for (var i = 0; i < this.vectors.length; i++) { + console.log("vector " + i + " (before): " + JSON.stringify(this.vectors[i])); + this.vectors[i] = lookAtMatrix.multiply(this.vectors[i]); + console.log("vector " + i + " (after) : " + JSON.stringify(this.vectors[i])); + } +}; + +GameLib.D3.Vector4.Points.prototype.distances = function () { + + var minX = this.vectors[0].x; + var minY = this.vectors[0].y; + var minZ = this.vectors[0].z; + + var maxX = this.vectors[0].x; + var maxY = this.vectors[0].y; + var maxZ = this.vectors[0].z; + + for (var i = 0; i < this.vectors.length; i++) { + if (this.vectors[i].x < minX) { + minX = this.vectors[i].x; + } + if (this.vectors[i].y < minY) { + minY = this.vectors[i].y; + } + if (this.vectors[i].z < minZ) { + minZ = this.vectors[i].z; + } + + if (this.vectors[i].x > maxX) { + maxX = this.vectors[i].x; + } + if (this.vectors[i].y > maxY) { + maxY = this.vectors[i].y; + } + if (this.vectors[i].z > maxZ) { + maxZ = this.vectors[i].z; + } + } + + return new GameLib.D3.Vector3( + Math.abs(maxX - minX), + Math.abs(maxY - minY), + Math.abs(maxY - minZ) + ) +}; + +GameLib.D3.Vector4.Points.prototype.average = function () { + var averageX = 0; + var averageY = 0; + var averageZ = 0; + + for (var i = 0; i < this.vectors.length; i++) { + averageX += this.vectors[i].x; + averageY += this.vectors[i].y; + averageZ += this.vectors[i].z; + } + + return new GameLib.D3.Vector3( + averageX / this.vectors.length, + averageY / this.vectors.length, + averageZ / this.vectors.length + ); +}; + +GameLib.D3.Vector4.Points.prototype.negative = function () { + + for (var i = 0; i < this.vectors.length; i++) { + this.vectors[i].x *= -1; + this.vectors[i].y *= -1; + this.vectors[i].z *= -1; + } + + return this; +}; + + +GameLib.D3.Vector4.Points.prototype.toOrigin = function () { + + var distanceFromOrigin = this.average().negative(); + + for (var i = 0; i < this.vectors.length; i++) { + this.vectors[i].translate(distanceFromOrigin); + } +}; +GameLib.D3.Vector4 = function(x, y, z, w) { + + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + + if (x) { + this.x = x; + } + + if (y) { + this.y = y; + } + + if (z) { + this.z = z; + } + + if (w) { + this.w = w; + } +}; + +GameLib.D3.Vector4.prototype.translate = function (v) { + this.x += v.x; + this.y += v.y; + this.z += v.z; + return this; +}; + +GameLib.D3.Vector4.prototype.copy = function () { + return new GameLib.D3.Vector4( + this.x, + this.y, + this.z, + this.w + ); +}; + +GameLib.D3.Vector4.prototype.multiply = function (s) { + if (s instanceof GameLib.D3.Vector3) { + this.x *= s.x; + this.y *= s.y; + this.z *= s.z; + } else if (s instanceof GameLib.D3.Matrix4) { + var x = s.rows[0].x * this.x + s.rows[0].y * this.y + s.rows[0].z * this.z + s.rows[0].w * this.w; + var y = s.rows[1].x * this.x + s.rows[1].y * this.y + s.rows[1].z * this.z + s.rows[1].w * this.w; + var z = s.rows[2].x * this.x + s.rows[2].y * this.y + s.rows[2].z * this.z + s.rows[2].w * this.w; + var w = s.rows[3].x * this.x + s.rows[3].y * this.y + s.rows[3].z * this.z + s.rows[3].w * this.w; + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } else { + console.log("functionality not implemented - please do this"); + throw new Error("not implemented"); + } +}; + + +GameLib.D3.Vector4.prototype.normalize = function () { + + // note - leave w untouched + var EPSILON = 0.000001; + + var v2 = this.x * this.x + this.y * this.y + this.z * this.z; + + if (v2 < EPSILON) { + return this; //do nothing for zero vector + } + + var invLength = 1 / Math.sqrt(v2); + + this.x *= invLength; + this.y *= invLength; + this.z *= invLength; + + return this; +}; + +GameLib.D3.Vector4.prototype.subtract = function (v) { + + if (v instanceof GameLib.D3.Vector3) { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + } + + if (v instanceof GameLib.D3.Vector4) { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; + } + + return this; +}; + + +/** + * The normal gets assigned when the face calculates its normal + * @param position + * @param boneWeights GameLib.D3.BoneWeight[] + * @constructor + */ +GameLib.D3.Vertex = function( + position, + boneWeights +) { + this.position = position; + this.boneWeights = boneWeights; +}; +/** + * World SuperSet - contains the custom world instance + * @param id + * @param name + * @param engine + * @param gravity + * @param broadphase + * @param solver + * @param rigidBodies + * @constructor + */ +GameLib.D3.World = function( + id, + name, + engine, + gravity, + broadphase, + solver, + rigidBodies +) { + + this.id = id; + + this.name = name; + + if (typeof gravity == 'undefined') { + gravity = new GameLib.D3.Vector3(0, -9.81, 0); + } + this.gravity = gravity; + + if (typeof broadphase == 'undefined') { + broadphase = new GameLib.D3.Physics.Broadphase( + null, + 'broadPhaseNaive', + GameLib.D3.Physics.BROADPHASE_TYPE_NAIVE + ); + } + this.broadphase = broadphase; + + if (typeof solver == 'undefined') { + solver = new GameLib.D3.Physics.Solver( + null, + 'GSSolver', + GameLib.D3.Physics.GS_SOLVER + ); + } + this.solver = solver; + + if (typeof rigidBodies == 'undefined') { + rigidBodies = []; + } + this.rigidBodies = rigidBodies; + + this.engine = null; + + this.worldInstance = null; + + /** + * We only set the physics property if we pass it in the constructor, + * because we don't always want the physics object (ex. when we store this world to the API - we also don't then + * want to store the custom worlds - we want to generate them after loading from API) + */ + if (engine) { + this.engine = engine; + this.worldInstance = this.createWorldInstance(); + } +}; + + +GameLib.D3.World.prototype.createWorldInstance = function() { + + this.engine.isNotCannonThrow(); + + var customWorld = new this.engine.instance.World(); + + var cannonBroadphase = null; + + + customWorld.broadphase = cannonBroadphase; + + var cannonSolver = null; + + if (this.solver.solverType == GameLib.D3.Physics.SPLIT_SOLVER) { + cannonSolver = new this.engine.instance.SplitSolver(); + } else if (this.solver.solverType == GameLib.D3.Physics.GS_SOLVER) { + cannonSolver = new this.engine.instance.GSSolver(); + cannonSolver.iterations = this.solver.iterations; + } + + customWorld.solver = cannonSolver; + + customWorld.gravity.x = this.gravity.x; + customWorld.gravity.y = this.gravity.y; + customWorld.gravity.z = this.gravity.z; + + for (var b = 0; b < this.rigidBodies.length; b++) { + + var customBody = this.createCustomBody(this.rigidBodies[b]); + + //customWorld.AddRigidBody(); + } + + customWorld.name = this.name; + + return customWorld; +}; + +GameLib.D3.Physics.World.prototype.AddShape = function( + shape, // d3.physics.shape + rigidBody, + offset, // vec3 + orientation //quaternion +) { + shape.shape = shape; + + /** + * TODO:: fix this? + */ + if (this.physics.engineType === GameLib.D3.Physics.TYPE_CANNON) { + + var _offset = null; + var _orientation = null; + + if(offset != null && typeof offset !== 'undefined') { + _offset = new this.physics.CANNON.Vec3(offset.x, offset.y, offset.z); + } + + if(orientation != null && typeof orientation !== 'undefined') { + _orientation = new this.physics.CANNON.Quaternion(orientation.x, orientation.y, orientation.z, orientation.w); + } + + rigidBody.bodyObject.addShape(shape.shapeObject, _offset, _orientation); + } +}; + +GameLib.D3.Physics.World.prototype.Wheel = function() { + +}; + +GameLib.D3.Physics.World.prototype.CreateRigidVehicle = function( + chassisBody // Physics.RigidBody +) { + var rigidVehicle = new GameLib.D3.Physics.RigidVehicle(); + + if (this.physics.engineType == GameLib.D3.Physics.TYPE_CANNON) { + var vehicle = new this.physics.CANNON.RigidVehicle({ + chassisBody: chassisBody.bodyObject + }); + rigidVehicle.vehicleObject = vehicle; + return rigidVehicle; + } +}; + +GameLib.D3.Physics.World.prototype.CreateRaycastVehicle = function( + chassisBody // Physics.RigidBody +) { + var raycastVehicle = new GameLib.D3.Physics.RaycastVehicle(); + + if(this.physics.engineType == GameLib.D3.Physics.TYPE_CANNON) { + var vehicle = new this.physics.CANNON.RaycastVehicle({ + chassisBody: chassisBody.bodyObject + }); + raycastVehicle.vehicleObject = vehicle; + return raycastVehicle; + } +}; + +GameLib.D3.Physics.World.prototype.AddWheelToRigidVehicle = function( + vehicle, + rigidBody, + position, + axis, + direction +) { + if(this.physics.engineType == GameLib.D3.Physics.TYPE_CANNON) { + vehicle.vehicleObject.addWheel({ + body: rigidBody.bodyObject, + position: new this.physics.CANNON.Vec3(position.x, position.y, position.z), + axis: new this.physics.CANNON.Vec3(axis.x, axis.y, axis.z), + direction: new this.physics.CANNON.Vec3(direction.x, direction.y, direction.z) + }); + } +}; + +GameLib.D3.Physics.World.prototype.AddWheelToRaycastVehicle = function ( + vehicle, // physics.raycastvehicle + options // cannon options +) { + if (this.physics.engineType == GameLib.D3.Physics.TYPE_CANNON) { + vehicle.vehicleObject.addWheel(options); + } else { + console.log("function for engine not implemented"); + } +}; + +GameLib.D3.Physics.World.prototype.RigidVehicle.prototype.GetWheelInfo = function( + +) { + // note: need a way to determine which engine we are currently using + return this.vehicleObject.wheelBodies; +}; + +GameLib.D3.Physics.World.prototype.RaycastVehicle.prototype.GetWheelInfo = function( + +) { + // note: need a way to determine which engine we are currently using + return this.vehicleObject.wheelInfos; +}; + + +GameLib.D3.Physics.World.prototype.CreateTriMeshShape = function( + vertices, // flat list of floats + indices // flat list of floats +) { + if(this.physics.engineType == GameLib.D3.Physics.TYPE_CANNON) { + return new GameLib.D3.Physics.Shape(new this.physics.CANNON.Trimesh(vertices, indices), GameLib.D3.Physics.SHAPE_TYPE_TRIMESH); + } +}; + +GameLib.D3.Physics.World.prototype.CreateSphereShape = function ( + radius +) { + if(this.physics.engineType == GameLib.D3.Physics.TYPE_CANNON) { + return new GameLib.D3.Physics.Shape(new this.physics.CANNON.Sphere(radius), GameLib.D3.Physics.SHAPE_TYPE_SPHERE); + } +}; + +GameLib.D3.Physics.World.prototype.CreateBoxShape = function( + halfExtensions // vec3 +) { + if(this.physics.engineType == GameLib.D3.Physics.TYPE_CANNON) { + return new GameLib.D3.Physics.Shape(new this.physics.CANNON.Box(new this.physics.CANNON.Vec3(halfExtensions.x, halfExtensions.y, halfExtensions.z)), GameLib.D3.Physics.SHAPE_TYPE_BOX); + } +}; + +GameLib.D3.Physics.World.prototype.CreateCylinderShape = function( + radiusTop, + radiusBottom, + height, + numSegments +) { + if(this.physics.engineType == GameLib.D3.Physics.TYPE_CANNON) { + return new GameLib.D3.Physics.Shape(new this.physics.CANNON.Cylinder(radiusTop, radiusBottom, height, numSegments), GameLib.D3.Physics.SHAPE_TYPE_CYLINDER); + } +}; + +GameLib.D3.Physics.World.prototype.AddRigidBody = function( + rigidBody // Physics.RigidBody +) { + if(this.physics.engineType === GameLib.D3.Physics.TYPE_CANNON) { + this.worldObject.addBody(rigidBody.bodyObject); + } +}; + +GameLib.D3.Physics.World.prototype.AddVehicle = function( + vehicle // note: physics.vehicle +) { + if(this.physics.engineType == GameLib.D3.Physics.TYPE_CANNON) { + vehicle.vehicleObject.addToWorld(this.worldObject); + } +}; + +GameLib.D3.Physics.World.prototype.Step = function( + timeStep +) { + if(this.physics.engineType == GameLib.D3.Physics.TYPE_CANNON) { + + // todo: figure out, why this call to internal step is more stable for trimesh collisions..... + //this.worldObject.internalStep(timeStep); + //return; + + var now = performance.now() / 1000; + + if(!this.lastCallTime){ + // last call time not saved, cant guess elapsed time. Take a simple step. + this.worldObject.step(timeStep); + this.lastCallTime = now; + return; + } + + var timeSinceLastCall = now - this.lastCallTime; + + this.worldObject.step(timeStep, timeSinceLastCall); + + this.lastCallTime = now; + } +}; + +GameLib.D3.Physics.World.prototype.GetIndexedVertices = function( + triangleMeshShape +) { + + if(this.engine.engineType == GameLib.D3.Physics.TYPE_CANNON) { + + return { + vertices : triangleMeshShape.vertices, + indices : triangleMeshShape.indices + }; + + } else { + // todo: implement this for other physics engines. + return null; + } + +}; + +GameLib.D3.Physics.World.prototype.GenerateWireframeViewMesh = function( + triangleMeshShape, + normalLength, + scale, + opacity, + wireframeColor +) { + var geometryTHREE = new THREE.Geometry(); + var wireframeTHREEMesh = new THREE.Mesh + ( + geometryTHREE, + new THREE.MeshBasicMaterial({ + color: wireframeColor ? wireframeColor : 0xfefefe, + wireframe: true, + opacity: opacity ? opacity : 0.5 + }) + ); + + var data = this.GetIndexedVertices(triangleMeshShape); + + for(var i = 0, l = data.vertices.length / 3; i < l; i++) { + geometryTHREE.vertices.push(new THREE.Vector3(data.vertices[i * 3], data.vertices[i * 3 + 1], data.vertices[i * 3 + 2])); + } + + for(var i = 0, l = data.indices.length / 3; i < l; i++) { + var i0 = data.indices[i * 3]; + var i1 = data.indices[i * 3 + 1]; + var i2 = data.indices[i * 3 + 2]; + + geometryTHREE.faces.push(new THREE.Face3(i0, i1, i2)); + + // Create debug view for normals + + // Center point on the mesh itself + var centroid = new THREE.Vector3() + .add(geometryTHREE.vertices[i0]) + .add(geometryTHREE.vertices[i1]) + .add(geometryTHREE.vertices[i2]) + .divideScalar(3); + + var normal = null; + if(this.engine.engineType == GameLib.D3.Physics.TYPE_CANNON) { + var normal = new this.physics.CANNON.Vec3(); + triangleMeshShape.getNormal(i, normal); + } else { + // todo: calculate the normal for v0, v1 & v2 here. + } + + var arrow = new THREE.ArrowHelper(new THREE.Vector3(normal.x, normal.y, normal.z), centroid, normalLength, new THREE.Color(normal.x, normal.y, normal.z)); + wireframeTHREEMesh.add( arrow ); + } + + wireframeTHREEMesh.scale.x = scale.x; + wireframeTHREEMesh.scale.y = scale.y; + wireframeTHREEMesh.scale.z = scale.z; + + return wireframeTHREEMesh; +}; + +GameLib.D3.Physics.World.prototype.GenerateTriangleCollisionMesh = function( + threeMesh, + mass, // default = 0 + friction, // default = 10 + createCollisionSubMeshes, // boolean. default = false + facesPerSubsection, // int. default = 0 + subsectionsToMerge // int. default = 0 +) { + var processedFaces = 0; + var facesPerSubSection = facesPerSubsection || 0; + var subMeshesToMerge = subsectionsToMerge || 0; + var totalAmtFaces = threeMesh.geometry.faces.length; + var facesToProcess = createCollisionSubMeshes ? (subMeshesToMerge * facesPerSubSection) : totalAmtFaces; + + var pairs = []; // output + + var vertices = []; + var indicies = []; + + for(var i = 0; i <= totalAmtFaces; i++) { + if(processedFaces == facesToProcess || i == totalAmtFaces) { + + var body = null; + + if(this.engine.engineType == GameLib.D3.Physics.TYPE_CANNON) { + + var meshShape = new this.physics.CANNON.Trimesh(vertices, indicies); + meshShape.setScale(new this.physics.CANNON.Vec3(threeMesh.scale.x, threeMesh.scale.y, threeMesh.scale.z)); + meshShape.updateAABB(); + meshShape.updateNormals(); + meshShape.updateEdges(); + meshShape.updateBoundingSphereRadius(); + meshShape.updateTree(); + + body = new this.physics.CANNON.Body({ mass: mass ? mass : 0, friction: friction ? friction : 10 }); + body.addShape(meshShape); + + } else if (this.engine.engineType == GameLib.D3.Physics.Engine.TYPE_AMMO) { + + } else if (this.engine.engineType == GameLib.D3.Physics.Engine.TYPE_GOBLIN) { + + } + + pairs.push({ + threeObject : createCollisionSubMeshes ? null : threeMesh, + physicsObject : body + }); + + vertices = []; + indicies = []; + processedFaces = 0; + + if(i == totalAmtFaces) { + return pairs; + } + } + + var face = threeMesh.geometry.faces[i]; + indicies.push(indicies.length); + indicies.push(indicies.length); + indicies.push(indicies.length); + + var v0 = threeMesh.geometry.vertices[face.a]; + var v1 = threeMesh.geometry.vertices[face.b]; + var v2 = threeMesh.geometry.vertices[face.c]; + + vertices.push(v0.x, v0.y, v0.z); + vertices.push(v1.x, v1.y, v1.z); + vertices.push(v2.x, v2.y, v2.z); + + processedFaces++; + } +}; + +if (typeof module !== 'undefined') { + module.exports = GameLib; +} \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index fdb7e86..1ae5504 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -11,8 +11,8 @@ gulp.task( .pipe(concat('game-lib.js')) .pipe(minify({ ext:{ - src:'-debug.js', - min:'.js' + src:'.js', + min:'-min.js' } })) .pipe(gulp.dest('./build/')); diff --git a/src/game-lib-broadphase.js b/src/game-lib-broadphase.js index af8aeaf..4be0c57 100644 --- a/src/game-lib-broadphase.js +++ b/src/game-lib-broadphase.js @@ -1,14 +1,18 @@ /** * Physics Broadphase Superset * @param id - * @param name - * @param broadphaseType + * @param name String + * @param broadphaseType Number + * @param engine GameLib.D3.Engine + * @param createInstance Boolean * @constructor */ GameLib.D3.Broadphase = function( id, name, - broadphaseType + broadphaseType, + engine, + createInstance ) { this.id = id; @@ -18,11 +22,50 @@ GameLib.D3.Broadphase = function( this.name = name; if (typeof broadphaseType == 'undefined') { - console.warn('undefined broadphase type'); - throw new Error('undefined broadphase type'); + broadphaseType = GameLib.D3.Broadphase.BROADPHASE_TYPE_NAIVE; + } + this.broadphaseType = broadphaseType; + + if (typeof engine == 'undefined') { + engine = null; + } + this.engine = engine; + + this.instance = null; + + if (createInstance) { + this.createInstance(); + } +}; + +/** + * Creates a custom Broadphase instance based on the engine type + */ +GameLib.D3.Broadphase.prototype.createInstance = function() { + + if (!(this.engine instanceof GameLib.D3.Engine)) { + console.warn('No Engine'); + throw new Error('No Engine'); } - this.broadphaseType = broadphaseType; + this.engine.isNotCannonThrow(); + + var instance = null; + + if (this.broadphaseType == GameLib.D3.Broadphase.BROADPHASE_TYPE_NAIVE) { + instance = new this.engine.instance.NaiveBroadphase(); + } else if (this.broadphaseType == GameLib.D3.Broadphase.BROADPHASE_TYPE_GRID) { + instance = new this.engine.instance.GridBroadphase(); + } else if (this.broadphaseType == GameLib.D3.Broadphase.BROADPHASE_TYPE_SAP) { + instance = new this.engine.instance.SAPBroardphase(); + } else { + console.warn('Unsupported broadphase type: ' + this.broadphaseType); + throw new Error('Unsupported broadphase type: ' + this.broadphaseType); + } + + this.instance = instance; + + return instance; }; /** diff --git a/src/game-lib-color.js b/src/game-lib-color.js index bf8e40e..caf8055 100644 --- a/src/game-lib-color.js +++ b/src/game-lib-color.js @@ -11,8 +11,4 @@ GameLib.D3.Color = function(r, g, b, a) { this.g = g; this.b = b; this.a = a; -}; - -if (typeof module !== 'undefined') { - module.exports = GameLib.D3.Color; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/game-lib-engine.js b/src/game-lib-engine.js index 241ed20..e019c55 100644 --- a/src/game-lib-engine.js +++ b/src/game-lib-engine.js @@ -1,7 +1,7 @@ /** * Engine Superset * @param engineType - * @param instance + * @param instance {CANNON | Ammo | Goblin} * @constructor */ GameLib.D3.Engine = function( @@ -30,7 +30,6 @@ GameLib.D3.Engine.prototype.isNotCannonThrow = function() { } }; - /** * True if Ammo physics * @returns {boolean} @@ -53,8 +52,4 @@ GameLib.D3.Engine.prototype.isGoblin = function() { */ GameLib.D3.Engine.ENGINE_TYPE_CANNON = 0x1; GameLib.D3.Engine.ENGINE_TYPE_AMMO = 0x2; -GameLib.D3.Engine.ENGINE_TYPE_GOBLIN = 0x3; - -if (typeof module !== 'undefined') { - module.exports = GameLib.D3.Engine; -} \ No newline at end of file +GameLib.D3.Engine.ENGINE_TYPE_GOBLIN = 0x3; \ No newline at end of file diff --git a/src/game-lib-fly-controls.js b/src/game-lib-fly-controls.js index 36d1c80..8daa8a1 100644 --- a/src/game-lib-fly-controls.js +++ b/src/game-lib-fly-controls.js @@ -49,9 +49,12 @@ GameLib.D3.FlyControls = function( this.element.requestPointerLock = this.element.requestPointerLock || this.element.mozRequestPointerLock || this.element.webkitRequestPointerLock; document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock; } - }; +/** + * Go forward / backward on mouse wheel + * @param event + */ GameLib.D3.FlyControls.prototype.onMouseWheel = function(event) { this.moveForward = true; this.applyTranslation(event.wheelDelta * 0.001); @@ -59,42 +62,39 @@ GameLib.D3.FlyControls.prototype.onMouseWheel = function(event) { this.moveForward = false; }; +/** + * Start rotating the camera on mouse middle button down + * @param event + */ GameLib.D3.FlyControls.prototype.onMouseDown = function(event) { - - // if (event.button == 0) { - // this.canRotate = true; - // this.canvas.addEventListener('mousemove', this.mouseMoveCallback, false); - // if (this.havePointerLock) { - // this.element.requestPointerLock(); - // } - // } - if (event.button == 1) { this.canRotate = true; this.canvas.addEventListener('mousemove', this.mouseMoveCallback, false); } }; +/** + * Stop rotating on middle mouse button down + * @param event + */ GameLib.D3.FlyControls.prototype.onMouseUp = function(event) { - - // if (event.button == 0) { - // this.canRotate = false; - // this.canvas.removeEventListener('mousemove', this.mouseMoveCallback); - // if (this.havePointerLock) { - // document.exitPointerLock(); - // } - // } - if (event.button == 1) { this.canRotate = false; this.canvas.removeEventListener('mousemove', this.mouseMoveCallback); } }; +/** + * Apply current yaw and pitch to camera + */ GameLib.D3.FlyControls.prototype.applyRotation = function() { this.camera.rotation.set(this.pitch, this.yaw, 0, "YXZ"); }; +/** + * Apply current position to camera + * @param deltaTime + */ GameLib.D3.FlyControls.prototype.applyTranslation = function(deltaTime) { var direction = new this.THREE.Vector3(0, 0, -1); var rotation = new this.THREE.Euler(0, 0, 0, "YXZ"); @@ -102,41 +102,35 @@ GameLib.D3.FlyControls.prototype.applyTranslation = function(deltaTime) { direction = direction.applyEuler(rotation); + var forward = direction.normalize(); + var right = forward.cross(new this.THREE.Vector3(0, 1, 0)); + if(this.moveForward) { - var newPos = direction.normalize(); - this.camera.position.x += newPos.x * (deltaTime * this.flySpeed); - this.camera.position.y += newPos.y * (deltaTime * this.flySpeed); - this.camera.position.z += newPos.z * (deltaTime * this.flySpeed); + this.camera.position.x += forward.x * (deltaTime * this.flySpeed); + this.camera.position.y += forward.y * (deltaTime * this.flySpeed); + this.camera.position.z += forward.z * (deltaTime * this.flySpeed); } else if(this.moveBackward) { - var newPos = direction.normalize(); - this.camera.position.x -= newPos.x * (deltaTime * this.flySpeed); - this.camera.position.y -= newPos.y * (deltaTime * this.flySpeed); - this.camera.position.z -= newPos.z * (deltaTime * this.flySpeed); + this.camera.position.x -= forward.x * (deltaTime * this.flySpeed); + this.camera.position.y -= forward.y * (deltaTime * this.flySpeed); + this.camera.position.z -= forward.z * (deltaTime * this.flySpeed); } if(this.moveLeft) { - var forward = direction.normalize(); - var right = forward.cross(new this.THREE.Vector3(0, 1, 0)); - var newPos = right; - this.camera.position.x -= newPos.x * (deltaTime * this.flySpeed); - this.camera.position.y -= newPos.y * (deltaTime * this.flySpeed); - this.camera.position.z -= newPos.z * (deltaTime * this.flySpeed); + this.camera.position.x -= right.x * (deltaTime * this.flySpeed); + this.camera.position.y -= right.y * (deltaTime * this.flySpeed); + this.camera.position.z -= right.z * (deltaTime * this.flySpeed); } else if(this.moveRight) { - var forward = direction.normalize(); - var right = forward.cross(new this.THREE.Vector3(0, 1, 0)); - var newPos = right; - this.camera.position.x += newPos.x * (deltaTime * this.flySpeed); - this.camera.position.y += newPos.y * (deltaTime * this.flySpeed); - this.camera.position.z += newPos.z * (deltaTime * this.flySpeed); + this.camera.position.x += right.x * (deltaTime * this.flySpeed); + this.camera.position.y += right.y * (deltaTime * this.flySpeed); + this.camera.position.z += right.z * (deltaTime * this.flySpeed); } - // Absolute Y-Axis if(this.moveUp) { this.camera.position.y += (deltaTime * this.flySpeed); @@ -148,11 +142,20 @@ GameLib.D3.FlyControls.prototype.applyTranslation = function(deltaTime) { } }; +/** + * This update function should be called from the animation function in order to apply the 'frame rate independent' + * movement to the camera + * @param deltaTime + */ GameLib.D3.FlyControls.prototype.update = function(deltaTime) { this.applyRotation(); this.applyTranslation(deltaTime); }; +/** + * Rotate on mouse move + * @param event + */ GameLib.D3.FlyControls.prototype.onMouseMove = function ( event ) { if (this.canRotate) { var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0; @@ -163,6 +166,10 @@ GameLib.D3.FlyControls.prototype.onMouseMove = function ( event ) { } }; +/** + * Keyboard controls + * @param event + */ GameLib.D3.FlyControls.prototype.onKeyDown = function ( event ) { switch ( event.keyCode ) { @@ -192,6 +199,10 @@ GameLib.D3.FlyControls.prototype.onKeyDown = function ( event ) { } }; +/** + * Keyboard controls + * @param event + */ GameLib.D3.FlyControls.prototype.onKeyUp = function ( event ) { switch ( event.keyCode ) { diff --git a/src/game-lib-heightmap.js b/src/game-lib-heightmap.js index a08ca63..b5ed5c8 100644 --- a/src/game-lib-heightmap.js +++ b/src/game-lib-heightmap.js @@ -1,22 +1,36 @@ -GameLib.D3.HeightmapData = function ( +/** + * + * @param sizeX Number + * @param sizeY Number + * @param matrix matrix 2D Array with height data (Column Major) + * @constructor + */ +GameLib.D3.Heightmap = function( sizeX, sizeY, matrix ) { - this.sizeX = sizeX || 0; - this.sizeY = sizeY || 0; + if (typeof sizeX == 'undefined') { + sizeX = 0; + } + this.sizeX = sizeX; - // 2D Array with height data - // Column-major - this.matrix = matrix || []; + if (typeof sizeY == 'undefined') { + sizeY = 0; + } + this.sizeY = sizeY; + + if (typeof matrix == 'undefined') { + matrix = []; + } + this.matrix = matrix; }; -// Note: this currently only works for cannon! -GameLib.D3.GenerateThreeMeshFromHeightField = function ( - heightFieldShape - // Physics type..... +GameLib.D3.Heightmap.prototype.generateThreeMeshFromHeightField = function( + THREE, + heightFieldShape, + engine ) { - var geometry = new THREE.Geometry(); var v0 = new this.physics.CANNON.Vec3(); @@ -50,7 +64,8 @@ GameLib.D3.GenerateThreeMeshFromHeightField = function ( return new THREE.Mesh(geometry, new THREE.MeshNormalMaterial({ wireframe : false, shading : THREE.SmoothShading })); }; -GameLib.D3.GenerateHeightmapDataFromImage = function ( + +GameLib.D3.Heightmap.prototype.generateHeightmapDataFromImage = function ( imagePath, callback // receives HeightmapData instance as the first argument ) { diff --git a/src/game-lib-world.js b/src/game-lib-world.js index d9f8854..1c008a7 100644 --- a/src/game-lib-world.js +++ b/src/game-lib-world.js @@ -75,16 +75,6 @@ GameLib.D3.World.prototype.createWorldInstance = function() { var cannonBroadphase = null; - if (this.broadphase.broadphaseType == GameLib.D3.Broadphase.BROADPHASE_TYPE_NAIVE) { - cannonBroadphase = new this.engine.instance.NaiveBroadphase(); - } else if (this.broadphase.broadphaseType == GameLib.D3.Broadphase.BROADPHASE_TYPE_GRID) { - cannonBroadphase = new this.engine.instance.GridBroadphase(); - } else if (this.broadphase.broadphaseType == GameLib.D3.Broadphase.BROADPHASE_TYPE_SAP) { - cannonBroadphase = new this.engine.instance.SAPBroardphase(); - } else { - console.warn('Unsupported broadphase type: ' + this.broadphase.broadphaseType); - throw new Error('Unsupported broadphase type: ' + this.broadphase.broadphaseType); - } customWorld.broadphase = cannonBroadphase;