diff --git a/gulpfile.js b/gulpfile.js index 3e4b80c..cb8c9f7 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -6,6 +6,7 @@ var plumber = require('gulp-plumber'); var istanbul = require('gulp-istanbul'); var mocha = require('gulp-mocha'); var watch = require('gulp-watch'); +var preprocessor = require('gulp-c-preprocessor'); gulp.task( 'build', @@ -58,10 +59,92 @@ gulp.task( } ); + +gulp.task('compileRuntime', ['build'], + function() { + gulp.src(['./build/defines/runtime.js', './build/game-lib.js']) + .pipe(concat('game-lib-runtime.js')) + .pipe(preprocessor( + { + // End of line character + endLine: '\n', + + // Escape '//#' & '/*#' comments (see extra/comments) + commentEscape: true, + + // Empty lines to add between code and included files + includeSpaces: 0, + + // Limit of empty following lines (0 = no limit) + emptyLinesLimit: 0, + + // Base path for including files + basePath: './', + + // Stop the compiler when an error ocurred ? + stopOnError: true, + + // Constants in #enum command must be in hexadecimal ? + enumInHex: true + + }) + ) + .pipe(minify({ + ext:{ + src:'.js', + min:'-min.js' + } + })) + .pipe(gulp.dest('./build/')); + } +); + +gulp.task('compileEditor', ['build'], + function() { + gulp.src(['./build/defines/editor.js', './build/game-lib.js']) + .pipe(concat('game-lib-editor.js')) + .pipe(preprocessor( + { + // End of line character + endLine: '\n', + + // Escape '//#' & '/*#' comments (see extra/comments) + commentEscape: true, + + // Empty lines to add between code and included files + includeSpaces: 0, + + // Limit of empty following lines (0 = no limit) + emptyLinesLimit: 0, + + // Base path for including files + basePath: './', + + // Stop the compiler when an error ocurred ? + stopOnError: true, + + // Constants in #enum command must be in hexadecimal ? + enumInHex: true + + }) + ) + .pipe(minify({ + ext:{ + src:'.js', + min:'-min.js' + } + })) + .pipe(gulp.dest('./build/')); + } +); + + gulp.task( 'default', [ - 'build' + 'build', + 'compileRuntime', + 'compileEditor' ], function() { return watch([ @@ -69,7 +152,9 @@ gulp.task( ], function() { gulp.start([ - 'build' + 'build', + 'compileRuntime', + 'compileEditor' ]); }) } diff --git a/src/game-lib-a.js b/src/game-lib-a.js index 7f7486e..686e6e0 100644 --- a/src/game-lib-a.js +++ b/src/game-lib-a.js @@ -19,9 +19,18 @@ if (typeof Q == 'undefined') { if (typeof _ == 'undefined') { if (typeof require == 'undefined') { - console.warn('You need the lowdash library for the GameLib.D3'); - throw new Error('You need the lowdash library for the GameLib.D3'); + console.warn('You need the lodash library for the GameLib.D3'); + throw new Error('You need the lodash library for the GameLib.D3'); } var _ = require('lodash'); } + + +//#ifdef RUNTIME__ +console.log("Loading runtime library..."); +//#endif + +//#ifdef EDITOR__ +console.log("Loading editor library..."); +//#endif \ No newline at end of file diff --git a/src/game-lib-component-colorlerp.js b/src/game-lib-component-colorlerp.js new file mode 100644 index 0000000..5a46a6c --- /dev/null +++ b/src/game-lib-component-colorlerp.js @@ -0,0 +1,66 @@ +GameLib.D3.ComponentColorLerp = function( + id, + name, + startColor, + endColor, + lerpSpeed +) { + this.id = id|| GameLib.D3.Tools.RandomId(); + if (typeof name == 'undefined') { + name = this.constructor.name; + } + this.name = name; + + this.parentEntity = null; + + // Todo: this should be executed somewhere in game-lib-z, so that we don't execute it on every construction of an object. + GameLib.D3.Utils.Extend(GameLib.D3.ComponentColorLerp, GameLib.D3.ComponentInterface); + + this.startColor = startColor || new GameLib.D3.Vector3(0, 0, 0); + this.endColor = endColor || new GameLib.D3.Vector3(1, 1, 1); + this.lerpSpeed = lerpSpeed || 1.0; + + this.lerpTarget = this.endColor; +}; + +///////////////////////// Methods to override ////////////////////////// +GameLib.D3.ComponentColorLerp.prototype.onUpdate = function( + deltaTime, + parentEntity +) { + var t = deltaTime * this.lerpSpeed; + // t = t * t * t * (t * (6.0 * t - 15.0) + 10.0); + + parentEntity.mesh.material.color.r = parentEntity.mesh.material.color.r + (this.endColor.x - parentEntity.mesh.material.color.r) * t; + parentEntity.mesh.material.color.g = parentEntity.mesh.material.color.g + (this.endColor.y - parentEntity.mesh.material.color.g) * t; + parentEntity.mesh.material.color.b = parentEntity.mesh.material.color.b + (this.endColor.z - parentEntity.mesh.material.color.b) * t; + + +/* if( parentEntity.mesh.material.color.r == this.endColor.x + && parentEntity.mesh.material.color.g == this.endColor.y + && parentEntity.mesh.material.color.b == this.endColor.z + ) { + + + console.error("switch target"); + //this.lerpTarget = this.startColor; + + } /!*else if (parentEntity.mesh.material.color.r == this.startColor.x + && parentEntity.mesh.material.color.g == this.startColor.y + && parentEntity.mesh.material.color.b == this.startColor.z) { + + this.lerpTarget = this.endColor; + }*!/*/ + +}; + +GameLib.D3.ComponentColorLerp.prototype.onSetParentEntity = function( + parentScene, + parentEntity +) { + parentEntity.mesh.material.color = new THREE.Color( + this.startColor.x, + this.startColor.y, + this.startColor.z + ); +}; \ No newline at end of file diff --git a/src/game-lib-component-fly-controls.js b/src/game-lib-component-fly-controls.js index 37103b7..42434b2 100644 --- a/src/game-lib-component-fly-controls.js +++ b/src/game-lib-component-fly-controls.js @@ -134,6 +134,47 @@ GameLib.D3.ComponentFlyControls.prototype.onSetParentEntity = function( document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock; } + // Swipe + + document.addEventListener('touchstart', function(evt) { + + component.xDown = evt.touches[0].clientX; + component.yDown = evt.touches[0].clientY; + + }, false); + + document.addEventListener('touchmove', function(evt) { + + if ( ! component.xDown || ! component.yDown ) { + return; + } + + var xUp = evt.touches[0].clientX; + var yUp = evt.touches[0].clientY; + + var xDiff = component.xDown - xUp; + var yDiff = component.yDown - yUp; + + if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/ + if ( xDiff > 0 ) { + /* left swipe */ + } else { + /* right swipe */ + } + } else { + if ( yDiff > 0 ) { + /* up swipe */ + } else { + /* down swipe */ + } + } + + component.yaw -= xDiff * 0.002; + component.pitch -= yDiff * 0.002; + + }, false); + + // Mouse move document.addEventListener('mousemove', function (event) { diff --git a/src/game-lib-component-mesh-permutation.js b/src/game-lib-component-mesh-permutation.js index 6ca117d..bc1740d 100644 --- a/src/game-lib-component-mesh-permutation.js +++ b/src/game-lib-component-mesh-permutation.js @@ -31,6 +31,7 @@ GameLib.D3.ComponentMeshPermutation = function ComponentMeshPermutation( GameLib.D3.Utils.Extend(GameLib.D3.ComponentMeshPermutation, GameLib.D3.ComponentInterface); }; +//#ifdef RUNTIME__ if(typeof THREE != "undefined") { ComponentMeshPermutation_quaternion = new THREE.Quaternion(); ComponentMeshPermutation_quaternionCopy = new THREE.Quaternion(); @@ -67,4 +68,5 @@ GameLib.D3.ComponentMeshPermutation.prototype.onLateUpdate = function( parentEntity.mesh.quaternion.copy(ComponentMeshPermutation_quaternion); parentEntity.mesh.scale.copy(ComponentMeshPermutation_scale); } -}; \ No newline at end of file +}; +//#endif \ No newline at end of file diff --git a/src/game-lib-component-path-controls.js b/src/game-lib-component-path-controls.js new file mode 100644 index 0000000..5f67ed7 --- /dev/null +++ b/src/game-lib-component-path-controls.js @@ -0,0 +1,130 @@ +/** + * + * @param id + * @param name + * @constructor + */ +GameLib.D3.ComponentPathControls = function ComponentPathFollowing( + id, + name +) { + this.id = id || GameLib.D3.Tools.RandomId(); + + if (typeof name == 'undefined') { + name = this.constructor.name; + } + + this.name = name; + this.parentEntity = null; + + // runtime + this.pathFollowingComponent = null; + this.keyLeftPressed = false; + this.keyRightPressed = false; + this.keyForwardPressed = false; + this.keyBackPressed = false; + this.keyBreakPressed = false; + + GameLib.D3.Utils.Extend(GameLib.D3.ComponentPathControls, GameLib.D3.ComponentInterface); +}; + +///////////////////////// Methods to override ////////////////////////// +GameLib.D3.ComponentPathControls.prototype.onUpdate = function( + deltaTime, + parentEntity +) { + + if (this.keyForwardPressed) { // Forward [i] + this.pathFollowingComponent.direction = 1; + } else if (this.keyBackPressed){ + this.pathFollowingComponent.direction = -1; + } else { + this.pathFollowingComponent.direction = 0; + } + + // left right + if (this.keyLeftPressed) { // Left [j] + this.pathFollowingComponent.offset.x = 0; + this.pathFollowingComponent.offset.y = 0; + this.pathFollowingComponent.offset.z = 1; + // also: set offset scale + } else if (this.keyRightPressed) { // Right [l] + this.pathFollowingComponent.offset.x = 0; + this.pathFollowingComponent.offset.y = 0; + this.pathFollowingComponent.offset.z = -1; + // also: set offset scale + } +}; + +GameLib.D3.ComponentPathControls.prototype.onSetParentEntity = function( + parentScene, + parentEntity +) { + + this.pathFollowingComponent = parentEntity.getComponent(GameLib.D3.ComponentPathFollowing); + if(!this.pathFollowingComponent) { + console.error("ComponentPathControls. NO PATH FOLLOWING COMPONENT"); + } + + var component = this; + + document.addEventListener('keydown', function(event) { + + if (event.keyCode == 73) { // Forward [i] + + component.keyForwardPressed = true; + + } else if (event.keyCode == 75) { // Back [k] + + component.keyBackPressed = true; + + } + + if (event.keyCode == 74) { // Left [j] + + component.keyLeftPressed = true; + + } else if (event.keyCode == 76) { // Right [l] + + component.keyRightPressed = true; + + } + + if (event.keyCode == 66) { + + component.keyBreakPressed = true; + + } + + }, false); + + document.addEventListener('keyup', function(event) { + + if (event.keyCode == 73) { // Forward [i] + + component.keyForwardPressed = false; + + } else if (event.keyCode == 75) { // Back [k] + + component.keyBackPressed = false; + + } + + if (event.keyCode == 74) { // Left [j] + + component.keyLeftPressed = false; + + } else if (event.keyCode == 76) { // Right [l] + + component.keyRightPressed = false; + + } + + if (event.keyCode == 66) { + + component.keyBreakPressed = false; + + } + + }, false); +}; \ No newline at end of file diff --git a/src/game-lib-component-path-following.js b/src/game-lib-component-path-following.js new file mode 100644 index 0000000..63d94ed --- /dev/null +++ b/src/game-lib-component-path-following.js @@ -0,0 +1,109 @@ +/** + * + * @param id + * @param name + * @param splineCurve3 + * @param accel + * @param maxSpeed + * @param baseOffset + * @constructor + */ +GameLib.D3.ComponentPathFollowing = function ComponentPathFollowing( + id, + name, + splineCurve3, + accel, + maxSpeed, + baseOffset +) { + this.id = id || GameLib.D3.Tools.RandomId(); + + if (typeof name == 'undefined') { + name = this.constructor.name; + } + + this.name = name; + this.parentEntity = null; + this.splineCurve3 = splineCurve3; + this.maxSpeed = maxSpeed || 10.0; + this.accel = accel || 2.0; + this.baseOffset = baseOffset || new GameLib.D3.Vector3(); + + + // runtime code + this.currentPathValue = 0.0; + this.offset = new GameLib.D3.Vector3(); + this.currentSpeed = 0.0; + this.direction = 0; + + + GameLib.D3.Utils.Extend(GameLib.D3.ComponentPathFollowing, GameLib.D3.ComponentInterface); +}; + +///////////////////////// Methods to override ////////////////////////// +GameLib.D3.ComponentPathFollowing.prototype.onUpdate = function( + deltaTime, + parentEntity +) { + + if(this.splineCurve3) { + + if(this.currentPathValue >= 1 || this.currentPathValue < 0) { + this.currentPathValue = 0; + } + + //To maintain a constant speed, you use .getPointAt( t ) instead of .getPoint( t ). + //http://stackoverflow.com/questions/18400667/three-js-object-following-a-spline-path-rotation-tanget-issues-constant-sp + + var position = this.splineCurve3.getPointAt(this.currentPathValue); + var rotation = this.splineCurve3.getTangentAt(this.currentPathValue).normalize(); + + var up = new THREE.Vector3(-1, 0, 0); + + var axis = new THREE.Vector3(); + axis.crossVectors(up, rotation).normalize(); + var radians = Math.acos(up.dot(rotation)); + var quaternion = new THREE.Quaternion().setFromAxisAngle( axis, radians ); + + // move the entity + var t = deltaTime * this.accel; + t = t * t * t * (t * (6.0 * t - 15.0) + 10.0); + this.currentSpeed = this.currentSpeed + (this.maxSpeed * this.direction - this.currentSpeed) * t; + + var transformedOffset = new THREE.Vector3( + this.baseOffset.x + this.offset.x, + this.baseOffset.y + this.offset.y, + this.baseOffset.z + this.offset.z + ).applyQuaternion(quaternion); + + // apply to parent rigidbody instead of direclty to the mesh. + parentEntity.position.x = position.x + transformedOffset.x; + parentEntity.position.y = position.y + transformedOffset.y; + parentEntity.position.z = position.z + transformedOffset.z; + + parentEntity.quaternion.x = quaternion.x; + parentEntity.quaternion.y = quaternion.y; + parentEntity.quaternion.z = quaternion.z; + parentEntity.quaternion.w = quaternion.w; + + console.log("this.currentSpeed", this.currentSpeed); + + this.currentPathValue += (this.currentSpeed); + + if(this.currentSpeed >= this.maxSpeed) { + this.currentSpeed = this.maxSpeed; + } else if (this.currentSpeed <= 0) { + this.currentSpeed = 0.0; + } + + } +}; + +GameLib.D3.ComponentPathFollowing.prototype.onSetParentEntity = function( + parentScene, + parentEntity +) { + if(!this.splineCurve3) { + console.error("NO PATH GIVEN"); + } +}; \ No newline at end of file diff --git a/src/game-lib-component-trigger-box-box.js b/src/game-lib-component-trigger-box-box.js index 5aaa2cd..ec44abc 100644 --- a/src/game-lib-component-trigger-box-box.js +++ b/src/game-lib-component-trigger-box-box.js @@ -31,10 +31,12 @@ GameLib.D3.ComponentTriggerBoxBox = function ComponentTriggerBoxBox( this.onLeave = onLeave || null; this.onSetParent = onSetParent || null; - // runtime code + // defines code this.entitiesInside = []; }; + +//#ifdef RUNTIME__ if(typeof THREE != "undefined") { ComponentTriggerBoxBox_BB = new THREE.Box3(); ComponentTriggerBoxBox_BBEntity = new THREE.Box3(); @@ -89,6 +91,8 @@ GameLib.D3.ComponentTriggerBoxBox.prototype.onUpdate = function( } } }; +//#endif + GameLib.D3.ComponentTriggerBoxBox.prototype.onSetParentEntity = function( parentScene, diff --git a/src/game-lib-component-trigger-box-sphere.js b/src/game-lib-component-trigger-box-sphere.js index ad76876..9a45a94 100644 --- a/src/game-lib-component-trigger-box-sphere.js +++ b/src/game-lib-component-trigger-box-sphere.js @@ -29,10 +29,11 @@ GameLib.D3.ComponentTriggerBoxSphere = function ComponentTriggerBoxSphere( this.onLeave = onLeave || null; this.onSetParent = onSetParent || null; - // runtime code + // defines code this.entitiesInside = []; }; +//#ifdef RUNTIME__ if(typeof THREE != "undefined") { ComponentTriggerBoxSphere_TargetPosition_Vec3 = new THREE.Vector3(); ComponentTriggerBoxSphere_TargetRadius = 0.0; @@ -115,6 +116,8 @@ GameLib.D3.ComponentTriggerBoxSphere.prototype.onUpdate = function( } }; +//#endif + GameLib.D3.ComponentTriggerBoxSphere.prototype.onSetParentEntity = function( parentScene, parentEntity diff --git a/src/game-lib-component-trigger-sphere-sphere.js b/src/game-lib-component-trigger-sphere-sphere.js index 0f68a81..960515c 100644 --- a/src/game-lib-component-trigger-sphere-sphere.js +++ b/src/game-lib-component-trigger-sphere-sphere.js @@ -41,10 +41,11 @@ GameLib.D3.ComponentTriggerSphereSphere = function ComponentTriggerSphereSphere( this.onLeave = onLeave || null; this.onSetParent = onSetParent || null; - // runtime code + // defines code this.entitiesInside = []; }; +//#ifdef RUNTIME__ if(typeof THREE != "undefined") { ComponentTriggerSphereSphere_spherePosition_Vector3 = new THREE.Vector3(); ComponentTriggerSphereSphere_targetBoundingSpherePosition_Vector3 = new THREE.Vector3(); @@ -126,6 +127,8 @@ GameLib.D3.ComponentTriggerSphereSphere.prototype.onUpdate = function( } }; +//#endif + GameLib.D3.ComponentTriggerSphereSphere.prototype.onSetParentEntity = function( parentScene, parentEntity diff --git a/src/game-lib-component-vehicle-ai-object-avoidance.js b/src/game-lib-component-vehicle-ai-object-avoidance.js new file mode 100644 index 0000000..b768544 --- /dev/null +++ b/src/game-lib-component-vehicle-ai-object-avoidance.js @@ -0,0 +1,570 @@ +/** + * + * @param id + * @param name + * @param physicsWorld + * @constructor + */ +GameLib.D3.ComponentVehicleAIObjectAvoidance = function( + id, + name, + physicsWorld +) { + this.id = id|| GameLib.D3.Tools.RandomId(); + if (typeof name == 'undefined') { + name = this.constructor.name; + } + this.name = name; + + this.parentEntity = null; + GameLib.D3.Utils.Extend(GameLib.D3.ComponentVehicleAIObjectAvoidance, GameLib.D3.ComponentInterface); + + this.raycastVehicleComponent = null; + this.physicsWorld = physicsWorld || null; + this.sensors = []; + + // debug + this.debugArrows = {}; + + + console.log("constructor for : ComponentVehicleAIObjectAvoidance"); +}; + +//#ifdef RUNTIME__ + +///////////////////////////////////////////////////////////////////////// +///////////////////////// Methods to override /////////////////////////// +///////////////////////////////////////////////////////////////////////// +GameLib.D3.ComponentVehicleAIObjectAvoidance.prototype.onSetParentEntity = function( + parentScene, + parentEntity +) { + this.parentEntity = parentEntity; + this.raycastVehicleComponent = parentEntity.getComponent(GameLib.D3.RaycastVehicle); + + console.log("onSetParentEntity for : ComponentVehicleAIObjectAvoidance"); + + + if(!this.raycastVehicleComponent) { + console.warn("NO RAYCAST VEHICLE FOUND!"); + } + + // create sensors + + var boundingBox = this.parentEntity.mesh.geometry.boundingBox; + // this is taken from the main.js. + // this should be configurable inside the editor + var carBoxScaleModifier = { x : 3 / 4, y : 1 / 2, z : 0.90 }; + + var sensorLength = 7.0; + var sensorColor = new THREE.Color(0, 0, 1); + + // . . . . . . . . . . FRONT . . . . . . . . . . + + // right + this.sensors.push( + { + sensorLength : sensorLength, + + sensorColor : sensorColor, + + sensorDirection : new THREE.Vector3( + 1, + 0, + 0 + ).normalize(), + + sensorPositionOffset : new THREE.Vector3( + boundingBox.max.x * this.parentEntity.mesh.scale.x * carBoxScaleModifier.x, + boundingBox.max.z * this.parentEntity.mesh.scale.z * carBoxScaleModifier.z, + 0//boundingBox.max.y * this.parentEntity.mesh.scale.y * carBoxScaleModifier.y // this is still swapped with y, because cannon. + ) + } + ); + + + // left + this.sensors.push( + { + sensorLength : sensorLength, + + sensorColor : sensorColor, + + sensorDirection : new THREE.Vector3( + 1, + 0, + 0 + ).normalize(), + + sensorPositionOffset : new THREE.Vector3( + boundingBox.max.x * this.parentEntity.mesh.scale.x * carBoxScaleModifier.x, + -boundingBox.max.z * this.parentEntity.mesh.scale.z * carBoxScaleModifier.z, + 0//boundingBox.max.y * this.parentEntity.mesh.scale.y * carBoxScaleModifier.y // this is still swapped with y, because cannon. + ) + } + ); + + + // center + this.sensors.push( + { + sensorLength : sensorLength, + + sensorColor : sensorColor, + + sensorDirection : new THREE.Vector3( + 1, + 0, + 0 + ).normalize(), + + sensorPositionOffset : new THREE.Vector3( + boundingBox.max.x * this.parentEntity.mesh.scale.x * carBoxScaleModifier.x, + 0, + 0 + ) + } + ); + + + // . . . . . . DIAGONAL FRONT . . . . . . . . + // right + this.sensors.push( + { + sensorLength : sensorLength, + + sensorColor : sensorColor, + + sensorDirection : new THREE.Vector3( + 0, + 1, + 0 + ).normalize(), + + sensorPositionOffset : new THREE.Vector3( + boundingBox.max.x * this.parentEntity.mesh.scale.x * carBoxScaleModifier.x, + boundingBox.max.z * this.parentEntity.mesh.scale.z * carBoxScaleModifier.z, + 0//boundingBox.max.y * this.parentEntity.mesh.scale.y * carBoxScaleModifier.y // this is still swapped with y, because cannon. + ) + } + ); + + + // left + this.sensors.push( + { + sensorLength : sensorLength, + + sensorColor : sensorColor, + + sensorDirection : new THREE.Vector3( + 0, + -1, + 0 + ).normalize(), + + sensorPositionOffset : new THREE.Vector3( + boundingBox.max.x * this.parentEntity.mesh.scale.x * carBoxScaleModifier.x, + -boundingBox.max.z * this.parentEntity.mesh.scale.z * carBoxScaleModifier.z, + 0//boundingBox.max.y * this.parentEntity.mesh.scale.y * carBoxScaleModifier.y // this is still swapped with y, because cannon. + ) + } + ); + + // right + this.sensors.push( + { + sensorLength : sensorLength, + + sensorColor : sensorColor, + + sensorDirection : new THREE.Vector3( + 0.5, + 0.5, + 0 + ).normalize(), + + sensorPositionOffset : new THREE.Vector3( + boundingBox.max.x * this.parentEntity.mesh.scale.x * carBoxScaleModifier.x, + boundingBox.max.z * this.parentEntity.mesh.scale.z * carBoxScaleModifier.z, + 0//boundingBox.max.y * this.parentEntity.mesh.scale.y * carBoxScaleModifier.y // this is still swapped with y, because cannon. + ) + } + ); + + + // left + this.sensors.push( + { + sensorLength : sensorLength, + + sensorColor : sensorColor, + + sensorDirection : new THREE.Vector3( + 0.5, + -0.5, + 0 + ).normalize(), + + sensorPositionOffset : new THREE.Vector3( + boundingBox.max.x * this.parentEntity.mesh.scale.x * carBoxScaleModifier.x, + -boundingBox.max.z * this.parentEntity.mesh.scale.z * carBoxScaleModifier.z, + 0//boundingBox.max.y * this.parentEntity.mesh.scale.y * carBoxScaleModifier.y // this is still swapped with y, because cannon. + ) + } + ); + + // right + this.sensors.push( + { + sensorLength : sensorLength, + + sensorColor : sensorColor, + + sensorDirection : new THREE.Vector3( + 0.75, + 0.25, + 0 + ).normalize(), + + sensorPositionOffset : new THREE.Vector3( + boundingBox.max.x * this.parentEntity.mesh.scale.x * carBoxScaleModifier.x, + boundingBox.max.z * this.parentEntity.mesh.scale.z * carBoxScaleModifier.z, + 0//boundingBox.max.y * this.parentEntity.mesh.scale.y * carBoxScaleModifier.y // this is still swapped with y, because cannon. + ) + } + ); + + + // left + this.sensors.push( + { + sensorLength : sensorLength, + + sensorColor : sensorColor, + + sensorDirection : new THREE.Vector3( + 0.75, + -0.25, + 0 + ).normalize(), + + sensorPositionOffset : new THREE.Vector3( + boundingBox.max.x * this.parentEntity.mesh.scale.x * carBoxScaleModifier.x, + -boundingBox.max.z * this.parentEntity.mesh.scale.z * carBoxScaleModifier.z, + 0//boundingBox.max.y * this.parentEntity.mesh.scale.y * carBoxScaleModifier.y // this is still swapped with y, because cannon. + ) + } + ); + + + console.log("pushed sensors", this.sensors.length); + +}; + +GameLib.D3.ComponentVehicleAIObjectAvoidance.prototype.onUpdate = function( + deltaTime, + parentEntity +) { + if(this.raycastVehicleComponent && this.physicsWorld) { + // debug + this.debugArrows.avgDirection = this.debugArrows.avgDirection || {}; + this.debugArrows.sensors = this.debugArrows.sensors || []; + + var vehicleVelocity = this.parentEntity.getComponent(GameLib.D3.RigidBody).instance.velocity; + var vehicleVelocityLength = 12; + + // shoot rays for each sensor & check collisions. + var world = this.physicsWorld.instance; + var result = new CANNON.RaycastResult(); + + this.debugArrows.avgDirection.vector = new THREE.Vector3(); + + for(var s = 0, l = this.sensors.length; s < l; ++s) { + + // debug + if(!this.debugArrows.sensors[s]) { + this.debugArrows.sensors[s] = {}; + } + + + var sensor = this.sensors[s]; + + var from = new THREE.Vector3( + this.parentEntity.position.x, + this.parentEntity.position.y, + this.parentEntity.position.z + ).add(new THREE.Vector3( + sensor.sensorPositionOffset.x, + sensor.sensorPositionOffset.y, + sensor.sensorPositionOffset.z + ).applyQuaternion(new THREE.Quaternion( + this.parentEntity.quaternion.x, + this.parentEntity.quaternion.y, + this.parentEntity.quaternion.z, + this.parentEntity.quaternion.w + ))); + + var fromC = new CANNON.Vec3( + from.x, + from.y, + from.z + ); + + var to = new THREE.Vector3( + sensor.sensorDirection.x, + sensor.sensorDirection.y, + sensor.sensorDirection.z + ).applyQuaternion(new THREE.Quaternion( + this.parentEntity.quaternion.x, + this.parentEntity.quaternion.y, + this.parentEntity.quaternion.z, + this.parentEntity.quaternion.w + )).normalize().multiplyScalar(sensor.sensorLength); + + + var toC = new CANNON.Vec3( + from.x + to.x, + from.y + to.y, + from.z + to.z + ); + + world.raycastClosest( + fromC, + + toC, + + { + collisionFilterMask : 2 // check only group 2 (track) + }, + + result + ); + + if(result.hasHit) { + sensor.sensorColor = new THREE.Color(1, 0, 0); + + var direction = new THREE.Vector3( + sensor.sensorDirection.x, + sensor.sensorDirection.y, + sensor.sensorDirection.z + ).applyQuaternion(new THREE.Quaternion( + this.parentEntity.quaternion.x, + this.parentEntity.quaternion.y, + this.parentEntity.quaternion.z, + this.parentEntity.quaternion.w + )).normalize(); + + /*var reflection = direction.reflect( + new THREE.Vector3( + result.hitNormalWorld.x, + result.hitNormalWorld.y, + result.hitNormalWorld.z + ) + ).normalize();*/ + + var reflection = direction.reflect( + new THREE.Vector3( + result.hitPointWorld.x, + result.hitPointWorld.y, + result.hitPointWorld.z + ).normalize().cross(direction) + ).normalize().negate(); + + var origin = new THREE.Vector3( + this.parentEntity.position.x, + this.parentEntity.position.y, + this.parentEntity.position.z + ).add(new THREE.Vector3( + sensor.sensorPositionOffset.x, + sensor.sensorPositionOffset.y, + sensor.sensorPositionOffset.z + ).applyQuaternion(new THREE.Quaternion( + this.parentEntity.quaternion.x, + this.parentEntity.quaternion.y, + this.parentEntity.quaternion.z, + this.parentEntity.quaternion.w + ))); + + var moveDirection = direction.add(reflection).setY(0).normalize(); + this.debugArrows.avgDirection.vector = this.debugArrows.avgDirection.vector.add(moveDirection); + + if(this.debugArrows.sensors[s].intersectionArrow && this.debugArrows.sensors[s].mesh) { + this.debugArrows.sensors[s].mesh.remove(this.debugArrows.sensors[s].intersectionArrow); + } + + if(this.debugArrows.sensors[s].mesh){ + + var arrow = new THREE.ArrowHelper( + + moveDirection, + + origin, + + 22, + + new THREE.Color(1, 1, 0) + ); + + this.debugArrows.sensors[s].intersectionArrow = arrow; + this.debugArrows.sensors[s].mesh.add(this.debugArrows.sensors[s].intersectionArrow); + } + + } else { + sensor.sensorColor = new THREE.Color(0, 0, 1); + + if(this.debugArrows.sensors[s].intersectionArrow && this.debugArrows.sensors[s].mesh) { + this.debugArrows.sensors[s].mesh.remove(this.debugArrows.sensors[s].intersectionArrow); + } + } + } + + + this.debugArrows.avgDirection.vector = this.debugArrows.avgDirection.vector.normalize(); + // draw the avg move direction + + /*if(!this.debugArrows.avgDirection.mesh) { + this.debugArrows.avgDirection.mesh = new THREE.Mesh( + new THREE.Geometry(), + new THREE.MeshBasicMaterial( + { + color : 0x00ffff, + wireframe : true + } + ) + ); + + sys.game.scenes["MainScene"].instance.add(this.debugArrows.avgDirection.mesh); + } + + if(this.debugArrows.avgDirection.arrow && this.debugArrows.avgDirection.mesh) { + this.debugArrows.avgDirection.mesh.remove(this.debugArrows.avgDirection.arrow); + }*/ + + /*this.debugArrows.avgDirection.arrow = new THREE.ArrowHelper( + this.debugArrows.avgDirection.vector, + + new THREE.Vector3( + this.parentEntity.position.x, + this.parentEntity.position.y, + this.parentEntity.position.z + ), + + 12, + + this.debugArrows.avgDirection.mesh.material.color + );*/ + + //this.debugArrows.avgDirection.mesh.add(this.debugArrows.avgDirection.arrow); + + // draw sensors + /*{ + for(var s = 0, l = this.sensors.length; s < l; ++s) { + + if(!this.debugArrows.sensors[s].mesh) { + + var geometry = new THREE.Geometry(); + var mesh = new THREE.Mesh( + geometry, + new THREE.MeshBasicMaterial( + { + color : 0x000000, + wireframe : true + } + ) + ); + + this.debugArrows.sensors[s].mesh = mesh; + sys.game.scenes["MainScene"].instance.add(this.debugArrows.sensors[s].mesh); + } + + // remove old arrow, if we have one + if(this.debugArrows.sensors[s].arrow) { + this.debugArrows.sensors[s].mesh.remove(this.debugArrows.sensors[s].arrow); + } + + + var sensor = this.sensors[s]; + var sensorLength = sensor.sensorLength; // should get this from the sensor itself + + this.debugArrows.sensors[s].arrow = new THREE.ArrowHelper( + new THREE.Vector3( + sensor.sensorDirection.x, + sensor.sensorDirection.y, + sensor.sensorDirection.z + ).applyQuaternion(new THREE.Quaternion( + this.parentEntity.quaternion.x, + this.parentEntity.quaternion.y, + this.parentEntity.quaternion.z, + this.parentEntity.quaternion.w + )).normalize(), + + new THREE.Vector3( + this.parentEntity.position.x, + this.parentEntity.position.y, + this.parentEntity.position.z + ).add(new THREE.Vector3( + sensor.sensorPositionOffset.x, + sensor.sensorPositionOffset.y, + sensor.sensorPositionOffset.z + ).applyQuaternion(new THREE.Quaternion( + this.parentEntity.quaternion.x, + this.parentEntity.quaternion.y, + this.parentEntity.quaternion.z, + this.parentEntity.quaternion.w + ))), + + sensorLength, + + sensor.sensorColor + ); + + this.debugArrows.sensors[s].mesh.add(this.debugArrows.sensors[s].arrow); + } + }*/ + + + // . . . . . . . . . . . . correct the path . . . . . . . . . . . . . . + if( this.debugArrows.avgDirection.vector.x != 0 + || this.debugArrows.avgDirection.vector.y != 0 + || this.debugArrows.avgDirection.vector.z != 0 + ) { + + // get forward direction OR better the velocity angle of the car + + var avgMoveVector = this.debugArrows.avgDirection.vector; + var forward = new THREE.Vector3( + 1, + 0, + 0 + ).applyQuaternion(new THREE.Quaternion( + this.parentEntity.quaternion.x, + this.parentEntity.quaternion.y, + this.parentEntity.quaternion.z, + this.parentEntity.quaternion.w + )).setY(0).normalize(); + + // get angle and steer. + var cos = avgMoveVector.dot(forward); + + var angleRadians = Math.acos(cos); + var steerAngleModifier = GameLib.D3.Vector3.AngleDirection( + forward, + avgMoveVector, + new GameLib.D3.Vector3( + 0, + 1, + 0 + ) + ); + + var steerAngle = Math.min(angleRadians, 0.5) * steerAngleModifier; + + this.raycastVehicleComponent.instance.setSteeringValue(steerAngle, 0); + this.raycastVehicleComponent.instance.setSteeringValue(steerAngle, 1); + } + + } +}; + +//#endif \ No newline at end of file diff --git a/src/game-lib-component-vehicle-ai-path-steering.js b/src/game-lib-component-vehicle-ai-path-steering.js new file mode 100644 index 0000000..981c3a4 --- /dev/null +++ b/src/game-lib-component-vehicle-ai-path-steering.js @@ -0,0 +1,244 @@ +/** + * + * @param id + * @param name + * @param targetEntity GameLib.D3.Entity + * @param steeringSpeed + * @param maxSteerAngle + * @constructor + */ +GameLib.D3.ComponentVehicleAIPathSteering = function( + id, + name, + targetEntity, + steeringSpeed, + maxSteerAngle +) { + this.id = id|| GameLib.D3.Tools.RandomId(); + if (typeof name == 'undefined') { + name = this.constructor.name; + } + this.name = name; + + this.parentEntity = null; + + // Todo: this should be executed somewhere in game-lib-z, so that we don't execute it on every construction of an object. + GameLib.D3.Utils.Extend(GameLib.D3.ComponentVehicleAIPathSteering, GameLib.D3.ComponentInterface); + + // + this.targetEntity = targetEntity; + this.steeringSpeed = steeringSpeed || 2.5; + this.maxSteerAngle = maxSteerAngle || 0.5; + + this.raycastVehicleComponent = null; + + this.debugArrows = {}; +}; + +///////////////////////// Methods to override ////////////////////////// + +GameLib.D3.ComponentVehicleAIPathSteering.prototype.onSetParentEntity = function( + parentScene, + parentEntity +) { + this.raycastVehicleComponent = parentEntity.getComponent(GameLib.D3.RaycastVehicle); + this.parentEntity = parentEntity; + + if(!this.raycastVehicleComponent) { + console.warn("NO RAYCAST VEHICLE FOUND!"); + } +}; + +//#ifdef RUNTIME__ +GameLib.D3.ComponentVehicleAIPathSteering.prototype.onUpdate = function( + deltaTime, + parentEntity +) { + + if(this.targetEntity && this.raycastVehicleComponent) { + + var v1 = new THREE.Vector3( + this.parentEntity.position.x, + this.parentEntity.position.y, + this.parentEntity.position.z + ); + + var v2 = new THREE.Vector3( + this.targetEntity.position.x, + this.targetEntity.position.y, + this.targetEntity.position.z + ); + + + // get forward vector of the car. + var v3 = new THREE.Vector3( + 1, + 0, + 0 + ); + + var q1 = new THREE.Quaternion( + this.parentEntity.quaternion.x, + this.parentEntity.quaternion.y, + this.parentEntity.quaternion.z, + this.parentEntity.quaternion.w + ); + + var q2 = new THREE.Quaternion( + this.targetEntity.quaternion.x, + this.targetEntity.quaternion.y, + this.targetEntity.quaternion.z, + this.targetEntity.quaternion.w + ); + + var v1ToV2 = v2.sub(v1); + var forward = v3.applyQuaternion(q1).normalize(); + + var v1v2cpy = new THREE.Vector3( + v1ToV2.x, + v1ToV2.y, + v1ToV2.z + ).applyQuaternion(q2).normalize(); + + v1v2cpy.y = 0; + v1v2cpy = v1v2cpy.normalize(); + + var forwardcpy = new THREE.Vector3( + forward.x, + 0, + forward.z + ).normalize(); + + var cos = v1v2cpy.dot(forwardcpy); + var angleRadians = Math.acos(cos); + var steerAngleModifier = GameLib.D3.Vector3.AngleDirection( + forwardcpy, + v1v2cpy, + new GameLib.D3.Vector3( + 0, + 1, + 0 + ) + ); + var steerAngle = Math.min(angleRadians, this.maxSteerAngle) * steerAngleModifier; + + if(isNaN(steerAngle)) { + console.log("NOT A NUMBER", steerAngle); + } + + // check if forward & movedirection are pointing in two different directions + /* if(cos < 0) { + console.log("movedirection & forward are pointing in different directions"); + }*/ + + //if(steerAngle > 0.002 || steerAngle < -0.002) { + this.raycastVehicleComponent.instance.setSteeringValue(steerAngle, 0); + this.raycastVehicleComponent.instance.setSteeringValue(steerAngle, 1); + //} + + /*// - - - - - - - - - - - DEBUG ARROW 1 - - - - - - - - - + { + if(!this.debugArrows.v1) { + this.debugArrows.v1 = this.debugArrows.v1 || {}; + } + + if(!this.debugArrows.v1.mesh) { + + var geometryTHREE = new THREE.Geometry(); + var wireframeMesh = new THREE.Mesh( + geometryTHREE, + new THREE.MeshBasicMaterial( + { + color: 0xff0000, + wireframe: true, + opacity: 1 + } + ) + ); + + this.debugArrows.v1.mesh = wireframeMesh; + + sys.game.scenes["MainScene"].instance.add(this.debugArrows.v1.mesh); + } + + if(this.debugArrows.v1.arrow) { + this.debugArrows.v1.mesh.remove(this.debugArrows.v1.arrow); + } + + var len = v1ToV2.length(); + this.debugArrows.v1.arrow = new THREE.ArrowHelper( + v1ToV2.normalize(), + + new THREE.Vector3( + this.parentEntity.position.x, + this.parentEntity.position.y, + this.parentEntity.position.z + ), + + len || 100, + + new THREE.Color( + 0, + 1, + 0 + ) + ); + + this.debugArrows.v1.mesh.add( this.debugArrows.v1.arrow ); + } + + // - - - - - - - - - - - DEBUG ARROW 2 - - - - - - - - - + { + if(!this.debugArrows.v2) { + this.debugArrows.v2 = this.debugArrows.v2 || {}; + } + + if(!this.debugArrows.v2.mesh) { + + var geometryTHREE = new THREE.Geometry(); + var wireframeMesh = new THREE.Mesh( + geometryTHREE, + new THREE.MeshBasicMaterial( + { + color: 0xff0000, + wireframe: true, + opacity: 1 + } + ) + ); + + this.debugArrows.v2.mesh = wireframeMesh; + + sys.game.scenes["MainScene"].instance.add(this.debugArrows.v2.mesh); + } + + if(this.debugArrows.v2.arrow) { + this.debugArrows.v2.mesh.remove(this.debugArrows.v2.arrow); + } + + this.debugArrows.v2.arrow = new THREE.ArrowHelper( + forward, + + new THREE.Vector3( + this.parentEntity.position.x, + this.parentEntity.position.y, + this.parentEntity.position.z + ), + + 12, + + new THREE.Color( + 1.0, + 0.0, + 0.0 + ) + ); + + this.debugArrows.v2.mesh.add( this.debugArrows.v2.arrow ); + }*/ + } + + this.raycastVehicleComponent.instance.applyEngineForce(-3500, 2); + this.raycastVehicleComponent.instance.applyEngineForce(-3500, 3); +}; +//#endif \ No newline at end of file diff --git a/src/game-lib-game.js b/src/game-lib-game.js index 8f70283..592b74f 100644 --- a/src/game-lib-game.js +++ b/src/game-lib-game.js @@ -26,8 +26,7 @@ GameLib.D3.Game.prototype.processPhysics = function ( GameLib.D3.Game.prototype.render = function( dt, - renderer, - camera + renderer ) { for(var s in this.scenes) { var scene = this.scenes[s]; @@ -45,7 +44,7 @@ GameLib.D3.Game.prototype.update = function( for(var w in scene.worlds) { var world = scene.worlds[w]; // NOTE: We are calling the step function with a variable timestep! - world.step(dt); + world.step(fixedDt, dt); } scene.update(dt); diff --git a/src/game-lib-vector-2.js b/src/game-lib-vector-2.js index 82b2873..8c493c5 100644 --- a/src/game-lib-vector-2.js +++ b/src/game-lib-vector-2.js @@ -1,15 +1,6 @@ GameLib.D3.Vector2 = function Vector2(x, y) { - - this.x = 0; - this.y = 0; - - if (x) { - this.x = x; - } - - if (y) { - this.y = y; - } + this.x = x || 0; + this.y = y || 0; }; GameLib.D3.Vector2.prototype.copy = function() { @@ -25,3 +16,95 @@ GameLib.D3.Vector2.prototype.equals = function(v) { ((this.y == v.x) && (this.x == v.y))); }; + +GameLib.D3.Vector2.prototype.add = function(v) { + return new GameLib.D3.Vector2( + this.x + v.x, + this.y + v.y + ); +}; + +GameLib.D3.Vector2.prototype.subtract = function(v) { + return new GameLib.D3.Vector2( + this.x - v.x, + this.y - v.y + ); +}; + +GameLib.D3.Vector2.prototype.multiply = function(v) { + if (v instanceof GameLib.D3.Vector2) { + return new GameLib.D3.Vector2( + this.x * v.x, + this.y * v.y + ); + } else if (isNumber(v)) { + return new GameLib.D3.Vector2( + this.x * v, + this.y * v + ); + } +}; + +GameLib.D3.Vector2.prototype.divide = function(v) { + if (v instanceof GameLib.D3.Vector2) { + return new GameLib.D3.Vector2( + this.x * (1.0 / v.x), + this.y * (1.0 / v.y) + ); + } else if (isNumber(v)) { + var invS = 1.0 / v; + return new GameLib.D3.Vector2( + this.x * invS, + this.y * invS + ); + } +}; + +GameLib.D3.Vector2.prototype.clone = function() { + return new GameLib.D3.Vector2( + this.x, + this.y + ); +}; + +GameLib.D3.Vector2.prototype.copy = function(v) { + this.x = v.x; + this.y = v.y; +}; + +GameLib.D3.Vector2.prototype.set = function(x, y) { + this.x = x; + this.y = y; +}; + +GameLib.D3.Vector2.prototype.clamp = function(min, max) { + return new GameLib.D3.Vector2( + Math.max(min.x, Math.min(max.x, this.x)), + Math.max(min.y, Math.min(max.y, this.y)) + ); +}; + +GameLib.D3.Vector2.prototype.length = function() { + return Math.sqrt(this.x * this.x + this.y * this.y); +}; + +GameLib.D3.Vector2.prototype.dot = function(v) { + return this.x * v.x + this.y * v.y; +}; + +GameLib.D3.Vector2.prototype.normalize = function() { + return this.multiply(1.0 / this.length()); +}; + +GameLib.D3.Vector2.prototype.angle = function() { + var angle = Math.atan2(this.y, this.x); + if ( angle < 0 ) angle += 2 * Math.PI; + return angle; +}; + +GameLib.D3.Vector2.prototype.lerp = function ( v, alpha ) { + return new GameLib.D3.Vector2( + this.x + ( v.x - this.x ) * alpha, + this.y + ( v.y - this.y ) * alpha + ); +}; \ No newline at end of file diff --git a/src/game-lib-vector-3.js b/src/game-lib-vector-3.js index 9598772..19fb1ff 100644 --- a/src/game-lib-vector-3.js +++ b/src/game-lib-vector-3.js @@ -1,35 +1,23 @@ GameLib.D3.Vector3 = function Vector3(x, y, z) { - - this.x = 0; - this.y = 0; - this.z = 0; - - if (x) { - this.x = x; - } - - if (y) { - this.y = y; - } - - if (z) { - this.z = z; - } + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; }; GameLib.D3.Vector3.prototype.subtract = function (v) { + return new GameLib.D3.Vector3( + this.x - v.x, + this.y - v.y, + this.z - v.z + ); +}; - if (v instanceof GameLib.D3.Vector3) { - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - } - - if (v instanceof GameLib.D3.Vector4) { - console.warn("trying to subtract vector of bigger length (4 vs 3))"); - } - - return this; +GameLib.D3.Vector3.prototype.sub = function (v) { + return new GameLib.D3.Vector3( + this.x - v.x, + this.y - v.y, + this.z - v.z + ); }; GameLib.D3.Vector3.prototype.cross = function (v) { @@ -40,21 +28,11 @@ GameLib.D3.Vector3.prototype.cross = function (v) { ); }; -GameLib.D3.Vector3.prototype.negative = function () { - this.x *= -1; - this.y *= -1; - this.z *= -1; - return this; -}; GameLib.D3.Vector3.clockwise = function (u, v, w, viewPoint) { - var normal = GameLib.D3.Vector3.normal(u, v, w); - var uv = u.copy(); - var winding = normal.dot(uv.subtract(viewPoint)); - return (winding > 0); }; @@ -65,10 +43,8 @@ GameLib.D3.Vector3.normal = function (u, v, w) { }; GameLib.D3.Vector3.prototype.lookAt = function (at, up) { - var lookAtMatrix = GameLib.D3.Matrix4.lookAt(this, at, up); - - this.multiply(lookAtMatrix); + return this.multiply(lookAtMatrix); }; GameLib.D3.Vector3.prototype.translate = function (v) { @@ -78,6 +54,14 @@ GameLib.D3.Vector3.prototype.translate = function (v) { return this; }; +GameLib.D3.Vector3.prototype.add = function (v) { + return new GameLib.D3.Vector3( + this.x + v.x, + this.y + v.y, + this.z + v.z + ); +}; + GameLib.D3.Vector3.prototype.squared = function () { return this.x * this.x + this.y * this.y + this.z * this.z; }; @@ -90,6 +74,12 @@ GameLib.D3.Vector3.prototype.copy = function () { ); }; +GameLib.D3.Vector3.prototype.set = function (x, y, z) { + this.x = x; + this.y = y; + this.z = z; +}; + GameLib.D3.Vector3.prototype.lerp = function ( v, alpha ) { return new GameLib.D3.Vector3( this.x + ( v.x - this.x ) * alpha, @@ -98,23 +88,77 @@ GameLib.D3.Vector3.prototype.lerp = function ( v, alpha ) { ); }; +GameLib.D3.Vector3.prototype.distanceTo = function(v) { + var dx = this.x - v.x, + dy = this.y - v.y, + dz = this.z - v.z; + return Math.sqrt(dx * dx + dy * dy + dz * dz); +}; + +/** + * @return {number} + */ +GameLib.D3.Vector3.AngleDirection = function(forward, directionToCheck, up) { + var perp = forward.cross(directionToCheck); + var dir = perp.dot(up); + + if (dir > 0.0) { + return 1.0; + } else if (dir < 0.0) { + return -1.0; + } else { + return 0.0; + } + +}; + GameLib.D3.Vector3.prototype.multiply = function (s) { if (s instanceof GameLib.D3.Vector3) { - this.x *= s.x; - this.y *= s.y; - this.z *= s.z; + + return new GameLib.D3.Vector3( + this.x * s.x, + this.y * s.y, + this.z * s.z + ) + } else if (s instanceof GameLib.D3.Matrix4) { + + var x = s.rows[0].x * this.x + s.rows[0].y * this.y + s.rows[0].z * this.z + s.rows[0].w; + var y = s.rows[1].x * this.x + s.rows[1].y * this.y + s.rows[1].z * this.z + s.rows[1].w; + var z = s.rows[2].x * this.x + s.rows[2].y * this.y + s.rows[2].z * this.z + s.rows[2].w; + + return new GameLib.D3.Vector3( + x, + y, + z + ); + + } else if (s instanceof GameLib.D3.Matrix3) { + var x = s.rows[0].x * this.x + s.rows[0].y * this.y + s.rows[0].z * this.z; var y = s.rows[1].x * this.x + s.rows[1].y * this.y + s.rows[1].z * this.z; var z = s.rows[2].x * this.x + s.rows[2].y * this.y + s.rows[2].z * this.z; - this.x = x; - this.y = y; - this.z = z; + + return new GameLib.D3.Vector3( + x, + y, + z + ); + + } else if(!isNaN(parseFloat(s)) && isFinite(s)) { + + return new GameLib.D3.Vector3( + this.x * s, + this.y * s, + this.z * s + ); + } else { + console.log("functionality not implemented - please do this"); throw new Error("not implemented"); + return this; } - return this; }; @@ -123,20 +167,73 @@ GameLib.D3.Vector3.prototype.dot = function (v) { }; GameLib.D3.Vector3.prototype.normalize = function () { - var EPSILON = 0.000001; - var v2 = this.squared(); if (v2 < EPSILON) { return this; //do nothing for zero vector } - var invLength = 1 / Math.sqrt(v2); + var invLength = 1.0 / Math.sqrt(v2); + return new GameLib.D3.Vector3( + this.x * invLength, + this.y * invLength, + this.z * invLength + ); +}; - this.x *= invLength; - this.y *= invLength; - this.z *= invLength; +GameLib.D3.Vector3.prototype.clone = function () { + return new GameLib.D3.Vector3( + this.x, + this.y, + this.z + ); +}; + +GameLib.D3.Vector3.prototype.applyQuaternion = function(q) { + var x = this.x, y = this.y, z = this.z; + var qx = q.x, qy = q.y, qz = q.z, qw = q.w; + + // calculate quat * vector + + var ix = qw * x + qy * z - qz * y; + var iy = qw * y + qz * x - qx * z; + var iz = qw * z + qx * y - qy * x; + var iw = - qx * x - qy * y - qz * z; + + // calculate result * inverse quat + + return new GameLib.D3.Vector3( + ix * qw + iw * - qx + iy * - qz - iz * - qy, + iy * qw + iw * - qy + iz * - qx - ix * - qz, + iz * qw + iw * - qz + ix * - qy - iy * - qx + ); +}; + +GameLib.D3.Vector3.prototype.clamp = function(min, max) { + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); + this.z = Math.max( min.z, Math.min( max.z, this.z ) ); return this; }; + +GameLib.D3.Vector3.prototype.negate = function() { + this.x = -this.x; + this.y = -this.y; + this.z = -this.z; + return this; +}; + +GameLib.D3.Vector3.prototype.length = function() { + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); +}; + +GameLib.D3.Vector3.prototype.reflect = function(normal) { + return this.sub( v1.copy( normal ).multiply( 2 * this.dot( normal ) ) ); +}; + +GameLib.D3.Vector3.prototype.angleTo = function (v) { + var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) ); + return Math.acos( exports.Math.clamp( theta, - 1, 1 ) ); +}; \ No newline at end of file diff --git a/src/game-lib-world.js b/src/game-lib-world.js index 1052503..07896d8 100644 --- a/src/game-lib-world.js +++ b/src/game-lib-world.js @@ -103,25 +103,24 @@ GameLib.D3.World.prototype.addVehicle = function( }; GameLib.D3.World.prototype.step = function( - timeStep + fixedStep, + dtStep ) { - // todo: figure out, why this call to internal step is more stable for trimesh collisions..... - //this.instance.internalStep(timeStep); - //return; - //console.log("this should not be called."); + this.instance.step(fixedStep, dtStep, 3); + return; - //var now = Date.now() / 1000; - var now = null; + var now = Date.now() / 1000.0; + //var now = null; if(!this.lastCallTime){ // last call time not saved, cant guess elapsed time. Take a simple step. - this.instance.step(timeStep); + this.instance.step(fixedStep); this.lastCallTime = now; return; } - var timeSinceLastCall = now - this.lastCallTime; + var timeSinceLastCall = (now - this.lastCallTime); - this.instance.step(timeStep, timeSinceLastCall); + this.instance.step(fixedStep, timeSinceLastCall, 4); this.lastCallTime = now; }; @@ -837,7 +836,7 @@ GameLib.D3.World.prototype.fixupTriangleMeshShape = function( // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Decide if we want to create new rigid bodies, or create a compound mesh + // Decide if we want to create new rigiwyd bodies, or create a compound mesh // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - for(var e in bFaceIndexed) { @@ -850,6 +849,10 @@ GameLib.D3.World.prototype.fixupTriangleMeshShape = function( } else { var body = new GameLib.D3.RigidBody(this.engine, 0, 12); + + //TODO: this is just a hack. + body.instance.collisionFilterGroup = 1 | 2; // puts this body in two groups. + body.addShape(shape); this.addRigidBody(body); }