From 3a422a5acf7aa7162f52486736e7d6b579405325 Mon Sep 17 00:00:00 2001 From: -=yb4f310 Date: Tue, 3 Oct 2017 14:50:34 +0200 Subject: [PATCH] before animation refactor --- src/game-lib-a-1-event.js | 8 + src/game-lib-d3-api-animation.js | 12 +- src/game-lib-d3-api-mesh.js | 25 +- src/game-lib-d3-api-z-animation.js | 25 +- src/game-lib-d3-helper.js | 5 + src/game-lib-d3-mesh-0.js | 274 +++++--- src/game-lib-entity-manager.js | 5 + src/game-lib-system-animation.js | 990 +++++++++++++++++++++-------- src/game-lib-system-gui.js | 14 +- 9 files changed, 995 insertions(+), 363 deletions(-) diff --git a/src/game-lib-a-1-event.js b/src/game-lib-a-1-event.js index 29c2196..b8efaf7 100644 --- a/src/game-lib-a-1-event.js +++ b/src/game-lib-a-1-event.js @@ -92,6 +92,10 @@ GameLib.Event.TOUCH_END = 0x4a; GameLib.Event.TOUCH_MOVE = 0x4b; GameLib.Event.TOUCH_CANCEL = 0x4c; GameLib.Event.GET_REMOTE_API_URL = 0x4d; +GameLib.Event.GET_GRAPHICS_IMPLEMENTATION = 0x4e; +GameLib.Event.GET_PHYSICS_IMPLEMENTATION = 0x4f; +GameLib.Event.GET_CODER_IMPLEMENTATION = 0x50; +GameLib.Event.ANIMATION_MESH_ADDED = 0x51; /** @@ -180,6 +184,10 @@ GameLib.Event.GetEventName = function(number) { case 0x4b : return 'touch_move'; case 0x4c : return 'touch_cancel'; case 0x4d : return 'get_remote_api_url'; + case 0x4e : return 'get_graphics_implementation'; + case 0x4f : return 'get_physics_implementation'; + case 0x50 : return 'get_coder_implementation'; + case 0x51 : return 'animation_mesh_added'; break; } diff --git a/src/game-lib-d3-api-animation.js b/src/game-lib-d3-api-animation.js index 8d14172..1dd8919 100644 --- a/src/game-lib-d3-api-animation.js +++ b/src/game-lib-d3-api-animation.js @@ -10,7 +10,7 @@ * @param scaleFn * @param blocking * @param applyToMeshWhenDone - * @param parentMesh + * @param meshes * @param parentEntity * @constructor */ @@ -25,7 +25,7 @@ GameLib.D3.API.Animation = function ( scaleFn, blocking, applyToMeshWhenDone, - parentMesh, + meshes, parentEntity ) { if (GameLib.Utils.UndefinedOrNull(id)) { @@ -78,10 +78,10 @@ GameLib.D3.API.Animation = function ( } this.applyToMeshWhenDone = applyToMeshWhenDone; - if (GameLib.Utils.UndefinedOrNull(parentMesh)) { - parentMesh = null; + if (GameLib.Utils.UndefinedOrNull(meshes)) { + meshes = []; } - this.parentMesh = parentMesh; + this.meshes = meshes; if (GameLib.Utils.UndefinedOrNull(parentEntity)) { parentEntity = null; @@ -110,7 +110,7 @@ GameLib.D3.API.Animation.FromObject = function(objectComponent) { objectComponent.scaleFn, objectComponent.blocking, objectComponent.applyToMeshWhenDone, - objectComponent.parentMesh, + objectComponent.meshes, objectComponent.parentEntity ); }; diff --git a/src/game-lib-d3-api-mesh.js b/src/game-lib-d3-api-mesh.js index a115499..a5814dd 100644 --- a/src/game-lib-d3-api-mesh.js +++ b/src/game-lib-d3-api-mesh.js @@ -13,12 +13,14 @@ * @param skinWeights * @param position GameLib.API.Vector3 * @param quaternion GameLib.API.Quaternion + * @param rotation * @param scale GameLib.API.Vector3 * @param up * @param modelMatrix GameLib.API.Matrix4 * @param parentEntity * @param renderOrder * @param isBufferMesh + * @param useQuaternion (use quaternion property for rotation rather than rotation property) * @constructor */ GameLib.D3.API.Mesh = function( @@ -35,12 +37,14 @@ GameLib.D3.API.Mesh = function( skinWeights, position, quaternion, + rotation, scale, up, modelMatrix, parentEntity, renderOrder, - isBufferMesh + isBufferMesh, + useQuaternion ) { if (GameLib.Utils.UndefinedOrNull(id)) { id = GameLib.Utils.RandomId(); @@ -107,6 +111,11 @@ GameLib.D3.API.Mesh = function( } this.quaternion = quaternion; + if (GameLib.Utils.UndefinedOrNull(rotation)) { + rotation = new GameLib.API.Vector3(0,0,0); + } + this.rotation = rotation; + if (GameLib.Utils.UndefinedOrNull(scale)) { scale = new GameLib.API.Vector3(1,1,1); } @@ -136,6 +145,11 @@ GameLib.D3.API.Mesh = function( isBufferMesh = false; } this.isBufferMesh = isBufferMesh; + + if (GameLib.Utils.UndefinedOrNull(useQuaternion)) { + useQuaternion = true; + } + this.useQuaternion = useQuaternion; }; GameLib.D3.API.Mesh.prototype = Object.create(GameLib.Component.prototype); @@ -192,6 +206,11 @@ GameLib.D3.API.Mesh.FromObject = function (objectMesh){ apiPosition = GameLib.API.Vector3.FromObject(objectMesh.position); } + var apiRotation = new GameLib.API.Vector3(); + if (objectMesh.rotation) { + apiRotation = GameLib.API.Vector3.FromObject(objectMesh.rotation); + } + var apiQuaternion = new GameLib.API.Quaternion(); if (objectMesh.quaternion) { apiQuaternion = GameLib.API.Quaternion.FromObject(objectMesh.quaternion); @@ -226,11 +245,13 @@ GameLib.D3.API.Mesh.FromObject = function (objectMesh){ objectMesh.skinWeights, apiPosition, apiQuaternion, + apiRotation, apiScale, apiUp, apiModelMatrix, objectMesh.parentEntity, objectMesh.renderOrder, - objectMesh.isBufferMesh + objectMesh.isBufferMesh, + objectMesh.useQuaternion ); }; diff --git a/src/game-lib-d3-api-z-animation.js b/src/game-lib-d3-api-z-animation.js index 5f370b4..27c525a 100644 --- a/src/game-lib-d3-api-z-animation.js +++ b/src/game-lib-d3-api-z-animation.js @@ -32,7 +32,7 @@ GameLib.D3.Animation = function( apiAnimation.scaleFn, apiAnimation.blocking, apiAnimation.applyToMeshWhenDone, - apiAnimation.parentMesh, + apiAnimation.meshes, apiAnimation.parentEntity ); @@ -48,7 +48,10 @@ GameLib.D3.Animation = function( GameLib.Component.call( this, - GameLib.Component.COMPONENT_ANIMATION + GameLib.Component.COMPONENT_ANIMATION, + { + 'meshes' : [GameLib.D3.Mesh] + } ); }; @@ -167,7 +170,11 @@ GameLib.D3.Animation.prototype.toApiObject = function() { this.scaleFn, this.blocking, this.applyToMeshWhenDone, - GameLib.Utils.IdOrNull(this.parentMesh), + this.meshes.map( + function(mesh) { + return GameLib.Utils.IdOrNull(mesh); + } + ), GameLib.Utils.IdOrNull(this.parentEntity) ); @@ -233,4 +240,16 @@ GameLib.D3.Animation.prototype.launchEditor = function(){ GameLib.D3.Animation.prototype.closeEditor = function(){ var dom = this.editor.getWrapperElement(); dom.parentElement.removeChild(dom); +}; + +GameLib.D3.Animation.prototype.addMesh = function(mesh) { + this.meshes.push(mesh); + GameLib.Event.Emit( + GameLib.Event.ANIMATION_MESH_ADDED, + { + animation : this, + mesh : mesh + } + ) + }; \ No newline at end of file diff --git a/src/game-lib-d3-helper.js b/src/game-lib-d3-helper.js index cf4062e..2491c8e 100644 --- a/src/game-lib-d3-helper.js +++ b/src/game-lib-d3-helper.js @@ -134,6 +134,11 @@ GameLib.D3.Helper.prototype.createInstance = function() { */ GameLib.D3.Helper.prototype.updateInstance = function() { this.instance.position.copy(this.object.instance.position); + + if (this.object.instance.parentMesh && this.object.instance.parentMesh.instance) { + this.instance.position.add(this.object.instance.parentMesh.instance.position); + } + this.instance.scale.copy(this.object.instance.scale); this.instance.quaternion.copy(this.object.instance.quaternion); }; diff --git a/src/game-lib-d3-mesh-0.js b/src/game-lib-d3-mesh-0.js index da86320..0adf967 100644 --- a/src/game-lib-d3-mesh-0.js +++ b/src/game-lib-d3-mesh-0.js @@ -34,15 +34,16 @@ GameLib.D3.Mesh = function ( apiMesh.skinWeights, apiMesh.position, apiMesh.quaternion, + apiMesh.rotation, apiMesh.scale, apiMesh.up, apiMesh.modelMatrix, apiMesh.parentEntity, apiMesh.renderOrder, - apiMesh.isBufferMesh + apiMesh.isBufferMesh, + apiMesh.useQuaternion ); - this.faces = this.faces.map( function(face) { if (face instanceof GameLib.D3.API.Face) { @@ -56,7 +57,6 @@ GameLib.D3.Mesh = function ( }.bind(this) ); - this.materials = this.materials.map( function(material) { if (material instanceof GameLib.D3.API.Material) { @@ -92,6 +92,18 @@ GameLib.D3.Mesh = function ( this ); + this.quaternion = new GameLib.Quaternion( + this.graphics, + this.quaternion, + this + ); + + this.rotation = new GameLib.Vector3( + this.graphics, + this.rotation, + this + ); + this.scale = new GameLib.Vector3( this.graphics, this.scale, @@ -104,12 +116,6 @@ GameLib.D3.Mesh = function ( this ); - this.quaternion = new GameLib.Quaternion( - this.graphics, - this.quaternion, - this - ); - this.modelMatrix = new GameLib.Matrix4( this.graphics, this.modelMatrix, @@ -466,7 +472,44 @@ GameLib.D3.Mesh.prototype.createInstance = function() { } } - this.createInstanceDefaults(instance); + instance.name = this.name; + + if (this.parentMesh && this.parentMesh.loaded) { + this.parentMesh.add(instance, this); + } + + instance.position.x = this.position.x; + instance.position.y = this.position.y; + instance.position.z = this.position.z; + + if (this.useQuaternion) { + instance.quaternion.x = this.quaternion.x; + instance.quaternion.y = this.quaternion.y; + instance.quaternion.z = this.quaternion.z; + instance.quaternion.w = this.quaternion.w; + instance.quaternion.setFromAxisAngle( + new THREE.Vector3( + this.quaternion.axis.x, + this.quaternion.axis.y, + this.quaternion.axis.z + ), + this.quaternion.angle + ); + } else { + instance.rotation.x = this.rotation.x; + instance.rotation.y = this.rotation.y; + instance.rotation.z = this.rotation.z; + } + + instance.scale.x = this.scale.x; + instance.scale.y = this.scale.y; + instance.scale.z = this.scale.z; + + instance.up.x = this.up.x; + instance.up.y = this.up.y; + instance.up.z = this.up.z; + + instance.renderOrder = this.renderOrder; return instance; }; @@ -485,13 +528,19 @@ GameLib.D3.Mesh.prototype.updateInstance = function() { this.instance.geometry = this.createInstanceGeometry(this.instance.geometry); } - if (this.updateRotationFromAxisAngle) { - this.updateInstanceRotationFromAxisAngle(); + if (this.useQuaternion) { + if (this.updateRotationFromAxisAngle) { + this.updateInstanceRotationFromAxisAngle(); + } else { + this.instance.quaternion.x = this.quaternion.x; + this.instance.quaternion.y = this.quaternion.y; + this.instance.quaternion.z = this.quaternion.z; + this.instance.quaternion.w = this.quaternion.w; + } } else { - this.instance.quaternion.x = this.quaternion.x; - this.instance.quaternion.y = this.quaternion.y; - this.instance.quaternion.z = this.quaternion.z; - this.instance.quaternion.w = this.quaternion.w; + this.instance.rotation.x = this.rotation.x; + this.instance.rotation.y = this.rotation.y; + this.instance.rotation.z = this.rotation.z; } if (this.parentMesh && this.parentMesh.loaded) { @@ -773,57 +822,114 @@ GameLib.D3.Mesh.prototype.applyBones = function(geometry) { }; -GameLib.D3.Mesh.prototype.setParentMesh = function(parentMesh) { - - this.parentMesh = parentMesh; - - if (parentMesh && parentMesh.loaded) { - - if (this.instance) { - this.instance.parent = this.parentMesh.instance; - } +/** + * Adds a child instance to this instance + * @param childInstance + * @param child + */ +GameLib.D3.Mesh.prototype.addChild = function(childInstance, child) { + if (!this.instance) { + throw new Error('mesh instance not loaded yet : ' + this.name); } + if (GameLib.Utils.UndefinedOrNull(childInstance)) { + throw new Error('no child mesh instance'); + } + + if (GameLib.Utils.UndefinedOrNull(child)) { + throw new Error('no child mesh'); + } + + this.instance.add(childInstance); + + childInstance.parent = this.instance; + + child.parentMesh = this; }; -GameLib.D3.Mesh.prototype.createInstanceDefaults = function(instance) { +/** + * Sets a parent for this mesh + * @param parentMesh + */ +GameLib.D3.Mesh.prototype.setParentMesh = function(parentMesh) { - instance.name = this.name; + /** + * Are we removing this child from the parent? + */ + if (GameLib.Utils.UndefinedOrNull(parentMesh)) { - if (this.parentMesh && this.parentMesh.loaded) { - if (this.parentMesh.loaded) { - instance.parent = this.parentMesh.instance; + if (this.instance && this.instance.parent && this.parentScene.instance) { + + /** + * Update the parent matrix world + */ + this.instance.parent.updateMatrixWorld(); + + /** + * Copy the child world position into a temporary vector + * @type {THREE.Vector3} + */ + var vector = new THREE.Vector3(); + vector.setFromMatrixPosition(this.instance.matrixWorld); + + /** + * Detach child from parent within this scene + */ + THREE.SceneUtils.detach( + this.instance, + this.instance.parent, + this.parentScene.instance + ); + + /** + * We remember from now on that we have no parent mesh + * @type {null} + */ + this.parentMesh = null; + + /** + * We store the world position back to the child + */ + this.position.x = vector.x; + this.position.y = vector.y; + this.position.z = vector.z; + + /** + * Update the instance position + */ + this.updateInstancePosition(); + + /** + * Don't touch this instance parent - since it is now probably a scene object + */ + + /** + * TODO: do we apply rotation somehow? + */ + + } else { + throw new Error('Not enough information to detach') } + + } else { + + if (!this.instance) { + throw new Error('No valid instance at time of adding to parent for mesh ' + this.name); + } + + if (!(parentMesh instanceof GameLib.D3.Mesh)) { + throw new Error('Not a valid parent mesh'); + } + + this.parentMesh = parentMesh; + + /** + * Add this as a child to the parent + */ + this.parentMesh.addChild(this.instance, this); } - instance.position.x = this.position.x; - instance.position.y = this.position.y; - instance.position.z = this.position.z; - - - instance.quaternion.x = this.quaternion.x; - instance.quaternion.y = this.quaternion.y; - instance.quaternion.z = this.quaternion.z; - instance.quaternion.w = this.quaternion.w; - instance.quaternion.setFromAxisAngle( - new THREE.Vector3( - this.quaternion.axis.x, - this.quaternion.axis.y, - this.quaternion.axis.z - ), - this.quaternion.angle - ); - - instance.scale.x = this.scale.x; - instance.scale.y = this.scale.y; - instance.scale.z = this.scale.z; - - instance.up.x = this.up.x; - instance.up.y = this.up.y; - instance.up.z = this.up.z; - - instance.renderOrder = this.renderOrder; }; @@ -886,11 +992,14 @@ GameLib.D3.Mesh.prototype.toApiObject = function() { this.skinWeights, this.position.toApiObject(), this.quaternion.toApiObject(), + this.rotation.toApiObject(), this.scale.toApiObject(), this.up.toApiObject(), this.modelMatrix.toApiObject(), GameLib.Utils.IdOrNull(this.parentEntity), - this.renderOrder + this.renderOrder, + this.isBufferMesh, + this.useQuaternion ); return apiMesh; @@ -1046,19 +1155,27 @@ GameLib.D3.Mesh.prototype.applyPositionRotationScale = function() { this.scale.z = 1; this.instance.scale.set(1,1,1); - /** - * Reset rotation - * @type {number} - */ - this.quaternion.x = 0; - this.quaternion.y = 0; - this.quaternion.z = 0; - this.quaternion.w = 1; - this.quaternion.axis.x = 0; - this.quaternion.axis.y = 0; - this.quaternion.axis.z = 0; - this.quaternion.angle = 0; - this.updateInstanceRotationFromAxisAngle(); + + if (this.useQuaternion) { + /** + * Reset rotation + * @type {number} + */ + this.quaternion.x = 0; + this.quaternion.y = 0; + this.quaternion.z = 0; + this.quaternion.w = 1; + this.quaternion.axis.x = 0; + this.quaternion.axis.y = 0; + this.quaternion.axis.z = 0; + this.quaternion.angle = 0; + this.updateInstanceRotationFromAxisAngle(); + } else { + this.rotation.x = 0; + this.rotation.y = 0; + this.rotation.z = 0; + this.instance.rotation.set(0,0,0); + } /** * Update our instance matrix @@ -1092,6 +1209,21 @@ GameLib.D3.Mesh.prototype.updateInstanceRotationFromAxisAngle = function(axis, a this.quaternion.w = this.quaternion.instance.w; }; +GameLib.D3.Mesh.prototype.updateInstanceRotation = function() { + + this.rotation.instance.set( + this.rotation.x, + this.rotation.y, + this.rotation.z + ); + + this.instance.rotation.copy(this.rotation.instance); + + if (this.helper) { + this.helper.updateInstance(); + } +}; + GameLib.D3.Mesh.prototype.updateInstancePosition = function() { this.position.instance.set( diff --git a/src/game-lib-entity-manager.js b/src/game-lib-entity-manager.js index 21e0b0c..007cd82 100644 --- a/src/game-lib-entity-manager.js +++ b/src/game-lib-entity-manager.js @@ -89,6 +89,11 @@ GameLib.EntityManager.prototype.removeComponent = function(data) { data.component.parentEntity.removeComponent(data.component); } + if (data.component instanceof GameLib.D3.Mesh && + data.component.parentScene instanceof GameLib.D3.Scene) { + data.component.parentScene.removeObject(data.component); + } + var index = this.register.indexOf(data.component); if (index !== -1) { diff --git a/src/game-lib-system-animation.js b/src/game-lib-system-animation.js index 60411da..bffebad 100644 --- a/src/game-lib-system-animation.js +++ b/src/game-lib-system-animation.js @@ -10,6 +10,9 @@ GameLib.System.Animation = function( this, apiSystem ); + + this.animations = {}; + this.latest = {}; }; GameLib.System.Animation.prototype = Object.create(GameLib.System.prototype); @@ -19,273 +22,609 @@ GameLib.System.Animation.prototype.start = function() { GameLib.System.prototype.start.call(this); - this.animationEntities = GameLib.EntityManager.Instance.findEntities([GameLib.D3.Animation]); + this.beforeRenderSubscription = GameLib.Event.Subscribe( + GameLib.Event.BEFORE_RENDER, + function(data) { - this.animationEntities.map( - function(animationEntity) { + var delta = data.delta; + + for (var property in this.animations) { + if (this.animations.hasOwnProperty(property)) { + + + if (this.animations[property].length > 0) { + + var done = false; + + var animationObject = this.animations[property][0]; + + if (animationObject.type === 'rotation') { + + var increment = animationObject.animation.rotationSpeed * delta; + + animationObject.from += increment; + + if (Math.abs(animationObject.from - animationObject.to) < increment) { + animationObject.mesh.instance.rotation[animationObject.axis] = animationObject.to; + done = true; + } else { + animationObject.mesh.instance.rotation[animationObject.axis] = animationObject.from; + } + } + + if (done) { + this.animations[property].splice(0, 1); + } + } + } + } + + }.bind(this) + ); + + this.animationMeshAddedSubscription = GameLib.Event.Subscribe( + GameLib.Event.ANIMATION_MESH_ADDED, + function(data) { + + var animation = data.animation; + var mesh = data.mesh; /** - * Get all meshes and all animation components + * Initialize the property with the original mesh z value */ - var meshComponents = animationEntity.getComponents(GameLib.D3.Mesh); - var animationComponents = animationEntity.getComponents(GameLib.D3.Animation); + this.latest[mesh.id] = { + rotation : { + x : mesh.rotation.x, + y : mesh.rotation.y, + z : mesh.rotation.z + }, + position : { + x : mesh.position.x, + y : mesh.position.y, + z : mesh.position.z + }, + scale : { + x : mesh.scale.x, + y : mesh.scale.y, + z : mesh.scale.z + }, + quaternion : { + axis : { + x : mesh.quaternion.axis.x, + y : mesh.quaternion.axis.y, + z : mesh.quaternion.axis.z + }, + angle : mesh.quaternion.angle + } + }; - animationComponents.map(function(animation){ + this.animations[mesh.id] = []; - meshComponents.map(function(mesh){ + mesh.backupQuaternionAngleDescriptor = Object.getOwnPropertyDescriptor(mesh.quaternion, 'angle'); + mesh.backupQuaternionAxisXDescriptor = Object.getOwnPropertyDescriptor(mesh.quaternion.axis, 'x'); + mesh.backupQuaternionAxisYDescriptor = Object.getOwnPropertyDescriptor(mesh.quaternion.axis, 'y'); + mesh.backupQuaternionAxisZDescriptor = Object.getOwnPropertyDescriptor(mesh.quaternion.axis, 'z'); + mesh.backupRotationXDescriptor = Object.getOwnPropertyDescriptor(mesh.rotation, 'x'); + mesh.backupRotationYDescriptor = Object.getOwnPropertyDescriptor(mesh.rotation, 'y'); + mesh.backupRotationZDescriptor = Object.getOwnPropertyDescriptor(mesh.rotation, 'z'); + mesh.backupPositionXDescriptor = Object.getOwnPropertyDescriptor(mesh.position, 'x'); + mesh.backupPositionYDescriptor = Object.getOwnPropertyDescriptor(mesh.position, 'y'); + mesh.backupPositionZDescriptor = Object.getOwnPropertyDescriptor(mesh.position, 'z'); + mesh.backupScaleXDescriptor = Object.getOwnPropertyDescriptor(mesh.scale, 'x'); + mesh.backupScaleYDescriptor = Object.getOwnPropertyDescriptor(mesh.scale, 'y'); + mesh.backupScaleZDescriptor = Object.getOwnPropertyDescriptor(mesh.scale, 'z'); - if (animation.rotationSpeed) { + Object.defineProperty( + mesh.quaternion, + 'angle', + { + 'get': this.getQuaternionAngle(mesh, animation), + 'set': this.setQuaternionAngle(mesh, animation), + 'configurable': true + } + ); - /** - * Back up the current property descriptor - */ - mesh.animationObject = { - backupAngleDescriptor : Object.getOwnPropertyDescriptor(mesh.quaternion, 'angle'), - targetAngle : mesh.quaternion.angle, - angleIncrement : true, - intermediateAngle : mesh.quaternion.angle, - subscription : null, - inProcess : false, - blocking : animation.blocking//, - // callbacks : [], - // storedValues : [] - }; + Object.defineProperty( + mesh.quaternion.axis, + 'x', + { + 'get': this.getQuaternionAxisX(mesh, animation), + 'set': this.setQuaternionAxisX(mesh, animation), + 'configurable': true + } + ); - var getIntermediateAngle = function() { - return mesh.animationObject.intermediateAngle; - }; + Object.defineProperty( + mesh.quaternion.axis, + 'y', + { + 'get': this.getQuaternionAxisY(mesh, animation), + 'set': this.setQuaternionAxisY(mesh, animation), + 'configurable': true + } + ); - var getTargetAngle = function() { + Object.defineProperty( + mesh.quaternion.axis, + 'z', + { + 'get': this.getQuaternionAxisZ(mesh, animation), + 'set': this.setQuaternionAxisZ(mesh, animation), + 'configurable': true + } + ); - // if (mesh.animationObject.storedValues.length > 0) { - // return mesh.animationObject.storedValues[mesh.animationObject.storedValues.length - 1]; - // } + Object.defineProperty( + mesh.rotation, + 'x', + { + 'get': this.getRotation(mesh, 'x'), + 'set': this.setRotation(mesh, animation, 'x'), + 'configurable': true + } + ); - return mesh.animationObject.targetAngle; - }; + Object.defineProperty( + mesh.rotation, + 'y', + { + 'get': this.getRotation(mesh, 'y'), + 'set': this.setRotation(mesh, animation, 'y'), + 'configurable': true + } + ); - var animateRotation = function(value) { + Object.defineProperty( + mesh.rotation, + 'z', + { + 'get': this.getRotation(mesh, 'z'), + 'set': this.setRotation(mesh, animation, 'z'), + 'configurable': true + } + ); - mesh.animationObject.intermediateAngle += value; + Object.defineProperty( + mesh.scale, + 'x', + { + 'get': this.getScaleX(mesh, animation), + 'set': this.setScaleX(mesh, animation), + 'configurable': true + } + ); - var done = false; + Object.defineProperty( + mesh.scale, + 'y', + { + 'get': this.getScaleY(mesh, animation), + 'set': this.setScaleY(mesh, animation), + 'configurable': true + } + ); - if (mesh.animationObject.angleIncrement) { - /** - * We are rotating upwards - */ - if (mesh.animationObject.intermediateAngle >= mesh.animationObject.targetAngle) { - /** - * We need to stop - */ - done = true; - } - } else { - /** - * We are rotating downwards - */ - if (mesh.animationObject.intermediateAngle <= mesh.animationObject.targetAngle) { - /** - * We need to stop - */ - done = true; - } - } + Object.defineProperty( + mesh.scale, + 'z', + { + 'get': this.getScaleZ(mesh, animation), + 'set': this.setScaleZ(mesh, animation), + 'configurable': true + } + ); - if (done) { + Object.defineProperty( + mesh.position, + 'x', + { + 'get': this.getPositionX(mesh, animation), + 'set': this.setPositionX(mesh, animation), + 'configurable': true + } + ); - /** - * We clamp to our target angle - */ - mesh.animationObject.intermediateAngle = mesh.animationObject.targetAngle; + Object.defineProperty( + mesh.position, + 'y', + { + 'get': this.getPositionY(mesh, animation), + 'set': this.setPositionY(mesh, animation), + 'configurable': true + } + ); - /** - * We limit our intermediate angle between values of -pi and pi - */ - while (mesh.animationObject.intermediateAngle > Math.PI) { - mesh.animationObject.intermediateAngle -= (Math.PI * 2); - } - - while (mesh.animationObject.intermediateAngle < -(Math.PI)) { - mesh.animationObject.intermediateAngle += (Math.PI * 2); - } - - /** - * We apply our new intermediate angle to our target - */ - mesh.animationObject.targetAngle = mesh.animationObject.intermediateAngle; - } - - /** - * Apply the actual rotation to the mesh - */ - mesh.updateInstanceRotationFromAxisAngle(mesh.quaternion.axis, mesh.animationObject.intermediateAngle); - - /** - * Check again if we are done, we need to do some additional work - - */ - if (done) { - - if (!mesh.animationObject.subscription) { - var message = 'mesh animation object subscription went missing for '; - message += mesh.name + ': '; - message += animation.name; - console.warn(message); - throw new Error(message); - } - - /** - * Stop subscribing to before render events - */ - mesh.animationObject.subscription.remove(); - - /** - * @type {null} - */ - mesh.animationObject.subscription = null; - - /** - * For some meshes, when we are done with the animation, we want to apply - * the current state of the mesh to the object data itself (i.e. update - * its vertices etc) - */ - if (animation.applyToMeshWhenDone) { - /** - * Now we say that our intermediate angle is zero, because we will apply our rotation - * and this will prevent us from re-registering a new 'animationRender' event - */ - mesh.animationObject.targetAngle = 0; - mesh.animationObject.intermediateAngle = 0; - - /** - * Apply our position, rotation and scale to the mesh - */ - mesh.applyPositionRotationScale(); - } - - /** - * Tell our animation component that it is no longer in process... - * @type {boolean} - */ - mesh.animationObject.inProcess = false; - - // if (mesh.animationObject.callbacks.length > 0) { - // var callback = mesh.animationObject.callbacks[0]; - // mesh.animationObject.callbacks.splice(0,1); - // callback(); - // } - - // mesh.animationObject.storedValues = []; - } - }; - - var setTargetAngle = function(value) { - - /** - * Check if we have work to do - */ - if (mesh.animationObject.intermediateAngle === value) { - - mesh.animationObject.inProcess = false; - - /** - * Nothing to do - */ - return; - } - - /** - * Check if we have another animation in process - */ - if (mesh.animationObject.inProcess && mesh.animationObject.blocking) { - - console.log('another animation is already in process'); - - // setTargetAngle(value); - - // GameLib.Utils.PushUnique(mesh.animationObject.storedValues, value); - // - // mesh.animationObject.callbacks.push( - // function(__value) { - // return function(){ - // mesh.quaternion.angle = __value; - // } - // }(value) - // ); - - /** - * Respond that our angle is actually our target angle (it will be that soon) - */ - return; - } - - /** - * We indicate that we now have an animation in process - * @type {boolean} - */ - mesh.animationObject.inProcess = true; - - /** - * Ok - all good - lets start the animation - */ - if (mesh.animationObject.intermediateAngle > value) { - /** - * We will rotate towards by decrementing - */ - mesh.animationObject.angleIncrement = false; - } else { - /** - * We will rotate towards by incrementing - */ - mesh.animationObject.angleIncrement = true; - } - - /** - * We say what our target angle is - when we reach our target angle, we want - * to stop our animation - */ - mesh.animationObject.targetAngle = value; - - /** - * Now we subscribe to 'before render' events, and slowly increment the value - * @type {{fn, remove}} - */ - mesh.animationObject.subscription = GameLib.Event.Subscribe( - GameLib.Event.BEFORE_RENDER, - function(data) { - - var increment = Math.abs(animation.rotationSpeed); - - if (!mesh.animationObject.angleIncrement) { - increment *= -1; - } - - animateRotation(data.delta * increment); - } - ); - }; - - - /** - * Override the property descriptor - to slowly increment the angle, but report back its - * target angle as its actual angle. - */ - Object.defineProperty( - mesh.quaternion, - 'angle', - { - 'get' : getTargetAngle, - 'set' : setTargetAngle, - 'configurable' : true - } - ); - } - }); - }); - } + Object.defineProperty( + mesh.position, + 'z', + { + 'get': this.getPositionZ(mesh, animation), + 'set': this.setPositionZ(mesh, animation), + 'configurable': true + } + ); + + }.bind(this) ); }; +GameLib.System.Animation.prototype.getQuaternionAngle = function(mesh, animation) { + + return function() { + return; + /** + * TODO: fix this shit.. + * Back up the current property descriptor + */ + mesh.animationObject = { + backupAngleDescriptor: Object.getOwnPropertyDescriptor(mesh.quaternion, 'angle'), + targetAngle: mesh.quaternion.angle, + angleIncrement: true, + intermediateAngle: mesh.quaternion.angle, + subscription: null, + inProcess: false, + blocking: animation.blocking//, + // callbacks : [], + // storedValues : [] + }; + + var getIntermediateAngle = function () { + return mesh.animationObject.intermediateAngle; + }; + + var getTargetAngle = function () { + + // if (mesh.animationObject.storedValues.length > 0) { + // return mesh.animationObject.storedValues[mesh.animationObject.storedValues.length - 1]; + // } + + return mesh.animationObject.targetAngle; + }; + + var animateRotation = function (value) { + + mesh.animationObject.intermediateAngle += value; + + var done = false; + + if (mesh.animationObject.angleIncrement) { + /** + * We are rotating upwards + */ + if (mesh.animationObject.intermediateAngle >= mesh.animationObject.targetAngle) { + /** + * We need to stop + */ + done = true; + } + } else { + /** + * We are rotating downwards + */ + if (mesh.animationObject.intermediateAngle <= mesh.animationObject.targetAngle) { + /** + * We need to stop + */ + done = true; + } + } + + if (done) { + + /** + * We clamp to our target angle + */ + mesh.animationObject.intermediateAngle = mesh.animationObject.targetAngle; + + /** + * We limit our intermediate angle between values of -pi and pi + */ + while (mesh.animationObject.intermediateAngle > Math.PI) { + mesh.animationObject.intermediateAngle -= (Math.PI * 2); + } + + while (mesh.animationObject.intermediateAngle < -(Math.PI)) { + mesh.animationObject.intermediateAngle += (Math.PI * 2); + } + + /** + * We apply our new intermediate angle to our target + */ + mesh.animationObject.targetAngle = mesh.animationObject.intermediateAngle; + } + + /** + * Apply the actual rotation to the mesh + */ + mesh.updateInstanceRotationFromAxisAngle(mesh.quaternion.axis, mesh.animationObject.intermediateAngle); + + /** + * Check again if we are done, we need to do some additional work - + */ + if (done) { + + if (!mesh.animationObject.subscription) { + var message = 'mesh animation object subscription went missing for '; + message += mesh.name + ': '; + message += animation.name; + console.warn(message); + throw new Error(message); + } + + /** + * Stop subscribing to before render events + */ + mesh.animationObject.subscription.remove(); + + /** + * @type {null} + */ + mesh.animationObject.subscription = null; + + /** + * For some meshes, when we are done with the animation, we want to apply + * the current state of the mesh to the object data itself (i.e. update + * its vertices etc) + */ + if (animation.applyToMeshWhenDone) { + /** + * Now we say that our intermediate angle is zero, because we will apply our rotation + * and this will prevent us from re-registering a new 'animationRender' event + */ + mesh.animationObject.targetAngle = 0; + mesh.animationObject.intermediateAngle = 0; + + /** + * Apply our position, rotation and scale to the mesh + */ + mesh.applyPositionRotationScale(); + } + + /** + * Tell our animation component that it is no longer in process... + * @type {boolean} + */ + mesh.animationObject.inProcess = false; + + // if (mesh.animationObject.callbacks.length > 0) { + // var callback = mesh.animationObject.callbacks[0]; + // mesh.animationObject.callbacks.splice(0,1); + // callback(); + // } + + // mesh.animationObject.storedValues = []; + } + }; + } +}; + +GameLib.System.Animation.prototype.setQuaternionAngle = function(mesh, animation) { + + return function(value) { + return; + /** + * TODO: update this shit + */ + /** + * Check if we have work to do + */ + if (mesh.animationObject.intermediateAngle === value) { + + mesh.animationObject.inProcess = false; + + /** + * Nothing to do + */ + return; + } + + /** + * Check if we have another animation in process + */ + if (mesh.animationObject.inProcess && mesh.animationObject.blocking) { + + console.log('another animation is already in process'); + + // setTargetAngle(value); + + // GameLib.Utils.PushUnique(mesh.animationObject.storedValues, value); + // + // mesh.animationObject.callbacks.push( + // function(__value) { + // return function(){ + // mesh.quaternion.angle = __value; + // } + // }(value) + // ); + + /** + * Respond that our angle is actually our target angle (it will be that soon) + */ + return; + } + + /** + * We indicate that we now have an animation in process + * @type {boolean} + */ + mesh.animationObject.inProcess = true; + + /** + * Ok - all good - lets start the animation + */ + if (mesh.animationObject.intermediateAngle > value) { + /** + * We will rotate towards by decrementing + */ + mesh.animationObject.angleIncrement = false; + } else { + /** + * We will rotate towards by incrementing + */ + mesh.animationObject.angleIncrement = true; + } + + /** + * We say what our target angle is - when we reach our target angle, we want + * to stop our animation + */ + mesh.animationObject.targetAngle = value; + + /** + * Now we subscribe to 'before render' events, and slowly increment the value + * @type {{fn, remove}} + */ + mesh.animationObject.subscription = GameLib.Event.Subscribe( + GameLib.Event.BEFORE_RENDER, + function(data) { + + var increment = Math.abs(animation.rotationSpeed); + + if (!mesh.animationObject.angleIncrement) { + increment *= -1; + } + + animateRotation(data.delta * increment); + } + ); + } +}; + +GameLib.System.Animation.prototype.getQuaternionAxisX = function(mesh, animation) { + return function() { + return this.latest[mesh.id].quaternion.axis.x; + }.bind(this); +}; + +GameLib.System.Animation.prototype.setQuaternionAxisX = function(mesh, animation) { + return function(value) { + this.latest[mesh.id].quaternion.axis.x = value; + }.bind(this); +}; + +GameLib.System.Animation.prototype.getQuaternionAxisY = function(mesh, animation) { + return function() { + return this.latest[mesh.id].quaternion.axis.y; + }.bind(this); +}; + +GameLib.System.Animation.prototype.setQuaternionAxisY = function(mesh, animation) { + return function(value) { + this.latest[mesh.id].quaternion.axis.y = value; + }.bind(this); +}; + +GameLib.System.Animation.prototype.getQuaternionAxisZ = function(mesh, animation) { + return function() { + return this.latest[mesh.id].quaternion.axis.z; + }.bind(this); +}; + +GameLib.System.Animation.prototype.setQuaternionAxisZ = function(mesh, animation) { + return function(value) { + this.latest[mesh.id].quaternion.axis.z = value; + }.bind(this); +}; + +GameLib.System.Animation.prototype.getProperty = function(mesh, axis, property) { + return function() { + return this.latest[mesh.id][property][axis]; + }.bind(this); +}; + +GameLib.System.Animation.prototype.setProperty = function(mesh, animation, axis, property) { + return function(value) { + + var from = Number(this.latest[mesh.id][property][axis]); + + this.latest[mesh.id][property][axis] = value; + + this.animations[mesh.id].push( + { + type : property, + axis : axis, + from : from, + to : value, + animation : animation, + mesh : mesh + } + ); + + }.bind(this) +}; + +GameLib.System.Animation.prototype.getPositionX = function(mesh, axis) { + return function() { + return this.latest[mesh.id].position[axis]; + }.bind(this); +}; + +GameLib.System.Animation.prototype.setPositionX = function(mesh, animation) { + return function(value) { + this.latest[mesh.id].position.x = value; + }.bind(this); +}; + +GameLib.System.Animation.prototype.getPositionY = function(mesh, animation) { + return function() { + return this.latest[mesh.id].position.y; + }.bind(this); +}; + +GameLib.System.Animation.prototype.setPositionY = function(mesh, animation) { + return function(value) { + this.latest[mesh.id].position.y = value; + }.bind(this); +}; + +GameLib.System.Animation.prototype.getPositionZ = function(mesh, animation) { + return function() { + return this.latest[mesh.id].position.z; + }.bind(this); +}; + +GameLib.System.Animation.prototype.setPositionZ = function(mesh, animation) { + return function(value) { + this.latest[mesh.id].position.z = value; + }.bind(this) +}; + +GameLib.System.Animation.prototype.getScaleX = function(mesh, animation) { + return function() { + return this.latest[mesh.id].scale.x; + }.bind(this); +}; + +GameLib.System.Animation.prototype.setScaleX = function(mesh, animation) { + return function(value) { + this.latest[mesh.id].scale.x = value; + }.bind(this); +}; + +GameLib.System.Animation.prototype.getScaleY = function(mesh, animation) { + return function() { + return this.latest[mesh.id].scale.y; + }.bind(this); +}; + +GameLib.System.Animation.prototype.setScaleY = function(mesh, animation) { + return function(value) { + this.latest[mesh.id].scale.y = value; + }.bind(this); +}; + +GameLib.System.Animation.prototype.getScaleZ = function(mesh, animation) { + return function() { + return this.latest[mesh.id].scale.z; + }.bind(this); +}; + +GameLib.System.Animation.prototype.setScaleZ = function(mesh, animation) { + return function(value) { + this.latest[mesh.id].scale.z = value; + }.bind(this) +}; + /** * Stop Animation System */ @@ -293,50 +632,143 @@ GameLib.System.Animation.prototype.stop = function() { GameLib.System.prototype.stop.call(this); - this.animationEntities = GameLib.EntityManager.Instance.findEntities([GameLib.D3.Animation]); + this.beforeRenderSubscription.remove(); - this.animationEntities.map( - function(animationEntity) { + this.animationMeshAddedSubscription.remove(); - /** - * Get all meshes and all animation components - */ - var meshComponents = animationEntity.getComponents(GameLib.D3.Mesh); - var animationComponents = animationEntity.getComponents(GameLib.D3.Animation); + var meshes = GameLib.EntityManager.Instance.queryComponents(GameLib.D3.Mesh); - animationComponents.map( - function (animation) { + meshes.map( + function(mesh) { + if (mesh.backupQuaternionAngleDescriptor) { + Object.defineProperty( + mesh.quaternion, + 'angle', + mesh.backupQuaternionAngleDescriptor + ); - meshComponents.map( - function (mesh) { + delete mesh.backupQuaternionAngleDescriptor; + } - if (animation.rotationSpeed) { + if (mesh.backupQuaternionAxisXDescriptor) { + Object.defineProperty( + mesh.quaternion.axis, + 'x', + mesh.backupQuaternionAxisXDescriptor + ); - if (mesh.animationObject) { + delete mesh.backupQuaternionAxisXDescriptor; + } - if (mesh.animationObject.backupAngleDescriptor) { - Object.defineProperty( - mesh.quaternion, - 'angle', - mesh.animationObject.backupAngleDescriptor - ) - } + if (mesh.backupQuaternionAxisYDescriptor) { + Object.defineProperty( + mesh.quaternion.axis, + 'y', + mesh.backupQuaternionAxisYDescriptor + ); - if (mesh.animationObject.targetAngle) { - mesh.quaternion.angle = mesh.animationObject.targetAngle; - } + delete mesh.backupQuaternionAxisYDescriptor; + } - if (mesh.animationObject.subscription) { - mesh.animationObject.subscription.remove(); - } + if (mesh.backupQuaternionAxisZDescriptor) { + Object.defineProperty( + mesh.quaternion.axis, + 'z', + mesh.backupQuaternionAxisZDescriptor + ); - delete mesh.animationObject; - } - } - } - ) - } - ) + delete mesh.backupQuaternionAxisXDescriptor; + } + + if (mesh.backupRotationXDescriptor) { + Object.defineProperty( + mesh.rotation, + 'x', + mesh.backupRotationXDescriptor + ); + + delete mesh.backupRotationXDescriptor; + } + + if (mesh.backupRotationYDescriptor) { + Object.defineProperty( + mesh.rotation, + 'y', + mesh.backupRotationYDescriptor + ); + + delete mesh.backupRotationYDescriptor; + } + + if (mesh.backupRotationZDescriptor) { + Object.defineProperty( + mesh.rotation, + 'z', + mesh.backupRotationZDescriptor + ); + + delete mesh.backupRotationZDescriptor; + } + + if (mesh.backupPositionXDescriptor) { + Object.defineProperty( + mesh.position, + 'x', + mesh.backupPositionXDescriptor + ); + + delete mesh.backupPositionXDescriptor; + } + + if (mesh.backupPositionYDescriptor) { + Object.defineProperty( + mesh.position, + 'y', + mesh.backupPositionYDescriptor + ); + + delete mesh.backupPositionYDescriptor; + } + + if (mesh.backupPositionZDescriptor) { + Object.defineProperty( + mesh.position, + 'z', + mesh.backupPositionZDescriptor + ); + + delete mesh.backupPositionZDescriptor; + } + + if (mesh.backupScaleXDescriptor) { + Object.defineProperty( + mesh.scale, + 'x', + mesh.backupScaleXDescriptor + ); + + delete mesh.backupScaleXDescriptor; + } + + if (mesh.backupScaleYDescriptor) { + Object.defineProperty( + mesh.scale, + 'y', + mesh.backupScaleYDescriptor + ); + + delete mesh.backupScaleYDescriptor; + } + + if (mesh.backupScaleZDescriptor) { + Object.defineProperty( + mesh.scale, + 'z', + mesh.backupScaleZDescriptor + ); + + delete mesh.backupScaleZDescriptor; + } } ); }; diff --git a/src/game-lib-system-gui.js b/src/game-lib-system-gui.js index 265b3bf..5ef4168 100644 --- a/src/game-lib-system-gui.js +++ b/src/game-lib-system-gui.js @@ -193,7 +193,15 @@ GameLib.System.GUI.prototype.onChange = function(property, subProperty, affected component[property][subProperty] = value; - if (typeof component[property].updateInstance === 'function') { + if (component instanceof GameLib.D3.Mesh && property === 'rotation') { + component.useQuaternion = false; + } + + if (component instanceof GameLib.D3.Mesh && property === 'quaternion') { + component.useQuaternion = true; + } + + if (typeof component[property].updateInstance === 'function') { component[property].updateInstance(); } else if (typeof component[property][subProperty].updateInstance === 'function') { component[property][subProperty].updateInstance(); @@ -276,6 +284,7 @@ GameLib.System.GUI.prototype.buildQuaternionControl = function(folder, component ).name('quaternion.axis.x').onChange( function(value) { affected.map(function(component){ + component.useQuaternion = true; component[property]['axis'].x = Number(value); component.updateInstance(); }) @@ -291,6 +300,7 @@ GameLib.System.GUI.prototype.buildQuaternionControl = function(folder, component ).name('quaternion.axis.y').onChange( function(value) { affected.map(function(component){ + component.useQuaternion = true; component[property]['axis'].y = Number(value); component.updateInstance(); }) @@ -306,6 +316,7 @@ GameLib.System.GUI.prototype.buildQuaternionControl = function(folder, component ).name('quaternion.axis.z').onChange( function(value) { affected.map(function(component){ + component.useQuaternion = true; component[property]['axis'].z = Number(value); component.updateInstance(); }) @@ -335,7 +346,6 @@ GameLib.System.GUI.prototype.buildVectorControl = function(folder, componentTemp var controllers = []; - if (GameLib.Utils.isVector4(object[property])) { controllers.push(this.controller(folder, object, property, 'w', step, listen, affected)); }