r3-legacy/build/game-lib.js

6557 lines
174 KiB
JavaScript

if (typeof GameLib == 'undefined') {
function GameLib() {}
}
if (typeof GameLib.D3 == 'undefined') {
GameLib.D3 = function(){};
}
if (typeof Q == 'undefined') {
if (typeof require == 'undefined') {
console.warn('You need the Q promise library for the GameLib.D3');
throw new Error('You need the Q promise library for the GameLib.D3');
}
var Q = require('q');
}
/**
* BoneWeight object - associates a vertex to a bone with some weight
* @param boneIndex int
* @param weight float
* @constructor
*/
GameLib.D3.BoneWeight = function(
boneIndex,
weight
) {
this.boneIndex = boneIndex;
this.weight = weight;
};
/**
* Bone Superset
* @param id
* @param name string
* @param boneId
* @param childBoneIds
* @param parentBoneId
* @param quaternion
* @param position
* @param rotation
* @param scale GameLib.D3.Vector3
* @param up
* @constructor
*/
GameLib.D3.Bone = function(
id,
boneId,
name,
childBoneIds,
parentBoneId,
quaternion,
position,
rotation,
scale,
up
) {
this.id = id;
this.name = name;
this.boneId = boneId;
if (typeof childBoneIds == 'undefined') {
childBoneIds = [];
}
this.childBoneIds = childBoneIds;
if (typeof parentBoneId == 'undefined') {
parentBoneId = null;
}
this.parentBoneId = parentBoneId;
if (typeof quaternion == 'undefined') {
quaternion = new GameLib.D3.Vector4();
}
this.quaternion = quaternion;
if (typeof position == 'undefined') {
position = new GameLib.D3.Vector3(0,0,0);
}
this.position = position;
if (typeof rotation == 'undefined') {
rotation = new GameLib.D3.Vector3(0,0,0);
}
this.rotation = rotation;
if (typeof scale == 'undefined') {
scale = new GameLib.D3.Vector3(1,1,1);
}
this.scale = scale;
if (typeof up == 'undefined') {
up = new GameLib.D3.Vector3(0,1,0);
}
this.up = up;
};
/**
* Physics Broadphase Superset
* @param id
* @param name String
* @param broadphaseType Number
* @param engine GameLib.D3.Engine
* @constructor
*/
GameLib.D3.Broadphase = function(
id,
name,
broadphaseType,
engine
) {
this.id = id;
if (typeof name == 'undefined') {
name = 'broadphase-' + broadphaseType;
}
this.name = name;
if (typeof broadphaseType == 'undefined') {
broadphaseType = GameLib.D3.Broadphase.BROADPHASE_TYPE_NAIVE;
}
this.broadphaseType = broadphaseType;
this.engine = engine;
this.engine.isNotCannonThrow();
this.instance = this.createInstance();
};
/**
* Creates a custom Broadphase instance based on the engine type
*/
GameLib.D3.Broadphase.prototype.createInstance = function() {
var instance = null;
if (this.broadphaseType == GameLib.D3.Broadphase.BROADPHASE_TYPE_NAIVE) {
instance = new this.engine.instance.NaiveBroadphase();
} else if (this.broadphaseType == GameLib.D3.Broadphase.BROADPHASE_TYPE_GRID) {
instance = new this.engine.instance.GridBroadphase();
} else if (this.broadphaseType == GameLib.D3.Broadphase.BROADPHASE_TYPE_SAP) {
instance = new this.engine.instance.SAPBroadphase();
} else {
console.warn('Unsupported broadphase type: ' + this.broadphaseType);
throw new Error('Unsupported broadphase type: ' + this.broadphaseType);
}
return instance;
};
/**
* Broadphase Types
* @type {number}
*/
GameLib.D3.Broadphase.BROADPHASE_TYPE_NAIVE = 0x1;
GameLib.D3.Broadphase.BROADPHASE_TYPE_GRID = 0x2;
GameLib.D3.Broadphase.BROADPHASE_TYPE_SAP = 0x3;
/**
* Color Superset
* @param r
* @param g
* @param b
* @param a
* @constructor
*/
GameLib.D3.Color = function(r, g, b, a) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
};
GameLib.D3.ComponentCamera = function(
componentId,
threeCamera
) {
this.componentId = componentId || GameLib.D3.Tools.RandomId();
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.ComponentCamera, GameLib.D3.ComponentInterface);
// Component fields
this.threeCamera = threeCamera;
};
///////////////////////// Methods to override //////////////////////////
GameLib.D3.ComponentCamera.prototype.onLateUpdate = function(
deltaTime,
parentEntity
) {
this.threeCamera.quaternion.copy(parentEntity.quaternion);
this.threeCamera.position.copy(parentEntity.position);
};
GameLib.D3.ComponentColorFlash = function(
componentId
) {
this.componentId = componentId || GameLib.D3.Tools.RandomId();
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.ComponentColorFlash, GameLib.D3.ComponentInterface);
};
///////////////////////// Methods to override //////////////////////////
GameLib.D3.ComponentColorFlash.prototype.onUpdate = function(
deltaTime,
parentEntity
) {
this.parentEntity.mesh.material.color = new THREE.Color(Math.random(), Math.random(), Math.random());
};
GameLib.D3.ComponentColorFlash.prototype.onSetParentEntity = function(
parentScene,
parentEntity
) {
parentEntity.mesh.material = new THREE.MeshBasicMaterial();
};
GameLib.D3.ComponentFlyControls = function(
componentId
) {
this.componentId = componentId || GameLib.D3.Tools.RandomId();
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.ComponentFlyControls, GameLib.D3.ComponentInterface);
// Component fields
this.pitch = 0;
this.yaw = 0;
this.canRotate = false;
this.moveSpeed = 22.2;
this.moveForward = false;
this.moveBackward = false;
this.moveLeft = false;
this.moveRight = false;
this.moveUp = false;
this.moveDown = false;
};
///////////////////////// Methods to override //////////////////////////
GameLib.D3.ComponentFlyControls.prototype.onUpdate = function(
deltaTime,
parentEntity
) {
// Apply rotation
var rotation = new THREE.Euler(
this.pitch,
this.yaw,
0,
"YXZ"
);
var quat = new THREE.Quaternion().setFromEuler(
rotation
);
parentEntity.quaternion.x = quat.x;
parentEntity.quaternion.y = quat.y;
parentEntity.quaternion.z = quat.z;
parentEntity.quaternion.w = quat.w;
// Apply translation
var direction = new THREE.Vector3(0, 0, -1);
direction = direction.applyEuler(rotation).normalize();
if(this.moveForward) {
parentEntity.position.x += direction.x * (deltaTime * this.moveSpeed);
parentEntity.position.y += direction.y * (deltaTime * this.moveSpeed);
parentEntity.position.z += direction.z * (deltaTime * this.moveSpeed);
} else if(this.moveBackward) {
parentEntity.position.x -= direction.x * (deltaTime * this.moveSpeed);
parentEntity.position.y -= direction.y * (deltaTime * this.moveSpeed);
parentEntity.position.z -= direction.z * (deltaTime * this.moveSpeed);
}
if(this.moveLeft) {
var right = direction.cross(new THREE.Vector3(0, 1, 0));
parentEntity.position.x -= right.x * (deltaTime * this.moveSpeed);
parentEntity.position.y -= right.y * (deltaTime * this.moveSpeed);
parentEntity.position.z -= right.z * (deltaTime * this.moveSpeed);
} else if(this.moveRight) {
var right = direction.cross(new THREE.Vector3(0, 1, 0));
parentEntity.position.x += right.x * (deltaTime * this.moveSpeed);
parentEntity.position.y += right.y * (deltaTime * this.moveSpeed);
parentEntity.position.z += right.z * (deltaTime * this.moveSpeed);
}
// Absolute Y-Axis
if(this.moveUp) {
parentEntity.position.y += (deltaTime * this.moveSpeed);
} else if(this.moveDown) {
parentEntity.position.y -= (deltaTime * this.moveSpeed);
}
};
GameLib.D3.ComponentFlyControls.prototype.onSetParentEntity = function(
parentScene,
parentEntity
) {
var component = this;
// Lock cursor
var havePointerLock = 'pointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document;
var element = document.body;
document.addEventListener('click', function(event) {
if(havePointerLock) {
if(event.button == 0) {
component.canRotate = true;
element.requestPointerLock();
} else if(event.button == 2) {
component.canRotate = false;
document.exitPointerLock();
}
}
});
if(havePointerLock) {
element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;
document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock;
}
// Mouse move
document.addEventListener('mousemove', function (event) {
if(component.canRotate) {
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
component.yaw -= movementX * 0.002;
component.pitch -= movementY * 0.002;
}
// Save mouseCoords
sys.mouseCoords.set(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1
);
}, false);
// Hook up keyboard controls
document.addEventListener('keydown', function (event) {
switch (event.keyCode) {
case 87: // w
component.moveForward = true;
break;
case 65: // a
component.moveLeft = true;
break;
case 83: // s
component.moveBackward = true;
break;
case 68: // d
component.moveRight = true;
break;
case 32: // space
component.moveUp = true;
break;
case 16:
component.moveDown = true;
break;
}
}, false);
document.addEventListener('keyup', function (event) {
switch (event.keyCode) {
case 38: // up
case 87: // w
component.moveForward = false;
break;
case 37: // left
case 65: // a
component.moveLeft = false;
break;
case 40: // down
case 83: // s
component.moveBackward = false;
break;
case 39: // right
case 68: // d
component.moveRight = false;
break;
case 32: // space
component.moveUp = false;
break;
case 16:
component.moveDown = false;
break;
}
}, false);
};
GameLib.D3.ComponentFollow = function(
componentId,
targetEntity,
targetOffset,
minDistance
) {
this.componentId = componentId || GameLib.D3.Tools.RandomId();
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.ComponentFollow, GameLib.D3.ComponentInterface);
//
this.targetEntity = targetEntity;
this.moveSpeed = 2.5;
if(GameLib.D3.Utils.UndefinedOrNull(targetOffset)) {
targetOffset = new GameLib.D3.Vector3(0, 0, 0);
}
this.targetOffset = targetOffset;
this.minDistance = minDistance || 0;
};
///////////////////////// Methods to override //////////////////////////
GameLib.D3.ComponentFollow.prototype.onUpdate = function(
deltaTime,
parentEntity
) {
if(this.targetEntity) {
var target = new THREE.Vector3().copy(this.targetEntity.position);
target.x += this.targetOffset.x;
target.y += this.targetOffset.y;
target.z += this.targetOffset.z;
if(target.distanceTo(parentEntity.position) > this.minDistance) {
parentEntity.position = parentEntity.position.lerp(target, this.moveSpeed * deltaTime);
}
}
};
GameLib.D3.ComponentInterface = function(
componentId
) {
this.componentId = componentId || GameLib.D3.Tools.RandomId();
// this will maybe not used
this.parentEntity = null;
};
// will be not used
GameLib.D3.ComponentInterface.prototype.setParentEntity = function(
parentScene,
parentEntity
) {
this.parentEntity = parentEntity;
this.onSetParentEntity(parentScene, parentEntity);
};
GameLib.D3.ComponentInterface.prototype.update = function(
deltaTime,
parentEntity
) {
this.onUpdate(deltaTime, parentEntity);
};
GameLib.D3.ComponentInterface.prototype.lateUpdate = function(
deltaTime,
parentEntity
) {
this.onLateUpdate(deltaTime, parentEntity);
};
GameLib.D3.ComponentInterface.prototype.register = function(
parentScene
) {
this.onRegistered(parentScene);
};
///////////////////////// Methods to override //////////////////////////
GameLib.D3.ComponentInterface.prototype.onUpdate = function(
deltaTime,
parentEntity
) {
};
GameLib.D3.ComponentInterface.prototype.onLateUpdate = function(
deltaTime,
parentEntity
) {
};
GameLib.D3.ComponentInterface.prototype.onRegistered = function(
parentScene
) {
};
GameLib.D3.ComponentInterface.prototype.onSetParentEntity = function(
parentScene,
parentEntity
) {
};
GameLib.D3.ComponentLookAt = function(
componentId,
targetEntity,
targetOffset
) {
this.componentId = componentId || GameLib.D3.Tools.RandomId();
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.ComponentLookAt, GameLib.D3.ComponentInterface);
// todo: USE TARGET OFFSET.
this.targetEntity = targetEntity;
this.targetOffset = targetOffset || new GameLib.D3.Vector3(0, 0, 0);
};
///////////////////////// Methods to override //////////////////////////
GameLib.D3.ComponentLookAt.prototype.onUpdate = function(
deltaTime,
parentEntity
) {
if(this.targetEntity) {
var target = this.targetEntity.position;
var lookAtMatrix = new THREE.Matrix4().lookAt(
new THREE.Vector3(
parentEntity.position.x,
parentEntity.position.y,
parentEntity.position.z
),
new THREE.Vector3(
target.x,
target.y,
target.z
),
new THREE.Vector3(
0,
1,
0
)
);
var quaternion = new THREE.Quaternion().setFromRotationMatrix(lookAtMatrix);
this.parentEntity.quaternion.x = quaternion.x;
this.parentEntity.quaternion.y = quaternion.y;
this.parentEntity.quaternion.z = quaternion.z;
this.parentEntity.quaternion.w = quaternion.w;
}
};
GameLib.D3.ComponentMeshPermutation = function(
componentId,
positionOffset,
quaternionOffset,
scaleOffset
) {
this.componentId = componentId || GameLib.D3.Tools.RandomId();
this.parentEntity = null;
this.positionOffset = positionOffset || new GameLib.D3.Vector3(0, 0, 0);
this.quaternionOffset = quaternionOffset || new GameLib.D3.Quaternion(0, 0, 0, 1);
this.scaleOffset = scaleOffset || new GameLib.D3.Vector3(0, 0, 0);
// 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.ComponentMeshPermutation, GameLib.D3.ComponentInterface);
};
///////////////////////// Methods to override //////////////////////////
GameLib.D3.ComponentMeshPermutation.prototype.onLateUpdate = function(
deltaTime,
parentEntity
) {
if(parentEntity && parentEntity.mesh) {
var quaternion = new THREE.Quaternion();
quaternion.copy(parentEntity.mesh.quaternion);
var quaternionCopy = quaternion.clone();
var position = new THREE.Vector3();
position.copy(parentEntity.mesh.position);
var offsetQuaternion = new THREE.Quaternion();
offsetQuaternion.copy(this.quaternionOffset);
quaternion = quaternion.multiply(offsetQuaternion).normalize();
var offsetPosition = new THREE.Vector3();
offsetPosition.copy(this.positionOffset);
position = position.add(offsetPosition.applyQuaternion(quaternionCopy));
var scale = new THREE.Vector3();
scale.copy(parentEntity.mesh.scale);
var scaleOffset = new THREE.Vector3();
scaleOffset.copy(this.scaleOffset);
scale = scale.add(scaleOffset);
parentEntity.mesh.position.copy(position);
parentEntity.mesh.quaternion.copy(quaternion);
parentEntity.mesh.scale.copy(scale);
}
};
GameLib.D3.ComponentRaycastVehicleControls = function(
componentId,
frontLWheelIndex,
frontRWheelIndex,
backLWheelIndex,
backRWheelIndex,
maxForce,
steering
) {
this.componentId = componentId || GameLib.D3.Tools.RandomId();
this.parentEntity = null;
// maybe we pass this in the constructor
this.raycastVehicleComponent = null;
this.frontLWheelIndex = frontLWheelIndex || 0;
this.frontRWheelIndex = frontRWheelIndex || 1;
this.backLWheelIndex = backLWheelIndex || 2;
this.backRWheelIndex = backRWheelIndex || 3;
this.maxForce = maxForce || 400;
this.steering = steering || 0.5;
// 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.ComponentRaycastVehicleControls, GameLib.D3.ComponentInterface);
};
///////////////////////// Methods to override //////////////////////////
GameLib.D3.ComponentRaycastVehicleControls.prototype.onSetParentEntity = function(
parentScene,
parentEntity
) {
console.log("Set parent!");
this.raycastVehicleComponent = parentEntity.getComponent(GameLib.D3.RaycastVehicle);
if(!this.raycastVehicleComponent) {
console.warn("NO RAYCAST VEHICLE FOUND!");
} else {
var component = this;
document.addEventListener('keydown', function(event) {
if (event.keyCode == 73) { // Forward [i]
component.raycastVehicleComponent.instance.applyEngineForce(-component.maxForce, component.backLWheelIndex);
component.raycastVehicleComponent.instance.applyEngineForce(-component.maxForce, component.backRWheelIndex);
} else if (event.keyCode == 74) { // Left [j]
component.raycastVehicleComponent.instance.setSteeringValue(component.steering, component.frontLWheelIndex);
component.raycastVehicleComponent.instance.setSteeringValue(component.steering, component.frontRWheelIndex);
} else if (event.keyCode == 75) { // Back [k]
component.raycastVehicleComponent.instance.applyEngineForce(component.maxForce, component.backLWheelIndex);
component.raycastVehicleComponent.instance.applyEngineForce(component.maxForce, component.backRWheelIndex);
} else if (event.keyCode == 76) { // Right [l]
component.raycastVehicleComponent.instance.setSteeringValue(-component.steering, component.frontLWheelIndex);
component.raycastVehicleComponent.instance.setSteeringValue(-component.steering, component.frontRWheelIndex);
}
}, false);
document.addEventListener('keyup', function(event) {
if (event.keyCode == 73) { // Forward [i]
component.raycastVehicleComponent.instance.applyEngineForce(0, component.backLWheelIndex);
component.raycastVehicleComponent.instance.applyEngineForce(0, component.backRWheelIndex);
} else if (event.keyCode == 74) { // Left [j]
component.raycastVehicleComponent.instance.setSteeringValue(0, component.frontLWheelIndex);
component.raycastVehicleComponent.instance.setSteeringValue(0, component.frontRWheelIndex);
} else if (event.keyCode == 75) { // Back [k]
component.raycastVehicleComponent.instance.applyEngineForce(0, component.backLWheelIndex);
component.raycastVehicleComponent.instance.applyEngineForce(0, component.backRWheelIndex);
} else if (event.keyCode == 76) { // Right [l]
component.raycastVehicleComponent.instance.setSteeringValue(0, component.frontLWheelIndex);
component.raycastVehicleComponent.instance.setSteeringValue(0, component.frontRWheelIndex);
}
}, false);
}
};
/**
* Engine Superset
* @param engineType
* @param instance {CANNON | Ammo | Goblin}
* @constructor
*/
GameLib.D3.Engine = function(
engineType,
instance
) {
this.engineType = engineType;
this.instance = instance;
};
/**
* True if CANNON physics
* @returns {boolean}
*/
GameLib.D3.Engine.prototype.isCannon = function() {
return (this.engineType == GameLib.D3.Engine.ENGINE_TYPE_CANNON)
};
/**
* Logs a warning and throws an error if not cannon
*/
GameLib.D3.Engine.prototype.isNotCannonThrow = function() {
if (this.engineType != GameLib.D3.Engine.ENGINE_TYPE_CANNON) {
console.warn('Only CANNON supported for this function');
throw new Error('Only CANNON supported for this function');
}
};
/**
* True if Ammo physics
* @returns {boolean}
*/
GameLib.D3.Engine.prototype.isAmmo = function() {
return (this.engineType == GameLib.D3.Engine.ENGINE_TYPE_AMMO)
};
/**
* True if Goblin physics
* @returns {boolean}
*/
GameLib.D3.Engine.prototype.isGoblin = function() {
return (this.engineType == GameLib.D3.Engine.ENGINE_TYPE_GOBLIN)
};
/**
* Physics GameLib.D3.Engine Types
* @type {number}
*/
GameLib.D3.Engine.ENGINE_TYPE_CANNON = 0x1;
GameLib.D3.Engine.ENGINE_TYPE_AMMO = 0x2;
GameLib.D3.Engine.ENGINE_TYPE_GOBLIN = 0x3;
GameLib.D3.Entity = function(
meshId,
componentIds,
position,
quaternion,
scale
) {
this.meshId = meshId;
if (typeof componentIds == 'undefined') {
componentIds = [];
}
this.componentIds = componentIds;
// constructed at runtime
this.parentScene = null;
this.mesh = null;
if(GameLib.D3.Utils.UndefinedOrNull(position)) {
position = new GameLib.D3.Vector3(0, 0, 0);
}
this.position = position;
if(GameLib.D3.Utils.UndefinedOrNull(quaternion)) {
quaternion = new GameLib.D3.Vector4(0, 0, 0, 1);
}
this.quaternion = quaternion;
if(GameLib.D3.Utils.UndefinedOrNull(scale)) {
scale = new GameLib.D3.Vector3(1, 1, 1);
}
this.scale = scale;
};
/**
* Updates the Entity and it's components
* @param deltaTime Number
*/
GameLib.D3.Entity.prototype.update = function(
deltaTime
) {
for(var c in this.componentIds) {
var componentId = this.componentIds[c];
var component = this.parentScene.componentIdToComponent[componentId];
if(component && component.onUpdate) {
component.onUpdate(deltaTime, this);
}
}
if(this.mesh) {
this.mesh.position.set(this.position.x, this.position.y, this.position.z);
this.mesh.quaternion.set(this.quaternion.x, this.quaternion.y, this.quaternion.z, this.quaternion.w);
this.mesh.scale.set(this.scale.x, this.scale.y, this.scale.z);
}
this.onUpdate(deltaTime);
};
/**
* Late updates the Entity and it's components
* @param deltaTime Number
*/
GameLib.D3.Entity.prototype.lateUpdate = function(
deltaTime
) {
for(var c in this.componentIds) {
var componentId = this.componentIds[c];
var component = this.parentScene.componentIdToComponent[componentId];
if(component && component.onLateUpdate) {
component.onLateUpdate(deltaTime, this);
}
}
this.onLateUpdate(deltaTime);
};
/**
* Gets called when the entity was registered with it's parent scene
* @param parentScene GameLib.D3.Scene
*/
GameLib.D3.Entity.prototype.register = function(
parentScene
) {
this.parentScene = parentScene;
if(this.meshId != null && parentScene.meshIdToMesh[this.meshId]) {
parentScene.instance.add(parentScene.meshIdToMesh[this.meshId]);
this.mesh = parentScene.meshIdToMesh[this.meshId];
}
this.onRegistered(parentScene);
};
/**
* Add an already registered component to the entity
* @param componentId Number
*/
GameLib.D3.Entity.prototype.addComponentId = function(
componentId
) {
this.componentIds.push(componentId);
};
/**
* Adds a components to the entity and registers it with the entity's parent scene
* @param component GameLib.D3.ComponentInterface
*/
GameLib.D3.Entity.prototype.addComponent = function(
component
) {
this.parentScene.registerComponent(component);
this.componentIds.push(component.componentId);
if(component.setParentEntity && typeof component.setParentEntity == 'function') {
component.setParentEntity(this.parentScene, this);
}
};
GameLib.D3.Entity.prototype.getComponent = function(
componentType
) {
for(var c in this.componentIds) {
var componentId = this.componentIds[c];
var component = this.parentScene.componentIdToComponent[componentId];
if (component instanceof componentType) {
return component;
}
}
return null;
};
///////////////////////// Methods to override //////////////////////////
GameLib.D3.Entity.prototype.onUpdate = function(
deltaTime
) {
};
GameLib.D3.Entity.prototype.onLateUpdate = function(
deltaTime
) {
};
GameLib.D3.Entity.prototype.onRegistered = function(
parentScene
) {
};
/**
* Fly Controls
* @param camera
* @param THREE
* @param canvas
* @constructor
*/
GameLib.D3.FlyControls = function(
camera,
THREE,
canvas
) {
this.flySpeed = 100;
this.canvas = canvas;
this.THREE = THREE;
this.yaw = 0;
this.pitch = 0;
this.canRotate = false;
this.moveForward = false;
this.moveBackward = false;
this.moveLeft = false;
this.moveRight = false;
this.moveUp = false;
this.moveDown = false;
this.mouseUpCallback = this.onMouseUp.bind(this);
this.mouseDownCallback = this.onMouseDown.bind(this);
this.mouseMoveCallback = this.onMouseMove.bind(this);
this.mouseWheelCallback = this.onMouseWheel.bind(this);
this.keyDownCallback = this.onKeyDown.bind(this);
this.keyUpCallback = this.onKeyUp.bind(this);
this.camera = camera;
this.canvas.addEventListener('keydown', this.keyDownCallback, false);
this.canvas.addEventListener('keyup', this.keyUpCallback, false);
this.canvas.addEventListener('mousedown', this.mouseDownCallback, false);
this.canvas.addEventListener('mouseup', this.mouseUpCallback, false);
this.canvas.addEventListener('mousewheel', this.mouseWheelCallback, false);
this.havePointerLock = 'pointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document;
this.element = document.body;
if (this.havePointerLock) {
this.element.requestPointerLock = this.element.requestPointerLock || this.element.mozRequestPointerLock || this.element.webkitRequestPointerLock;
document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock;
}
};
/**
* Go forward / backward on mouse wheel
* @param event
*/
GameLib.D3.FlyControls.prototype.onMouseWheel = function(event) {
this.moveForward = true;
this.applyTranslation(event.wheelDelta * 0.001);
event.preventDefault();
this.moveForward = false;
};
/**
* Start rotating the camera on mouse middle button down
* @param event
*/
GameLib.D3.FlyControls.prototype.onMouseDown = function(event) {
if (event.button == 1) {
this.canRotate = true;
this.canvas.addEventListener('mousemove', this.mouseMoveCallback, false);
}
};
/**
* Stop rotating on middle mouse button down
* @param event
*/
GameLib.D3.FlyControls.prototype.onMouseUp = function(event) {
if (event.button == 1) {
this.canRotate = false;
this.canvas.removeEventListener('mousemove', this.mouseMoveCallback);
}
};
/**
* Apply current yaw and pitch to camera
*/
GameLib.D3.FlyControls.prototype.applyRotation = function() {
this.camera.rotation.set(this.pitch, this.yaw, 0, "YXZ");
};
/**
* Apply current position to camera
* @param deltaTime
*/
GameLib.D3.FlyControls.prototype.applyTranslation = function(deltaTime) {
var direction = new this.THREE.Vector3(0, 0, -1);
var rotation = new this.THREE.Euler(0, 0, 0, "YXZ");
rotation.set(this.pitch, this.yaw, 0, "YXZ");
direction = direction.applyEuler(rotation);
var forward = direction.normalize();
var right = forward.cross(new this.THREE.Vector3(0, 1, 0));
if(this.moveForward) {
this.camera.position.x += forward.x * (deltaTime * this.flySpeed);
this.camera.position.y += forward.y * (deltaTime * this.flySpeed);
this.camera.position.z += forward.z * (deltaTime * this.flySpeed);
} else if(this.moveBackward) {
this.camera.position.x -= forward.x * (deltaTime * this.flySpeed);
this.camera.position.y -= forward.y * (deltaTime * this.flySpeed);
this.camera.position.z -= forward.z * (deltaTime * this.flySpeed);
}
if(this.moveLeft) {
this.camera.position.x -= right.x * (deltaTime * this.flySpeed);
this.camera.position.y -= right.y * (deltaTime * this.flySpeed);
this.camera.position.z -= right.z * (deltaTime * this.flySpeed);
} else if(this.moveRight) {
this.camera.position.x += right.x * (deltaTime * this.flySpeed);
this.camera.position.y += right.y * (deltaTime * this.flySpeed);
this.camera.position.z += right.z * (deltaTime * this.flySpeed);
}
if(this.moveUp) {
this.camera.position.y += (deltaTime * this.flySpeed);
} else if(this.moveDown) {
this.camera.position.y -= (deltaTime * this.flySpeed);
}
};
/**
* This update function should be called from the animation function in order to apply the 'frame rate independent'
* movement to the camera
* @param deltaTime
*/
GameLib.D3.FlyControls.prototype.update = function(deltaTime) {
this.applyRotation();
this.applyTranslation(deltaTime);
};
/**
* Rotate on mouse move
* @param event
*/
GameLib.D3.FlyControls.prototype.onMouseMove = function ( event ) {
if (this.canRotate) {
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
this.yaw -= movementX * 0.002;
this.pitch -= movementY * 0.002;
}
};
/**
* Keyboard controls
* @param event
*/
GameLib.D3.FlyControls.prototype.onKeyDown = function ( event ) {
switch ( event.keyCode ) {
case 87: // w
this.moveForward = true;
break;
case 65: // a
this.moveLeft = true;
break;
case 83: // s
this.moveBackward = true;
break;
case 68: // d
this.moveRight = true;
break;
case 104: // keypad up arrow
this.moveUp = true;
break;
case 98: // keypad down arrow
this.moveDown = true;
break;
}
};
/**
* Keyboard controls
* @param event
*/
GameLib.D3.FlyControls.prototype.onKeyUp = function ( event ) {
switch ( event.keyCode ) {
case 38: // up
case 87: // w
this.moveForward = false;
break;
case 37: // left
case 65: // a
this.moveLeft = false;
break;
case 40: // down
case 83: // s
this.moveBackward = false;
break;
case 39: // right
case 68: // d
this.moveRight = false;
break;
case 104: // keypad up arrow
this.moveUp = false;
break;
case 98: // keypad down arrow
this.moveDown = false;
break;
}
};
GameLib.D3.Game = function (
) {
this.scenes = {};
};
GameLib.D3.Game.prototype.addScene = function(
scene,
identifer
) {
this.scenes[identifer] = scene;
};
GameLib.D3.Game.prototype.processPhysics = function (
dt
) {
for(var s in this.scenes) {
var scene = this.scenes[s];
for(var w in scene.worlds) {
var world = scene.worlds[w];
world.step(dt);
}
}
};
GameLib.D3.Game.prototype.render = function(
dt,
renderer,
camera
) {
for(var s in this.scenes) {
var scene = this.scenes[s];
scene.render(dt, renderer, camera);
}
};
GameLib.D3.Game.prototype.update = function(
dt,
fixedDt
) {
for(var s in this.scenes) {
var scene = this.scenes[s];
for(var w in scene.worlds) {
var world = scene.worlds[w];
world.step(fixedDt);
}
scene.update(dt);
scene.lateUpdate(dt);
}
};
/**
* Graphics Superset
* @param graphicsType
* @param instance {THREE}
* @constructor
*/
GameLib.D3.Graphics = function(
graphicsType,
instance
) {
this.graphicsType = graphicsType;
this.instance = instance;
};
/**
* True if THREE physics
* @returns {boolean}
*/
GameLib.D3.Graphics.prototype.isThree = function() {
return (this.graphicsType == GameLib.D3.Graphics.GRAPHICS_TYPE_THREE)
};
/**
* Logs a warning and throws an error if not cannon
*/
GameLib.D3.Graphics.prototype.isNotThreeThrow = function() {
if (this.graphicsType != GameLib.D3.Graphics.GRAPHICS_TYPE_THREE) {
console.warn('Only THREE supported for this function');
throw new Error('Only THREE supported for this function');
}
};
/**
* Physics GameLib.D3.Graphics Types
* @type {number}
*/
GameLib.D3.Graphics.GRAPHICS_TYPE_THREE = 0x1;
/**
* @param sizeX Number
* @param sizeY Number
* @param matrix matrix 2D Array with height data (Column Major)
* @param elementSize Number
* @param heightScale Number
* @constructor
*/
GameLib.D3.Heightmap = function(
sizeX,
sizeY,
matrix,
elementSize,
heightScale
) {
if (typeof sizeX == 'undefined') {
sizeX = 0;
}
this.sizeX = sizeX;
if (typeof sizeY == 'undefined') {
sizeY = 0;
}
this.sizeY = sizeY;
if (typeof matrix == 'undefined') {
matrix = [];
}
this.matrix = matrix;
if (typeof elementSize == 'undefined') {
elementSize = 10;
}
this.elementSize = elementSize;
if (typeof heightScale == 'undefined') {
heightScale = 15;
}
this.elementSize = heightScale;
};
/**
* Creates a graphics instance mesh from the graphics, physics and physics shape
* @param graphics GameLib.D3.Graphics
* @param shape GameLib.D3.Shape
* @param engine GameLib.D3.Engine
* @returns {THREE.Mesh|this.meshes}
*/
GameLib.D3.Heightmap.GenerateInstanceMesh = function(
graphics,
shape,
engine
) {
graphics.isNotThreeThrow();
engine.isNotCannonThrow();
var geometry = new graphics.instance.Geometry();
var v0 = new engine.instance.Vec3();
var v1 = new engine.instance.Vec3();
var v2 = new engine.instance.Vec3();
for (var xi = 0; xi < shape.instance.data.length - 1; xi++) {
for (var yi = 0; yi < shape.instance.data[xi].length - 1; yi++) {
for (var k = 0; k < 2; k++) {
shape.instance.getConvexTrianglePillar(xi, yi, k===0);
v0.copy(shape.instance.pillarConvex.vertices[0]);
v1.copy(shape.instance.pillarConvex.vertices[1]);
v2.copy(shape.instance.pillarConvex.vertices[2]);
v0.vadd(shape.instance.pillarOffset, v0);
v1.vadd(shape.instance.pillarOffset, v1);
v2.vadd(shape.instance.pillarOffset, v2);
geometry.vertices.push(
new graphics.instance.Vector3(v0.x, v0.y, v0.z),
new graphics.instance.Vector3(v1.x, v1.y, v1.z),
new graphics.instance.Vector3(v2.x, v2.y, v2.z)
);
var i = geometry.vertices.length - 3;
geometry.faces.push(new graphics.instance.Face3(i, i+1, i+2));
}
}
}
geometry.computeBoundingSphere();
geometry.computeFaceNormals();
return new graphics.instance.Mesh(
geometry,
new graphics.instance.MeshNormalMaterial(
{
wireframe: false,
shading : graphics.instance.SmoothShading
}
)
);
};
/**
* Needs an RGBA
* @param imagePath string
* @param heightScale
* @param callback
* @constructor
*/
GameLib.D3.Heightmap.GenerateHeightmapDataFromImage = function (
imagePath,
heightScale,
callback
) {
var img = new Image();
img.onload = function () {
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0);
var imgd = context.getImageData(0, 0, img.width, img.height);
var pixels = imgd.data;
var heightList = [];
for (var i = 0, n = pixels.length; i < n; i += (4)) {
heightList.push(pixels[i]);
}
var matrix = [];
var sizeX = img.width,
sizeY = img.height;
for (var x = 0; x < sizeX; x++) {
matrix.push([]);
for (var j = 0; j < sizeY; j++) {
var height = (heightList[(sizeX - x) + j * sizeY] / 255) * heightScale;
matrix[x].push(height);
}
}
// todo: delete canvas here
callback(
new GameLib.D3.Heightmap(
sizeX,
sizeY,
matrix,
10,
heightScale
)
);
};
img.src = imagePath;
};
/**
* Image
* @param id
* @param textureLink
* @param filename
* @param size
* @param contentType
* @constructor
*/
GameLib.D3.Image = function(
id,
textureLink,
filename,
size,
contentType
) {
this.id = id;
this.filename = filename;
this.textureLink = textureLink;
if (typeof size == 'undefined') {
size = 0;
}
this.size = size;
if (typeof contentType == 'undefined') {
contentType = 'application/octet-stream';
if (this.filename.match(/(png)$/i)) {
contentType = 'image/png';
}
if (this.filename.match(/(jpg|jpeg)$/i)) {
contentType = 'image/jpeg';
}
if (this.filename.match(/(gif)$/i)) {
contentType = 'image/gif';
}
}
this.contentType = contentType;
};
/**
* Light Superset
* @param id
* @param lightType
* @param name
* @param color
* @param intensity
* @param position
* @param targetPosition
* @param quaternion
* @param rotation
* @param scale
* @param distance
* @param decay
* @param power
* @param angle
* @param penumbra
* @constructor
*/
GameLib.D3.Light = function(
id,
lightType,
name,
color,
intensity,
position,
targetPosition,
quaternion,
rotation,
scale,
distance,
decay,
power,
angle,
penumbra
) {
this.id = id;
this.lightType = lightType;
this.name = name;
this.color = color;
this.intensity = intensity;
if (typeof position == 'undefined') {
position = new GameLib.D3.Vector3(0,0,0);
}
this.position = position;
if (typeof targetPosition == 'undefined') {
targetPosition = new GameLib.D3.Vector3(0,0,0);
}
this.targetPosition = targetPosition;
if (typeof quaternion == 'undefined'){
quaternion = new GameLib.D3.Vector4();
}
this.quaternion = quaternion;
if (typeof rotation == 'undefined'){
rotation = new GameLib.D3.Vector3(0,0,0);
}
this.rotation = rotation;
if (typeof scale == 'undefined'){
scale = new GameLib.D3.Vector3(1,1,1);
}
this.scale = scale;
if (typeof distance == 'undefined'){
distance = 0;
}
this.distance = distance;
if (typeof decay == 'undefined'){
decay = 1;
}
this.decay = decay;
if (typeof power == 'undefined'){
power = 4 * Math.PI;
}
this.power = power;
if (typeof angle == 'undefined'){
angle = Math.PI / 3;
}
this.angle = angle;
if (typeof penumbra == 'undefined'){
penumbra = 0;
}
this.penumbra = penumbra;
};
/**
* Material Superset
* @param id
* @param name
* @param materialType
* @param opacity
* @param side
* @param transparent
* @param maps
* @param specular
* @param lightMapIntensity
* @param aoMapIntensity
* @param color
* @param emissive
* @param emissiveIntensity
* @param combine
* @param shininess
* @param reflectivity
* @param refractionRatio
* @param fog
* @param wireframe
* @param wireframeLineWidth
* @param wireframeLineCap
* @param wireframeLineJoin
* @param vertexColors
* @param skinning
* @param morphTargets
* @param morphNormals
* @param lineWidth
* @param lineCap
* @param lineJoin
* @param dashSize
* @param gapWidth
* @param blending
* @param blendSrc
* @param blendDst
* @param blendEquation
* @param depthTest
* @param depthFunc
* @param depthWrite
* @param polygonOffset
* @param polygonOffsetFactor
* @param polygonOffsetUnits
* @param alphaTest
* @param clippingPlanes
* @param clipShadows
* @param visible
* @param overdraw
* @param shading
* @param bumpScale
* @param normalScale
* @param displacementScale
* @param displacementBias
* @param roughness
* @param metalness
* @param pointSize
* @param pointSizeAttenuation
* @param spriteRotation
* @param envMapIntensity
* @constructor
*/
GameLib.D3.Material = function(
id,
name,
materialType,
opacity,
side,
transparent,
maps,
specular,
lightMapIntensity,
aoMapIntensity,
color,
emissive,
emissiveIntensity,
combine,
shininess,
reflectivity,
refractionRatio,
fog,
wireframe,
wireframeLineWidth,
wireframeLineCap,
wireframeLineJoin,
vertexColors,
skinning,
morphTargets,
morphNormals,
lineWidth,
lineCap,
lineJoin,
dashSize,
gapWidth,
blending,
blendSrc,
blendDst,
blendEquation,
depthTest,
depthFunc,
depthWrite,
polygonOffset,
polygonOffsetFactor,
polygonOffsetUnits,
alphaTest,
clippingPlanes,
clipShadows,
visible,
overdraw,
shading,
bumpScale,
normalScale,
displacementScale,
displacementBias,
roughness,
metalness,
pointSize,
pointSizeAttenuation,
spriteRotation,
envMapIntensity
) {
this.id = id;
this.name = name;
if (typeof materialType == 'undefined') {
materialType = GameLib.D3.Material.TYPE_MESH_STANDARD;
}
this.materialType = materialType;
if (typeof opacity == 'undefined') {
opacity = 1.0;
}
this.opacity = opacity;
if (typeof side == 'undefined') {
side = GameLib.D3.Material.TYPE_FRONT_SIDE;
}
this.side = side;
if (typeof transparent == 'undefined') {
transparent = false;
}
this.transparent = transparent;
if (typeof maps == 'undefined') {
maps = {
alpha: null,
ao: null,
bump: null,
diffuse: null,
displacement: null,
emissive: null,
environment: null,
light: null,
metalness: null,
normal: null,
roughness: null,
specular: null
};
}
this.maps = maps;
if (typeof specular == 'undefined') {
specular = new GameLib.D3.Color(0.06, 0.06, 0.06, 0.06);
}
this.specular = specular;
if (typeof lightMapIntensity == 'undefined') {
lightMapIntensity = 1;
}
this.lightMapIntensity = lightMapIntensity;
if (typeof aoMapIntensity == 'undefined') {
aoMapIntensity = 1;
}
this.aoMapIntensity = aoMapIntensity;
if (typeof color == 'undefined') {
color = new GameLib.D3.Color(1, 1, 1, 1)
}
this.color = color;
if (typeof emissive == 'undefined') {
emissive = new GameLib.D3.Color(0, 0, 0, 0);
}
this.emissive = emissive;
if (typeof emissiveIntensity == 'undefined') {
emissiveIntensity = 1;
}
this.emissiveIntensity = emissiveIntensity;
if (typeof combine == 'undefined') {
combine = GameLib.D3.Material.TYPE_MULTIPLY_OPERATION;
}
this.combine = combine;
if (typeof shininess == 'undefined') {
shininess = 30;
}
this.shininess = shininess;
if (typeof reflectivity == 'undefined') {
reflectivity = 1;
}
this.reflectivity = reflectivity;
if (typeof refractionRatio == 'undefined') {
refractionRatio = 0.98;
}
this.refractionRatio = refractionRatio;
if (typeof fog == 'undefined') {
fog = true;
}
this.fog = fog;
if (typeof wireframe == 'undefined') {
wireframe = false;
}
this.wireframe = wireframe;
if (typeof wireframeLineWidth == 'undefined') {
wireframeLineWidth = 1;
}
this.wireframeLineWidth = wireframeLineWidth;
if (typeof wireframeLineCap == 'undefined') {
wireframeLineCap = 'round';
}
this.wireframeLineCap = wireframeLineCap;
if (typeof wireframeLineJoin == 'undefined') {
wireframeLineJoin = 'round';
}
this.wireframeLineJoin = wireframeLineJoin;
if (typeof vertexColors == 'undefined') {
vertexColors = GameLib.D3.Material.TYPE_NO_COLORS;
}
this.vertexColors = vertexColors;
if (typeof skinning == 'undefined') {
skinning = false;
}
this.skinning = skinning;
if (typeof morphTargets == 'undefined') {
morphTargets = false;
}
this.morphTargets = morphTargets;
if (typeof morphNormals == 'undefined') {
morphNormals = false;
}
this.morphNormals = morphNormals;
if (typeof overdraw == 'undefined') {
overdraw = 0;
}
this.overdraw = overdraw;
if (typeof lineWidth == 'undefined') {
lineWidth = 1;
}
this.lineWidth = lineWidth;
if (typeof lineCap == 'undefined') {
lineCap = 'round';
}
this.lineCap = lineCap;
if (typeof lineJoin == 'undefined') {
lineJoin = 'round';
}
this.lineJoin = lineJoin;
if (typeof dashSize == 'undefined') {
dashSize = 3;
}
this.dashSize = dashSize;
if (typeof gapWidth == 'undefined') {
gapWidth = 1;
}
this.gapWidth = gapWidth;
if (typeof blending == 'undefined') {
blending = GameLib.D3.Material.TYPE_NORMAL_BLENDING;
}
this.blending = blending;
if (typeof blendSrc == 'undefined') {
blendSrc = GameLib.D3.Material.TYPE_SRC_ALPHA_FACTOR;
}
this.blendSrc = blendSrc;
if (typeof blendDst == 'undefined') {
blendDst = GameLib.D3.Material.TYPE_ONE_MINUS_SRC_ALPHA_FACTOR;
}
this.blendDst = blendDst;
if (typeof blendEquation == 'undefined') {
blendEquation = GameLib.D3.Material.TYPE_ADD_EQUATION;
}
this.blendEquation = blendEquation;
if (typeof depthTest == 'undefined') {
depthTest = true;
}
this.depthTest = depthTest;
if (typeof depthFunc == 'undefined') {
depthFunc = GameLib.D3.Material.TYPE_LESS_EQUAL_DEPTH;
}
this.depthFunc = depthFunc;
if (typeof depthWrite == 'undefined') {
depthWrite = true;
}
this.depthWrite = depthWrite;
if (typeof polygonOffset == 'undefined') {
polygonOffset = false;
}
this.polygonOffset = polygonOffset;
if (typeof polygonOffsetFactor == 'undefined') {
polygonOffsetFactor = 1;
}
this.polygonOffsetFactor = polygonOffsetFactor;
if (typeof polygonOffsetUnits == 'undefined') {
polygonOffsetUnits = 1;
}
this.polygonOffsetUnits = polygonOffsetUnits;
if (typeof alphaTest == 'undefined') {
alphaTest = 0;
}
this.alphaTest = alphaTest;
if (typeof clippingPlanes == 'undefined') {
clippingPlanes = [];
}
this.clippingPlanes = clippingPlanes;
if (typeof clipShadows == 'undefined') {
clipShadows = false;
}
this.clipShadows = clipShadows;
if (typeof visible == 'undefined') {
visible = true;
}
this.visible = visible;
if (typeof shading == 'undefined') {
shading = GameLib.D3.Material.TYPE_FLAT_SHADING;
}
this.shading = shading;
if (typeof bumpScale == 'undefined') {
bumpScale = 1;
}
this.bumpScale = bumpScale;
if (typeof normalScale == 'undefined') {
normalScale = 1;
}
this.normalScale = normalScale;
if (typeof displacementScale == 'undefined') {
displacementScale = 1;
}
this.displacementScale = displacementScale;
if (typeof displacementBias == 'undefined') {
displacementBias = 0;
}
this.displacementBias = displacementBias;
if (typeof roughness == 'undefined') {
roughness = 0.5;
}
this.roughness = roughness;
if (typeof metalness == 'undefined') {
metalness = 0.5;
}
this.metalness = metalness;
if (typeof pointSize == 'undefined') {
pointSize = 1;
}
this.pointSize = pointSize;
if (typeof pointSizeAttenuation == 'undefined') {
pointSizeAttenuation = true;
}
this.pointSizeAttenuation = pointSizeAttenuation;
if (typeof spriteRotation == 'undefined') {
spriteRotation = 0;
}
this.spriteRotation = spriteRotation;
if (typeof envMapIntensity == 'undefined') {
envMapIntensity = 1.0;
}
this.envMapIntensity = envMapIntensity;
};
/**
* Combine Method
* @type {number}
*/
GameLib.D3.Material.TYPE_MULTIPLY_OPERATION = 0;
GameLib.D3.Material.TYPE_MIX_OPERATION = 1;
GameLib.D3.Material.TYPE_ADD_OPERATION = 2;
/**
* Vertex Color Mode
* @type {number}
*/
GameLib.D3.Material.TYPE_NO_COLORS = 0;
GameLib.D3.Material.TYPE_FACE_COLORS = 1;
GameLib.D3.Material.TYPE_VERTEX_COLORS = 2;
/**
* Blending Mode
* @type {number}
*/
GameLib.D3.Material.TYPE_NORMAL_BLENDING = 1;
GameLib.D3.Material.TYPE_ADDITIVE_BLENDING = 2;
GameLib.D3.Material.TYPE_SUBTRACTIVE_BLENDING = 3;
GameLib.D3.Material.TYPE_MULTIPLY_BLENDING = 4;
GameLib.D3.Material.TYPE_CUSTOM_BLENDING = 5;
/**
* Blend Source and Destination
* @type {number}
*/
GameLib.D3.Material.TYPE_ZERO_FACTOR = 200;
GameLib.D3.Material.TYPE_ONE_FACTOR = 201;
GameLib.D3.Material.TYPE_SRC_COLOR_FACTOR = 202;
GameLib.D3.Material.TYPE_ONE_MINUS_SRC_COLOR_FACTOR = 203;
GameLib.D3.Material.TYPE_SRC_ALPHA_FACTOR = 204;
GameLib.D3.Material.TYPE_ONE_MINUS_SRC_ALPHA_FACTOR = 205;
GameLib.D3.Material.TYPE_DST_ALPHA_FACTOR = 206;
GameLib.D3.Material.TYPE_ONE_MINUS_DST_ALPHA_FACTOR = 207;
GameLib.D3.Material.TYPE_DST_COLOR_FACTOR = 208;
GameLib.D3.Material.TYPE_ONE_MINUS_DST_COLOR_FACTOR = 209;
GameLib.D3.Material.TYPE_SRC_ALPHA_SATURATE_FACTOR = 210;
/**
* Blend Operation
* @type {number}
*/
GameLib.D3.Material.TYPE_ADD_EQUATION = 100;
GameLib.D3.Material.TYPE_SUBTRACT_EQUATION = 101;
GameLib.D3.Material.TYPE_REVERSE_SUBTRACT_EQUATION = 102;
GameLib.D3.Material.TYPE_MIN_EQUATION = 103;
GameLib.D3.Material.TYPE_MAX_EQUATION = 104;
/**
* Depth Function
* @type {number}
*/
GameLib.D3.Material.TYPE_NEVER_DEPTH = 0;
GameLib.D3.Material.TYPE_ALWAYS_DEPTH = 1;
GameLib.D3.Material.TYPE_LESS_DEPTH = 2;
GameLib.D3.Material.TYPE_LESS_EQUAL_DEPTH = 3;
GameLib.D3.Material.TYPE_EQUAL_DEPTH = 4;
GameLib.D3.Material.TYPE_GREATER_EQUAL_DEPTH = 5;
GameLib.D3.Material.TYPE_GREATER_DEPTH = 6;
GameLib.D3.Material.TYPE_NOT_EQUAL_DEPTH = 7;
/**
* Culling Mode
* @type {number}
*/
GameLib.D3.Material.TYPE_FRONT_SIDE = 0;
GameLib.D3.Material.TYPE_BACK_SIDE = 1;
GameLib.D3.Material.TYPE_DOUBLE_SIDE = 2;
/**
* Shading Type
* @type {number}
*/
GameLib.D3.Material.TYPE_FLAT_SHADING = 1;
GameLib.D3.Material.TYPE_SMOOTH_SHADING = 2;
/**
* Material Type
* @type {string}
*/
GameLib.D3.Material.TYPE_LINE_BASIC = "LineBasicMaterial";
GameLib.D3.Material.TYPE_LINE_DASHED = "LineDashedMaterial";
GameLib.D3.Material.TYPE_MESH_BASIC = "MeshBasicMaterial";
GameLib.D3.Material.TYPE_MESH_DEPTH = "MeshDepthMaterial";
GameLib.D3.Material.TYPE_MESH_LAMBERT = "MeshLambertMaterial";
GameLib.D3.Material.TYPE_MESH_NORMAL = "MeshNormalMaterial";
GameLib.D3.Material.TYPE_MESH_PHONG = "MeshPhongMaterial";
GameLib.D3.Material.TYPE_MESH_STANDARD = "MeshStandardMaterial";
GameLib.D3.Material.TYPE_POINTS = "PointsMaterial";
GameLib.D3.Material.TYPE_SPRITE = "SpriteMaterial";
GameLib.D3.Material.TYPE_MULTI_MATERIAL= "MultiMaterial";
/**
* Creates an instance Material from a GameLib.D3.Material
* @param gameLibMaterial GameLib.D3.Material
* @param graphics GameLib.D3.Graphics
* @param uploadUrl String
* @param progressCallback
*/
GameLib.D3.Material.createInstanceMaterial = function(
gameLibMaterial,
graphics,
uploadUrl,
progressCallback
) {
var defer = Q.defer();
var instanceMaterial = null;
var blenderMaps = [];
if (gameLibMaterial.materialType == GameLib.D3.Material.TYPE_MESH_STANDARD) {
instanceMaterial = new graphics.instance.MeshStandardMaterial({
name: gameLibMaterial.name,
opacity: gameLibMaterial.opacity,
transparent: gameLibMaterial.transparent,
blending: gameLibMaterial.blending,
blendSrc: gameLibMaterial.blendSrc,
blendDst: gameLibMaterial.blendDst,
blendEquation: gameLibMaterial.blendEquation,
depthTest: gameLibMaterial.depthTest,
depthFunc: gameLibMaterial.depthFunc,
depthWrite: gameLibMaterial.depthWrite,
polygonOffset: gameLibMaterial.polygonOffset,
polygonOffsetFactor: gameLibMaterial.polygonOffsetFactor,
polygonOffsetUnits: gameLibMaterial.polygonOffsetUnits,
alphaTest: gameLibMaterial.alphaTest,
clippingPlanes: gameLibMaterial.clippingPlanes,
clipShadows: gameLibMaterial.clipShadows,
overdraw: gameLibMaterial.overdraw,
visible: gameLibMaterial.visible,
side: gameLibMaterial.side,
color: new graphics.instance.Color(
gameLibMaterial.color.r,
gameLibMaterial.color.g,
gameLibMaterial.color.b
),
roughness: gameLibMaterial.roughness,
metalness: gameLibMaterial.metalness,
lightMapIntensity: gameLibMaterial.lightMapIntensity,
aoMapIntensity: gameLibMaterial.aoMapIntensity,
emissive: new graphics.instance.Color(
gameLibMaterial.emissive.r,
gameLibMaterial.emissive.g,
gameLibMaterial.emissive.b
),
emissiveIntensity: gameLibMaterial.emissiveIntensity,
bumpScale: gameLibMaterial.bumpScale,
normalScale: gameLibMaterial.normalScale,
displacementScale: gameLibMaterial.displacementScale,
refractionRatio: gameLibMaterial.refractionRatio,
fog: gameLibMaterial.fog,
shading: gameLibMaterial.shading,
wireframe: gameLibMaterial.wireframe,
wireframeLinewidth: gameLibMaterial.wireframeLineWidth,
wireframeLinecap: gameLibMaterial.wireframeLineCap,
wireframeLinejoin: gameLibMaterial.wireframeLineJoin,
vertexColors: gameLibMaterial.vertexColors,
skinning: gameLibMaterial.skinning,
morphTargets: gameLibMaterial.morphTargets,
morphNormals: gameLibMaterial.morphNormals
});
blenderMaps.push(
'diffuse',
'light',
'ao',
'emissive',
'bump',
'normal',
'displacement',
'roughness',
'metalness',
'alpha',
'environment'
);
} else if (gameLibMaterial.materialType == GameLib.D3.Material.TYPE_MESH_PHONG) {
instanceMaterial = new graphics.instance.MeshPhongMaterial({
name: gameLibMaterial.name,
opacity: gameLibMaterial.opacity,
transparent: gameLibMaterial.transparent,
blending: gameLibMaterial.blending,
blendSrc: gameLibMaterial.blendSrc,
blendDst: gameLibMaterial.blendDst,
blendEquation: gameLibMaterial.blendEquation,
depthTest: gameLibMaterial.depthTest,
depthFunc: gameLibMaterial.depthFunc,
depthWrite: gameLibMaterial.depthWrite,
polygonOffset: gameLibMaterial.polygonOffset,
polygonOffsetFactor: gameLibMaterial.polygonOffsetFactor,
polygonOffsetUnits: gameLibMaterial.polygonOffsetUnits,
alphaTest: gameLibMaterial.alphaTest,
clippingPlanes: gameLibMaterial.clippingPlanes,
clipShadows: gameLibMaterial.clipShadows,
overdraw: gameLibMaterial.overdraw,
visible: gameLibMaterial.visible,
side: gameLibMaterial.side,
color: new graphics.instance.Color(
gameLibMaterial.color.r,
gameLibMaterial.color.g,
gameLibMaterial.color.b
),
specular: new graphics.instance.Color(
gameLibMaterial.specular.r,
gameLibMaterial.specular.g,
gameLibMaterial.specular.b
),
shininess: gameLibMaterial.shininess,
lightMapIntensity: gameLibMaterial.lightMapIntensity,
aoMapIntensity: gameLibMaterial.aoMapIntensity,
emissive: new graphics.instance.Color(
gameLibMaterial.emissive.r,
gameLibMaterial.emissive.g,
gameLibMaterial.emissive.b
),
emissiveIntensity: gameLibMaterial.emissiveIntensity,
bumpScale: gameLibMaterial.bumpScale,
normalScale: gameLibMaterial.normalScale,
displacementScale: gameLibMaterial.displacementScale,
combine: gameLibMaterial.combine,
refractionRatio: gameLibMaterial.refractionRatio,
fog: gameLibMaterial.fog,
shading: gameLibMaterial.shading,
wireframe: gameLibMaterial.wireframe,
wireframeLinewidth: gameLibMaterial.wireframeLineWidth,
wireframeLinecap: gameLibMaterial.wireframeLineCap,
wireframeLinejoin: gameLibMaterial.wireframeLineJoin,
vertexColors: gameLibMaterial.vertexColors,
skinning: gameLibMaterial.skinning,
morphTargets: gameLibMaterial.morphTargets,
morphNormals: gameLibMaterial.morphNormals
});
blenderMaps.push(
'diffuse',
'light',
'ao',
'emissive',
'bump',
'normal',
'displacement',
'specular',
'alpha',
'environment'
);
} else {
console.log("material type is not implemented yet: " + gameLibMaterial.materialType + " - material indexes could be screwed up");
}
if (blenderMaps.length > 0) {
var textureMaps = GameLib.D3.Texture.loadMaps(
gameLibMaterial,
blenderMaps,
instanceMaterial,
graphics,
uploadUrl,
progressCallback
);
Q.all(textureMaps).then(
function(){
defer.resolve(instanceMaterial);
}
).catch(
function(error){
console.log(error);
defer.reject(error);
}
)
} else {
defer.resolve(instanceMaterial);
}
return defer.promise;
};
/**
* Matrix 3 Maths
* @param row0 GameLib.D3.Vector3
* @param row1 GameLib.D3.Vector3
* @param row2 GameLib.D3.Vector3
* @constructor
*/
GameLib.D3.Matrix3 = function(
row0,
row1,
row2
) {
this.identity();
if (row0) {
this.rows[0] = row0;
}
if (row1) {
this.rows[1] = row1;
}
if (row2) {
this.rows[2] = row2;
}
};
/**
* Set matrix to identity
*/
GameLib.D3.Matrix3.prototype.identity = function () {
this.rows = [
new GameLib.D3.Vector4(1, 0, 0),
new GameLib.D3.Vector4(0, 1, 0),
new GameLib.D3.Vector4(0, 0, 1)
];
};
GameLib.D3.Matrix4 = function(
row0,
row1,
row2,
row3
) {
this.identity();
if (row0) {
this.rows[0] = row0;
}
if (row1) {
this.rows[1] = row1;
}
if (row2) {
this.rows[2] = row2;
}
if (row3) {
this.rows[3] = row3;
}
};
GameLib.D3.Matrix4.prototype.rotationMatrixX = function (radians) {
this.identity();
this.rows[1] = new GameLib.D3.Vector4(0, Math.cos(radians), -1 * Math.sin(radians), 0);
this.rows[2] = new GameLib.D3.Vector4(0, Math.sin(radians), Math.cos(radians), 0);
return this;
};
GameLib.D3.Matrix4.prototype.rotationMatrixY = function (radians) {
this.identity();
this.rows[0] = new GameLib.D3.Vector4(
Math.cos(radians),
0,
Math.sin(radians),
0
);
this.rows[2] = new GameLib.D3.Vector4(
-1 * Math.sin(radians),
0,
Math.cos(radians),
0
);
return this;
};
GameLib.D3.Matrix4.prototype.rotationMatrixZ = function (radians) {
this.identity();
this.rows[0] = new GameLib.D3.Vector4(Math.cos(radians), -1 * Math.sin(radians), 0, 0);
this.rows[1] = new GameLib.D3.Vector4(Math.sin(radians), Math.cos(radians), 0, 0);
return this;
};
GameLib.D3.Matrix4.prototype.rotateX = function (radians, point) {
this.identity();
this.rotationMatrixX(radians);
return this.multiply(point);
};
GameLib.D3.Matrix4.prototype.rotateY = function (radians, point) {
this.identity();
this.rotationMatrixY(radians);
return this.multiply(point);
};
GameLib.D3.Matrix4.prototype.rotateZ = function (radians, point) {
this.identity();
this.rotationMatrixZ(radians);
return this.multiply(point);
};
GameLib.D3.Matrix4.prototype.multiply = function (mvp) {
if (mvp instanceof GameLib.D3.Vector4) {
return new GameLib.D3.Vector4(
this.rows[0].x * mvp.x + this.rows[0].y * mvp.y + this.rows[0].z * mvp.z + this.rows[0].w * mvp.w,
this.rows[1].x * mvp.x + this.rows[1].y * mvp.y + this.rows[1].z * mvp.z + this.rows[1].w * mvp.w,
this.rows[2].x * mvp.x + this.rows[2].y * mvp.y + this.rows[2].z * mvp.z + this.rows[2].w * mvp.w,
this.rows[3].x * mvp.x + this.rows[3].y * mvp.y + this.rows[3].z * mvp.z + this.rows[3].w * mvp.w
);
} else if (mvp instanceof GameLib.D3.Vector3) {
return new GameLib.D3.Vector3(
this.rows[0].x * mvp.x + this.rows[0].y * mvp.y + this.rows[0].z * mvp.z,
this.rows[1].x * mvp.x + this.rows[1].y * mvp.y + this.rows[1].z * mvp.z,
this.rows[2].x * mvp.x + this.rows[2].y * mvp.y + this.rows[2].z * mvp.z
);
}
};
GameLib.D3.Matrix4.prototype.identity = function () {
this.rows = [
new GameLib.D3.Vector4(1, 0, 0, 0),
new GameLib.D3.Vector4(0, 1, 0, 0),
new GameLib.D3.Vector4(0, 0, 1, 0),
new GameLib.D3.Vector4(0, 0, 0, 1)
];
};
GameLib.D3.Matrix4.prototype.lookAt = function (position, target, up) {
var pv = new GameLib.D3.Vector3(position.x, position.y, position.z);
var z = pv.subtract(target).normalize();
if (z.squared() === 0) {
z.z = 1;
}
var x = up.cross(z).normalize();
if (x.squared() === 0) {
z.x += 0.0001;
x = up.cross(z).normalize();
}
var y = z.cross(x);
this.rows[0].x = x.x;
this.rows[0].y = x.y;
this.rows[0].z = x.z;
this.rows[1].x = y.x;
this.rows[1].y = y.y;
this.rows[1].z = y.z;
this.rows[2].x = z.x;
this.rows[2].y = z.y;
this.rows[2].z = z.z;
return this;
// te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;
// te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;
// te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;
// var matrix4 = new Matrix4();
//
// matrix4.rows[0] = side.negative();
// matrix4.rows[1] = _up;
// matrix4.rows[2] = forward;
//
// matrix4.setColumn(0, side.negative());
// matrix4.setColumn(1, _up);
// matrix4.setColumn(2, forward);
//return matrix4;
// return new Matrix4(
// new Vector4(
// side.x,
// side.y,
// side.z,
// side.negative().dot(position)
// ),
// new Vector4(
// _up.x,
// _up.y,
// _up.z,
// _up.negative().dot(position)
// ),
// new Vector4(
// forward.negative().x,
// forward.negative().y,
// forward.negative().z,
// forward.dot(position)
// )
// )
};
/**
* Mesh Superset
* @param id
* @param path
* @param name
* @param meshType
* @param vertices
* @param faces
* @param skeleton
* @param faceVertexUvs
* @param skinIndices
* @param skinWeights
* @param materials
* @param position
* @param quaternion
* @param rotation
* @param scale
* @param up
* @param physics
* @param parentMeshId
* @param parentSceneId
* @constructor
*/
GameLib.D3.Mesh = function(
id,
path,
name,
meshType,
vertices,
faces,
skeleton,
faceVertexUvs,
skinIndices,
skinWeights,
materials,
position,
quaternion,
rotation,
scale,
up,
physics,
parentMeshId,
parentSceneId
) {
this.id = id;
this.meshId = GameLib.D3.Tools.RandomId();
this.path = path;
this.name = name;
this.meshType = meshType;
this.vertices = vertices;
this.faces = faces;
if (typeof skeleton == 'undefined') {
skeleton = null;
}
this.skeleton = skeleton;
if (typeof faceVertexUvs == 'undefined') {
faceVertexUvs = [];
}
this.faceVertexUvs = faceVertexUvs;
if (typeof skinIndices == 'undefined') {
skinIndices = [];
}
this.skinIndices = skinIndices;
if (typeof skinWeights == 'undefined') {
skinWeights = [];
}
this.skinWeights = skinWeights;
if (typeof materials == 'undefined') {
materials = [];
}
this.materials = materials;
if (typeof position == 'undefined') {
position = new GameLib.D3.Vector3(0,0,0);
}
this.position = position;
if (typeof quaternion == 'undefined') {
new GameLib.D3.Vector4();
}
this.quaternion = quaternion;
if (typeof rotation == 'undefined') {
rotation = new GameLib.D3.Vector3(0,0,0);
}
this.rotation = rotation;
if (typeof scale == 'undefined') {
scale = new GameLib.D3.Vector3(1,1,1);
}
this.scale = scale;
if (typeof up == 'undefined') {
up = new GameLib.D3.Vector3(0,1,0);
}
this.up = up;
this.physics = physics;
this.parentMeshId = parentMeshId;
this.parentSceneId = parentSceneId;
};
/**
* Mesh Type
* @type {number}
*/
GameLib.D3.Mesh.TYPE_NORMAL = 0;
GameLib.D3.Mesh.TYPE_SKINNED = 1;
/**
* Creates a THREE Mesh from GameLib.D3.Mesh
* @param gameLibMesh GameLib.D3.Mesh
* @param instanceGeometry
* @param instanceMaterial
* @param graphics
* @returns {*}
*/
GameLib.D3.Mesh.createInstanceMesh = function(gameLibMesh, instanceGeometry, instanceMaterial, graphics) {
var threeMesh = null;
if (gameLibMesh.meshType == GameLib.D3.Mesh.TYPE_NORMAL) {
threeMesh = new graphics.instance.Mesh(instanceGeometry, instanceMaterial);
}
if (gameLibMesh.meshType == GameLib.D3.Mesh.TYPE_SKINNED) {
var bones = gameLibMesh.skeleton.bones;
var skinIndices = gameLibMesh.skinIndices;
var skinWeights = gameLibMesh.skinWeights;
var threeBones = [];
for (var bi = 0; bi < bones.length; bi++) {
var bone = new graphics.instance.Bone();
bone.name = bones[bi].name;
bone.position.x = bones[bi].position.x;
bone.position.y = bones[bi].position.y;
bone.position.z = bones[bi].position.z;
bone.rotation.x = bones[bi].rotation.x;
bone.rotation.y = bones[bi].rotation.y;
bone.rotation.z = bones[bi].rotation.z;
bone.quaternion.x = bones[bi].quaternion.x;
bone.quaternion.y = bones[bi].quaternion.y;
bone.quaternion.z = bones[bi].quaternion.z;
bone.quaternion.w = bones[bi].quaternion.w;
bone.scale.x = bones[bi].scale.x;
bone.scale.y = bones[bi].scale.y;
bone.scale.z = bones[bi].scale.z;
bone.up.x = bones[bi].up.x;
bone.up.y = bones[bi].up.y;
bone.up.z = bones[bi].up.z;
threeBones.push(bone);
}
/**
* Setup the bone relationships
*/
for (var br = 0; br < bones.length; br++) {
for (var cbi = 0; cbi < bones[br].childBoneIds.length; cbi++) {
threeBones[br].add(threeBones[bones[br].childBoneIds[cbi]]);
}
}
/**
* Setup bones (indexes)
*/
for (var si = 0; si < skinIndices.length; si++) {
instanceGeometry.skinIndices.push(
new graphics.instance.Vector4(
skinIndices[si].x,
skinIndices[si].y,
skinIndices[si].z,
skinIndices[si].w
)
);
}
/**
* Setup bones (weights)
*/
for (var sw = 0; sw < skinWeights.length; sw++) {
instanceGeometry.skinWeights.push(
new graphics.instance.Vector4(
skinWeights[sw].x,
skinWeights[sw].y,
skinWeights[sw].z,
skinWeights[sw].w
)
);
}
threeMesh = new graphics.instance.SkinnedMesh(instanceGeometry, instanceMaterial);
var skeleton = new graphics.instance.Skeleton(threeBones);
skeleton.useVertexTexture = gameLibMesh.skeleton.useVertexTexture;
for (var i = 0; i < bones.length; i++) {
if (bones[i].parentBoneId === null) {
threeMesh.add(threeBones[i]);
break;
}
}
threeMesh.bind(skeleton);
threeMesh.pose();
threeMesh.skeleton.skeletonHelper = new graphics.instance.SkeletonHelper(threeMesh);
threeMesh.skeleton.skeletonHelper.material.linewidth = 5;
}
if (threeMesh == null) {
console.log('cannot handle meshes of type ' + gameLibMesh.meshType + ' yet.');
}
gameLibMesh.threeMeshId = threeMesh.id;
return threeMesh;
};
GameLib.D3.prototype.invertWindingOrder = function(triangles) {
for (var i = 0; i < triangles.length; i++) {
var v1 = triangles[i].v1;
triangles[i].v1 = triangles[i].v2;
triangles[i].v2 = v1;
var backupUV = triangles[i].triangle.v1uv;
triangles[i].triangle.v1uv = triangles[i].triangle.v2uv;
triangles[i].triangle.v2uv = backupUV;
}
return triangles;
};
/**
* This function resets a the winding order of a mesh from a reference point V (the average center of the mesh)
*/
GameLib.D3.prototype.resetWindingOrder = function(faces, vertices) {
var vertexList = new GameLib.D3.Vector3.Points();
for (var v = 0; v < vertices.length; v++) {
vertexList.add(new GameLib.D3.Vector3(
vertices[v].position.x,
vertices[v].position.y,
vertices[v].position.z
));
}
var V = vertexList.average();
var triangles = [];
for (var s = 0; s < faces.length; s += 3) {
var v0 = faces[s];
var v1 = faces[s+1];
var v2 = faces[s+2];
triangles.push(
{
v0 : v0,
v1 : v1,
v2 : v2,
edges : [
{v0: v0, v1: v1},
{v0: v1, v1: v2},
{v0: v2, v1: v0}
],
winding : 0,
edgeIndex : -1,
processed : false
}
);
}
for (var i = 0; i < triangles.length; i++) {
if (
GameLib.D3.Vector3.clockwise(
vertices[triangles[i].v0].position,
vertices[triangles[i].v1].position,
vertices[triangles[i].v2].position,
V
)
) {
console.log('clockwise');
var bv1 = triangles[i].v1;
triangles[i].v1 = triangles[i].v2;
triangles[i].v2 = bv1;
} else {
console.log('not clockwise');
}
}
return triangles;
};
/**
* This function resets the winding order for triangles in faces, given an initial triangle and orientation edge
* used pseudocode from
* http://stackoverflow.com/questions/17036970/how-to-correct-winding-of-triangles-to-counter-clockwise-direction-of-a-3d-mesh
* We need to use a graph traversal algorithm,
* lets assume we have method that returns neighbor of triangle on given edge
*
* neighbor_on_egde( next_tria, edge )
*
* to_process = set of pairs triangle and orientation edge, initial state is one good oriented triangle with any edge on it
* processed = set of processed triangles; initial empty
*
* while to_process is not empty:
* next_tria, orientation_edge = to_process.pop()
* add next_tria in processed
* if next_tria is not opposite oriented than orientation_edge:
* change next_tria (ABC) orientation (B<->C)
* for each edge (AB) in next_tria:
* neighbor_tria = neighbor_on_egde( next_tria, edge )
* if neighbor_tria exists and neighbor_tria not in processed:
* to_process add (neighbor_tria, edge opposite oriented (BA))
* @param faces GameLib.D3.TriangleFace[]
* @param orientationEdge GameLib.D3.Vector2
* @returns {Array}
*/
GameLib.D3.fixWindingOrder = function(faces, orientationEdge) {
/**
* Checks if a TriangleFace belonging to a TriangleEdge has already been processed
* @param processed TriangleEdge[]
* @param triangle TriangleFace
* @returns {boolean}
*/
function inProcessed(processed, triangle) {
for (var i = 0; i < processed.length; i++) {
if (processed[i].triangle.equals(triangle)) {
return true;
}
}
return false;
}
/**
* Returns a neighbouring triangle on a specific edge - preserving the edge orientation
* @param edge GameLib.D3.Vector2
* @param faces GameLib.D3.TriangleFace[]
* @param currentTriangle
* @returns {*}
*/
function neighbourOnEdge(edge, faces, currentTriangle) {
for (var i = 0; i < faces.length; i++) {
if (
(faces[i].v0 == edge.x && faces[i].v1 == edge.y) ||
(faces[i].v1 == edge.x && faces[i].v2 == edge.y) ||
(faces[i].v2 == edge.x && faces[i].v0 == edge.y) ||
(faces[i].v0 == edge.y && faces[i].v1 == edge.x) ||
(faces[i].v1 == edge.y && faces[i].v2 == edge.x) ||
(faces[i].v2 == edge.y && faces[i].v0 == edge.x)
) {
var triangle = new GameLib.D3.TriangleFace(
faces[i].v0,
faces[i].v1,
faces[i].v2,
faces[i].materialIndex,
faces[i].v0uv,
faces[i].v1uv,
faces[i].v2uv
);
if (triangle.equals(currentTriangle)) {
continue;
}
return new GameLib.D3.TriangleEdge(
triangle,
edge
);
}
}
return null;
}
var toProcess = [
new GameLib.D3.TriangleEdge(
new GameLib.D3.TriangleFace(
faces[0].v0,
faces[0].v1,
faces[0].v2,
faces[0].materialIndex,
faces[0].v0uv,
faces[0].v1uv,
faces[0].v2uv
),
orientationEdge
)
];
var processed = [];
while (toProcess.length > 0) {
var triangleEdge = toProcess.pop();
/**
* If edge is the same orientation (i.e. the edge order is the same as the given triangle edge) it needs to be reversed
* to have the same winding order)
*/
if (
(triangleEdge.triangle.v0 == triangleEdge.edge.x &&
triangleEdge.triangle.v1 == triangleEdge.edge.y) ||
(triangleEdge.triangle.v1 == triangleEdge.edge.x &&
triangleEdge.triangle.v2 == triangleEdge.edge.y) ||
(triangleEdge.triangle.v2 == triangleEdge.edge.x &&
triangleEdge.triangle.v0 == triangleEdge.edge.y)
) {
var backupV = triangleEdge.triangle.v1;
triangleEdge.triangle.v1 = triangleEdge.triangle.v2;
triangleEdge.triangle.v2 = backupV;
var backupUV = triangleEdge.triangle.v1uv;
triangleEdge.triangle.v1uv = triangleEdge.triangle.v2uv;
triangleEdge.triangle.v2uv = backupUV;
}
processed.push(triangleEdge);
var edges = [
new GameLib.D3.Vector2(
triangleEdge.triangle.v0,
triangleEdge.triangle.v1
),
new GameLib.D3.Vector2(
triangleEdge.triangle.v1,
triangleEdge.triangle.v2
),
new GameLib.D3.Vector2(
triangleEdge.triangle.v2,
triangleEdge.triangle.v0
)
];
for (var j = 0; j < edges.length; j++) {
var neighbour = neighbourOnEdge(edges[j], faces, triangleEdge.triangle);
if (neighbour && !inProcessed(processed, neighbour.triangle)) {
toProcess.push(neighbour);
}
}
}
/**
* In processed - we will have some duplicates - only add the unique ones
* @type {Array}
*/
var triangles = [];
for (var i = 0; i < processed.length; i++) {
var found = false;
for (var k = 0; k < triangles.length; k++) {
if (triangles[k].equals(processed[i].triangle)){
found = true;
break;
}
}
if (!found) {
triangles.push(processed[i].triangle);
}
}
return triangles;
};
/**
* This is a work-around function to fix polys which don't triangulate because
* they could lie on Z-plane (XZ or YZ)) - we translate the poly to the origin, systematically rotate the poly around
* Z then Y axis
* @param verticesFlat []
* @param grain is the amount to systematically rotate the poly by - a finer grain means a more accurate maximum XY
* @return []
*/
GameLib.D3.fixPolyZPlane = function(verticesFlat, grain) {
if ((verticesFlat.length % 3) != 0 && !(verticesFlat.length > 9)) {
console.log("The vertices are not in the right length : " + verticesFlat.length);
}
var vertices = [];
var points = new GameLib.D3.Vector4.Points();
for (var i = 0; i < verticesFlat.length; i += 3) {
points.add(new GameLib.D3.Vector3(
verticesFlat[i],
verticesFlat[i + 1],
verticesFlat[i + 2]
));
}
points.toOrigin();
points.maximizeXDistance(grain);
points.maximizeYDistance(grain);
for (i = 0; i < points.vectors.length; i++) {
vertices.push(
[
points.vectors[i].x,
points.vectors[i].y
]
);
}
return vertices;
};
/**
* Physics SuperSet Namespace Object
* @param id
* @param name
* @param engine GameLib.D3.Engine
* @param worlds
* @returns {{World: World}}
* @constructor
*/
GameLib.D3.Physics = function(
id,
name,
engine,
worlds
) {
this.id = id;
this.name = name;
this.engine = engine;
if (typeof worlds == 'undefined') {
worlds = [];
}
this.worlds = worlds;
};
/**
* Solver Types
* @type {number}
*/
GameLib.D3.Physics.SPLIT_SOLVER = 0x1;
GameLib.D3.Physics.GS_SOLVER = 0x2;
/**
* Contains a Poly vertex data structure
* @param localIndex
* @param mvertIndex
* @param uv GameLib.D3.Vector2
* @param materialIndex
* @param edgeIndex
* @constructor
*/
GameLib.D3.PolyVertex = function(
localIndex,
mvertIndex,
uv,
materialIndex,
edgeIndex
) {
this.localIndex = localIndex;
this.mvertIndex = mvertIndex;
this.uv = uv;
this.materialIndex = materialIndex;
this.edgeIndex = edgeIndex;
};
/**
* Clone a PolyVertex
* @returns {GameLib.D3.PolyVertex}
*/
GameLib.D3.PolyVertex.prototype.clone = function() {
return new GameLib.D3.PolyVertex(
this.localIndex,
this.mvertIndex,
this.uv.copy(),
this.materialIndex,
this.edgeIndex
)
};
/**
* Raycast Vehicles :)
* @param engine GameLib.D3.Engine
* @param chassisBody GameLib.D3.RigidBody
* @param wheels GameLib.D3.RaycastWheel[]
* @constructor
*/
GameLib.D3.RaycastVehicle = function(
engine,
chassisBody,
wheels,
wheelBodies
) {
this.engine = engine;
this.engine.isNotCannonThrow();
this.componentId = GameLib.D3.Tools.RandomId();
this.chassisBody = chassisBody;
if (typeof wheels == 'undefined') {
wheels = [];
}
this.wheels = wheels;
if(GameLib.D3.Utils.UndefinedOrNull(wheelBodies)) {
wheelBodies = [];
}
this.wheelBodies = wheelBodies;
this.instance = this.createInstance();
GameLib.D3.Utils.Extend(GameLib.D3.RaycastVehicle, GameLib.D3.ComponentInterface);
};
/**
* private
* @returns {GameLib.D3.RaycastVehicle|GameLib.D3.Physics.RaycastVehicle|*}
*/
GameLib.D3.RaycastVehicle.prototype.createInstance = function() {
return new this.engine.instance.RaycastVehicle({
chassisBody: this.chassisBody.instance
});
};
/**
* Adds a raycast wheel to this vehicle
* @param wheel GameLib.D3.RaycastWheel
* @param wheelRigidBody GameLib.D3.RigidBody
*/
GameLib.D3.RaycastVehicle.prototype.addWheel = function (
wheel,
wheelRigidBody
) {
this.wheels.push(wheel);
this.wheelBodies.push(wheelRigidBody);
wheel.wheelIndex = this.instance.addWheel(wheel.instance);
};
/**
* Returns updated wheel info
* @returns {*}
* @constructor
*/
GameLib.D3.RaycastVehicle.prototype.getWheelInfo = function() {
return this.instance.wheelInfos;
};
// Override component methods //
GameLib.D3.RaycastVehicle.prototype.onUpdate = function(
deltaTime,
parentEntity
) {
for (var i = 0; i < this.getWheelInfo().length; i++) {
this.instance.updateWheelTransform(i);
var t = this.getWheelInfo()[i].worldTransform;
var wheelBody = this.wheelBodies[i].instance;
wheelBody.position.copy(t.position);
wheelBody.quaternion.copy(t.quaternion);
}
};
GameLib.D3.RaycastVehicle.prototype.onRegistered = function(
parentScene
) {
};
GameLib.D3.RaycastWheel = function(
engine,
chassisConnectionPointLocal,
chassisConnectionPointWorld,
directionLocal,
directionWorld,
axleLocal,
axleWorld,
suspensionRestLength,
suspensionMaxLength,
radius,
suspensionStiffness,
dampingCompression,
dampingRelaxation,
frictionSlip,
steering,
rotation,
deltaRotation,
rollInfluence,
maxSuspensionForce,
clippedInvContactDotSuspension,
suspensionRelativeVelocity,
suspensionForce,
skidInfo,
suspensionLength,
maxSuspensionTravel,
useCustomSlidingRotationalSpeed,
customSlidingRotationalSpeed
) {
this.engine = engine;
this.engine.isNotCannonThrow();
this.componentId = GameLib.D3.Tools.RandomId();
if(typeof chassisConnectionPointLocal == 'undefined' || chassisConnectionPointLocal == null) {
chassisConnectionPointLocal = new this.engine.instance.Vec3();
}
this.chassisConnectionPointLocal = chassisConnectionPointLocal;
if(typeof chassisConnectionPointWorld == 'undefined' || chassisConnectionPointWorld == null) {
chassisConnectionPointWorld = new this.engine.instance.Vec3();
}
this.chassisConnectionPointWorld = chassisConnectionPointWorld;
if(typeof directionLocal == 'undefined' || directionLocal == null) {
directionLocal = new this.engine.instance.Vec3();
}
this.directionLocal = directionLocal;
if(typeof directionWorld == 'undefined' || directionWorld == null) {
directionWorld = new this.engine.instance.Vec3();
}
this.directionWorld = directionWorld;
if(typeof axleLocal == 'undefined' || axleLocal == null) {
axleLocal = new this.engine.instance.Vec3();
}
this.axleLocal = axleLocal;
if(typeof axleWorld == 'undefined' || axleWorld == null) {
axleWorld = new this.engine.instance.Vec3();
}
this.axleWorld = axleWorld;
if(typeof suspensionRestLength == 'undefined' || suspensionRestLength == null) {
suspensionRestLength = 1;
}
this.suspensionRestLength = suspensionRestLength;
if(typeof suspensionMaxLength == 'undefined' || suspensionMaxLength == null) {
suspensionMaxLength = 2;
}
this.suspensionMaxLength = suspensionMaxLength;
if(typeof radius == 'undefined' || radius == null) {
radius = 1;
}
this.radius = radius;
if(typeof suspensionStiffness == 'undefined' || suspensionStiffness == null) {
suspensionStiffness = 100;
}
this.suspensionStiffness = suspensionStiffness;
if(typeof dampingCompression == 'undefined' || dampingCompression == null) {
dampingCompression = 10;
}
this.dampingCompression = dampingCompression;
if(typeof dampingRelaxation == 'undefined' || dampingRelaxation == null) {
dampingRelaxation = 10;
}
this.dampingRelaxation = dampingRelaxation;
if(typeof frictionSlip == 'undefined' || frictionSlip == null) {
frictionSlip = 10000;
}
this.frictionSlip = frictionSlip;
if(typeof steering == 'undefined' || steering == null) {
steering = 0;
}
this.steering = steering;
if(typeof rotation == 'undefined' || rotation == null) {
rotation = 0;
}
this.rotation = rotation;
if(typeof deltaRotation == 'undefined' || deltaRotation == null) {
deltaRotation = 0;
}
this.deltaRotation = deltaRotation;
if(typeof rollInfluence == 'undefined' || rollInfluence == null) {
rollInfluence = 0.01;
}
this.rollInfluence = rollInfluence;
if(typeof maxSuspensionForce == 'undefined' || maxSuspensionForce == null) {
maxSuspensionForce = Number.MAX_VALUE;
}
this.maxSuspensionForce = maxSuspensionForce;
if(typeof clippedInvContactDotSuspension == 'undefined' || clippedInvContactDotSuspension == null) {
clippedInvContactDotSuspension = 1;
}
this.clippedInvContactDotSuspension = clippedInvContactDotSuspension;
if(typeof suspensionRelativeVelocity == 'undefined' || suspensionRelativeVelocity == null) {
suspensionRelativeVelocity = 0;
}
this.suspensionRelativeVelocity = suspensionRelativeVelocity;
if(typeof suspensionForce == 'undefined' || suspensionForce == null) {
suspensionForce = 0;
}
this.suspensionForce = suspensionForce;
if(typeof skidInfo == 'undefined' || skidInfo == null) {
skidInfo = 0;
}
this.skidInfo = skidInfo;
if(typeof suspensionLength == 'undefined' || suspensionLength == null) {
suspensionLength = 0;
}
this.suspensionLength = suspensionLength;
if(typeof maxSuspensionTravel == 'undefined' || maxSuspensionTravel == null) {
maxSuspensionTravel = 1;
}
this.maxSuspensionTravel = maxSuspensionTravel;
if(typeof useCustomSlidingRotationalSpeed == 'undefined' || useCustomSlidingRotationalSpeed == null) {
useCustomSlidingRotationalSpeed = false;
}
this.useCustomSlidingRotationalSpeed = useCustomSlidingRotationalSpeed;
if(typeof customSlidingRotationalSpeed == 'undefined' || customSlidingRotationalSpeed == null) {
customSlidingRotationalSpeed = -0.1;
}
this.customSlidingRotationalSpeed = customSlidingRotationalSpeed;
this.instance = this.createInstance();
// this gets assigned at runtime, when the wheel gets added to a vehicle
this.wheelIndex = -1;
};
GameLib.D3.RaycastWheel.prototype.createInstance = function() {
return {
chassisConnectionPointLocal : this.chassisConnectionPointLocal,
chassisConnectionPointWorld : this.chassisConnectionPointWorld,
directionLocal : this.directionLocal,
directionWorld : this.directionWorld,
axleLocal : this.axleLocal,
axleWorld : this.axleWorld,
suspensionRestLength : this.suspensionRestLength,
suspensionMaxLength : this.suspensionMaxLength,
radius : this.radius,
suspensionStiffness : this.suspensionStiffness,
dampingCompression : this.dampingCompression,
dampingRelaxation : this.dampingRelaxation,
frictionSlip : this.frictionSlip,
steering : this.steering,
rotation : this.rotation,
deltaRotation : this.deltaRotation,
rollInfluence : this.rollInfluence,
maxSuspensionForce : this.maxSuspensionForce,
clippedInvContactDotSuspension : this.clippedInvContactDotSuspension,
suspensionRelativeVelocity : this.suspensionRelativeVelocity,
suspensionForce : this.suspensionForce,
skidInfo : this.skidInfo,
suspensionLength : this.suspensionLength,
maxSuspensionTravel : this.maxSuspensionTravel,
useCustomSlidingRotationalSpeed : this.useCustomSlidingRotationalSpeed,
customSlidingRotationalSpeed : this.customSlidingRotationalSpeed
};
};
/**
* Physics Rigid Body Vehicle Superset
* @param engine GameLib.D3.Engine
* @param chassisBody GameLib.D3.RigidBody
* @param wheels GameLib.D3.RigidWheel[]
* @constructor
*/
GameLib.D3.RigidBodyVehicle = function(
engine,
chassisBody,
wheels
) {
this.componentId = GameLib.D3.Tools.RandomId();
this.engine = engine;
this.engine.isNotCannonThrow();
this.chassisBody = chassisBody;
if (typeof wheels == 'undefined') {
wheels = [];
}
this.wheels = wheels;
this.instance = this.createInstance();
};
/**
* Returns physics wheelbody info (for updates)
* @returns {Array}
*/
GameLib.D3.RigidBodyVehicle.prototype.getWheelInfo = function() {
return this.instance.wheelBodies;
};
/**
*
* @returns {GameLib.D3.RigidVehicle}
*/
GameLib.D3.RigidBodyVehicle.prototype.createInstance = function() {
return new this.engine.instance.RigidVehicle({
chassisBody: this.chassisBody.instance
});
};
/**
* Adds a wheel to this rigid body vehicle
* @param wheel GameLib.D3.RigidWheel
*/
GameLib.D3.RigidBodyVehicle.prototype.addWheel = function(wheel) {
this.wheels.push(wheel);
this.instance.addWheel({
body: wheel.body.instance,
position: new this.engine.instance.Vec3(
wheel.position.x,
wheel.position.y,
wheel.position.z
),
axis: new this.engine.instance.Vec3(
wheel.axis.x,
wheel.axis.y,
wheel.axis.z
),
direction: new this.engine.instance.Vec3(
wheel.direction.x,
wheel.direction.y,
wheel.direction.z
)
});
};
/**
* RigidBody Superset
* @param engine GameLib.D3.Engine
* @param mass
* @param friction
* @param position
* @param quaternion
* @param velocity
* @param angularVelocity
* @param linearDamping
* @param angularDamping
* @param allowSleep
* @param sleepSpeedLimit
* @param sleepTimeLimit
* @param collisionFilterGroup
* @param collisionFilterMask
* @param fixedRotation
* @param shape GameLib.D3.Shape
* @returns {GameLib.D3.Physics.RigidBody}
* @constructor
*/
GameLib.D3.RigidBody = function(
engine,
mass,
friction,
position,
quaternion,
velocity,
angularVelocity,
linearDamping,
angularDamping,
allowSleep,
sleepSpeedLimit,
sleepTimeLimit,
collisionFilterGroup,
collisionFilterMask,
fixedRotation,
shape
) {
this.componentId = GameLib.D3.Tools.RandomId();
this.position = position || new GameLib.D3.Vector3();
this.velocity = velocity || new GameLib.D3.Vector3();
this.angularVelocity = angularVelocity || new GameLib.D3.Vector3();
this.quaternion = quaternion || new GameLib.D3.Vector4(0, 0, 0, 1);
this.mass = typeof mass == "undefined" ? 0 : mass;
this.friction = typeof friction == "undefined" ? 5 : friction;
this.linearDamping = typeof linearDamping == "undefined" ? 0.01 : linearDamping;
this.angularDamping = typeof angularDamping == "undefined" ? 0.01 : angularDamping;
this.allowSleep = typeof allowSleep == "undefined" ? true : allowSleep;
this.sleepSpeedLimit = typeof sleepSpeedLimit == "undefined" ? 0.1 : sleepSpeedLimit;
this.sleepTimeLimit = typeof sleepTimeLimit == "undefined" ? 1.0 : sleepTimeLimit;
this.collisionFilterGroup = typeof collisionFilterGroup == "undefined" ? 1 : collisionFilterGroup;
this.collisionFilterMask = typeof collisionFilterMask == "undefined" ? 1 : collisionFilterMask;
this.fixedRotation = typeof fixedRotation == "undefined" ? false : fixedRotation;
this.shape = typeof shape == "undefined" ? null : shape;
this.engine = engine;
this.engine.isNotCannonThrow();
this.instance = this.createInstance();
// 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.RigidBody, GameLib.D3.ComponentInterface);
};
/**
* private function
* @returns {*}
*/
GameLib.D3.RigidBody.prototype.createInstance = function() {
var instance = new this.engine.instance.Body({
mass: this.mass,
friction: this.friction,
position: new this.engine.instance.Vec3(
this.position.x,
this.position.y,
this.position.z
),
velocity: new this.engine.instance.Vec3(
this.velocity.x,
this.velocity.y,
this.velocity.z
),
quaternion: new this.engine.instance.Quaternion(
this.quaternion.x,
this.quaternion.y,
this.quaternion.z,
this.quaternion.w
),
angularVelocity: new this.engine.instance.Vec3(
this.angularVelocity.x,
this.angularVelocity.y,
this.angularVelocity.z
),
linearDamping: this.linearDamping,
angularDamping: this.angularDamping,
allowSleep: this.allowSleep,
sleepSpeedLimit: this.sleepSpeedLimit,
sleepTimeLimit: this.sleepTimeLimit,
collisionFilterGroup: this.collisionFilterGroup,
collisionFilterMask: this.collisionFilterMask,
fixedRotation: this.fixedRotation,
shape: this.shape && this.shape.instance ? this.shape.instance : null
});
this.instance = instance;
return instance;
};
/**
* Adds a shape to this rigid body
* @param shape GameLib.D3.Shape
* @param offset GameLib.D3.Vector3
* @param orientation GameLib.D3.Vector4
* @constructor
*/
GameLib.D3.RigidBody.prototype.addShape = function(
shape,
offset,
orientation
) {
if (!offset || typeof offset == 'undefined') {
offset = new GameLib.D3.Vector3(0,0,0);
}
if (!orientation || typeof orientation == 'undefined') {
orientation = new GameLib.D3.Vector4(0,0,0,1);
}
this.instance.addShape(
shape.instance,
new this.engine.instance.Vec3(
offset.x,
offset.y,
offset.z
),
new this.engine.instance.Quaternion(
orientation.x,
orientation.y,
orientation.z,
orientation.w
)
);
};
///////////////////////// Methods to override //////////////////////////
GameLib.D3.RigidBody.prototype.onUpdate = function(
deltaTime,
parentEntity
) {
if(parentEntity) {
var quaternion = new THREE.Quaternion();
quaternion.copy(this.instance.quaternion);
var position = new THREE.Vector3();
position.copy(this.instance.position);
parentEntity.position.x = position.x;
parentEntity.position.y = position.y;
parentEntity.position.z = position.z;
parentEntity.quaternion.x = quaternion.x;
parentEntity.quaternion.y = quaternion.y;
parentEntity.quaternion.z = quaternion.z;
parentEntity.quaternion.w = quaternion.w;
}
};
/**
* Rigid Wheel superset
* @param body GameLib.D3.RigidBody
* @param position GameLib.D3.Vector3
* @param axis GameLib.D3.Vector3
* @param direction GameLib.D3.Vector3
* @constructor
*/
GameLib.D3.RigidWheel = function(
body,
position,
axis,
direction
) {
this.componentId = GameLib.D3.Tools.RandomId();
this.body = body;
this.position = position;
this.axis = axis;
this.direction = direction;
};
/**
* Scenes are objects putting meshes into 'world space'
* @param id
* @param path String
* @param name String
* @param meshes GameLib.D3.Mesh[]
* @param quaternion
* @param position
* @param rotation
* @param scale
* @param parentSceneId
* @param lights
* @param worlds
* @param entities
* @constructor
*/
GameLib.D3.Scene = function(
id,
path,
name,
meshes,
quaternion,
position,
rotation,
scale,
parentSceneId,
lights,
worlds,
entities
) {
this.id = id;
this.sceneId = GameLib.D3.Tools.RandomId();
this.path = path;
this.name = name;
if (this.name.trim() == "") {
this.name = 'unnamed';
}
if (typeof meshes == 'undefined') {
meshes = [];
}
this.meshes = meshes;
if (typeof quaternion == 'undefined') {
quaternion = new GameLib.D3.Vector4();
}
this.quaternion = quaternion;
if (typeof position == 'undefined') {
position = new GameLib.D3.Vector3(0,0,0);
}
this.position = position;
if (typeof rotation == 'undefined') {
rotation = new GameLib.D3.Vector3(0,0,0);
}
this.rotation = rotation;
if (typeof scale == 'undefined') {
scale = new GameLib.D3.Vector3(1,1,1);
}
this.scale = scale;
if (typeof parentSceneId == 'undefined') {
parentSceneId = null;
}
this.parentSceneId = parentSceneId;
if (typeof lights == 'undefined') {
lights = [];
}
this.lights = lights;
if (typeof worlds == 'undefined') {
worlds = [];
}
this.worlds = worlds;
if (typeof entities == 'undefined') {
entities = [];
}
this.entities = entities;
this.instance = this.createInstance();
// assoc array
this.meshIdToMesh = {};
// assoc array
this.componentIdToComponent = {};
};
GameLib.D3.Scene.prototype.createInstance = function (
) {
var scene = new THREE.Scene();
scene.render = true;
return scene;
};
/**
* Loads a scene directly from the API
* @param gameLibScene GameLib.D3.Scene
* @param onLoaded callback
* @param graphics GameLib.D3.Graphics
* @param uploadUrl String
* @param progressCallback callback
* @param apiUrl
*/
GameLib.D3.Scene.LoadSceneFromApi = function(
gameLibScene,
onLoaded,
graphics,
uploadUrl,
progressCallback,
apiUrl
) {
/**
* First check if this is a client or server side request
*/
if (typeof XMLHttpRequest == 'undefined') {
console.warn('implement server side loading from API here');
return onLoaded(null, new Error('not implemented'));
}
var xhr = new XMLHttpRequest();
xhr.open(
'GET',
apiUrl + '/scene/load' + gameLibScene.path + '/' + gameLibScene.name
);
xhr.onreadystatechange = function(xhr) {
return function() {
if (xhr.readyState == 4) {
var response = JSON.parse(xhr.responseText);
if (!response.scene || response.scene.length == 0) {
return onLoaded(null, null, new Error('Could not load scene'));
}
var scene = response.scene[0];
var physics3ds = [];
if (scene.worlds && scene.worlds.length > 0) {
console.warn('Implement physics worlds code here');
//TODO: physics world code here
}
var lights3d = [];
for (var l = 0; l < scene.lights.length; l++) {
var light = scene.lights[l];
var light3d = new GameLib.D3.Light(
light.id,
light.lightType,
light.name,
new GameLib.D3.Color(
light.color.r,
light.color.g,
light.color.b,
light.color.a
),
light.intensity,
new GameLib.D3.Vector3(
light.position.x,
light.position.y,
light.position.z
),
new GameLib.D3.Vector3(
light.targetPosition.x,
light.targetPosition.y,
light.targetPosition.z
),
new GameLib.D3.Vector4(
light.quaternion.x,
light.quaternion.y,
light.quaternion.z,
light.quaternion.w
),
new GameLib.D3.Vector3(
light.rotation.x,
light.rotation.y,
light.rotation.z
),
new GameLib.D3.Vector3(
light.scale.x,
light.scale.y,
light.scale.z
),
light.distance,
light.decay,
light.power,
light.angle,
light.penumbra
);
lights3d.push(light3d);
}
var scene3d = new GameLib.D3.Scene(
scene._id || scene.id,
scene.path,
scene.name,
scene.meshes,
new GameLib.D3.Vector4(
scene.quaternion.x,
scene.quaternion.y,
scene.quaternion.z,
scene.quaternion.w
),
new GameLib.D3.Vector3(
scene.position.x,
scene.position.y,
scene.position.z
),
new GameLib.D3.Vector3(
scene.rotation.x,
scene.rotation.y,
scene.rotation.z
),
new GameLib.D3.Vector3(
scene.scale.x,
scene.scale.y,
scene.scale.z
),
scene.parentSceneId,
lights3d,
physics3ds
);
GameLib.D3.Scene.LoadScene(
scene3d,
onLoaded,
false,
graphics,
uploadUrl,
progressCallback
);
}
}
}(xhr);
xhr.send();
};
/**
* Loads a GameLib.D3.Scene object into a Graphics Instance Scene object
* @param gameLibScene GameLib.D3.Scene
* @param onLoaded callback when all meshes have loaded
* @param computeNormals set to true to compute new face and vertex normals during load
* @param graphics GameLib.D3.Graphics
* @param uploadUrl
* @param progressCallback
*/
GameLib.D3.Scene.LoadScene = function(
gameLibScene,
onLoaded,
computeNormals,
graphics,
uploadUrl,
progressCallback
) {
console.log("loading scene " + gameLibScene.name);
graphics.isNotThreeThrow();
var meshQ = [];
for (var m = 0; m < gameLibScene.meshes.length; m++) {
var mesh = gameLibScene.meshes[m];
console.log("loading mesh " + mesh.name);
var geometry = new graphics.instance.Geometry();
var vertices = mesh.vertices;
var faces = mesh.faces;
var faceVertexUvs = mesh.faceVertexUvs;
var materials = mesh.materials;
/**
* Setup vertices
*/
for (var v = 0; v < vertices.length; v++) {
geometry.vertices.push(
new graphics.instance.Vector3(
vertices[v].position.x,
vertices[v].position.y,
vertices[v].position.z
)
)
}
/**
* Setup faces
*/
for (var f = 0; f < faces.length; f++) {
var face = new graphics.instance.Face3(
faces[f].v0,
faces[f].v1,
faces[f].v2,
new graphics.instance.Vector3(
faces[f].normal.x,
faces[f].normal.y,
faces[f].normal.z
),
new graphics.instance.Color(
faces[f].color.r,
faces[f].color.g,
faces[f].color.b
),
faces[f].materialIndex
);
face.vertexColors = [
new graphics.instance.Color(
faces[f].vertexColors[0].r,
faces[f].vertexColors[0].g,
faces[f].vertexColors[0].b
),
new graphics.instance.Color(
faces[f].vertexColors[1].r,
faces[f].vertexColors[1].g,
faces[f].vertexColors[1].b
),
new graphics.instance.Color(
faces[f].vertexColors[2].r,
faces[f].vertexColors[2].g,
faces[f].vertexColors[2].b
)
];
face.normal = new graphics.instance.Vector3(
faces[f].normal.x,
faces[f].normal.y,
faces[f].normal.z
);
face.vertexNormals = [
new graphics.instance.Vector3(
faces[f].vertexNormals[0].x,
faces[f].vertexNormals[0].y,
faces[f].vertexNormals[0].z
),
new graphics.instance.Vector3(
faces[f].vertexNormals[1].x,
faces[f].vertexNormals[1].y,
faces[f].vertexNormals[1].z
),
new graphics.instance.Vector3(
faces[f].vertexNormals[2].x,
faces[f].vertexNormals[2].y,
faces[f].vertexNormals[2].z
)
];
geometry.faces.push(face);
}
geometry.faceVertexUvs = [];
/**
* Setup face UVs
*/
for (var fm = 0; fm < faceVertexUvs.length; fm++) {
var faceMaterialVertexUvs = faceVertexUvs[fm];
geometry.faceVertexUvs[fm] = [];
for (var fuv = 0; fuv < faceMaterialVertexUvs.length; fuv++) {
geometry.faceVertexUvs[fm][fuv] = [];
geometry.faceVertexUvs[fm][fuv].push(
new graphics.instance.Vector2(
faceMaterialVertexUvs[fuv][0].x,
faceMaterialVertexUvs[fuv][0].y
),
new graphics.instance.Vector2(
faceMaterialVertexUvs[fuv][1].x,
faceMaterialVertexUvs[fuv][1].y
),
new graphics.instance.Vector2(
faceMaterialVertexUvs[fuv][2].x,
faceMaterialVertexUvs[fuv][2].y
)
);
}
}
/**
* Re-calculate normals (if we have to)
* @type {Array}
*/
if (computeNormals) {
geometry.computeFaceNormals();
geometry.computeVertexNormals();
}
var instanceMaterialLoaders = [];
/**
* Setup materials
*/
for (var mi = 0; mi < materials.length; mi++) {
instanceMaterialLoaders.push(
GameLib.D3.Material.createInstanceMaterial(
materials[mi],
graphics,
uploadUrl,
progressCallback
)
);
}
var result = Q.all(instanceMaterialLoaders).then(
function(mesh, geometry) {
return function(materials) {
console.log("loaded material : " + materials[0].name);
/**
* We don't support MultiMaterial atm - it doesn't work with raycasting
*/
var material = materials[0];
var threeMesh = GameLib.D3.Mesh.createInstanceMesh(
mesh,
geometry,
material,
graphics
);
threeMesh.name = mesh.name;
threeMesh.position.x = mesh.position.x;
threeMesh.position.y = mesh.position.y;
threeMesh.position.z = mesh.position.z;
threeMesh.rotation.x = mesh.rotation.x;
threeMesh.rotation.y = mesh.rotation.y;
threeMesh.rotation.z = mesh.rotation.z;
threeMesh.scale.x = mesh.scale.x;
threeMesh.scale.y = mesh.scale.y;
threeMesh.scale.z = mesh.scale.z;
threeMesh.quaternion.x = mesh.quaternion.x;
threeMesh.quaternion.y = mesh.quaternion.y;
threeMesh.quaternion.z = mesh.quaternion.z;
threeMesh.quaternion.w = mesh.quaternion.w;
return threeMesh;
};
}(mesh, geometry)
).catch(function(error){
console.log(error);
});
meshQ.push(result);
}
Q.all(meshQ).then(
function(instanceMeshes){
console.log("all meshes have loaded");
if (typeof onLoaded != 'undefined') {
var instanceLights = [];
for (var sli = 0; sli < gameLibScene.lights.length; sli++) {
var gameLibLight = gameLibScene.lights[sli];
var light = null;
if (gameLibLight.lightType == 'AmbientLight') {
light = new graphics.instance.AmbientLight(gameLibLight.color, gameLibLight.intensity);
}
if (gameLibLight.lightType == 'DirectionalLight') {
light = new graphics.instance.DirectionalLight(gameLibLight.color, gameLibLight.intensity);
}
if (gameLibLight.lightType == 'PointLight') {
light = new graphics.instance.PointLight(gameLibLight.color, gameLibLight.intensity);
light.distance = gameLibLight.distance;
light.decay = gameLibLight.decay;
}
if (gameLibLight.lightType == 'SpotLight') {
light = new graphics.instance.SpotLight(gameLibLight.color, gameLibLight.intensity);
light.distance = gameLibLight.distance;
light.angle = gameLibLight.angle;
light.penumbra = gameLibLight.penumbra;
light.decay = gameLibLight.decay;
}
light.position.x = gameLibLight.position.x;
light.position.y = gameLibLight.position.y;
light.position.z = gameLibLight.position.z;
light.rotation.x = gameLibLight.rotation.x;
light.rotation.y = gameLibLight.rotation.y;
light.rotation.z = gameLibLight.rotation.z;
if (light == null) {
console.warn('Does not support lights of type :' + gameLibLight.lightType + ', not imported');
} else {
light.name = gameLibLight.name;
instanceLights.push(light);
}
}
var instanceScene = new graphics.instance.Scene();
instanceScene.name = gameLibScene.name;
instanceScene.position.x = gameLibScene.position.x;
instanceScene.position.y = gameLibScene.position.y;
instanceScene.position.z = gameLibScene.position.z;
instanceScene.rotation.x = gameLibScene.rotation.x;
instanceScene.rotation.y = gameLibScene.rotation.y;
instanceScene.rotation.z = gameLibScene.rotation.z;
instanceScene.scale.x = gameLibScene.scale.x;
instanceScene.scale.y = gameLibScene.scale.y;
instanceScene.scale.z = gameLibScene.scale.z;
instanceScene.quaternion.x = gameLibScene.quaternion.x;
instanceScene.quaternion.y = gameLibScene.quaternion.y;
instanceScene.quaternion.z = gameLibScene.quaternion.z;
instanceScene.quaternion.w = gameLibScene.quaternion.w;
for (var m = 0; m < instanceMeshes.length; m++) {
instanceScene.add(instanceMeshes[m]);
}
for (var l = 0; l < instanceLights.length; l++) {
instanceScene.add(instanceLights[l]);
}
onLoaded(
gameLibScene,
{
scene: instanceScene,
lights: instanceLights,
meshes: instanceMeshes
}
);
}
}).catch(
function(error){
console.log(error);
}
);
};
// - - - - - - - - - - - - - RUNTIME FUNCTIONS - - - - - - - - - - - - - - - - -
/**
* Updates the scene
* @param deltaTime
*/
GameLib.D3.Scene.prototype.update = function(
deltaTime
) {
for(var e in this.entities) {
this.entities[e].update(deltaTime);
}
};
/**
* Updates the scene
* @param deltaTime
*/
GameLib.D3.Scene.prototype.lateUpdate = function(
deltaTime
) {
for(var e in this.entities) {
this.entities[e].lateUpdate(deltaTime);
}
};
/**
* renders the scene
* @param deltaTime
* @param renderer
* @param camera
*/
GameLib.D3.Scene.prototype.render = function(
deltaTime,
renderer,
camera
) {
renderer.render(this.instance, camera);
};
/**
* Registers an entity to the scene.
* This method also links the entity and it's components,
* if they are defined inside the componentIds array.
* The setParentEntity and onSetParentEntity methods are being called here.
* @param entity GameLib.D3.Entity
*/
GameLib.D3.Scene.prototype.registerEntity = function(
entity
) {
this.entities.push(entity);
entity.register(this);
// link components to entity
for(var c in entity.componentIds) {
var componentId = entity.componentIds[c];
var component = this.componentIdToComponent[componentId];
if(component) {
component.setParentEntity(this, entity);
}
}
};
/**
* Registers a component to the scene.
* This method also calls the onRegistered-method on the component
* @param component GameLib.D3.ComponentInterface
*/
GameLib.D3.Scene.prototype.registerComponent = function(
component
) {
this.componentIdToComponent[component.componentId] = component;
if(component.onRegistered && typeof component.onRegistered == 'function') {
component.onRegistered(this);
}
};
/**
* Registers a light to the scene
* @param light THREE.Light
*/
GameLib.D3.Scene.prototype.registerLight = function(
light
) {
this.instance.add(light);
};
/**
* Physics Shape Superset
* @param engine GameLib.D3.Engine
* @param shapeType
* @param scale GameLib.D3.Vector3
* @param vertices Number[]
* @param indices Number[]
* @param radius Number
* @param halfExtensions GameLib.D3.Vector3
* @param radiusTop Number
* @param radiusBottom Number
* @param height Number
* @param numSegments Number
* @param heightmap GameLib.D3.Heightmap
* @param elementSize
* @constructor
*/
GameLib.D3.Shape = function(
engine,
shapeType,
scale,
vertices,
indices,
radius,
halfExtensions,
radiusTop,
radiusBottom,
height,
numSegments,
heightmap
) {
this.engine = engine;
this.engine.isNotCannonThrow();
this.shapeType = shapeType;
if (typeof scale == 'undefined') {
scale = new GameLib.D3.Vector3(1, 1, 1)
}
this.scale = scale;
if (typeof vertices == 'undefined') {
vertices = [];
}
this.vertices = vertices;
if (typeof indices == 'undefined') {
indices = [];
}
this.indices = indices;
if (typeof radius == 'undefined') {
radius = 1;
}
this.radius = radius;
if (typeof halfExtensions == 'undefined') {
halfExtensions = new GameLib.D3.Vector3(1,1,1);
}
this.halfExtensions = halfExtensions;
if (typeof radiusTop == 'undefined') {
radiusTop = 1;
}
this.radiusTop = radiusTop;
if (typeof radiusBottom == 'undefined') {
radiusBottom = 1;
}
this.radiusBottom = radiusBottom;
if (typeof height == 'undefined') {
height = 1;
}
this.height = height;
if (typeof numSegments == 'undefined') {
numSegments = 1;
}
this.numSegments = numSegments;
if (typeof heightmap == 'undefined') {
heightmap = new GameLib.D3.Heightmap();
}
this.heightmap = heightmap;
this.instance = this.createInstance();
};
/**
* Shape constants
* @type {number}
*/
GameLib.D3.Shape.SHAPE_TYPE_SPHERE = 1;
GameLib.D3.Shape.SHAPE_TYPE_BOX = 2;
GameLib.D3.Shape.SHAPE_TYPE_TRIMESH = 3;
GameLib.D3.Shape.SHAPE_TYPE_CYLINDER = 4;
GameLib.D3.Shape.SHAPE_TYPE_HEIGHT_MAP = 5;
GameLib.D3.Shape.SHAPE_TYPE_CONVEX_HULL = 6;
/**
*
*/
GameLib.D3.Shape.prototype.createInstance = function() {
var instance = null;
if (this.shapeType == GameLib.D3.Shape.SHAPE_TYPE_TRIMESH) {
instance = new this.engine.instance.Trimesh(
this.vertices,
this.indices
);
} else if (this.shapeType == GameLib.D3.Shape.SHAPE_TYPE_SPHERE) {
instance = new this.engine.instance.Sphere(
this.radius
);
} else if (this.shapeType == GameLib.D3.Shape.SHAPE_TYPE_BOX) {
instance = new this.engine.instance.Box(
new this.engine.instance.Vec3(
this.halfExtensions.x,
this.halfExtensions.y,
this.halfExtensions.z
)
);
} else if (this.shapeType == GameLib.D3.Shape.SHAPE_TYPE_CYLINDER) {
instance = new this.engine.instance.Cylinder(
this.radiusTop,
this.radiusBottom,
this.height,
this.numSegments
);
} else if (this.shapeType == GameLib.D3.Shape.SHAPE_TYPE_HEIGHT_MAP) {
instance = new this.engine.instance.Heightfield(
this.heightmap.matrix,
{
elementSize: this.heightmap.elementSize
}
);
} else if (this.shapeType == GameLib.D3.Shape.SHAPE_TYPE_CONVEX_HULL) {
console.warn('Shape type not implemented: ' + this.shapeType);
throw new Error('Shape type not implemented: ' + this.shapeType);
} else {
console.warn('Shape type not implemented: ' + this.shapeType);
throw new Error('Shape type not implemented: ' + this.shapeType);
}
this.instance = instance;
return instance;
};
/**
* update
*/
GameLib.D3.Shape.prototype.update = function(
engine
) {
engine.isNotCannonThrow();
if(this.shapeType === GameLib.D3.Shape.SHAPE_TYPE_TRIMESH) {
this.instance.setScale(
new engine.instance.Vec3(
this.scale.x,
this.scale.y,
this.scale.z
)
);
this.instance.updateAABB();
this.instance.updateNormals();
this.instance.updateEdges();
this.instance.updateBoundingSphereRadius();
this.instance.updateTree();
}
};
/**
* Skeleton Superset
* @param id
* @param bones GameLib.D3.Bone
* @param boneInverses
* @param useVertexTexture
* @param boneTextureWidth
* @param boneTextureHeight
* @param boneMatrices
* @param boneTexture
* @constructor
*/
GameLib.D3.Skeleton = function(
id,
bones,
boneInverses,
useVertexTexture,
boneTextureWidth,
boneTextureHeight,
boneMatrices,
boneTexture
) {
this.id = id;
this.bones = bones;
/**
* An array of Matrix4s that represent the inverse of the matrixWorld of the individual bones.
* @type GameLib.D3.Matrix4[]
*/
if (typeof boneInverses == 'undefined') {
boneInverses = [];
}
this.boneInverses = boneInverses;
/**
* Use a vertex texture in the shader - allows for more than 4 bones per vertex, not supported by all devices
* @type {boolean}
*/
if (typeof useVertexTexture == 'undefined') {
useVertexTexture = false;
}
this.useVertexTexture = useVertexTexture;
if (this.useVertexTexture == true) {
console.warn('support for vertex texture bones is not supported yet - something could break somewhere');
}
if (typeof boneTextureWidth == 'undefined') {
boneTextureWidth = 0;
}
this.boneTextureWidth = boneTextureWidth;
if (typeof boneTextureHeight == 'undefined') {
boneTextureHeight = 0;
}
this.boneTextureHeight = boneTextureHeight;
if (typeof boneMatrices == 'undefined') {
boneMatrices = [];
}
this.boneMatrices = boneMatrices;
if (typeof boneTexture == 'undefined') {
boneTexture = [];
}
this.boneTexture = boneTexture;
};
GameLib.D3.SkyBox = function (
graphics
) {
this.id = null;
this.graphics = graphics;
this.graphics.isNotThreeThrow();
this.texturesFolder = null;
};
GameLib.D3.SkyBox.prototype.load = function (
texturesFolder
) {
this.texturesFolder = texturesFolder;
this.textures = [];
this.materials = [];
this.mesh = {};
this.scene = new this.graphics.instance.Scene();
this.textureCube = null;
var textureLoader = new this.graphics.instance.TextureLoader();
// this textures are used to display the skybox
this.textures.push(textureLoader.load(this.texturesFolder + "px.png"));
this.textures.push(textureLoader.load(this.texturesFolder + "nx.png"));
this.textures.push(textureLoader.load(this.texturesFolder + "py.png"));
this.textures.push(textureLoader.load(this.texturesFolder + "ny.png"));
this.textures.push(textureLoader.load(this.texturesFolder + "pz.png"));
this.textures.push(textureLoader.load(this.texturesFolder + "nz.png"));
// assign textures to each cube face
for (var i = 0; i < 6; i ++) {
this.materials.push(new this.graphics.instance.MeshBasicMaterial({ map: this.textures[i] }));
}
// create cube geometry
this.mesh = new this.graphics.instance.Mesh(new this.graphics.instance.CubeGeometry(1, 1, 1), new this.graphics.instance.MeshFaceMaterial(this.materials));
this.mesh.applyMatrix(new this.graphics.instance.Matrix4().makeScale(1, 1, -1));
this.scene.add(this.mesh);
// Load env textureCube
// this is used for reflections on meshes
// mesh.material.envMap = this.textureCube;
this.textureCube = new this.graphics.instance.CubeTextureLoader().load([
this.texturesFolder + "px.png", this.texturesFolder + "nx.png",
this.texturesFolder + "py.png", this.texturesFolder + "ny.png",
this.texturesFolder + "pz.png", this.texturesFolder + "nz.png"
]);
};
GameLib.D3.SkyBox.prototype.render = function (
threeRenderer,
threeCamera
) {
var cameraPosition = new this.graphics.instance.Vector3(threeCamera.position.x, threeCamera.position.y, threeCamera.position.z);
threeCamera.position.set(0, 0, 0);
var gl = threeRenderer.context;
gl.disable(gl.DEPTH_TEST);
threeRenderer.render(this.scene, threeCamera);
gl.enable(gl.DEPTH_TEST);
threeCamera.position.copy(cameraPosition);
};
/**
* Physics Solver Superset
* @param id
* @param engine GameLib.D3.Engine
* @param solverType
* @param name
* @param iterations
* @param tolerance
* @constructor
*/
GameLib.D3.Solver = function(
id,
engine,
solverType,
name,
iterations,
tolerance
) {
this.id = id;
this.engine = engine;
this.engine.isNotCannonThrow();
if (typeof solverType == 'undefined') {
solverType = GameLib.D3.Solver.GS_SOLVER;
}
this.solverType = solverType;
if (typeof name == 'undefined') {
if (this.solverType == GameLib.D3.Solver.SPLIT_SOLVER) {
name = 'split solver';
} else if (this.solverType == GameLib.D3.Solver.GS_SOLVER) {
name = 'gs solver';
} else {
name = 'unknown solver';
}
}
this.name = name;
if (typeof iterations == 'undefined') {
iterations = 10;
}
this.iterations = iterations;
if (typeof tolerance == 'undefined') {
tolerance = 1e-7;
}
this.tolerance = tolerance;
this.instance = this.createInstance();
this.instance.tolerance = tolerance;
this.instance.iterations = iterations;
};
/**
* Creates a custom solver instance
* @returns {*}
*/
GameLib.D3.Solver.prototype.createInstance = function(){
var instance = null;
if (this.solverType == GameLib.D3.Solver.SPLIT_SOLVER) {
instance = new this.engine.instance.SplitSolver();
} else if (this.solverType == GameLib.D3.Solver.GS_SOLVER) {
instance = new this.engine.instance.GSSolver();
} else {
console.warn('Unsupported solver type: ' + this.solverType);
throw new Error('Unsupported solver type: ' + this.solverType);
}
return instance;
};
/**
* Solver Types
* @type {number}
*/
GameLib.D3.Solver.SPLIT_SOLVER = 0x1;
GameLib.D3.Solver.GS_SOLVER = 0x2;
/**
* Texture Superset
* @param id
* @param name
* @param image
* @param wrapS
* @param wrapT
* @param repeat
* @param data
* @param format
* @param mapping
* @param magFilter
* @param minFilter
* @param textureType
* @param anisotropy
* @param offset
* @param generateMipmaps
* @param flipY
* @param mipmaps
* @param unpackAlignment
* @param premultiplyAlpha
* @param encoding
* @constructor
*/
GameLib.D3.Texture = function(
id,
name,
image,
wrapS,
wrapT,
repeat,
data,
format,
mapping,
magFilter,
minFilter,
textureType,
anisotropy,
offset,
generateMipmaps,
flipY,
mipmaps,
unpackAlignment,
premultiplyAlpha,
encoding
) {
this.id = id;
this.name = name;
this.image = image;
if (typeof wrapS == 'undefined') {
wrapS = GameLib.D3.Texture.TYPE_REPEAT_WRAPPING;
}
this.wrapS = wrapS;
if (typeof wrapT == 'undefined') {
wrapT = GameLib.D3.Texture.TYPE_REPEAT_WRAPPING;
}
this.wrapT = wrapT;
if (typeof repeat == 'undefined') {
repeat = new GameLib.D3.Vector2(1, 1);
}
this.repeat = repeat;
if (typeof data == 'undefined') {
data = null;
}
this.data = data;
if (typeof format == 'undefined') {
format = GameLib.D3.Texture.TYPE_RGBA_FORMAT;
}
this.format = format;
if (typeof mapping == 'undefined') {
mapping = GameLib.D3.Texture.TYPE_UV_MAPPING;
}
this.mapping = mapping;
if (typeof magFilter == 'undefined') {
magFilter = GameLib.D3.Texture.TYPE_LINEAR_FILTER;
}
this.magFilter = magFilter;
if (typeof minFilter == 'undefined') {
minFilter = GameLib.D3.Texture.TYPE_LINEAR_MIPMAP_LINEAR_FILTER;
}
this.minFilter = minFilter;
if (typeof textureType == 'undefined') {
textureType = GameLib.D3.Texture.TYPE_UNSIGNED_BYTE;
}
this.textureType = textureType;
if (typeof anisotropy == 'undefined') {
anisotropy = 1;
}
this.anisotropy = anisotropy;
if (typeof offset == 'undefined') {
offset = new GameLib.D3.Vector2(0, 0);
}
this.offset = offset;
if (typeof generateMipmaps == 'undefined') {
generateMipmaps = true;
}
this.generateMipmaps = generateMipmaps;
if (typeof flipY == 'undefined') {
flipY = true;
}
this.flipY = flipY;
if (typeof mipmaps == 'undefined') {
mipmaps = [];
}
this.mipmaps = mipmaps;
if (typeof unpackAlignment == 'undefined') {
unpackAlignment = 4;
}
this.unpackAlignment = unpackAlignment;
if (typeof premultiplyAlpha == 'undefined') {
premultiplyAlpha = false;
}
this.premultiplyAlpha = premultiplyAlpha;
if (typeof encoding == 'undefined') {
encoding = GameLib.D3.Texture.TYPE_LINEAR_ENCODING;
}
this.encoding = encoding;
};
/**
* Texture Formats
* @type {number}
*/
GameLib.D3.Texture.TYPE_ALPHA_FORMAT = 1019;
GameLib.D3.Texture.TYPE_RGB_FORMAT = 1020;
GameLib.D3.Texture.TYPE_RGBA_FORMAT = 1021;
GameLib.D3.Texture.TYPE_LUMINANCE_FORMAT = 1022;
GameLib.D3.Texture.TYPE_LUMINANCE_ALPHA_FORMAT = 1023;
GameLib.D3.Texture.TYPE_DEPTH_FORMAT = 1026;
/**
* Mapping modes
* @type {number}
*/
GameLib.D3.Texture.TYPE_UV_MAPPING = 300;
GameLib.D3.Texture.TYPE_CUBE_REFLECTION_MAPPING = 301;
GameLib.D3.Texture.TYPE_CUBE_REFRACTION_MAPPING = 302;
GameLib.D3.Texture.TYPE_EQUI_RECTANGULAR_REFLECTION_MAPPING = 303;
GameLib.D3.Texture.TYPE_EQUI_RECTANGULAR_REFRACTION_MAPPING = 304;
GameLib.D3.Texture.TYPE_SPHERICAL_REFLECTION_MAPPING = 305;
GameLib.D3.Texture.TYPE_CUBE_UV_REFLECTION_MAPPING = 306;
GameLib.D3.Texture.TYPE_CUBE_UV_REFRACTION_MAPPING = 307;
/**
* Wrapping Modes
* @type {number}
*/
GameLib.D3.Texture.TYPE_REPEAT_WRAPPING = 1000;
GameLib.D3.Texture.TYPE_CLAMP_TO_EDGE_WRAPPING = 1001;
GameLib.D3.Texture.TYPE_MIRRORED_REPEAT_WRAPPING = 1002;
/**
* Mipmap Filters
* @type {number}
*/
GameLib.D3.Texture.TYPE_NEAREST_FILTER = 1003;
GameLib.D3.Texture.TYPE_NEAREST_MIPMAP_NEAREST_FILTER = 1004;
GameLib.D3.Texture.TYPE_NEAREST_MIPMAP_LINEAR_FILTER = 1005;
GameLib.D3.Texture.TYPE_LINEAR_FILTER = 1006;
GameLib.D3.Texture.TYPE_LINEAR_MIPMAP_NEAREST_FILTER = 1007;
GameLib.D3.Texture.TYPE_LINEAR_MIPMAP_LINEAR_FILTER = 1008;
/**
* Texture Data Types
* @type {number}
*/
GameLib.D3.Texture.TYPE_UNSIGNED_BYTE = 1009;
GameLib.D3.Texture.TYPE_BYTE = 1010;
GameLib.D3.Texture.TYPE_SHORT = 1011;
GameLib.D3.Texture.TYPE_UNSIGNED_SHORT = 1012;
GameLib.D3.Texture.TYPE_INT = 1013;
GameLib.D3.Texture.TYPE_UNSIGNED_INT = 1014;
GameLib.D3.Texture.TYPE_FLOAT = 1015;
GameLib.D3.Texture.TYPE_HALF_FLOAT = 1025;
/**
* Encoding Modes
* @type {number}
*/
GameLib.D3.Texture.TYPE_LINEAR_ENCODING = 3000; // NO ENCODING AT ALL.
GameLib.D3.Texture.TYPE_SRGB_ENCODING = 3001;
GameLib.D3.Texture.TYPE_GAMMA_ENCODING = 3007; // USES GAMMA_FACTOR, FOR BACKWARDS COMPATIBILITY WITH WEBGLRENDERER.GAMMAINPUT/GAMMAOUTPUT
GameLib.D3.Texture.TYPE_RGBE_ENCODING = 3002; // AKA RADIANCE.
GameLib.D3.Texture.TYPE_LOG_LUV_ENCODING = 3003;
GameLib.D3.Texture.TYPE_RGBM7_ENCODING = 3004;
GameLib.D3.Texture.TYPE_RGBM16_ENCODING = 3005;
GameLib.D3.Texture.TYPE_RGBD_ENCODING = 3006; // MAXRANGE IS 256.
/**
* Defers loading of an image and resolves once image is loaded
* @param gameLibTexture GameLib.D3.Texture
* @param instanceMaterial
* @param instanceMaterialMapType
* @param graphics GameLib.D3.Graphics
* @param uploadUrl String
* @param progressCallback
* @returns {Promise}
*/
GameLib.D3.Texture.loadMap = function(
gameLibTexture,
instanceMaterial,
instanceMaterialMapType,
graphics,
uploadUrl,
progressCallback
) {
var defer = Q.defer();
var imagePath = null;
var textureLoader = new graphics.instance.TextureLoader();
if (gameLibTexture && gameLibTexture.image && gameLibTexture.image.filename) {
/**
* Else, load from upload source
*/
imagePath = uploadUrl + '/' + gameLibTexture.image.filename;
}
if (imagePath) {
textureLoader.crossOrigin = '';
textureLoader.load(
imagePath,
function(texture) {
/**
* onLoad
*/
instanceMaterial[instanceMaterialMapType] = texture;
instanceMaterial[instanceMaterialMapType].name = gameLibTexture.name;
instanceMaterial[instanceMaterialMapType].anisotropy = gameLibTexture.anisotropy;
instanceMaterial[instanceMaterialMapType].encoding = gameLibTexture.encoding;
instanceMaterial[instanceMaterialMapType].flipY = gameLibTexture.flipY;
/**
* We don't restore the format since this changing from OS to OS and breaks the implementation sometimes
*/
instanceMaterial[instanceMaterialMapType].generateMipmaps = gameLibTexture.generateMipmaps;
instanceMaterial[instanceMaterialMapType].magFilter = gameLibTexture.magFilter;
instanceMaterial[instanceMaterialMapType].minFilter = gameLibTexture.minFilter;
instanceMaterial[instanceMaterialMapType].mapping = gameLibTexture.mapping;
instanceMaterial[instanceMaterialMapType].mipmaps = gameLibTexture.mipmaps;
instanceMaterial[instanceMaterialMapType].offset = new graphics.instance.Vector2(
gameLibTexture.offset.x,
gameLibTexture.offset.y
);
instanceMaterial[instanceMaterialMapType].premultiplyAlpha = gameLibTexture.premultiplyAlpha;
instanceMaterial[instanceMaterialMapType].textureType = gameLibTexture.textureType;
instanceMaterial[instanceMaterialMapType].wrapS = gameLibTexture.wrapS;
instanceMaterial[instanceMaterialMapType].wrapT = gameLibTexture.wrapT;
instanceMaterial[instanceMaterialMapType].unpackAlignment = gameLibTexture.unpackAlignment;
instanceMaterial.needsUpdate = true;
defer.resolve(true);
},
function(xhr) {
/**
* onProgress
*/
if (progressCallback) {
progressCallback(Math.round(xhr.loaded / xhr.total * 100));
}
},
function(error) {
/**
* onError
*/
console.log("an error occurred while trying to load the image : " + imagePath);
defer.resolve(null);
}
);
} else {
defer.resolve(null);
}
return defer.promise;
};
/**
* Returns an array of image loading Promises
* @param gameLibMaterial
* @param blenderMaps
* @param instanceMaterial
* @param graphics GameLib.D3.Graphics
* @param uploadUrl String
* @param progressCallback
* @returns Q[]
*/
GameLib.D3.Texture.loadMaps = function(
gameLibMaterial,
blenderMaps,
instanceMaterial,
graphics,
uploadUrl,
progressCallback
) {
var textureMaps = [];
for (var ti = 0; ti < blenderMaps.length; ti++) {
var map = blenderMaps[ti];
if (gameLibMaterial.maps.hasOwnProperty(map)) {
var blenderTexture = gameLibMaterial.maps[map];
if (
blenderTexture &&
blenderTexture.image &&
blenderTexture.image.filename
) {
var instanceMap = null;
if (map == 'alpha') {
instanceMap = 'alhpaMap';
}
if (map == 'ao') {
instanceMap = 'aoMap';
}
if (map == 'bump') {
instanceMap = 'bumpMap';
}
if (map == 'displacement') {
instanceMap = 'displacementMap';
}
if (map == 'emissive') {
instanceMap = 'emissiveMap';
}
if (map == 'environment') {
instanceMap = 'envMap';
}
if (map == 'light') {
instanceMap = 'lightMap';
}
if (map == 'specular') {
instanceMap = 'specularMap';
}
if (map == 'diffuse') {
instanceMap = 'map';
}
if (map == 'roughness') {
instanceMap = 'roughnessMap';
}
if (map == 'metalness') {
instanceMap = 'metalnessMap';
}
if (instanceMap == null) {
console.warn("unsupported map type : " + map);
}
textureMaps.push(
GameLib.D3.Texture.loadMap(
gameLibMaterial.maps[map],
instanceMaterial,
instanceMap,
graphics,
uploadUrl,
progressCallback
)
);
}
}
}
return textureMaps;
};
GameLib.D3.Tools = function(){};
/**
* Generates a random ID
* @returns {string}
* @constructor
*/
GameLib.D3.Tools.RandomId = function() {
return Math.random().toString(36).substr(2, 10);
};
/**
* TriangleEdge
* @param triangle
* @param edge
* @constructor
*/
GameLib.D3.TriangleEdge = function(
triangle,
edge
) {
this.triangle = triangle;
this.edge = edge;
};
/**
* TriangleFace
* @param v0
* @param v1
* @param v2
* @param materialIndex
* @param v0uv
* @param v1uv
* @param v2uv
* @param color
* @param vertexColors
* @param vertexNormals
* @param normal
* @constructor
*/
GameLib.D3.TriangleFace = function(
v0,
v1,
v2,
materialIndex,
v0uv,
v1uv,
v2uv,
color,
vertexColors,
vertexNormals,
normal
) {
this.v0 = v0;
this.v1 = v1;
this.v2 = v2;
this.materialIndex = materialIndex;
this.v0uv = v0uv;
this.v1uv = v1uv;
this.v2uv = v2uv;
if (!color) {
color = new GameLib.D3.Color(0xff, 0xff, 0xff, 0xff);
}
this.color = color;
if (!vertexColors) {
vertexColors = [
new GameLib.D3.Color(0xff, 0xff, 0xff, 0xff),
new GameLib.D3.Color(0xff, 0xff, 0xff, 0xff),
new GameLib.D3.Color(0xff, 0xff, 0xff, 0xff)
];
}
this.vertexColors = vertexColors;
if (!vertexNormals) {
vertexNormals = [
new GameLib.D3.Vector3(),
new GameLib.D3.Vector3(),
new GameLib.D3.Vector3()
]
}
this.vertexNormals = vertexNormals;
if (!normal) {
normal = new GameLib.D3.Vector3(0);
}
this.normal = normal;
};
/**
* Clone a TriangleFace
* @returns {GameLib.D3.TriangleFace}
*/
GameLib.D3.TriangleFace.prototype.clone = function(){
return new GameLib.D3.TriangleFace(
this.v0,
this.v1,
this.v2,
this.materialIndex,
this.v0uv.copy(),
this.v1uv.copy(),
this.v2uv.copy()
);
};
/**
* Returns true if two triangles are equal (their vertex indices match in some order)
* @param triangle
* @returns {boolean}
*/
GameLib.D3.TriangleFace.prototype.equals = function(triangle) {
return !!(
(
(this.v0 == triangle.v0) &&
(this.v1 == triangle.v1) &&
(this.v2 == triangle.v2)
)
||
(
(this.v0 == triangle.v0) &&
(this.v1 == triangle.v2) &&
(this.v2 == triangle.v1)
)
||
(
(this.v0 == triangle.v1) &&
(this.v1 == triangle.v0) &&
(this.v2 == triangle.v2)
)
||
(
(this.v0 == triangle.v1) &&
(this.v1 == triangle.v2) &&
(this.v2 == triangle.v0)
)
||
(
(this.v0 == triangle.v2) &&
(this.v1 == triangle.v0) &&
(this.v2 == triangle.v1)
)
||
(
(this.v0 == triangle.v2) &&
(this.v1 == triangle.v1) &&
(this.v2 == triangle.v0)
));
};
GameLib.D3.Utils = function() {};
GameLib.D3.Utils.Extend = function(
child, // child class
parent // parent class
) {
for(var prop in parent.prototype) {
if(!child.prototype[prop]) {
child.prototype[prop] = parent.prototype[prop];
}
}
};
GameLib.D3.Utils.UndefinedOrNull = function (
variable
) {
return typeof variable == 'undefined' || variable == null;
};
GameLib.D3.Utils.Raycast = function (
from,
to,
options,
world
) {
console.log("not implemented yet");
};
GameLib.D3.Vector2 = function(x, y) {
this.x = 0;
this.y = 0;
if (x) {
this.x = x;
}
if (y) {
this.y = y;
}
};
GameLib.D3.Vector2.prototype.copy = function() {
return new GameLib.D3.Vector2(
this.x,
this.y
);
};
GameLib.D3.Vector2.prototype.equals = function(v) {
return !!(((this.x == v.x) &&
(this.y == v.y)) ||
((this.y == v.x) &&
(this.x == v.y)));
};
GameLib.D3.Vector3 = function(x, y, z) {
this.x = 0;
this.y = 0;
this.z = 0;
if (x) {
this.x = x;
}
if (y) {
this.y = y;
}
if (z) {
this.z = z;
}
};
GameLib.D3.Vector3.prototype.subtract = function (v) {
if (v instanceof GameLib.D3.Vector3) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
}
if (v instanceof GameLib.D3.Vector4) {
console.warn("trying to subtract vector of bigger length (4 vs 3))");
}
return this;
};
GameLib.D3.Vector3.prototype.cross = function (v) {
return new GameLib.D3.Vector3(
this.y * v.z - this.z * v.y,
this.z * v.x - this.x * v.z,
this.x * v.y - this.y * v.x
);
};
GameLib.D3.Vector3.prototype.negative = function () {
this.x *= -1;
this.y *= -1;
this.z *= -1;
return this;
};
GameLib.D3.Vector3.clockwise = function (u, v, w, viewPoint) {
var normal = GameLib.D3.Vector3.normal(u, v, w);
var uv = u.copy();
var winding = normal.dot(uv.subtract(viewPoint));
return (winding > 0);
};
GameLib.D3.Vector3.normal = function (u, v, w) {
var vv = v.copy();
var wv = w.copy();
return vv.subtract(u).cross(wv.subtract(u));
};
GameLib.D3.Vector3.prototype.lookAt = function (at, up) {
var lookAtMatrix = GameLib.D3.Matrix4.lookAt(this, at, up);
this.multiply(lookAtMatrix);
};
GameLib.D3.Vector3.prototype.translate = function (v) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
};
GameLib.D3.Vector3.prototype.squared = function () {
return this.x * this.x + this.y * this.y + this.z * this.z;
};
GameLib.D3.Vector3.prototype.copy = function () {
return new GameLib.D3.Vector3(
this.x,
this.y,
this.z
);
};
GameLib.D3.Vector3.prototype.multiply = function (s) {
if (s instanceof GameLib.D3.Vector3) {
this.x *= s.x;
this.y *= s.y;
this.z *= s.z;
} else if (s instanceof GameLib.D3.Matrix4) {
var x = s.rows[0].x * this.x + s.rows[0].y * this.y + s.rows[0].z * this.z;
var y = s.rows[1].x * this.x + s.rows[1].y * this.y + s.rows[1].z * this.z;
var z = s.rows[2].x * this.x + s.rows[2].y * this.y + s.rows[2].z * this.z;
this.x = x;
this.y = y;
this.z = z;
} else {
console.log("functionality not implemented - please do this");
throw new Error("not implemented");
}
return this;
};
GameLib.D3.Vector3.prototype.dot = function (v) {
return (this.x * v.x) + (this.y * v.y) + (this.z * v.z);
};
GameLib.D3.Vector3.prototype.normalize = function () {
var EPSILON = 0.000001;
var v2 = this.squared();
if (v2 < EPSILON) {
return this; //do nothing for zero vector
}
var invLength = 1 / Math.sqrt(v2);
this.x *= invLength;
this.y *= invLength;
this.z *= invLength;
return this;
};
GameLib.D3.Vector4 = function(x, y, z, w) {
this.x = 0;
this.y = 0;
this.z = 0;
this.w = 0;
if (x) {
this.x = x;
}
if (y) {
this.y = y;
}
if (z) {
this.z = z;
}
if (w) {
this.w = w;
}
};
GameLib.D3.Vector4.prototype.translate = function (v) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
};
GameLib.D3.Vector4.prototype.copy = function () {
return new GameLib.D3.Vector4(
this.x,
this.y,
this.z,
this.w
);
};
GameLib.D3.Vector4.prototype.multiply = function (s) {
if (s instanceof GameLib.D3.Vector3) {
this.x *= s.x;
this.y *= s.y;
this.z *= s.z;
} else if (s instanceof GameLib.D3.Matrix4) {
var x = s.rows[0].x * this.x + s.rows[0].y * this.y + s.rows[0].z * this.z + s.rows[0].w * this.w;
var y = s.rows[1].x * this.x + s.rows[1].y * this.y + s.rows[1].z * this.z + s.rows[1].w * this.w;
var z = s.rows[2].x * this.x + s.rows[2].y * this.y + s.rows[2].z * this.z + s.rows[2].w * this.w;
var w = s.rows[3].x * this.x + s.rows[3].y * this.y + s.rows[3].z * this.z + s.rows[3].w * this.w;
this.x = x;
this.y = y;
this.z = z;
this.w = w;
} else {
console.log("functionality not implemented - please do this");
throw new Error("not implemented");
}
};
GameLib.D3.Vector4.prototype.normalize = function () {
// note - leave w untouched
var EPSILON = 0.000001;
var v2 = this.x * this.x + this.y * this.y + this.z * this.z;
if (v2 < EPSILON) {
return this; //do nothing for zero vector
}
var invLength = 1 / Math.sqrt(v2);
this.x *= invLength;
this.y *= invLength;
this.z *= invLength;
return this;
};
GameLib.D3.Vector4.prototype.subtract = function (v) {
if (v instanceof GameLib.D3.Vector3) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
}
if (v instanceof GameLib.D3.Vector4) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
this.w -= v.w;
}
return this;
};
GameLib.D3.Vector4.Points = function () {
this.vectors = [];
};
GameLib.D3.Vector4.Points.prototype.add = function (vector) {
if (vector instanceof GameLib.D3.Vector3) {
vector = new GameLib.D3.Vector4(
vector.x,
vector.y,
vector.z,
1
)
}
if (!vector instanceof GameLib.D3.Vector4) {
console.warn("Vector needs to be of type Vector4");
throw new Error("Vector needs to be of type Vector4");
}
this.vectors.push(vector);
return this;
};
GameLib.D3.Vector4.Points.prototype.copy = function () {
var vectors = [];
for (var i = 0; i < this.vectors.length; i++) {
vectors.push(this.vectors[i].copy());
}
return vectors;
};
GameLib.D3.Vector4.Points.prototype.maximizeXDistance = function (grain) {
// console.log("vectors (before): " + JSON.stringify(this.vectors, null, 2));
var multiplier = 0;
var rotationMatrixY = new GameLib.D3.Matrix4().rotationMatrixY(grain);
var totalRadians = 0;
var backupVectors = this.copy();
var maxXDistance = 0;
for (var i = 0; i < Math.PI * 2; i += grain) {
multiplier++;
for (var j = 0; j < this.vectors.length; j++) {
this.vectors[j] = rotationMatrixY.multiply(this.vectors[j]);
}
var distances = this.distances();
if (distances.x > maxXDistance) {
maxXDistance = distances.x;
totalRadians = multiplier * grain;
}
}
this.vectors = backupVectors;
// console.log("distance: " + maxXDistance + " radians : " + totalRadians);
var maxRotationMatrix = new GameLib.D3.Matrix4().rotationMatrixY(totalRadians);
for (var k = 0; k < this.vectors.length; k++) {
this.vectors[k] = maxRotationMatrix.multiply(this.vectors[k]);
}
// console.log("vectors (after): " + JSON.stringify(this.vectors, null, 2));
};
GameLib.D3.Vector4.Points.prototype.maximizeYDistance = function (grain) {
// console.log("vectors (before): " + JSON.stringify(this.vectors, null, 2));
var multiplier = 0;
var rotationMatrixX = new GameLib.D3.Matrix4().rotationMatrixX(grain);
var totalRadians = 0;
var backupVectors = this.copy();
var maxYDistance = 0;
for (var i = 0; i < Math.PI * 2; i += grain) {
multiplier++;
for (var j = 0; j < this.vectors.length; j++) {
this.vectors[j] = rotationMatrixX.multiply(this.vectors[j]);
}
var distances = this.distances();
if (distances.y > maxYDistance) {
maxYDistance = distances.y;
totalRadians = multiplier * grain;
}
}
this.vectors = backupVectors;
// console.log("distance: " + maxYDistance + " radians : " + totalRadians);
var maxRotationMatrix = new GameLib.D3.Matrix4().rotationMatrixX(totalRadians);
for (var k = 0; k < this.vectors.length; k++) {
this.vectors[k] = maxRotationMatrix.multiply(this.vectors[k]);
}
// console.log("vectors (after): " + JSON.stringify(this.vectors, null, 2));
};
GameLib.D3.Vector4.Points.prototype.lookAt = function (at, up) {
var polyCenter = this.average();
console.log("poly center : " + JSON.stringify(polyCenter));
var lookAtMatrix = new GameLib.D3.Matrix4().lookAt(polyCenter, at, up);
lookAtMatrix.rows[0] = new GameLib.D3.Vector4(1, 0, 0, 0);
lookAtMatrix.rows[1] = new GameLib.D3.Vector4(0, 0, 1, 0);
lookAtMatrix.rows[2] = new GameLib.D3.Vector4(0, 1, 0, 0);
console.log("look at matrix : " + JSON.stringify(lookAtMatrix, null, 2));
for (var i = 0; i < this.vectors.length; i++) {
console.log("vector " + i + " (before): " + JSON.stringify(this.vectors[i]));
this.vectors[i] = lookAtMatrix.multiply(this.vectors[i]);
console.log("vector " + i + " (after) : " + JSON.stringify(this.vectors[i]));
}
};
GameLib.D3.Vector4.Points.prototype.distances = function () {
var minX = this.vectors[0].x;
var minY = this.vectors[0].y;
var minZ = this.vectors[0].z;
var maxX = this.vectors[0].x;
var maxY = this.vectors[0].y;
var maxZ = this.vectors[0].z;
for (var i = 0; i < this.vectors.length; i++) {
if (this.vectors[i].x < minX) {
minX = this.vectors[i].x;
}
if (this.vectors[i].y < minY) {
minY = this.vectors[i].y;
}
if (this.vectors[i].z < minZ) {
minZ = this.vectors[i].z;
}
if (this.vectors[i].x > maxX) {
maxX = this.vectors[i].x;
}
if (this.vectors[i].y > maxY) {
maxY = this.vectors[i].y;
}
if (this.vectors[i].z > maxZ) {
maxZ = this.vectors[i].z;
}
}
return new GameLib.D3.Vector3(
Math.abs(maxX - minX),
Math.abs(maxY - minY),
Math.abs(maxY - minZ)
)
};
GameLib.D3.Vector4.Points.prototype.average = function () {
var averageX = 0;
var averageY = 0;
var averageZ = 0;
for (var i = 0; i < this.vectors.length; i++) {
averageX += this.vectors[i].x;
averageY += this.vectors[i].y;
averageZ += this.vectors[i].z;
}
return new GameLib.D3.Vector3(
averageX / this.vectors.length,
averageY / this.vectors.length,
averageZ / this.vectors.length
);
};
GameLib.D3.Vector4.Points.prototype.negative = function () {
for (var i = 0; i < this.vectors.length; i++) {
this.vectors[i].x *= -1;
this.vectors[i].y *= -1;
this.vectors[i].z *= -1;
}
return this;
};
GameLib.D3.Vector4.Points.prototype.toOrigin = function () {
var distanceFromOrigin = this.average().negative();
for (var i = 0; i < this.vectors.length; i++) {
this.vectors[i].translate(distanceFromOrigin);
}
};
/**
* The normal gets assigned when the face calculates its normal
* @param position
* @param boneWeights GameLib.D3.BoneWeight[]
* @constructor
*/
GameLib.D3.Vertex = function(
position,
boneWeights
) {
this.position = position;
this.boneWeights = boneWeights;
};
/**
* World SuperSet - contains the custom world instance
* @param id
* @param name
* @param engine
* @param gravity
* @param broadphase
* @param solver
* @param rigidBodies
* @constructor
*/
GameLib.D3.World = function(
id,
name,
engine,
gravity,
broadphase,
solver,
rigidBodies
) {
this.id = id;
this.name = name;
if (typeof gravity == 'undefined') {
gravity = new GameLib.D3.Vector3(0, -9.81, 0);
}
this.gravity = gravity;
if (typeof broadphase == 'undefined') {
broadphase = new GameLib.D3.Broadphase(
null,
'broadPhaseNaive',
GameLib.D3.Broadphase.BROADPHASE_TYPE_NAIVE,
engine
);
}
this.broadphase = broadphase;
if (typeof solver == 'undefined') {
solver = new GameLib.D3.Solver(
null,
engine,
GameLib.D3.Solver.GS_SOLVER
);
}
this.solver = solver;
if (typeof rigidBodies == 'undefined') {
rigidBodies = [];
}
this.rigidBodies = rigidBodies;
this.engine = engine;
this.engine.isNotCannonThrow();
this.instance = this.createInstance();
};
/**
* private
* @returns {GameLib.D3.World|GameLib.D3.Physics.World|*}
*/
GameLib.D3.World.prototype.createInstance = function() {
var instance = new this.engine.instance.World();
instance.broadphase = this.broadphase.instance;
instance.solver = this.solver.instance;
instance.gravity.x = this.gravity.x;
instance.gravity.y = this.gravity.y;
instance.gravity.z = this.gravity.z;
instance.name = this.name;
return instance;
};
/**
*
* @param rigidBody GameLib.D3.RigidBody
* @constructor
*/
GameLib.D3.World.prototype.addRigidBody = function(
rigidBody
) {
this.instance.addBody(rigidBody.instance);
};
/**
*
* @param vehicle (GameLib.D3.RigidBodyVehicle | GameLib.D3.RaycastVehicle)
* @constructor
*/
GameLib.D3.World.prototype.addVehicle = function(
vehicle
) {
vehicle.instance.addToWorld(this.instance);
};
GameLib.D3.World.prototype.step = function(
timeStep
) {
// todo: figure out, why this call to internal step is more stable for trimesh collisions.....
//this.worldObject.internalStep(timeStep);
//return;
var now = performance.now() / 1000;
if(!this.lastCallTime){
// last call time not saved, cant guess elapsed time. Take a simple step.
this.instance.step(timeStep);
this.lastCallTime = now;
return;
}
var timeSinceLastCall = now - this.lastCallTime;
this.instance.step(timeStep, timeSinceLastCall);
this.lastCallTime = now;
};
GameLib.D3.World.prototype.GetIndexedVertices = function(
triangleMeshShape
) {
if(this.engine.engineType == GameLib.D3.Physics.TYPE_CANNON) {
return {
vertices : triangleMeshShape.vertices,
indices : triangleMeshShape.indices
};
} else {
// todo: implement this for other physics engines.
return null;
}
};
/**
* @param triangleMeshShape GameLib.D3.Shape
* @param normalLength Number
* @param scale GameLib.D3.Vector3
* @param opacity Number
* @param wireframeColor HexCode
* @param graphics THREE
* @returns {THREE.Mesh|this.meshes}
* @constructor
*/
GameLib.D3.World.prototype.generateWireframeViewTriangleMesh = function(
graphics,
triangleMeshShape,
normalLength,
scale,
opacity,
wireframeColor
) {
graphics.isNotThreeThrow();
this.engine.isNotCannonThrow();
if(typeof normalLength == 'undefined') {
normalLength = 10;
}
if(typeof scale == 'undefined') {
scale = new graphics.instance.Vector3(1, 1, 1);
}
if(typeof opacity == 'undefined') {
opacity = 0.5;
}
if(typeof wireframeColor == 'undefined') {
wireframeColor = 0xfefefe;
}
var graphicsGeometry = new graphics.instance.Geometry();
var wireframeMesh = new graphics.instance.Mesh(
graphicsGeometry,
new graphics.instance.MeshBasicMaterial({
color: wireframeColor,
wireframe: true,
opacity: opacity
})
);
for(var v = 0, l = triangleMeshShape.instance.vertices.length / 3; v < l; ++v) {
graphicsGeometry.vertices.push(
new graphics.instance.Vector3(
triangleMeshShape.instance.vertices[v * 3],
triangleMeshShape.instance.vertices[v * 3 + 1],
triangleMeshShape.instance.vertices[v * 3 + 2]
)
);
}
for(var i = 0, l = triangleMeshShape.instance.indices.length / 3; i < l; ++i) {
var i0 = triangleMeshShape.instance.indices[i * 3];
var i1 = triangleMeshShape.instance.indices[i * 3 + 1];
var i2 = triangleMeshShape.instance.indices[i * 3 + 2];
graphicsGeometry.faces.push(
new graphics.instance.Face3(
i0,
i1,
i2
)
);
// Center point on the current triangle
var centroid = new graphics.instance.Vector3()
.add(graphicsGeometry.vertices[i0])
.add(graphicsGeometry.vertices[i1])
.add(graphicsGeometry.vertices[i2])
.divideScalar(3);
// Get the normal from the mesh shape itself
var normal = new this.engine.instance.Vec3();
triangleMeshShape.instance.getNormal(i , normal);
var arrow = new graphics.instance.ArrowHelper(
new graphics.instance.Vector3(
normal.x,
normal.y,
normal.z
),
centroid,
normalLength,
new graphics.instance.Color(
normal.x,
normal.y,
normal.z
)
);
wireframeMesh.add( arrow );
}
wireframeMesh.scale.x = scale.x;
wireframeMesh.scale.y = scale.y;
wireframeMesh.scale.z = scale.z;
return wireframeMesh;
};
/**
* @param convexPolyMeshShape GameLib.D3.Shape
* @param normalLength Number
* @param scale GameLib.D3.Vector3
* @param opacity Number
* @param wireframeColor HexCode
* @param graphics THREE
* @returns {THREE.Mesh|this.meshes}
* @constructor
*/
GameLib.D3.World.prototype.generateWireframeViewConvexPolyMesh = function(
graphics,
convexPolyMeshShape,
normalLength,
scale,
opacity,
wireframeColor
) {
graphics.isNotThreeThrow();
this.engine.isNotCannonThrow();
if(typeof normalLength == 'undefined') {
normalLength = 10;
}
if(typeof scale == 'undefined') {
scale = new graphics.instance.Vector3(1, 1, 1);
}
if(typeof opacity == 'undefined') {
opacity = 0.5;
}
if(typeof wireframeColor == 'undefined') {
wireframeColor = 0xfefefe;
}
var graphicsGeometry = new graphics.instance.Geometry();
var wireframeMesh = new graphics.instance.Mesh(
graphicsGeometry,
new graphics.instance.MeshBasicMaterial({
color: wireframeColor,
wireframe: true,
opacity: opacity
})
);
for(var i = 0, l = convexPolyMeshShape.instance.vertices.length; i < l; i++) {
var vertex = convexPolyMeshShape.instance.vertices[i];
graphicsGeometry.vertices.push(new graphics.instance.Vector3(vertex.x, vertex.y, vertex.z));
}
for(var i = 0, l = convexPolyMeshShape.instance.faces.length; i < l; i++) {
var face = convexPolyMeshShape.instance.faces[i];
var i0 = face[0];
var i1 = face[1];
var i2 = face[2];
graphicsGeometry.faces.push(new graphics.instance.Face3(i0, i1, i2));
// Center point on the current triangle
var centroid = new graphics.instance.Vector3()
.add(graphicsGeometry.vertices[i0])
.add(graphicsGeometry.vertices[i1])
.add(graphicsGeometry.vertices[i2])
.divideScalar(3);
var normalVec3 = convexPolyMeshShape.instance.faceNormals[i];
var normal = new graphics.instance.Vector3(
normalVec3.x,
normalVec3.y,
normalVec3.z
);
var arrow = new graphics.instance.ArrowHelper(
normal,
centroid,
normalLength,
new graphics.instance.Color(
normal.x,
normal.y,
normal.z
)
);
wireframeMesh.add( arrow );
}
wireframeMesh.scale.x = scale.x;
wireframeMesh.scale.y = scale.y;
wireframeMesh.scale.z = scale.z;
return wireframeMesh;
};
/**
* @param graphics GameLib.D3.Graphics
* @param graphicsMesh THREE.Mesh
* @param mass Number
* @param friction Number
* @param createCollisionSubMeshes Boolean
* @param facesPerSubsection Number
* @param subsectionsToMerge Number
* @returns {Object}
* @constructor
*/
GameLib.D3.World.prototype.generateTriangleMeshShapeDivided = function(
graphics,
graphicsMesh,
mass,
friction,
createCollisionSubMeshes,
facesPerSubsection,
subsectionsToMerge
) {
graphics.isNotThreeThrow();
this.engine.isNotCannonThrow();
if(mass == null || typeof mass == 'undefined') {
mass = 0;
}
if(friction == null || typeof friction == 'undefined') {
friction = 10;
}
if(createCollisionSubMeshes == null || typeof createCollisionSubMeshes == 'undefined') {
createCollisionSubMeshes = false;
}
var processedFaces = 0;
var facesPerSubSection = facesPerSubsection || 0;
var subMeshesToMerge = subsectionsToMerge || 0;
var totalAmtFaces = graphicsMesh.geometry.faces.length;
var facesToProcess = createCollisionSubMeshes ? (subMeshesToMerge * facesPerSubSection) : totalAmtFaces;
var pairs = []; // output
var vertices = [];
var indicies = [];
for(var i = 0; i <= totalAmtFaces; i++) {
if(processedFaces == facesToProcess || i == totalAmtFaces) {
var body = null;
var meshShape = new this.engine.instance.Trimesh(vertices, indicies);
meshShape.setScale(new this.engine.instance.Vec3(
graphicsMesh.scale.x,
graphicsMesh.scale.y,
graphicsMesh.scale.z
));
meshShape.updateAABB();
meshShape.updateNormals();
meshShape.updateEdges();
meshShape.updateBoundingSphereRadius();
meshShape.updateTree();
body = new this.engine.instance.Body({
mass: mass,
friction: friction
});
body.addShape(meshShape);
pairs.push({
threeObject : createCollisionSubMeshes ? null : graphicsMesh,
physicsObject : body
});
vertices = [];
indicies = [];
processedFaces = 0;
if(i == totalAmtFaces) {
return pairs;
}
}
var face = graphicsMesh.geometry.faces[i];
indicies.push(indicies.length);
indicies.push(indicies.length);
indicies.push(indicies.length);
var v0 = graphicsMesh.geometry.vertices[face.a];
var v1 = graphicsMesh.geometry.vertices[face.b];
var v2 = graphicsMesh.geometry.vertices[face.c];
vertices.push(v0.x, v0.y, v0.z);
vertices.push(v1.x, v1.y, v1.z);
vertices.push(v2.x, v2.y, v2.z);
processedFaces++;
}
};
/**
* @param graphics GameLib.D3.Graphics
* @param graphicsMesh THREE.Mesh
* @returns {GameLib.D3.Shape}
* @constructor
*/
GameLib.D3.World.prototype.generateTriangleMeshShape = function(
graphics,
graphicsMesh
) {
// - - - - - - - - - - - - - - - - - - - - - - - - -
// Note: I did not test this yet with the API data.
// - - - - - - - - - - - - - - - - - - - - - - - - -
var scaledVertices = [];
for(var i = 0, l = graphicsMesh.geometry.vertices.length; i < l; i++) {
var vertex = graphicsMesh.geometry.vertices[i];
scaledVertices.push(new this.engine.instance.Vec3(
vertex.x * graphicsMesh.scale.x,
vertex.y * graphicsMesh.scale.y,
vertex.z * graphicsMesh.scale.z
));
}
var triangleFaces = [];
for(var f = 0, fl = graphicsMesh.geometry.faces.length; f < fl; f++) {
var i0 = graphicsMesh.geometry.faces[f].a;
var i1 = graphicsMesh.geometry.faces[f].b;
var i2 = graphicsMesh.geometry.faces[f].c;
triangleFaces.push([
i0, i1, i2
]);
}
// - - - - - - - - - - - - - - - - - - -
// Create collision mesh
// - - - - - - - - - - - - - - - - - - -
var reindexedFaces = {};
var vertices = [];
var faces = [];
var processedFaces = 0;
var totalFacesToProcess = triangleFaces.length;
var flLastIndex = 0;
for(var f = 0; f < totalFacesToProcess; f++) {
var i0 = triangleFaces[f][0];
var i1 = triangleFaces[f][1];
var i2 = triangleFaces[f][2];
if(typeof reindexedFaces[i0] === 'undefined') {
vertices.push(scaledVertices[i0].x, scaledVertices[i0].y, scaledVertices[i0].z);
reindexedFaces[i0] = flLastIndex;
flLastIndex++;
}
if(typeof reindexedFaces[i1] === 'undefined') {
vertices.push(scaledVertices[i1].x, scaledVertices[i1].y, scaledVertices[i1].z);
reindexedFaces[i1] = flLastIndex;
flLastIndex++;
}
if(typeof reindexedFaces[i2] === 'undefined') {
vertices.push(scaledVertices[i2].x, scaledVertices[i2].y, scaledVertices[i2].z);
reindexedFaces[i2] = flLastIndex;
flLastIndex++;
}
faces.push(reindexedFaces[i0], reindexedFaces[i1], reindexedFaces[i2]);
processedFaces++;
}
return new GameLib.D3.Shape(this.engine, GameLib.D3.Shape.SHAPE_TYPE_TRIMESH, {x : 1, y : 1, z : 1}, vertices, faces);
};
/**
* @param triangleMeshBody GameLib.D3.RigidBody
* @param rayscale Number
* @param maxTriangleDistance Number
* @param createCompoundShape Boolean
* @param graphics GameLib.D3.Graphics
* @param triangleMeshShapes GameLib.D3.Shape[]
* @param createDebugView Boolean
* @returns {GameLib.D3.RigidBody}
* @constructor
*/
GameLib.D3.World.prototype.fixupTriangleMeshShape = function(
triangleMeshBody,
triangleMeshShapes,
rayscale,
maxTriangleDistance,
createCompoundShape,
graphics,
createDebugView
) {
this.engine.isNotCannonThrow();
graphics.isNotThreeThrow();
if(rayscale == null || typeof rayscale == 'undefined' || rayscale == 0) {
rayscale = 10;
}
if(maxTriangleDistance == null || typeof maxTriangleDistance == 'undefined') {
maxTriangleDistance = 13;
}
var world = this.instance;
var raycastResult = new this.engine.instance.RaycastResult();
var brokenFaceIndicators = [];
var totalFaces = 0;
var totalBrokenFaces = 0;
var totalFixedFaces = 0;
var fixedTriangleMeshObjects = [];
for(var i in triangleMeshShapes) {
var trimesh = triangleMeshShapes[i].instance;
var brokenFaces = [];
totalFaces += (trimesh.indices.length / 3);
for(var face = 0; face < trimesh.indices.length / 3; face++) {
var i0 = trimesh.indices[face * 3];
var i1 = trimesh.indices[face * 3 + 1];
var i2 = trimesh.indices[face * 3 + 2];
var triangleCenterPoint = new graphics.instance.Vector3()
.add(new graphics.instance.Vector3(
trimesh.vertices[i0 * 3],
trimesh.vertices[i0 * 3 + 1],
trimesh.vertices[i0 * 3 + 2])
)
.add(new graphics.instance.Vector3(
trimesh.vertices[i1 * 3],
trimesh.vertices[i1 * 3 + 1],
trimesh.vertices[i1 * 3 + 2])
)
.add(new graphics.instance.Vector3(
trimesh.vertices[i2 * 3],
trimesh.vertices[i2 * 3 + 1],
trimesh.vertices[i2 * 3 + 2])
)
.divideScalar(3);
var triangleNormal = new this.engine.instance.Vec3();
trimesh.getNormal(face , triangleNormal);
var from = new this.engine.instance.Vec3(
triangleCenterPoint.x + triangleNormal.x,
triangleCenterPoint.y + triangleNormal.y,
triangleCenterPoint.z + triangleNormal.z
);
var to = new this.engine.instance.Vec3(
from.x - triangleNormal.x * rayscale,
from.y - triangleNormal.y * rayscale,
from.z - triangleNormal.z * rayscale
);
world.raycastClosest(from, to, {}, raycastResult);
// visualize results
if(createDebugView){
var graphicsGeometry = new graphics.instance.Geometry();
var wireframeMesh = new graphics.instance.Mesh(
graphicsGeometry,
new graphics.instance.MeshBasicMaterial({
color: 0xff0000,
wireframe: true,
opacity: 1
})
);
var arrow = new graphics.instance.ArrowHelper(
new graphics.instance.Vector3(
triangleNormal.x,
triangleNormal.y,
triangleNormal.z
).normalize(),
new graphics.instance.Vector3(
from.x,
from.y,
from.z
),
rayscale / 2,
raycastResult.hasHit ? new graphics.instance.Color(0, 1, 0)
: new graphics.instance.Color(1, 0, 0)
);
wireframeMesh.add( arrow );
brokenFaceIndicators.push(wireframeMesh);
}
if(!raycastResult.hasHit) {
brokenFaces.push({
faceIndex : face,
vertices : [
new this.engine.instance.Vec3(
trimesh.vertices[i0 * 3],
trimesh.vertices[i0 * 3 + 1],
trimesh.vertices[i0 * 3 + 2]
),
new this.engine.instance.Vec3(
trimesh.vertices[i1 * 3],
trimesh.vertices[i1 * 3 + 1],
trimesh.vertices[i1 * 3 + 2]
),
new this.engine.instance.Vec3(
trimesh.vertices[i2 * 3],
trimesh.vertices[i2 * 3 + 1],
trimesh.vertices[i2 * 3 + 2]
)
],
center : triangleCenterPoint,
parent : trimesh
});
}
}
// fix up broken faces
var bFaceIndexed = {};
for(var b = 0; b < brokenFaces.length; b++) {
var brokenFace = brokenFaces[b];
if(brokenFace.marked) {
continue;
}
bFaceIndexed[b] = {
indices : [],
vertices : []
};
var indicesAmount = bFaceIndexed[b].indices.length;
// add the current broken face itself to the array
bFaceIndexed[b].indices.push(
indicesAmount,
indicesAmount + 1,
indicesAmount + 2
);
bFaceIndexed[b].vertices.push(
brokenFace.vertices[0].x,
brokenFace.vertices[0].y,
brokenFace.vertices[0].z
);
bFaceIndexed[b].vertices.push(
brokenFace.vertices[1].x,
brokenFace.vertices[1].y,
brokenFace.vertices[1].z
);
bFaceIndexed[b].vertices.push(
brokenFace.vertices[2].x,
brokenFace.vertices[2].y,
brokenFace.vertices[2].z
);
for(var bb = 0; bb < brokenFaces.length; bb++) {
if(bb == b) {
continue;
}
var otherBrokenFace = brokenFaces[bb];
if(otherBrokenFace.marked) {
continue;
}
if(brokenFace.center.distanceTo(otherBrokenFace.center) <= maxTriangleDistance) {
var indicesAmount = bFaceIndexed[b].indices.length;
bFaceIndexed[b].indices.push(
indicesAmount,
indicesAmount + 1,
indicesAmount + 2
);
bFaceIndexed[b].vertices.push(
otherBrokenFace.vertices[0].x,
otherBrokenFace.vertices[0].y,
otherBrokenFace.vertices[0].z
);
bFaceIndexed[b].vertices.push(
otherBrokenFace.vertices[1].x,
otherBrokenFace.vertices[1].y,
otherBrokenFace.vertices[1].z
);
bFaceIndexed[b].vertices.push(
otherBrokenFace.vertices[2].x,
otherBrokenFace.vertices[2].y,
otherBrokenFace.vertices[2].z
);
otherBrokenFace.marked = true;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Decide if we want to create new rigid bodies, or create a compound mesh
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
for(var e in bFaceIndexed) {
var element = bFaceIndexed[e];
var shape = new GameLib.D3.Shape(this.engine, GameLib.D3.Shape.SHAPE_TYPE_TRIMESH, { x : 1, y : 1, z : 1 }, element.vertices, element.indices);
if(createCompoundShape) {
triangleMeshBody.addShape(shape);
} else {
var body = new GameLib.D3.RigidBody(this.engine, 0, 12);
body.addShape(shape);
this.addRigidBody(body);
}
fixedTriangleMeshObjects.push(shape);
totalFixedFaces += element.indices.length / 3;
}
// TODO: remove duplicate indices
/*trimesh.updateNormals();
trimesh.updateEdges();
trimesh.updateTree();
trimesh.updateAABB();
trimesh.updateBoundingSphereRadius();*/
// map faceIndex to flat face index (faceIndex * 3) +0, 1, 2 -> triangle indices
console.log("i = " + i, brokenFaces);
totalBrokenFaces += brokenFaces.length;
}
console.log("total faces", totalFaces);
console.log("total broken faces", totalBrokenFaces);
console.log("broken faces in percent", (totalBrokenFaces / totalFaces) * 100);
console.log("total fixed faces", totalFixedFaces);
console.log("fixed triangle mesh shapes", fixedTriangleMeshObjects.length);
return {
brokenFaceIndicators : brokenFaceIndicators,
fixedTriangleMeshShapes : fixedTriangleMeshObjects
};
};
if (typeof module !== 'undefined') {
module.exports = GameLib;
}