diff --git a/src/game-lib-a-1-event.js b/src/game-lib-a-1-event.js index 8227cef..3ec714f 100644 --- a/src/game-lib-a-1-event.js +++ b/src/game-lib-a-1-event.js @@ -62,6 +62,8 @@ GameLib.Event.EVENT_LIST = 0x2c; GameLib.Event.COMPILE_SUCCESS = 0x2d; GameLib.Event.COMPILE_FAILED = 0x2e; GameLib.Event.IMAGE_CHANGED = 0x2f; +GameLib.Event.PARENT_ENTITY_CHANGED = 0x30; +GameLib.Event.MATERIAL_TEXTURES_UPDATED = 0x31; /** * Returns string name of event ID @@ -119,6 +121,8 @@ GameLib.Event.GetEventName = function(number) { case 0x2d : return 'compile_success'; case 0x2e : return 'compile_failed'; case 0x2f : return 'image_changed'; + case 0x30 : return 'parent_entity_changed'; + case 0x31 : return 'material_textures_updated'; break; } diff --git a/src/game-lib-a-2-utils.js b/src/game-lib-a-2-utils.js index 9e22539..f77c3c4 100644 --- a/src/game-lib-a-2-utils.js +++ b/src/game-lib-a-2-utils.js @@ -334,16 +334,16 @@ GameLib.Utils.ResetWindingOrder = function(faces, vertices) { * 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 faces GameLib.D3.Face[] * @param orientationEdge GameLib.API.Vector2 * @returns {Array} */ GameLib.Utils.FixWindingOrder = function(faces, orientationEdge) { /** - * Checks if a TriangleFace belonging to a TriangleEdge has already been processed + * Checks if a Face belonging to a TriangleEdge has already been processed * @param processed TriangleEdge[] - * @param triangle TriangleFace + * @param triangle Face * @returns {boolean} */ function inProcessed(processed, triangle) { @@ -360,7 +360,7 @@ GameLib.Utils.FixWindingOrder = function(faces, orientationEdge) { /** * Returns a neighbouring triangle on a specific edge - preserving the edge orientation * @param edge GameLib.API.Vector2 - * @param faces GameLib.D3.TriangleFace[] + * @param faces GameLib.D3.Face[] * @param currentTriangle * @returns {*} */ @@ -376,14 +376,14 @@ GameLib.Utils.FixWindingOrder = function(faces, orientationEdge) { (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, + var triangle = new GameLib.D3.API.Face( + null, + null, + faces[i].v0index, + faces[i].v1index, + faces[i].v2index, faces[i].materialIndex, - faces[i].v0uv, - faces[i].v1uv, - faces[i].v2uv + faces[i].uvs ); if (triangle.equals(currentTriangle)) { @@ -402,14 +402,14 @@ GameLib.Utils.FixWindingOrder = function(faces, orientationEdge) { var toProcess = [ new GameLib.D3.TriangleEdge( - new GameLib.D3.TriangleFace( - faces[0].v0, - faces[0].v1, - faces[0].v2, + new GameLib.D3.API.Face( + null, + null, + faces[0].v0index, + faces[0].v1index, + faces[0].v2index, faces[0].materialIndex, - faces[0].v0uv, - faces[0].v1uv, - faces[0].v2uv + faces[0].uvs ), orientationEdge ) @@ -426,36 +426,40 @@ GameLib.Utils.FixWindingOrder = function(faces, orientationEdge) { * 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) + (triangleEdge.triangle.v0index === triangleEdge.edge.x && + triangleEdge.triangle.v1index === triangleEdge.edge.y) || + (triangleEdge.triangle.v1index === triangleEdge.edge.x && + triangleEdge.triangle.v2index === triangleEdge.edge.y) || + (triangleEdge.triangle.v2index === triangleEdge.edge.x && + triangleEdge.triangle.v0index === triangleEdge.edge.y) ) { - var backupV = triangleEdge.triangle.v1; - triangleEdge.triangle.v1 = triangleEdge.triangle.v2; - triangleEdge.triangle.v2 = backupV; + var backupV = triangleEdge.triangle.v1index; + triangleEdge.triangle.v1index = triangleEdge.triangle.v2index; + triangleEdge.triangle.v2index = backupV; - var backupUV = triangleEdge.triangle.v1uv; - triangleEdge.triangle.v1uv = triangleEdge.triangle.v2uv; - triangleEdge.triangle.v2uv = backupUV; + // var backupUV = triangleEdge.triangle.v1uv; + // triangleEdge.triangle.v1uv = triangleEdge.triangle.v2uv; + // triangleEdge.triangle.v2uv = backupUV; + // + var backupUV = triangleEdge.triangle.uvs[0][1]; + triangleEdge.triangle.uvs[0][1] = triangleEdge.triangle.uvs[0][2]; + triangleEdge.triangle.uvs[0][2] = backupUV; } processed.push(triangleEdge); var edges = [ new GameLib.API.Vector2( - triangleEdge.triangle.v0, - triangleEdge.triangle.v1 + triangleEdge.triangle.v0index, + triangleEdge.triangle.v1index ), new GameLib.API.Vector2( - triangleEdge.triangle.v1, - triangleEdge.triangle.v2 + triangleEdge.triangle.v1index, + triangleEdge.triangle.v2index ), new GameLib.API.Vector2( - triangleEdge.triangle.v2, - triangleEdge.triangle.v0 + triangleEdge.triangle.v2index, + triangleEdge.triangle.v0index ) ]; diff --git a/src/game-lib-a-component-a.js b/src/game-lib-a-component-a.js index 4128683..826fc17 100644 --- a/src/game-lib-a-component-a.js +++ b/src/game-lib-a-component-a.js @@ -145,6 +145,7 @@ GameLib.Component.COMPONENT_SHAPE_PLANE = 0x2f; GameLib.Component.COMPONENT_CONTROLS = 0x30; GameLib.Component.COMPONENT_CONTROLS_EDITOR = 0x31; GameLib.Component.COMPONENT_CONTROLS_FLY = 0x32; +// GameLib.Component.COMPONENT_FACE = 0x33; /** * Returns string name for component number @@ -204,6 +205,7 @@ GameLib.Component.GetComponentName = function(number) { case 0x30 : return 'GameLib.D3.Controls'; case 0x31 : return 'GameLib.D3.Controls.Editor'; case 0x32 : return 'GameLib.D3.Controls.Fly'; + // case 0x33 : return 'GameLib.D3.Face'; break; } diff --git a/src/game-lib-d3-api-face.js b/src/game-lib-d3-api-face.js new file mode 100644 index 0000000..3e630ca --- /dev/null +++ b/src/game-lib-d3-api-face.js @@ -0,0 +1,175 @@ +/** + * Face + * @param id + * @param name + * @param v0index + * @param v1index + * @param v2index + * @param materialIndex + * @param uvs [[v0uv (GameLib.Vector2), v1uv(GameLib.Vector2), v2uv(GameLib.Vector2)]] + * @param color + * @param vertexColors + * @param vertexNormals + * @param normal + * @param parentEntity GameLib.Entity + * @constructor + */ +GameLib.D3.API.Face = function( + id, + name, + v0index, + v1index, + v2index, + materialIndex, + uvs, + color, + vertexColors, + vertexNormals, + normal +) { + + if (GameLib.Utils.UndefinedOrNull(id)) { + id = GameLib.Utils.RandomId(); + } + this.id = id; + + if (GameLib.Utils.UndefinedOrNull(name)) { + name = 'Face ' + id; + } + this.name = name; + + if (GameLib.Utils.UndefinedOrNull(v0index)) { + v0index = -1; + } + this.v0index = v0index; + + if (GameLib.Utils.UndefinedOrNull(v1index)) { + v1index = -1; + } + this.v1index = v1index; + + if (GameLib.Utils.UndefinedOrNull(v2index)) { + v2index = -1; + } + this.v2index = v2index; + + if (GameLib.Utils.UndefinedOrNull(materialIndex)) { + materialIndex = -1; + } + this.materialIndex = materialIndex; + + if (GameLib.Utils.UndefinedOrNull(uvs)) { + uvs = [[]]; + } + this.uvs = uvs; + + if (GameLib.Utils.UndefinedOrNull(color)) { + color = null; + } + this.color = color; + + if (GameLib.Utils.UndefinedOrNull(vertexColors)) { + vertexColors = []; + } + this.vertexColors = vertexColors; + + if (GameLib.Utils.UndefinedOrNull(vertexNormals)) { + vertexNormals = []; + } + this.vertexNormals = vertexNormals; + + if (GameLib.Utils.UndefinedOrNull(normal)) { + normal = null; + } + this.normal = normal; +}; + +// GameLib.D3.API.Face.prototype = Object.create(GameLib.Component.prototype); +// GameLib.D3.API.Face.prototype.constructor = GameLib.D3.API.Face; + +/** + * Returns an API Face from a data object + * @constructor + * @param objectFace + */ +GameLib.D3.API.Face.FromObject = function(objectFace) { + return new GameLib.D3.API.Face( + objectFace.id, + objectFace.name, + objectFace.v0index, + objectFace.v1index, + objectFace.v2index, + objectFace.materialIndex, + objectFace.uvs, + objectFace.color, + objectFace.vertexColors, + objectFace.vertexNormals, + objectFace.normal + ); +}; + +/** + * Clone a Face + * @returns {GameLib.D3.API.Face} + */ +GameLib.D3.API.Face.prototype.clone = function(){ + return new GameLib.D3.API.Face( + this.id, + this.name, + this.v0index, + this.v1index, + this.v2index, + this.materialIndex, + this.uvs, + this.color, + this.vertexColors, + this.vertexNormals, + this.normal + ); + +}; + +/** + * Returns true if two triangles are equal (their vertex indices match in some order) + * @param triangle + * @returns {boolean} + */ +GameLib.D3.API.Face.prototype.equals = function(triangle) { + return ( + ( + (this.v0index === triangle.v0index) && + (this.v1index === triangle.v1index) && + (this.v2index === triangle.v2index) + ) + || + ( + (this.v0index === triangle.v0index) && + (this.v1index === triangle.v2index) && + (this.v2index === triangle.v1index) + ) + || + ( + (this.v0index === triangle.v1index) && + (this.v1index === triangle.v0index) && + (this.v2index === triangle.v2index) + ) + || + ( + (this.v0index === triangle.v1index) && + (this.v1index === triangle.v2index) && + (this.v2index === triangle.v0index) + ) + || + ( + (this.v0index === triangle.v2index) && + (this.v1index === triangle.v0index) && + (this.v2index === triangle.v1index) + ) + || + ( + (this.v0index === triangle.v2index) && + (this.v1index === triangle.v1index) && + (this.v2index === triangle.v0index) + ) + ); +}; diff --git a/src/game-lib-d3-api-mesh.js b/src/game-lib-d3-api-mesh.js index 5cc4aa0..dc9432e 100644 --- a/src/game-lib-d3-api-mesh.js +++ b/src/game-lib-d3-api-mesh.js @@ -4,8 +4,7 @@ * @param meshType * @param name * @param vertices GameLib.D3.Vertex[] - * @param faces GameLib.D3.TriangleFace[] - * @param faceVertexUvs + * @param faces GameLib.D3.Face[] * @param materials GameLib.D3.API.Material[] * @param parentMesh * @param parentScene @@ -22,6 +21,7 @@ * @param modelMatrix GameLib.API.Matrix4 * @param parentEntity * @param renderOrder + * @param isBufferMesh * @constructor */ GameLib.D3.API.Mesh = function( @@ -30,7 +30,6 @@ GameLib.D3.API.Mesh = function( name, vertices, faces, - faceVertexUvs, materials, parentMesh, parentScene, @@ -46,7 +45,8 @@ GameLib.D3.API.Mesh = function( up, modelMatrix, parentEntity, - renderOrder + renderOrder, + isBufferMesh ) { if (GameLib.Utils.UndefinedOrNull(id)) { id = GameLib.Utils.RandomId(); @@ -59,7 +59,7 @@ GameLib.D3.API.Mesh = function( this.meshType = meshType; if (GameLib.Utils.UndefinedOrNull(name)) { - name = 'Mesh (' + meshType + ')'; + name = 'Mesh ' + id; } this.name = name; @@ -88,11 +88,6 @@ GameLib.D3.API.Mesh = function( } this.skeleton = skeleton; - if (GameLib.Utils.UndefinedOrNull(faceVertexUvs)) { - faceVertexUvs = []; - } - this.faceVertexUvs = faceVertexUvs; - if (GameLib.Utils.UndefinedOrNull(skinIndices)) { skinIndices = []; } @@ -153,10 +148,15 @@ GameLib.D3.API.Mesh = function( } this.renderOrder = renderOrder; - if (GameLib.Utils.UndefinedOrNull(parentEntity)) { - parentEntity = null; + if (GameLib.Utils.UndefinedOrNull(parentEntity)) { + parentEntity = null; + } + this.parentEntity = parentEntity; + + if (GameLib.Utils.UndefinedOrNull(isBufferMesh)) { + isBufferMesh = false; } - this.parentEntity = parentEntity; + this.isBufferMesh = isBufferMesh; }; GameLib.D3.API.Mesh.prototype = Object.create(GameLib.Component.prototype); @@ -169,8 +169,16 @@ GameLib.D3.API.Mesh.prototype.constructor = GameLib.D3.API.Mesh; */ GameLib.D3.API.Mesh.FromObject = function (objectMesh){ + var apiFaces = []; + if (objectMesh.faces) { + apiFaces = objectMesh.faces.map( + function(face) { + return GameLib.D3.API.Face.FromObject(face); + } + ); + } + var apiSkeleton = null; - if (objectMesh.skeleton) { apiSkeleton = GameLib.D3.API.Skeleton.FromObject(objectMesh.skeleton); } @@ -245,8 +253,7 @@ GameLib.D3.API.Mesh.FromObject = function (objectMesh){ objectMesh.meshType, objectMesh.name, apiVertices, - objectMesh.faces, - objectMesh.faceVertexUvs, + apiFaces, apiMaterials, objectMesh.parentMesh, objectMesh.parentScene, @@ -262,6 +269,7 @@ GameLib.D3.API.Mesh.FromObject = function (objectMesh){ apiUp, apiModelMatrix, objectMesh.parentEntity, - objectMesh.renderOrder + objectMesh.renderOrder, + objectMesh.isBufferMesh ); }; diff --git a/src/game-lib-d3-controls-editor.js b/src/game-lib-d3-controls-editor.js index feccc08..e339737 100644 --- a/src/game-lib-d3-controls-editor.js +++ b/src/game-lib-d3-controls-editor.js @@ -117,27 +117,27 @@ GameLib.D3.Controls.Editor.prototype.updateInstance = function() { */ GameLib.D3.Controls.Editor.prototype.toApiObject = function() { - var apiMesh = GameLib.D3.Mesh.prototype.toApiObject.call(this); + var apiControls = GameLib.D3.Controls.prototype.toApiObject.call(this); - apiMesh.raycaster = GameLib.Utils.IdOrNull(this.raycaster); - apiMesh.renderer = GameLib.Utils.IdOrNull(this.renderer); + apiControls.raycaster = GameLib.Utils.IdOrNull(this.raycaster); + apiControls.renderer = GameLib.Utils.IdOrNull(this.renderer); - return apiMesh; + return apiControls; }; /** * Construct an Editor Controls object from data * @param graphics - * @param objectMesh + * @param objectControls * @returns {GameLib.D3.Controls.Editor} * @constructor */ -GameLib.D3.Controls.Editor.FromObject = function(graphics, objectMesh) { +GameLib.D3.Controls.Editor.FromObject = function(graphics, objectControls) { - var apiMesh = GameLib.D3.API.Controls.FromObject(objectMesh); + var apiMesh = GameLib.D3.API.Controls.FromObject(objectControls); - apiMesh.renderer = objectMesh.renderer; - apiMesh.raycaster = objectMesh.raycaster; + apiMesh.renderer = objectControls.renderer; + apiMesh.raycaster = objectControls.raycaster; return new GameLib.D3.Controls.Editor( graphics, diff --git a/src/game-lib-d3-face.js b/src/game-lib-d3-face.js new file mode 100644 index 0000000..8ecc590 --- /dev/null +++ b/src/game-lib-d3-face.js @@ -0,0 +1,120 @@ +/** + * Face + * @constructor + * @param graphics + * @param apiFace + */ +GameLib.D3.Face = function Face( + graphics, + apiFace +) { + this.graphics = graphics; + this.graphics.isNotThreeThrow(); + + if (GameLib.Utils.UndefinedOrNull(apiFace)) { + apiFace = {}; + } + + if (apiFace instanceof GameLib.D3.Face) { + return apiFace; + } + + GameLib.D3.API.Face.call( + this, + apiFace.id, + apiFace.name, + apiFace.v0index, + apiFace.v1index, + apiFace.v2index, + apiFace.materialIndex, + apiFace.uvs, + apiFace.color, + apiFace.vertexColors, + apiFace.vertexNormals, + apiFace.normal + ); + + this.color = new GameLib.Color( + this.graphics, + this.color, + this + ); + + this.vertexColors = this.vertexColors.map(function(vertexColor){ + if (vertexColor instanceof GameLib.Color) { + return vertexColor; + } + + if (vertexColor instanceof GameLib.API.Color) { + return new GameLib.Color( + this.graphics, + vertexColor, + this + ) + } + + console.warn('unknown vertex color type', vertexColor); + }.bind(this)); + + this.vertexNormals = this.vertexNormals.map(function(vertexNormal){ + if (vertexNormal instanceof GameLib.Vector3) { + return vertexNormal; + } + + if (vertexNormal instanceof GameLib.API.Vector3) { + return new GameLib.Vector3( + this.graphics, + vertexNormal, + this + ) + } + + console.warn('unknown vertex normal type', vertexNormal); + }.bind(this)); + + this.normal = new GameLib.Vector3( + this.graphics, + this.normal, + this + ); + +}; + +GameLib.D3.Face.prototype = Object.create(GameLib.D3.API.Face.prototype); +GameLib.D3.Face.prototype.constructor = GameLib.D3.Face; + +GameLib.D3.Face.prototype.createInstance = function() { + +}; + +GameLib.D3.Face.prototype.updateInstance = function() { + +}; + +GameLib.D3.Face.prototype.toApiObject = function() { + return new GameLib.D3.API.Face( + this.id, + this.name, + this.v0index, + this.v1index, + this.v2index, + this.materialIndex, + this.uvs, + this.color.toApiObject(), + this.vertexColors.map(function(vertexColor){ + return vertexColor.toApiObject(); + }), + this.vertexNormals.map(function(vertexNormal){ + return vertexNormal.toApiObject(); + }), + this.normal.toApiObject() + ); +}; + +GameLib.D3.Face.FromObject = function(graphics, objectFace) { + return new GameLib.D3.Face( + graphics, + GameLib.D3.API.Face.FromObject(objectFace) + ); +}; + diff --git a/src/game-lib-d3-material.js b/src/game-lib-d3-material.js index 320aeef..0ee65ae 100644 --- a/src/game-lib-d3-material.js +++ b/src/game-lib-d3-material.js @@ -486,71 +486,90 @@ GameLib.D3.Material.prototype.createMeshBasicMaterialInstance = function() { }); }; +GameLib.D3.Material.prototype.checkTexture = function(runtimeMap, instanceMap) { + + var textureChanged = false; + + if (this[runtimeMap] && this[runtimeMap].instance) { + if (this.instance[instanceMap] !== this[runtimeMap].instance) { + this.instance[instanceMap] = this[runtimeMap].instance; + textureChanged = true; + } + } else { + if (this.instance[instanceMap] !== null) { + this.instance[instanceMap] = null; + textureChanged = true; + } + } + + return textureChanged; +}; + /** * updates textures */ GameLib.D3.Material.prototype.updateTextures = function() { - if (this.alphaMap && this.alphaMap.instance) { - this.instance.alphaMap = this.alphaMap.instance; - } else { - this.instance.alphaMap = null; - } - if (this.aoMap && this.aoMap.instance) { - this.instance.aoMap = this.aoMap.instance; - } else { - this.instance.aoMap = null; - } - if (this.bumpMap && this.bumpMap.instance) { - this.instance.bumpMap = this.bumpMap.instance; - } else { - this.instance.bumpMap = null; - } - if (this.diffuseMap && this.diffuseMap.instance) { - this.instance.map = this.diffuseMap.instance; - } else { - this.instance.map = null; - } - if (this.displacementMap && this.displacementMap.instance) { - this.instance.displacementMap = this.displacementMap.instance; - } else { - this.instance.displacementMap = null; - } - if (this.emissiveMap && this.emissiveMap.instance) { - this.instance.emissiveMap = this.emissiveMap.instance; - } else { - this.instance.emissiveMap = null; - } - if (this.environmentMap && this.environmentMap.instance) { - this.instance.envMap = this.environmentMap.instance; - } else { - this.instance.envMap = null; - } - if (this.lightMap && this.lightMap.instance) { - this.instance.lightMap = this.lightMap.instance; - } else { - this.instance.lightMap = null; - } - if (this.metalnessMap && this.metalnessMap.instance) { - this.instance.metalnessMap = this.metalnessMap.instance; - } else { - this.instance.metalnessMap = null; - } - if (this.normalMap && this.normalMap.instance) { - this.instance.normalMap = this.normalMap.instance; - } else { - this.instance.normalMap = null; - } - if (this.roughnessMap && this.roughnessMap.instance) { - this.instance.roughnessMap = this.roughnessMap.instance; - } else { - this.instance.roughnessMap = null; - } - if (this.specularMap && this.specularMap.instance) { - this.instance.specularMap = this.specularMap.instance; - } else { - this.instance.specularMap = null; - } + var textureChanged = false; + + if (this.checkTexture('alphaMap', 'alphaMap')) { + textureChanged = true; + } + + if (this.checkTexture('aoMap', 'aoMap')) { + textureChanged = true; + } + + if (this.checkTexture('bumpMap', 'bumpMap')) { + textureChanged = true; + } + + if (this.checkTexture('diffuseMap', 'map')) { + textureChanged = true; + } + + if (this.checkTexture('displacementMap', 'displacementMap')) { + textureChanged = true; + } + + if (this.checkTexture('emissiveMap', 'emissiveMap')) { + textureChanged = true; + } + + if (this.checkTexture('environmentMap', 'envMap')) { + textureChanged = true; + } + + if (this.checkTexture('lightMap', 'lightMap')) { + textureChanged = true; + } + + if (this.checkTexture('metalnessMap', 'metalnessMap')) { + textureChanged = true; + } + + if (this.checkTexture('normalMap', 'normalMap')) { + textureChanged = true; + } + + if (this.checkTexture('roughnessMap', 'roughnessMap')) { + textureChanged = true; + } + + if (this.checkTexture('specularMap', 'specularMap')) { + textureChanged = true; + } + + if (textureChanged) { + this.publish( + GameLib.Event.MATERIAL_TEXTURES_UPDATED, + { + material : this + } + ); + } + + return textureChanged; }; @@ -729,7 +748,7 @@ GameLib.D3.Material.prototype.createInstance = function() { this.instance = instance; - this.updateTextures(); + this.updateTextures(); return instance; }; diff --git a/src/game-lib-d3-mesh-0.js b/src/game-lib-d3-mesh-0.js index b659154..d4eed50 100644 --- a/src/game-lib-d3-mesh-0.js +++ b/src/game-lib-d3-mesh-0.js @@ -26,7 +26,6 @@ GameLib.D3.Mesh = function ( apiMesh.name, apiMesh.vertices, apiMesh.faces, - apiMesh.faceVertexUvs, apiMesh.materials, apiMesh.parentMesh, apiMesh.parentScene, @@ -42,10 +41,26 @@ GameLib.D3.Mesh = function ( apiMesh.up, apiMesh.modelMatrix, apiMesh.parentEntity, - apiMesh.renderOrder + apiMesh.renderOrder, + apiMesh.isBufferMesh ); - this.materials = this.materials.map( + + this.faces = this.faces.map( + function(face) { + if (face instanceof GameLib.D3.API.Face) { + return new GameLib.D3.Face( + this.graphics, + face + ) + } else { + return face; + } + }.bind(this) + ); + + + this.materials = this.materials.map( function(material) { if (material instanceof GameLib.D3.API.Material) { return new GameLib.D3.Material( @@ -164,6 +179,7 @@ GameLib.D3.Mesh.MESH_TYPE_SPHERE = 0x3; GameLib.D3.Mesh.MESH_TYPE_PLANE = 0x4; GameLib.D3.Mesh.prototype.createInstanceGeometry = function() { + /** * Setup face indexes - first we sort according to the material index, because later we will create * groups for each vertice group @@ -189,15 +205,15 @@ GameLib.D3.Mesh.prototype.createInstanceGeometry = function() { this.faces.reduce( function(result, face){ - result.push(this.vertices[face.v0].position.x); - result.push(this.vertices[face.v0].position.y); - result.push(this.vertices[face.v0].position.z); - result.push(this.vertices[face.v1].position.x); - result.push(this.vertices[face.v1].position.y); - result.push(this.vertices[face.v1].position.z); - result.push(this.vertices[face.v2].position.x); - result.push(this.vertices[face.v2].position.y); - result.push(this.vertices[face.v2].position.z); + result.push(this.vertices[face.v0index].position.x); + result.push(this.vertices[face.v0index].position.y); + result.push(this.vertices[face.v0index].position.z); + result.push(this.vertices[face.v1index].position.x); + result.push(this.vertices[face.v1index].position.y); + result.push(this.vertices[face.v1index].position.z); + result.push(this.vertices[face.v2index].position.x); + result.push(this.vertices[face.v2index].position.y); + result.push(this.vertices[face.v2index].position.z); return result; }.bind(this), [] @@ -205,107 +221,187 @@ GameLib.D3.Mesh.prototype.createInstanceGeometry = function() { ); - var geometry = new THREE.BufferGeometry(); + var geometry = null; - geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3)); - /** - * Setyp mesh vertices colors - */ - var colors = Float32Array.from( - this.faces.reduce( - function(result, face){ - result.push(1,1,1,1,1,1,1,1,1); - // result.push(face.color.r); - // result.push(face.color.g); - // result.push(face.color.b); - // result.push(face.color.r); - // result.push(face.color.g); - // result.push(face.color.b); - // result.push(face.color.r); - // result.push(face.color.g); - // result.push(face.color.b); - return result; - }.bind(this), - [] - ) - ); - geometry.addAttribute('color', new THREE.BufferAttribute(colors, 3, true)); + if (this.isBufferMesh) { + + geometry = new THREE.BufferGeometry(); + + geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3)); + + /** + * Setyp mesh vertices colors + */ + var colors = Float32Array.from( + this.faces.reduce( + function(result, face){ + result.push(1,1,1,1,1,1,1,1,1); + // result.push(face.color.r); + // result.push(face.color.g); + // result.push(face.color.b); + // result.push(face.color.r); + // result.push(face.color.g); + // result.push(face.color.b); + // result.push(face.color.r); + // result.push(face.color.g); + // result.push(face.color.b); + return result; + }.bind(this), + [] + ) + ); + geometry.addAttribute('color', new THREE.BufferAttribute(colors, 3, true)); + + /** + * Setup face UVs + */ + var uvs = Float32Array.from( + this.faces.reduce( + function(result, face) { + + face.uvs[0].map( + function(uv) { + result.push(uv.x); + result.push(uv.y); + } + ); + + return result; + }, + [] + ) + ); + geometry.addAttribute('uv', new THREE.BufferAttribute(uvs, 2)); + + /** + * Setup material groups - this means creating a new group for each material index change + * We know faces are sorted according to material index + */ + var groupIndexCounts = this.faces.reduce( + function(result, face) { + + var currentGroup = result.pop(); + + if (currentGroup.index !== face.materialIndex) { + /** + * We have a new group + */ + result.push(currentGroup); + result.push({ + index: face.materialIndex, + count: 3 + }) + } else { + currentGroup.count += 3; + result.push(currentGroup); + } - /** - * Setup face UVs - */ - var uvs = Float32Array.from( - this.faceVertexUvs[0].reduce( - function(result, uv) { - result.push(uv[0].x); - result.push(uv[0].y); - result.push(uv[1].x); - result.push(uv[1].y); - result.push(uv[2].x); - result.push(uv[2].y); return result; }, - [] - ) - ); - geometry.addAttribute('uv', new THREE.BufferAttribute(uvs, 2)); + [ + { + index : 0, + count : 0 + } + ] + ); - /** - * Setup material groups - this means creating a new group for each material index change - * We know faces are sorted according to material index - */ - var groupIndexCounts = this.faces.reduce( - function(result, face) { + groupIndexCounts.reduce( + function(start, group) { + geometry.addGroup(start, group.count, group.index); + return start + group.count; + }, + 0 + ); - var currentGroup = result.pop(); - if (currentGroup.index !== face.materialIndex) { - /** - * We have a new group - */ - result.push(currentGroup); - result.push({ - index: face.materialIndex, - count: 3 - }) - } else { - currentGroup.count += 3; - result.push(currentGroup); + } else { + + geometry = new THREE.Geometry(); + + var standardUvs = []; + + /** + * Face data + * @type {Array} + */ + geometry.faces = this.faces.map( + function (face) { + + standardUvs.push(face.uvs[0].map( + function(uv) { + return new THREE.Vector2( + uv.x, + uv.y + ) + } + )); + + var faceInstance = new THREE.Face3( + face.v0index, + face.v1index, + face.v2index + ); + + if (face.normal) { + faceInstance.normal = new THREE.Vector3( + face.normal.x, + face.normal.y, + face.normal.z + ); + } + + if (face.color) { + faceInstance.color = new THREE.Color( + face.color.r, + face.color.g, + face.color.b + ) + } + + faceInstance.materialIndex = face.materialIndex; + + return faceInstance; } + ); - return result; - }, - [ - { - index : 0, - count : 0 + /** + * Vertex data + * @type {Array} + */ + geometry.vertices = this.vertices.map( + function (vertex) { + return new THREE.Vector3( + vertex.position.x, + vertex.position.y, + vertex.position.z + ) } - ] - ); + ); + geometry.verticesNeedUpdate = true; - groupIndexCounts.reduce( - function(start, group) { - geometry.addGroup(start, group.count, group.index); - return start + group.count; - }, - 0 - ); + /** + * UV data + */ + geometry.faceVertexUvs = [standardUvs]; + /** + * Re-compute normals - we don't do this for buffer geometry because it assigns to every vertex normal the face + * normal - essentially disabling 'smooth shading' + */ + geometry.computeFaceNormals(); + geometry.computeVertexNormals(); + } /** * Apply skin indices */ // this.applyBones(geometry); - /** - * Re-compute normals - */ - geometry.computeFaceNormals(); - geometry.computeVertexNormals(); + return geometry; - }; @@ -426,15 +522,13 @@ GameLib.D3.Mesh.prototype.updateVerticesFromGeometryInstance = function(geometry this.faces = []; - this.faceVertexUvs = [[]]; - if (geometryInstance instanceof THREE.BufferGeometry) { - var vertices = this.instance.geometry.getAttribute('position').array; + var vertices = geometryInstance.getAttribute('position').array; - var uvs = this.instance.geometry.getAttribute('uv').array; + var uvs = geometryInstance.getAttribute('uv').array; - this.instance.geometry.groups.map(function(group){ + geometryInstance.groups.map(function(group){ var materialIndex = group.materialIndex; @@ -493,17 +587,23 @@ GameLib.D3.Mesh.prototype.updateVerticesFromGeometryInstance = function(geometry if (faceIndexes.length === 3) { - this.faces.push(new GameLib.D3.TriangleFace( - faceIndexes[0], - faceIndexes[1], - faceIndexes[2], - materialIndex - )); + this.faces.push( + new GameLib.D3.Face( + this.graphics, + new GameLib.D3.API.Face( + null, + null, + faceIndexes[0], + faceIndexes[1], + faceIndexes[2], + materialIndex, + [[indexedUvs[0], indexedUvs[1], indexedUvs[2]]] + ) + ) + ); - this.faceVertexUvs[0].push([indexedUvs[0], indexedUvs[1], indexedUvs[2]]); - - indexedUvs = []; - faceIndexes = []; + indexedUvs = []; + faceIndexes = []; } } @@ -514,18 +614,110 @@ GameLib.D3.Mesh.prototype.updateVerticesFromGeometryInstance = function(geometry console.log('todo : update vertices from buffer geometry'); } else { - geometryInstance.faces.map(function(face){ + var processed = 0; + + geometryInstance.faces.map(function(face, faceIndex){ + + processed++; + + if (processed % 100 === 0) { + console.log('processed ' + processed + ' faces'); + } + this.faces.push( - new GameLib.D3.TriangleFace( - face.a, - face.b, - face.c, - face.materialIndex + new GameLib.D3.Face( + this.graphics, + new GameLib.D3.API.Face( + null, + null, + face.a, + face.b, + face.c, + face.materialIndex, + [[ + // new GameLib.Vector2( + // this.graphics, + // new GameLib.API.Vector2( + { + x: geometryInstance.faceVertexUvs[0][faceIndex][0].x, + y: geometryInstance.faceVertexUvs[0][faceIndex][0].y + }, + { + x: geometryInstance.faceVertexUvs[0][faceIndex][1].x, + y: geometryInstance.faceVertexUvs[0][faceIndex][1].y + }, + { + x: geometryInstance.faceVertexUvs[0][faceIndex][2].x, + y: geometryInstance.faceVertexUvs[0][faceIndex][2].y + } + // ) + // ), + // new GameLib.Vector2( + // this.graphics, + // new GameLib.API.Vector2( + // geometryInstance.faceVertexUvs[0][faceIndex][1].x, + // geometryInstance.faceVertexUvs[0][faceIndex][1].y + // ) + // ), + // new GameLib.Vector2( + // this.graphics, + // new GameLib.API.Vector2( + // geometryInstance.faceVertexUvs[0][faceIndex][2].x, + // geometryInstance.faceVertexUvs[0][faceIndex][2].y + // ) + // ) + ]] + // new GameLib.Color( + // this.graphics, + // new GameLib.API.Color( + // face.color.r, + // face.color.g, + // face.color.b + // ) + // ), + // face.vertexColors.map(function(vertexColor){ + // return new GameLib.Color( + // this.graphics, + // new GameLib.API.Color( + // vertexColor.r, + // vertexColor.g, + // vertexColor.b + // ) + // ) + // }.bind(this)), + // face.vertexNormals.map(function(vertexNormal){ + // return new GameLib.Vector3( + // this.graphics, + // new GameLib.API.Vector3( + // vertexNormal.x, + // vertexNormal.y, + // vertexNormal.z + // ) + // ) + // }.bind(this)), + // new GameLib.Vector3( + // this.graphics, + // new GameLib.API.Vector3( + // face.normal.x, + // face.normal.y, + // face.normal.z + // ) + // ) + ) ) ) }.bind(this)); + processed = 0; + geometryInstance.vertices.map(function(vertex){ + + processed++; + + if (processed % 100 === 0) { + console.log('processed ' + processed + ' vertices'); + } + this.vertices.push( new GameLib.D3.Vertex( this.graphics, @@ -542,32 +734,6 @@ GameLib.D3.Mesh.prototype.updateVerticesFromGeometryInstance = function(geometry ) ) }.bind(this)); - - geometryInstance.faceVertexUvs[0].map(function(uvs){ - this.faceVertexUvs[0].push([ - new GameLib.Vector2( - this.graphics, - new GameLib.API.Vector2( - uvs[0].x, - uvs[0].y - ) - ), - new GameLib.Vector2( - this.graphics, - new GameLib.API.Vector2( - uvs[1].x, - uvs[1].y - ) - ), - new GameLib.Vector2( - this.graphics, - new GameLib.API.Vector2( - uvs[2].x, - uvs[2].y - ) - ) - ]) - }.bind(this)) } }; @@ -690,17 +856,10 @@ GameLib.D3.Mesh.prototype.getBoundingBox = function() { * Converts a GameLib.D3.Mesh to a GameLib.D3.API.Mesh * @returns {GameLib.D3.API.Mesh} */ -GameLib.D3.Mesh.prototype.toApiObject = function(save) { - - if (GameLib.Utils.UndefinedOrNull(save)) { - save = false; - } +GameLib.D3.Mesh.prototype.toApiObject = function() { var apiSkeleton = null; if (this.skeleton) { - if (save) { - this.skeleton.save(); - } apiSkeleton = this.skeleton.id; } @@ -708,14 +867,17 @@ GameLib.D3.Mesh.prototype.toApiObject = function(save) { if (this.materials) { apiMaterials = this.materials.map( function(material) { - if (save) { - material.save(); - } return material.id; } ) } + var apiFaces = this.faces.map( + function(face){ + return face.toApiObject(); + } + ); + var apiMesh = new GameLib.D3.API.Mesh( this.id, this.meshType, @@ -725,8 +887,7 @@ GameLib.D3.Mesh.prototype.toApiObject = function(save) { return vertex.toApiObject(); } ), - this.faces, - this.faceVertexUvs, + apiFaces, apiMaterials, GameLib.Utils.IdOrNull(this.parentMesh), GameLib.Utils.IdOrNull(this.parentScene), diff --git a/src/game-lib-d3-mesh-plane.js b/src/game-lib-d3-mesh-plane.js index 0fd5c1b..95e6bee 100644 --- a/src/game-lib-d3-mesh-plane.js +++ b/src/game-lib-d3-mesh-plane.js @@ -14,7 +14,9 @@ GameLib.D3.Mesh.Plane = function ( width, height, widthSegments, - heightSegments + heightSegments, + heightMapScale, + isHeightMap ) { this.graphics = graphics; this.graphics.isNotThreeThrow(); @@ -39,7 +41,17 @@ GameLib.D3.Mesh.Plane = function ( } this.heightSegments = heightSegments; - GameLib.D3.Mesh.call( + if (GameLib.Utils.UndefinedOrNull(heightMapScale)) { + heightMapScale = 1; + } + this.heightMapScale = heightMapScale; + + if (GameLib.Utils.UndefinedOrNull(isHeightMap)) { + isHeightMap = false; + } + this.isHeightMap = isHeightMap; + + GameLib.D3.Mesh.call( this, this.graphics, apiMesh @@ -55,13 +67,9 @@ GameLib.D3.Mesh.Plane.prototype.createInstance = function() { var geometry = null; /** - * If this geometry is coming from the database, apply the vertex data + * If this geometry is not coming from the database, apply the vertex data from the instance to our runtime object */ - if (this.vertices.length > 0) { - - geometry = this.createInstanceGeometry(); - - } else { + if (this.vertices.length === 0) { geometry = new THREE.PlaneGeometry( this.width, @@ -70,45 +78,68 @@ GameLib.D3.Mesh.Plane.prototype.createInstance = function() { this.heightSegments ); - this.updateVerticesFromGeometryInstance(geometry); + this.updateVerticesFromGeometryInstance(geometry); - geometry = this.createInstanceGeometry(); + this.materials.push( + new GameLib.D3.Material( + this.graphics, + new GameLib.D3.API.Material( + null, + GameLib.D3.Material.MATERIAL_TYPE_STANDARD, + 'Material Plane ' + this.id + ) + ) + ) } - var materialInstances = this.materials.reduce( - function(result, material) { - result.push(material.instance); - return result; - }, - [] - ); + /** + * If this is a heightmap - first generate the z-coordinates + */ + if (this.isHeightMap) { + this.generateHeightMapFromBumpMap(); + } - var instance = new THREE.Mesh(geometry, materialInstances); + /** + * Now construct the mesh instance + */ + var instance = GameLib.D3.Mesh.prototype.createInstance.call(this); - instance.userData.width = this.width; + /** + * Apply some plane specific data to the instance + */ + instance.userData.width = this.width; instance.userData.height = this.height; instance.userData.widthSegments = this.widthSegments; instance.userData.heightSegments = this.heightSegments; - - this.createInstanceDefaults(instance); + instance.userData.heightMapScale = this.heightMapScale; + instance.userData.isHeightMap = this.isHeightMap; return instance; }; +/** + * + */ GameLib.D3.Mesh.Plane.prototype.updateInstance = function() { + var geometry = null; + if ( this.instance.userData.width !== this.width || this.instance.userData.height !== this.height || this.instance.userData.widthSegments !== this.widthSegments || - this.instance.userData.heightSegments !== this.heightSegments + this.instance.userData.heightSegments !== this.heightSegments || + this.instance.userData.isHeightMap !== this.isHeightMap || + this.instance.userData.heightMapScale !== this.heightMapScale ) { this.instance.userData.width = this.width; this.instance.userData.height = this.height; this.instance.userData.widthSegments = this.widthSegments; this.instance.userData.heightSegments = this.heightSegments; + this.instance.userData.isHeightMap = this.isHeightMap; + this.instance.userData.heightMapScale = this.heightMapScale; - var geometry = new THREE.PlaneGeometry( + geometry = new THREE.PlaneGeometry( this.width, this.height, this.widthSegments, @@ -117,13 +148,14 @@ GameLib.D3.Mesh.Plane.prototype.updateInstance = function() { this.updateVerticesFromGeometryInstance(geometry); - this.instance.geometry = this.createInstanceGeometry(); - - if (this.helper) { - this.removeHelper(); - this.createHelper(); + if (this.isHeightMap) { + this.generateHeightMapFromBumpMap(); } - } + + geometry = this.createInstanceGeometry(); + + this.instance.geometry = geometry; + } GameLib.D3.Mesh.prototype.updateInstance.call(this); }; @@ -140,6 +172,8 @@ GameLib.D3.Mesh.Plane.prototype.toApiObject = function() { apiMesh.height = this.height; apiMesh.widthSegments = this.widthSegments; apiMesh.heightSegments = this.heightSegments; + apiMesh.heightMapScale = this.heightMapScale; + apiMesh.isHeightMap = this.isHeightMap; return apiMesh; }; @@ -161,45 +195,60 @@ GameLib.D3.Mesh.Plane.FromObject = function(graphics, objectMesh) { objectMesh.width, objectMesh.height, objectMesh.widthSegments, - objectMesh.heightSegments + objectMesh.heightSegments, + objectMesh.heightMapScale, + objectMesh.isHeightMap ); }; -GameLib.D3.Mesh.Plane.prototype.getHeightData = function(img,scale) { +GameLib.D3.Mesh.Plane.prototype.getHeightData = function() { - if (GameLib.Utils.UndefinedOrNull(scale)) { - scale = 1; - } + var i; - if (GameLib.Utils.UndefinedOrNull(img)) { - img = this.instance.material.bumpMap.image; - } + var img = this.materials.reduce( + function(result, material) { + if ( + material.bumpMap && + material.bumpMap.image && + material.bumpMap.image.instance + ) { + result = material.bumpMap.image.instance; + } - var canvas = document.createElement( 'canvas' ); - canvas.width = img.width; - canvas.height = img.height; - var context = canvas.getContext( '2d' ); + return result; + }, + null + ); - var size = img.width * img.height; - var data = new Float32Array( size ); + if (!img) { + throw new Error('bumpmap image not found'); + } - context.drawImage(img,0,0); + var canvas = document.createElement( 'canvas' ); + canvas.width = img.width; + canvas.height = img.height; + var context = canvas.getContext( '2d' ); - for ( var i = 0; i < size; i ++ ) { - data[i] = 0 - } + var size = img.width * img.height; + var data = new Float32Array( size ); - var imgd = context.getImageData(0, 0, img.width, img.height); - var pix = imgd.data; + context.drawImage(img,0,0); - var j=0; - for (var i = 0; i