diff --git a/build/game-lib.js b/build/game-lib.js index c28f2dd..cb7932c 100644 --- a/build/game-lib.js +++ b/build/game-lib.js @@ -5037,97 +5037,248 @@ GameLib.D3.World.prototype.GetIndexedVertices = function( }; /** - * TODO: FIX - * @param triangleMeshShape - * @param normalLength - * @param scale - * @param opacity - * @param wireframeColor + * @param triangleMeshShape GameLib.D3.Shape + * @param normalLength Number + * @param scale GameLib.D3.Vector3 + * @param opacity Number + * @param wireframeColor HexCode + * @param graphics THREE * @returns {THREE.Mesh|this.meshes} * @constructor */ -GameLib.D3.World.GenerateWireframeViewMesh = function( +GameLib.D3.World.prototype.generateWireframeViewTriangleMesh = function( + graphics, triangleMeshShape, normalLength, scale, opacity, wireframeColor ) { - var geometryTHREE = new THREE.Geometry(); - var wireframeTHREEMesh = new THREE.Mesh - ( - geometryTHREE, - new THREE.MeshBasicMaterial({ - color: wireframeColor ? wireframeColor : 0xfefefe, + graphics.isNotThreeThrow(); + this.engine.isNotCannonThrow(); + + if(typeof normalLength == 'undefined') { + normalLength = 10; + } + + if(typeof scale == 'undefined') { + scale = new graphics.instance.Vector3(1, 1, 1); + } + + if(typeof opacity == 'undefined') { + opacity = 0.5; + } + + if(typeof wireframeColor == 'undefined') { + wireframeColor = 0xfefefe; + } + + var graphicsGeometry = new graphics.instance.Geometry(); + + var wireframeMesh = new graphics.instance.Mesh( + graphicsGeometry, + new graphics.instance.MeshBasicMaterial({ + color: wireframeColor, wireframe: true, - opacity: opacity ? opacity : 0.5 + opacity: opacity }) ); - 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 = triangleMeshShape.instance.vertices.length / 3; i < l; ++i) { + graphicsGeometry.vertices.push( + new graphics.instance.Vector3( + triangleMeshShape.instance.vertices[i * 3], + triangleMeshShape.instance.vertices[i * 3 + 1], + triangleMeshShape.instance.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]; + for(var i = 0, l = triangleMeshShape.instance.indices.length / 3; i < l; ++i) { + var i0 = triangleMeshShape.instance.indices[i * 3]; + var i1 = triangleMeshShape.instance.indices[i * 3 + 1]; + var i2 = triangleMeshShape.instance.indices[i * 3 + 2]; - geometryTHREE.faces.push(new THREE.Face3(i0, i1, i2)); + graphicsGeometry.faces.push( + new graphics.instance.Face3( + i0, + i1, + i2 + ) + ); - // Create debug view for normals + // Center point on the current triangle - // Center point on the mesh itself - var centroid = new THREE.Vector3() - .add(geometryTHREE.vertices[i0]) - .add(geometryTHREE.vertices[i1]) - .add(geometryTHREE.vertices[i2]) + var centroid = new graphics.instance.Vector3() + .add(graphicsGeometry.vertices[i0]) + .add(graphicsGeometry.vertices[i1]) + .add(graphicsGeometry.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. - } + // Get the normal from the mesh shape itself + var normal = new this.engine.instance.Vec3(); + triangleMeshShape.instance.getNormal(i , normal); - 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 ); + var arrow = new graphics.instance.ArrowHelper( + new graphics.instance.Vector3( + normal.x, + normal.y, + normal.z + ), + centroid, + normalLength, + new graphics.instance.Color( + normal.x, + normal.y, + normal.z + ) + ); + wireframeMesh.add( arrow ); } - wireframeTHREEMesh.scale.x = scale.x; - wireframeTHREEMesh.scale.y = scale.y; - wireframeTHREEMesh.scale.z = scale.z; + wireframeMesh.scale.x = scale.x; + wireframeMesh.scale.y = scale.y; + wireframeMesh.scale.z = scale.z; - return wireframeTHREEMesh; + return wireframeMesh; }; /** - * TODO: FIX - * @param threeMesh - * @param mass - * @param friction - * @param createCollisionSubMeshes - * @param facesPerSubsection - * @param subsectionsToMerge - * @returns {Array} + * @param convexPolyMeshShape GameLib.D3.Shape + * @param normalLength Number + * @param scale GameLib.D3.Vector3 + * @param opacity Number + * @param wireframeColor HexCode + * @param graphics THREE + * @returns {THREE.Mesh|this.meshes} * @constructor */ -GameLib.D3.World.GenerateTriangleCollisionMesh = function( - threeMesh, - mass, // default = 0 - friction, // default = 10 - createCollisionSubMeshes, // boolean. default = false - facesPerSubsection, // int. default = 0 - subsectionsToMerge // int. default = 0 +GameLib.D3.World.prototype.generateWireframeViewConvexPolyMesh = function( + graphics, + convexPolyMeshShape, + normalLength, + scale, + opacity, + wireframeColor ) { + graphics.isNotThreeThrow(); + this.engine.isNotCannonThrow(); + + if(typeof normalLength == 'undefined') { + normalLength = 10; + } + + if(typeof scale == 'undefined') { + scale = new graphics.instance.Vector3(1, 1, 1); + } + + if(typeof opacity == 'undefined') { + opacity = 0.5; + } + + if(typeof wireframeColor == 'undefined') { + wireframeColor = 0xfefefe; + } + + + var graphicsGeometry = new graphics.instance.Geometry(); + var wireframeMesh = new graphics.instance.Mesh( + graphicsGeometry, + new graphics.instance.MeshBasicMaterial({ + color: wireframeColor, + wireframe: true, + opacity: opacity + }) + ); + + for(var i = 0, l = convexPolyMeshShape.instance.vertices.length; i < l; i++) { + var vertex = convexPolyMeshShape.instance.vertices[i]; + graphicsGeometry.vertices.push(new graphics.instance.Vector3(vertex.x, vertex.y, vertex.z)); + } + + for(var i = 0, l = convexPolyMeshShape.instance.faces.length; i < l; i++) { + var face = convexPolyMeshShape.instance.faces[i]; + + var i0 = face[0]; + var i1 = face[1]; + var i2 = face[2]; + + graphicsGeometry.faces.push(new graphics.instance.Face3(i0, i1, i2)); + + // Center point on the current triangle + var centroid = new graphics.instance.Vector3() + .add(graphicsGeometry.vertices[i0]) + .add(graphicsGeometry.vertices[i1]) + .add(graphicsGeometry.vertices[i2]) + .divideScalar(3); + + var normalVec3 = convexPolyMeshShape.instance.faceNormals[i]; + var normal = new graphics.instance.Vector3( + normalVec3.x, + normalVec3.y, + normalVec3.z + ); + + var arrow = new graphics.instance.ArrowHelper( + normal, + centroid, + normalLength, + new graphics.instance.Color( + normal.x, + normal.y, + normal.z + ) + ); + + wireframeMesh.add( arrow ); + } + + wireframeMesh.scale.x = scale.x; + wireframeMesh.scale.y = scale.y; + wireframeMesh.scale.z = scale.z; + + return wireframeMesh; +}; + +/** + * @param graphics GameLib.D3.Graphics + * @param graphicsMesh THREE.Mesh + * @param mass Number + * @param friction Number + * @param createCollisionSubMeshes Boolean + * @param facesPerSubsection Number + * @param subsectionsToMerge Number + * @returns {Object} + * @constructor + */ +GameLib.D3.World.prototype.generateTriangleMeshShapeDivided = function( + graphics, + graphicsMesh, + mass, + friction, + createCollisionSubMeshes, + facesPerSubsection, + subsectionsToMerge +) { + graphics.isNotThreeThrow(); + this.engine.isNotCannonThrow(); + + if(mass == null || typeof mass == 'undefined') { + mass = 0; + } + + if(friction == null || typeof friction == 'undefined') { + friction = 10; + } + + if(createCollisionSubMeshes == null || typeof createCollisionSubMeshes == 'undefined') { + createCollisionSubMeshes = false; + } + var processedFaces = 0; var facesPerSubSection = facesPerSubsection || 0; var subMeshesToMerge = subsectionsToMerge || 0; - var totalAmtFaces = threeMesh.geometry.faces.length; + var totalAmtFaces = graphicsMesh.geometry.faces.length; var facesToProcess = createCollisionSubMeshes ? (subMeshesToMerge * facesPerSubSection) : totalAmtFaces; var pairs = []; // output @@ -5140,27 +5291,28 @@ GameLib.D3.World.GenerateTriangleCollisionMesh = function( var body = null; - if(this.engine.engineType == GameLib.D3.Physics.TYPE_CANNON) { + var meshShape = new this.engine.instance.Trimesh(vertices, indicies); - 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(); + meshShape.setScale(new this.engine.instance.Vec3( + graphicsMesh.scale.x, + graphicsMesh.scale.y, + graphicsMesh.scale.z + )); - body = new this.physics.CANNON.Body({ mass: mass ? mass : 0, friction: friction ? friction : 10 }); - body.addShape(meshShape); + meshShape.updateAABB(); + meshShape.updateNormals(); + meshShape.updateEdges(); + meshShape.updateBoundingSphereRadius(); + meshShape.updateTree(); - } else if (this.engine.engineType == GameLib.D3.Physics.Engine.TYPE_AMMO) { - - } else if (this.engine.engineType == GameLib.D3.Physics.Engine.TYPE_GOBLIN) { - - } + body = new this.engine.instance.Body({ + mass: mass, + friction: friction + }); + body.addShape(meshShape); pairs.push({ - threeObject : createCollisionSubMeshes ? null : threeMesh, + threeObject : createCollisionSubMeshes ? null : graphicsMesh, physicsObject : body }); @@ -5173,14 +5325,14 @@ GameLib.D3.World.GenerateTriangleCollisionMesh = function( } } - var face = threeMesh.geometry.faces[i]; + var face = graphicsMesh.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]; + var v0 = graphicsMesh.geometry.vertices[face.a]; + var v1 = graphicsMesh.geometry.vertices[face.b]; + var v2 = graphicsMesh.geometry.vertices[face.c]; vertices.push(v0.x, v0.y, v0.z); vertices.push(v1.x, v1.y, v1.z); @@ -5190,6 +5342,395 @@ GameLib.D3.World.GenerateTriangleCollisionMesh = function( } }; +/** + * @param graphics GameLib.D3.Graphics + * @param graphicsMesh THREE.Mesh + * @returns {GameLib.D3.RigidBody} + * @constructor + */ +GameLib.D3.World.prototype.generateTriangleMeshShape = function( + graphics, + graphicsMesh +) { + + // - - - - - - - - - - - - - - - - - - - - - - - - - + // Note: I did not test this yet with the API data. + // - - - - - - - - - - - - - - - - - - - - - - - - - + + var scaledVertices = []; + for(var i = 0, l = graphicsMesh.geometry.vertices.length; i < l; i++) { + + var vertex = graphicsMesh.geometry.vertices[i]; + + scaledVertices.push(new this.engine.instance.Vec3( + vertex.x * graphicsMesh.scale.x, + vertex.y * graphicsMesh.scale.y, + vertex.z * graphicsMesh.scale.z + )); + + } + + var triangleFaces = []; + for(var f = 0, fl = graphicsMesh.geometry.faces.length; f < fl; f++) { + var i0 = graphicsMesh.geometry.faces[f].a; + var i1 = graphicsMesh.geometry.faces[f].b; + var i2 = graphicsMesh.geometry.faces[f].c; + + triangleFaces.push([ + i0, + i1, + i2 + ]); + } + + // - - - - - - - - - - - - - - - - - - - + // Create collision mesh + // - - - - - - - - - - - - - - - - - - - + + var reindexedFaces = {}; + var vertices = []; + var faces = []; + + var processedFaces = 0; + var totalFacesToProcess = triangleFaces.length; + var flLastIndex = 0; + + for(var f = 0; f < totalFacesToProcess; f++) { + + var i0 = triangleFaces[f][0]; + var i1 = triangleFaces[f][1]; + var i2 = triangleFaces[f][2]; + + if(typeof reindexedFaces[i0] === 'undefined') { + vertices.push(scaledVertices[i0].x, scaledVertices[i0].y, scaledVertices[i0].z); + reindexedFaces[i0] = flLastIndex; + flLastIndex++; + } + + if(typeof reindexedFaces[i1] === 'undefined') { + vertices.push(scaledVertices[i1].x, scaledVertices[i1].y, scaledVertices[i1].z); + reindexedFaces[i1] = flLastIndex; + flLastIndex++; + } + + if(typeof reindexedFaces[i2] === 'undefined') { + vertices.push(scaledVertices[i2].x, scaledVertices[i2].y, scaledVertices[i2].z); + reindexedFaces[i2] = flLastIndex; + flLastIndex++; + } + + faces.push(reindexedFaces[i0], reindexedFaces[i1], reindexedFaces[i2]); + + processedFaces++; + } + + var shape = GameLib.D3.Shape(this.engine, GameLib.D3.Shape.SHAPE_TYPE_TRIMESH, graphicsMesh.scale, vertices, faces); + var body = GameLib.D3.RigidBody(this.engine, 0, 12); + + body.addShape(shape); + this.addRigidBody(body); + + // process the mesh children recursively + for(var c in graphicsMesh.children) { + this.generateTriangleMeshShape(graphics, graphicsMesh.children[c]); + } + + return body; +}; + +/** + * @param triangleMeshBody GameLib.D3.RigidBody + * @param rayscale Number + * @param maxTriangleDistance Number + * @param createCompoundShape Boolean + * @param graphics GameLib.D3.Graphics + * @param triangleMeshShapes GameLib.D3.Shape[] + * @param createDebugView Boolean + * @returns {GameLib.D3.RigidBody} + * @constructor + */ +GameLib.D3.World.prototype.fixupTriangleMeshShape = function( + triangleMeshBody, + triangleMeshShapes, + rayscale, + maxTriangleDistance, + createCompoundShape, + graphics, + createDebugView +) { + this.engine.isNotCannonThrow(); + + graphics.isNotThreeThrow(); + + if(rayscale == null || typeof rayscale == 'undefined' || rayscale == 0) { + rayscale = 10; + } + + if(maxTriangleDistance == null || typeof maxTriangleDistance == 'undefined') { + maxTriangleDistance = 13; + } + + var world = this.instance; + + var raycastResult = new this.engine.instance.RaycastResult(); + + var brokenFaceIndicators = []; + var wireframeMeshes = []; + + var totalFaces = 0; + var totalBrokenFaces = 0; + var totalFixedFaces = 0; + var fixedTriangleMeshObjects = []; + + for(var i in triangleMeshShapes) { + var trimesh = triangleMeshShapes[i]; + + var brokenFaces = []; + totalFaces += (trimesh.indices.length / 3); + + for(var face = 0; face < trimesh.indices.length / 3; face++) { + + var i0 = trimesh.indices[face * 3]; + var i1 = trimesh.indices[face * 3 + 1]; + var i2 = trimesh.indices[face * 3 + 2]; + + var triangleCenterPoint = new graphics.instance.Vector3() + .add(new graphics.instance.Vector3( + trimesh.vertices[i0 * 3], + trimesh.vertices[i0 * 3 + 1], + trimesh.vertices[i0 * 3 + 2]) + ) + .add(new graphics.instance.Vector3( + trimesh.vertices[i1 * 3], + trimesh.vertices[i1 * 3 + 1], + trimesh.vertices[i1 * 3 + 2]) + ) + .add(new graphics.instance.Vector3( + trimesh.vertices[i2 * 3], + trimesh.vertices[i2 * 3 + 1], + trimesh.vertices[i2 * 3 + 2]) + ) + .divideScalar(3); + + var triangleNormal = new this.engine.instance.Vec3(); + trimesh.getNormal(face , triangleNormal); + + var from = new this.engine.instance.Vec3( + triangleCenterPoint.x + triangleNormal.x, + triangleCenterPoint.y + triangleNormal.y, + triangleCenterPoint.z + triangleNormal.z + ); + + var to = new this.engine.instance.Vec3( + from.x - triangleNormal.x * rayscale, + from.y - triangleNormal.y * rayscale, + from.z - triangleNormal.z * rayscale + ); + + world.raycastClosest(from, to, {}, raycastResult); + + // visualize results + if(createDebugView){ + var graphicsGeometry = new graphics.instance.Geometry(); + var wireframeMesh = new graphics.instance.Mesh( + graphicsGeometry, + new graphics.instance.MeshBasicMaterial({ + color: 0xff0000, + wireframe: true, + opacity: 1 + }) + ); + + var arrow = new graphics.instance.ArrowHelper( + new graphics.instance.Vector3( + triangleNormal.x, + triangleNormal.y, + triangleNormal.z + ).normalize(), + + new graphics.instance.Vector3( + from.x, + from.y, + from.z + ), + + rayscale / 2, + raycastResult.hasHit ? new graphics.instance.Color(0, 1, 0) + : new graphics.instance.Color(1, 0, 0) + ); + + wireframeMesh.add( arrow ); + + wireframeMeshes.add(wireframeMesh); + brokenFaceIndicators.push(wireframeMesh); + } + + if(!raycastResult.hasHit) { + brokenFaces.push({ + faceIndex : face, + + vertices : [ + new this.engine.instance.Vec3( + trimesh.vertices[i0 * 3], + trimesh.vertices[i0 * 3 + 1], + trimesh.vertices[i0 * 3 + 2] + ), + + new this.engine.instance.Vec3( + trimesh.vertices[i1 * 3], + trimesh.vertices[i1 * 3 + 1], + trimesh.vertices[i1 * 3 + 2] + ), + + new this.engine.instance.Vec3( + trimesh.vertices[i2 * 3], + trimesh.vertices[i2 * 3 + 1], + trimesh.vertices[i2 * 3 + 2] + ) + ], + + center : triangleCenterPoint, + + parent : trimesh + }); + } + } + + // fix up broken faces + + var bFaceIndexed = {}; + for(var b = 0; b < brokenFaces.length; b++) { + var brokenFace = brokenFaces[b]; + + if(brokenFace.marked) { + continue; + } + + bFaceIndexed[b] = { + indices : [], + vertices : [] + }; + + var indicesAmount = bFaceIndexed[b].indices.length; + + // add the current broken face itself to the array + bFaceIndexed[b].indices.push( + indicesAmount, + indicesAmount + 1, + indicesAmount + 2 + ); + + bFaceIndexed[b].vertices.push( + brokenFace.vertices[0].x, + brokenFace.vertices[0].y, + brokenFace.vertices[0].z + ); + + bFaceIndexed[b].vertices.push( + brokenFace.vertices[1].x, + brokenFace.vertices[1].y, + brokenFace.vertices[1].z + ); + + bFaceIndexed[b].vertices.push( + brokenFace.vertices[2].x, + brokenFace.vertices[2].y, + brokenFace.vertices[2].z + ); + + for(var bb = 0; bb < brokenFaces.length; bb++) { + + if(bb == b) { + continue; + } + + var otherBrokenFace = brokenFaces[bb]; + + if(otherBrokenFace.marked) { + continue; + } + + if(brokenFace.center.distanceTo(otherBrokenFace.center) <= maxTriangleDistance) { + var indicesAmount = bFaceIndexed[b].indices.length; + + bFaceIndexed[b].indices.push( + indicesAmount, + indicesAmount + 1, + indicesAmount + 2 + ); + + bFaceIndexed[b].vertices.push( + otherBrokenFace.vertices[0].x, + otherBrokenFace.vertices[0].y, + otherBrokenFace.vertices[0].z + ); + + bFaceIndexed[b].vertices.push( + otherBrokenFace.vertices[1].x, + otherBrokenFace.vertices[1].y, + otherBrokenFace.vertices[1].z + ); + + bFaceIndexed[b].vertices.push( + otherBrokenFace.vertices[2].x, + otherBrokenFace.vertices[2].y, + otherBrokenFace.vertices[2].z + ); + + otherBrokenFace.marked = true; + } + } + } + + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Decide if we want to create new rigid bodies, or create a compound mesh + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + for(var e in bFaceIndexed) { + var element = bFaceIndexed[e]; + + var shape = new GameLib.D3.Shape(this.engine, GameLib.D3.Shape.SHAPE_TYPE_TRIMESH, null, element.vertices, element.indices); + + if(createCompoundShape) { + triangleMeshBody.addShape(shape); + } else { + + var body = new GameLib.D3.RigidBody(this.engine, 0, 12); + body.addShape(shape); + this.instance.addRigidBody(body); + } + + fixedTriangleMeshObjects.push(shape); + totalFixedFaces += element.indices.length / 3; + + console.log("created mesh shape", element.indices.length / 3); + } + + // TODO: remove duplicate indices + /*trimesh.updateNormals(); + trimesh.updateEdges(); + trimesh.updateTree(); + trimesh.updateAABB(); + trimesh.updateBoundingSphereRadius();*/ + + // map faceIndex to flat face index (faceIndex * 3) +0, 1, 2 -> triangle indices + console.log("i = " + i, brokenFaces); + totalBrokenFaces += brokenFaces.length; + } + + console.log("total faces", totalFaces); + console.log("total broken faces", totalBrokenFaces); + console.log("broken faces in percent", (totalBrokenFaces / totalFaces) * 100); + console.log("total fixed faces", totalFixedFaces); + console.log("fixed triangle mesh shapes", fixedTriangleMeshObjects.length); + + return { + brokenFaceIndicators : brokenFaceIndicators, + fixedTriangleMeshShapes : fixedTriangleMeshObjects, + wireframeMeshes : wireframeMeshes + }; +}; if (typeof module !== 'undefined') { module.exports = GameLib; } \ No newline at end of file diff --git a/src/game-lib-world.js b/src/game-lib-world.js index 09d7078..9ce352d 100644 --- a/src/game-lib-world.js +++ b/src/game-lib-world.js @@ -142,97 +142,248 @@ GameLib.D3.World.prototype.GetIndexedVertices = function( }; /** - * TODO: FIX - * @param triangleMeshShape - * @param normalLength - * @param scale - * @param opacity - * @param wireframeColor + * @param triangleMeshShape GameLib.D3.Shape + * @param normalLength Number + * @param scale GameLib.D3.Vector3 + * @param opacity Number + * @param wireframeColor HexCode + * @param graphics THREE * @returns {THREE.Mesh|this.meshes} * @constructor */ -GameLib.D3.World.GenerateWireframeViewMesh = function( +GameLib.D3.World.prototype.generateWireframeViewTriangleMesh = function( + graphics, triangleMeshShape, normalLength, scale, opacity, wireframeColor ) { - var geometryTHREE = new THREE.Geometry(); - var wireframeTHREEMesh = new THREE.Mesh - ( - geometryTHREE, - new THREE.MeshBasicMaterial({ - color: wireframeColor ? wireframeColor : 0xfefefe, + graphics.isNotThreeThrow(); + this.engine.isNotCannonThrow(); + + if(typeof normalLength == 'undefined') { + normalLength = 10; + } + + if(typeof scale == 'undefined') { + scale = new graphics.instance.Vector3(1, 1, 1); + } + + if(typeof opacity == 'undefined') { + opacity = 0.5; + } + + if(typeof wireframeColor == 'undefined') { + wireframeColor = 0xfefefe; + } + + var graphicsGeometry = new graphics.instance.Geometry(); + + var wireframeMesh = new graphics.instance.Mesh( + graphicsGeometry, + new graphics.instance.MeshBasicMaterial({ + color: wireframeColor, wireframe: true, - opacity: opacity ? opacity : 0.5 + opacity: opacity }) ); - 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 = triangleMeshShape.instance.vertices.length / 3; i < l; ++i) { + graphicsGeometry.vertices.push( + new graphics.instance.Vector3( + triangleMeshShape.instance.vertices[i * 3], + triangleMeshShape.instance.vertices[i * 3 + 1], + triangleMeshShape.instance.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]; + for(var i = 0, l = triangleMeshShape.instance.indices.length / 3; i < l; ++i) { + var i0 = triangleMeshShape.instance.indices[i * 3]; + var i1 = triangleMeshShape.instance.indices[i * 3 + 1]; + var i2 = triangleMeshShape.instance.indices[i * 3 + 2]; - geometryTHREE.faces.push(new THREE.Face3(i0, i1, i2)); + graphicsGeometry.faces.push( + new graphics.instance.Face3( + i0, + i1, + i2 + ) + ); - // Create debug view for normals + // Center point on the current triangle - // Center point on the mesh itself - var centroid = new THREE.Vector3() - .add(geometryTHREE.vertices[i0]) - .add(geometryTHREE.vertices[i1]) - .add(geometryTHREE.vertices[i2]) + var centroid = new graphics.instance.Vector3() + .add(graphicsGeometry.vertices[i0]) + .add(graphicsGeometry.vertices[i1]) + .add(graphicsGeometry.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. - } + // Get the normal from the mesh shape itself + var normal = new this.engine.instance.Vec3(); + triangleMeshShape.instance.getNormal(i , normal); - 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 ); + var arrow = new graphics.instance.ArrowHelper( + new graphics.instance.Vector3( + normal.x, + normal.y, + normal.z + ), + centroid, + normalLength, + new graphics.instance.Color( + normal.x, + normal.y, + normal.z + ) + ); + wireframeMesh.add( arrow ); } - wireframeTHREEMesh.scale.x = scale.x; - wireframeTHREEMesh.scale.y = scale.y; - wireframeTHREEMesh.scale.z = scale.z; + wireframeMesh.scale.x = scale.x; + wireframeMesh.scale.y = scale.y; + wireframeMesh.scale.z = scale.z; - return wireframeTHREEMesh; + return wireframeMesh; }; /** - * TODO: FIX - * @param threeMesh - * @param mass - * @param friction - * @param createCollisionSubMeshes - * @param facesPerSubsection - * @param subsectionsToMerge - * @returns {Array} + * @param convexPolyMeshShape GameLib.D3.Shape + * @param normalLength Number + * @param scale GameLib.D3.Vector3 + * @param opacity Number + * @param wireframeColor HexCode + * @param graphics THREE + * @returns {THREE.Mesh|this.meshes} * @constructor */ -GameLib.D3.World.GenerateTriangleCollisionMesh = function( - threeMesh, - mass, // default = 0 - friction, // default = 10 - createCollisionSubMeshes, // boolean. default = false - facesPerSubsection, // int. default = 0 - subsectionsToMerge // int. default = 0 +GameLib.D3.World.prototype.generateWireframeViewConvexPolyMesh = function( + graphics, + convexPolyMeshShape, + normalLength, + scale, + opacity, + wireframeColor ) { + graphics.isNotThreeThrow(); + this.engine.isNotCannonThrow(); + + if(typeof normalLength == 'undefined') { + normalLength = 10; + } + + if(typeof scale == 'undefined') { + scale = new graphics.instance.Vector3(1, 1, 1); + } + + if(typeof opacity == 'undefined') { + opacity = 0.5; + } + + if(typeof wireframeColor == 'undefined') { + wireframeColor = 0xfefefe; + } + + + var graphicsGeometry = new graphics.instance.Geometry(); + var wireframeMesh = new graphics.instance.Mesh( + graphicsGeometry, + new graphics.instance.MeshBasicMaterial({ + color: wireframeColor, + wireframe: true, + opacity: opacity + }) + ); + + for(var i = 0, l = convexPolyMeshShape.instance.vertices.length; i < l; i++) { + var vertex = convexPolyMeshShape.instance.vertices[i]; + graphicsGeometry.vertices.push(new graphics.instance.Vector3(vertex.x, vertex.y, vertex.z)); + } + + for(var i = 0, l = convexPolyMeshShape.instance.faces.length; i < l; i++) { + var face = convexPolyMeshShape.instance.faces[i]; + + var i0 = face[0]; + var i1 = face[1]; + var i2 = face[2]; + + graphicsGeometry.faces.push(new graphics.instance.Face3(i0, i1, i2)); + + // Center point on the current triangle + var centroid = new graphics.instance.Vector3() + .add(graphicsGeometry.vertices[i0]) + .add(graphicsGeometry.vertices[i1]) + .add(graphicsGeometry.vertices[i2]) + .divideScalar(3); + + var normalVec3 = convexPolyMeshShape.instance.faceNormals[i]; + var normal = new graphics.instance.Vector3( + normalVec3.x, + normalVec3.y, + normalVec3.z + ); + + var arrow = new graphics.instance.ArrowHelper( + normal, + centroid, + normalLength, + new graphics.instance.Color( + normal.x, + normal.y, + normal.z + ) + ); + + wireframeMesh.add( arrow ); + } + + wireframeMesh.scale.x = scale.x; + wireframeMesh.scale.y = scale.y; + wireframeMesh.scale.z = scale.z; + + return wireframeMesh; +}; + +/** + * @param graphics GameLib.D3.Graphics + * @param graphicsMesh THREE.Mesh + * @param mass Number + * @param friction Number + * @param createCollisionSubMeshes Boolean + * @param facesPerSubsection Number + * @param subsectionsToMerge Number + * @returns {Object} + * @constructor + */ +GameLib.D3.World.prototype.generateTriangleMeshShapeDivided = function( + graphics, + graphicsMesh, + mass, + friction, + createCollisionSubMeshes, + facesPerSubsection, + subsectionsToMerge +) { + graphics.isNotThreeThrow(); + this.engine.isNotCannonThrow(); + + if(mass == null || typeof mass == 'undefined') { + mass = 0; + } + + if(friction == null || typeof friction == 'undefined') { + friction = 10; + } + + if(createCollisionSubMeshes == null || typeof createCollisionSubMeshes == 'undefined') { + createCollisionSubMeshes = false; + } + var processedFaces = 0; var facesPerSubSection = facesPerSubsection || 0; var subMeshesToMerge = subsectionsToMerge || 0; - var totalAmtFaces = threeMesh.geometry.faces.length; + var totalAmtFaces = graphicsMesh.geometry.faces.length; var facesToProcess = createCollisionSubMeshes ? (subMeshesToMerge * facesPerSubSection) : totalAmtFaces; var pairs = []; // output @@ -245,27 +396,28 @@ GameLib.D3.World.GenerateTriangleCollisionMesh = function( var body = null; - if(this.engine.engineType == GameLib.D3.Physics.TYPE_CANNON) { + var meshShape = new this.engine.instance.Trimesh(vertices, indicies); - 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(); + meshShape.setScale(new this.engine.instance.Vec3( + graphicsMesh.scale.x, + graphicsMesh.scale.y, + graphicsMesh.scale.z + )); - body = new this.physics.CANNON.Body({ mass: mass ? mass : 0, friction: friction ? friction : 10 }); - body.addShape(meshShape); + meshShape.updateAABB(); + meshShape.updateNormals(); + meshShape.updateEdges(); + meshShape.updateBoundingSphereRadius(); + meshShape.updateTree(); - } else if (this.engine.engineType == GameLib.D3.Physics.Engine.TYPE_AMMO) { - - } else if (this.engine.engineType == GameLib.D3.Physics.Engine.TYPE_GOBLIN) { - - } + body = new this.engine.instance.Body({ + mass: mass, + friction: friction + }); + body.addShape(meshShape); pairs.push({ - threeObject : createCollisionSubMeshes ? null : threeMesh, + threeObject : createCollisionSubMeshes ? null : graphicsMesh, physicsObject : body }); @@ -278,14 +430,14 @@ GameLib.D3.World.GenerateTriangleCollisionMesh = function( } } - var face = threeMesh.geometry.faces[i]; + var face = graphicsMesh.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]; + var v0 = graphicsMesh.geometry.vertices[face.a]; + var v1 = graphicsMesh.geometry.vertices[face.b]; + var v2 = graphicsMesh.geometry.vertices[face.c]; vertices.push(v0.x, v0.y, v0.z); vertices.push(v1.x, v1.y, v1.z); @@ -294,3 +446,393 @@ GameLib.D3.World.GenerateTriangleCollisionMesh = function( processedFaces++; } }; + +/** + * @param graphics GameLib.D3.Graphics + * @param graphicsMesh THREE.Mesh + * @returns {GameLib.D3.RigidBody} + * @constructor + */ +GameLib.D3.World.prototype.generateTriangleMeshShape = function( + graphics, + graphicsMesh +) { + + // - - - - - - - - - - - - - - - - - - - - - - - - - + // Note: I did not test this yet with the API data. + // - - - - - - - - - - - - - - - - - - - - - - - - - + + var scaledVertices = []; + for(var i = 0, l = graphicsMesh.geometry.vertices.length; i < l; i++) { + + var vertex = graphicsMesh.geometry.vertices[i]; + + scaledVertices.push(new this.engine.instance.Vec3( + vertex.x * graphicsMesh.scale.x, + vertex.y * graphicsMesh.scale.y, + vertex.z * graphicsMesh.scale.z + )); + + } + + var triangleFaces = []; + for(var f = 0, fl = graphicsMesh.geometry.faces.length; f < fl; f++) { + var i0 = graphicsMesh.geometry.faces[f].a; + var i1 = graphicsMesh.geometry.faces[f].b; + var i2 = graphicsMesh.geometry.faces[f].c; + + triangleFaces.push([ + i0, + i1, + i2 + ]); + } + + // - - - - - - - - - - - - - - - - - - - + // Create collision mesh + // - - - - - - - - - - - - - - - - - - - + + var reindexedFaces = {}; + var vertices = []; + var faces = []; + + var processedFaces = 0; + var totalFacesToProcess = triangleFaces.length; + var flLastIndex = 0; + + for(var f = 0; f < totalFacesToProcess; f++) { + + var i0 = triangleFaces[f][0]; + var i1 = triangleFaces[f][1]; + var i2 = triangleFaces[f][2]; + + if(typeof reindexedFaces[i0] === 'undefined') { + vertices.push(scaledVertices[i0].x, scaledVertices[i0].y, scaledVertices[i0].z); + reindexedFaces[i0] = flLastIndex; + flLastIndex++; + } + + if(typeof reindexedFaces[i1] === 'undefined') { + vertices.push(scaledVertices[i1].x, scaledVertices[i1].y, scaledVertices[i1].z); + reindexedFaces[i1] = flLastIndex; + flLastIndex++; + } + + if(typeof reindexedFaces[i2] === 'undefined') { + vertices.push(scaledVertices[i2].x, scaledVertices[i2].y, scaledVertices[i2].z); + reindexedFaces[i2] = flLastIndex; + flLastIndex++; + } + + faces.push(reindexedFaces[i0], reindexedFaces[i1], reindexedFaces[i2]); + + processedFaces++; + } + + var shape = GameLib.D3.Shape(this.engine, GameLib.D3.Shape.SHAPE_TYPE_TRIMESH, graphicsMesh.scale, vertices, faces); + var body = GameLib.D3.RigidBody(this.engine, 0, 12); + + body.addShape(shape); + this.addRigidBody(body); + + // process the mesh children recursively + for(var c in graphicsMesh.children) { + this.generateTriangleMeshShape(graphics, graphicsMesh.children[c]); + } + + return body; +}; + +/** + * @param triangleMeshBody GameLib.D3.RigidBody + * @param rayscale Number + * @param maxTriangleDistance Number + * @param createCompoundShape Boolean + * @param graphics GameLib.D3.Graphics + * @param triangleMeshShapes GameLib.D3.Shape[] + * @param createDebugView Boolean + * @returns {GameLib.D3.RigidBody} + * @constructor + */ +GameLib.D3.World.prototype.fixupTriangleMeshShape = function( + triangleMeshBody, + triangleMeshShapes, + rayscale, + maxTriangleDistance, + createCompoundShape, + graphics, + createDebugView +) { + this.engine.isNotCannonThrow(); + + graphics.isNotThreeThrow(); + + if(rayscale == null || typeof rayscale == 'undefined' || rayscale == 0) { + rayscale = 10; + } + + if(maxTriangleDistance == null || typeof maxTriangleDistance == 'undefined') { + maxTriangleDistance = 13; + } + + var world = this.instance; + + var raycastResult = new this.engine.instance.RaycastResult(); + + var brokenFaceIndicators = []; + var wireframeMeshes = []; + + var totalFaces = 0; + var totalBrokenFaces = 0; + var totalFixedFaces = 0; + var fixedTriangleMeshObjects = []; + + for(var i in triangleMeshShapes) { + var trimesh = triangleMeshShapes[i]; + + var brokenFaces = []; + totalFaces += (trimesh.indices.length / 3); + + for(var face = 0; face < trimesh.indices.length / 3; face++) { + + var i0 = trimesh.indices[face * 3]; + var i1 = trimesh.indices[face * 3 + 1]; + var i2 = trimesh.indices[face * 3 + 2]; + + var triangleCenterPoint = new graphics.instance.Vector3() + .add(new graphics.instance.Vector3( + trimesh.vertices[i0 * 3], + trimesh.vertices[i0 * 3 + 1], + trimesh.vertices[i0 * 3 + 2]) + ) + .add(new graphics.instance.Vector3( + trimesh.vertices[i1 * 3], + trimesh.vertices[i1 * 3 + 1], + trimesh.vertices[i1 * 3 + 2]) + ) + .add(new graphics.instance.Vector3( + trimesh.vertices[i2 * 3], + trimesh.vertices[i2 * 3 + 1], + trimesh.vertices[i2 * 3 + 2]) + ) + .divideScalar(3); + + var triangleNormal = new this.engine.instance.Vec3(); + trimesh.getNormal(face , triangleNormal); + + var from = new this.engine.instance.Vec3( + triangleCenterPoint.x + triangleNormal.x, + triangleCenterPoint.y + triangleNormal.y, + triangleCenterPoint.z + triangleNormal.z + ); + + var to = new this.engine.instance.Vec3( + from.x - triangleNormal.x * rayscale, + from.y - triangleNormal.y * rayscale, + from.z - triangleNormal.z * rayscale + ); + + world.raycastClosest(from, to, {}, raycastResult); + + // visualize results + if(createDebugView){ + var graphicsGeometry = new graphics.instance.Geometry(); + var wireframeMesh = new graphics.instance.Mesh( + graphicsGeometry, + new graphics.instance.MeshBasicMaterial({ + color: 0xff0000, + wireframe: true, + opacity: 1 + }) + ); + + var arrow = new graphics.instance.ArrowHelper( + new graphics.instance.Vector3( + triangleNormal.x, + triangleNormal.y, + triangleNormal.z + ).normalize(), + + new graphics.instance.Vector3( + from.x, + from.y, + from.z + ), + + rayscale / 2, + raycastResult.hasHit ? new graphics.instance.Color(0, 1, 0) + : new graphics.instance.Color(1, 0, 0) + ); + + wireframeMesh.add( arrow ); + + wireframeMeshes.add(wireframeMesh); + brokenFaceIndicators.push(wireframeMesh); + } + + if(!raycastResult.hasHit) { + brokenFaces.push({ + faceIndex : face, + + vertices : [ + new this.engine.instance.Vec3( + trimesh.vertices[i0 * 3], + trimesh.vertices[i0 * 3 + 1], + trimesh.vertices[i0 * 3 + 2] + ), + + new this.engine.instance.Vec3( + trimesh.vertices[i1 * 3], + trimesh.vertices[i1 * 3 + 1], + trimesh.vertices[i1 * 3 + 2] + ), + + new this.engine.instance.Vec3( + trimesh.vertices[i2 * 3], + trimesh.vertices[i2 * 3 + 1], + trimesh.vertices[i2 * 3 + 2] + ) + ], + + center : triangleCenterPoint, + + parent : trimesh + }); + } + } + + // fix up broken faces + + var bFaceIndexed = {}; + for(var b = 0; b < brokenFaces.length; b++) { + var brokenFace = brokenFaces[b]; + + if(brokenFace.marked) { + continue; + } + + bFaceIndexed[b] = { + indices : [], + vertices : [] + }; + + var indicesAmount = bFaceIndexed[b].indices.length; + + // add the current broken face itself to the array + bFaceIndexed[b].indices.push( + indicesAmount, + indicesAmount + 1, + indicesAmount + 2 + ); + + bFaceIndexed[b].vertices.push( + brokenFace.vertices[0].x, + brokenFace.vertices[0].y, + brokenFace.vertices[0].z + ); + + bFaceIndexed[b].vertices.push( + brokenFace.vertices[1].x, + brokenFace.vertices[1].y, + brokenFace.vertices[1].z + ); + + bFaceIndexed[b].vertices.push( + brokenFace.vertices[2].x, + brokenFace.vertices[2].y, + brokenFace.vertices[2].z + ); + + for(var bb = 0; bb < brokenFaces.length; bb++) { + + if(bb == b) { + continue; + } + + var otherBrokenFace = brokenFaces[bb]; + + if(otherBrokenFace.marked) { + continue; + } + + if(brokenFace.center.distanceTo(otherBrokenFace.center) <= maxTriangleDistance) { + var indicesAmount = bFaceIndexed[b].indices.length; + + bFaceIndexed[b].indices.push( + indicesAmount, + indicesAmount + 1, + indicesAmount + 2 + ); + + bFaceIndexed[b].vertices.push( + otherBrokenFace.vertices[0].x, + otherBrokenFace.vertices[0].y, + otherBrokenFace.vertices[0].z + ); + + bFaceIndexed[b].vertices.push( + otherBrokenFace.vertices[1].x, + otherBrokenFace.vertices[1].y, + otherBrokenFace.vertices[1].z + ); + + bFaceIndexed[b].vertices.push( + otherBrokenFace.vertices[2].x, + otherBrokenFace.vertices[2].y, + otherBrokenFace.vertices[2].z + ); + + otherBrokenFace.marked = true; + } + } + } + + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Decide if we want to create new rigid bodies, or create a compound mesh + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + for(var e in bFaceIndexed) { + var element = bFaceIndexed[e]; + + var shape = new GameLib.D3.Shape(this.engine, GameLib.D3.Shape.SHAPE_TYPE_TRIMESH, null, element.vertices, element.indices); + + if(createCompoundShape) { + triangleMeshBody.addShape(shape); + } else { + + var body = new GameLib.D3.RigidBody(this.engine, 0, 12); + body.addShape(shape); + this.instance.addRigidBody(body); + } + + fixedTriangleMeshObjects.push(shape); + totalFixedFaces += element.indices.length / 3; + + console.log("created mesh shape", element.indices.length / 3); + } + + // TODO: remove duplicate indices + /*trimesh.updateNormals(); + trimesh.updateEdges(); + trimesh.updateTree(); + trimesh.updateAABB(); + trimesh.updateBoundingSphereRadius();*/ + + // map faceIndex to flat face index (faceIndex * 3) +0, 1, 2 -> triangle indices + console.log("i = " + i, brokenFaces); + totalBrokenFaces += brokenFaces.length; + } + + console.log("total faces", totalFaces); + console.log("total broken faces", totalBrokenFaces); + console.log("broken faces in percent", (totalBrokenFaces / totalFaces) * 100); + console.log("total fixed faces", totalFixedFaces); + console.log("fixed triangle mesh shapes", fixedTriangleMeshObjects.length); + + return { + brokenFaceIndicators : brokenFaceIndicators, + fixedTriangleMeshShapes : fixedTriangleMeshObjects, + wireframeMeshes : wireframeMeshes + }; +}; \ No newline at end of file