Deltakosh 12 éve
commit
4b747f4c71
47 módosított fájl, 8221 hozzáadás és 0 törlés
  1. 38 0
      Animations/babylon.animatable.js
  2. 167 0
      Animations/babylon.animation.js
  3. 142 0
      Cameras/babylon.arcRotateCamera.js
  4. 42 0
      Cameras/babylon.camera.js
  5. 252 0
      Cameras/babylon.freeCamera.js
  6. 126 0
      Cameras/babylon.touchCamera.js
  7. 307 0
      Collisions/babylon.collider.js
  8. 34 0
      Collisions/babylon.collisionPlane.js
  9. 121 0
      Culling/babylon.boundingBox.js
  10. 106 0
      Culling/babylon.boundingInfo.js
  11. 63 0
      Culling/babylon.boundingSphere.js
  12. 94 0
      Layer/babylon.layer.js
  13. 19 0
      Lights/babylon.directionalLight.js
  14. 19 0
      Lights/babylon.light.js
  15. 19 0
      Lights/babylon.pointLight.js
  16. 153 0
      Materials/babylon.effect.js
  17. 61 0
      Materials/babylon.material.js
  18. 22 0
      Materials/babylon.multiMaterial.js
  19. 359 0
      Materials/babylon.standardMaterial.js
  20. 89 0
      Materials/textures/babylon.baseTexture.js
  21. 20 0
      Materials/textures/babylon.cubeTexture.js
  22. 32 0
      Materials/textures/babylon.dynamicTexture.js
  23. 40 0
      Materials/textures/babylon.mirrorTexture.js
  24. 89 0
      Materials/textures/babylon.renderTargetTexture.js
  25. 89 0
      Materials/textures/babylon.texture.js
  26. 703 0
      Mesh/babylon.mesh.js
  27. 97 0
      Mesh/babylon.subMesh.js
  28. 16 0
      Particles/babylon.particle.js
  29. 359 0
      Particles/babylon.particleSystem.js
  30. 221 0
      Shaders/default.fragment.fx
  31. 179 0
      Shaders/default.vertex.fx
  32. 175 0
      Shaders/iedefault.fragment.fx
  33. 179 0
      Shaders/iedefault.vertex.fx
  34. 14 0
      Shaders/layer.fragment.fx
  35. 16 0
      Shaders/layer.vertex.fx
  36. 23 0
      Shaders/particles.fragment.fx
  37. 47 0
      Shaders/particles.vertex.fx
  38. 22 0
      Shaders/sprites.fragment.fx
  39. 39 0
      Shaders/sprites.vertex.fx
  40. 65 0
      Sprites/babylon.sprite.js
  41. 150 0
      Sprites/babylon.spriteManager.js
  42. 1192 0
      Tools/babylon.math.js
  43. 446 0
      Tools/babylon.sceneLoader.js
  44. 220 0
      Tools/babylon.tools.dds.js
  45. 173 0
      Tools/babylon.tools.js
  46. 763 0
      babylon.engine.js
  47. 619 0
      babylon.scene.js

+ 38 - 0
Animations/babylon.animatable.js

@@ -0,0 +1,38 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON._Animatable = function (target, from, to, loop, speedRatio) {
+        this.target = target;
+        this.fromFrame = from;
+        this.toFrame = to;
+        this.loopAnimation = loop;
+        this.animationStartDate = new Date();
+        this.speedRatio = speedRatio ? speedRatio : 1.0;
+    };
+    
+    // Members
+    BABYLON._Animatable.prototype.target = null;
+    BABYLON._Animatable.prototype.animationStarted = false;
+    BABYLON._Animatable.prototype.loopAnimation = false;
+    BABYLON._Animatable.prototype.fromFrame = false;
+    BABYLON._Animatable.prototype.toFrame = false;
+    BABYLON._Animatable.prototype.speedRatio = 1.0;
+    
+    // Methods
+    BABYLON._Animatable.prototype._animate = function () {
+
+        // Getting time
+        var now = new Date();
+        var delay = now - this.animationStartDate;
+
+        // Animating
+        var running = false;
+        var animations = this.target.animations;
+        for (var index = 0; index < animations.length; index++) {
+            var isRunning = animations[index].animate(this.target, delay, this.fromFrame, this.toFrame, this.loopAnimation, this.speedRatio);
+            running = running || isRunning;
+        }
+
+        return running;
+    };
+})();

+ 167 - 0
Animations/babylon.animation.js

@@ -0,0 +1,167 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Animation = function (name, targetProperty, framePerSecond, dataType, loopMode) {
+        this.name = name;
+        this.targetPropertyPath = targetProperty.split(".");
+        this.framePerSecond = framePerSecond;
+        this.dataType = dataType;
+        this.loopMode = loopMode === undefined ? BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE : loopMode;
+
+        this._keys = [];
+        
+        // Cache
+        this._offsetsCache = {};
+        this._highLimitsCache = {};
+    };
+
+    // Methods   
+    BABYLON.Animation.prototype.clone = function() {
+        var clone = new BABYLON.Animation(this.name, this.targetPropertyPath.join("."), this.framePerSecond, this.dataType, this.loopMode);
+
+        clone.setKeys(this._keys);
+
+        return clone;
+    };
+
+    BABYLON.Animation.prototype.setKeys = function(values) {
+        this._keys = values.slice(0);
+        this._offsetsCache = {};
+        this._highLimitsCache = {};
+    };
+
+    BABYLON.Animation.prototype._interpolate = function (currentFrame, repeatCount, loopMode, offsetValue, highLimitValue) {
+        if (loopMode === BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT && repeatCount > 0) {
+            return highLimitValue.clone ? highLimitValue.clone() : highLimitValue;
+        }
+
+        for (var key = 0; key < this._keys.length; key++) {
+            if (this._keys[key + 1].frame >= currentFrame) {
+                var startValue = this._keys[key].value;
+                var endValue = this._keys[key + 1].value;
+                var gradient = (currentFrame - this._keys[key].frame) / (this._keys[key + 1].frame - this._keys[key].frame);
+
+                switch (this.dataType) {
+                    // Float
+                    case BABYLON.Animation.ANIMATIONTYPE_FLOAT:
+                        switch (loopMode) {
+                            case BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE:
+                            case BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                return startValue + (endValue - startValue) * gradient;                                
+                            case BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                return offsetValue * repeatCount + (startValue + (endValue - startValue) * gradient);
+                        }
+                        break;
+                    // Quaternion
+                    case BABYLON.Animation.ANIMATIONTYPE_QUATERNION:
+                        var quaternion = null;
+                        switch (loopMode) {
+                            case BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE:
+                            case BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                quaternion = BABYLON.Quaternion.Slerp(startValue, endValue, gradient);
+                                break;
+                            case BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                quaternion = BABYLON.Quaternion.Slerp(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
+                                break;
+                        }
+
+                        return quaternion.toEulerAngles();
+                    // Vector3
+                    case BABYLON.Animation.ANIMATIONTYPE_VECTOR3:
+                        switch (loopMode) {
+                            case BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE:
+                            case BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                return BABYLON.Vector3.Lerp(startValue, endValue, gradient);
+                            case BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                return BABYLON.Vector3.Lerp(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
+                        }
+                    default:
+                        break;
+                }
+                break;
+            }
+        }
+        return this._keys[this._keys.length - 1].value;
+    };
+
+    BABYLON.Animation.prototype.animate = function (target, delay, from, to, loop, speedRatio) {
+        if (!this.targetPropertyPath || this.targetPropertyPath.length < 1) {
+            return false;
+        }
+
+        // Check limits
+        if (from < this._keys[0].frame || from > this._keys[this._keys.length - 1].frame) {
+            from = this._keys[0].frame;
+        }
+        if (to < this._keys[0].frame || to > this._keys[this._keys.length - 1].frame) {
+            to = this._keys[this._keys.length - 1].frame;
+        }
+
+        // Compute ratio
+        var range = to - from;
+        var ratio = delay * (this.framePerSecond * speedRatio) / 1000.0;
+
+        if (ratio > range && !loop) { // If we are out of range and not looping get back to caller
+            return false;
+        }
+        
+        // Get max value if required
+        var offsetValue = 0;
+        var highLimitValue = 0;
+        if (this.loopMode != BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE) {
+            var keyOffset = to.toString() + from.toString();
+            if (!this._offsetsCache[keyOffset]) {
+                var fromValue = this._interpolate(from, 0, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
+                var toValue = this._interpolate(to, 0, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
+                switch (this.dataType) {
+                    // Float
+                    case BABYLON.Animation.ANIMATIONTYPE_FLOAT:
+                        this._offsetsCache[keyOffset] = toValue - fromValue;
+                        break;
+                    // Quaternion
+                    case BABYLON.Animation.ANIMATIONTYPE_QUATERNION:
+                        this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
+                        break;
+                    // Vector3
+                    case BABYLON.Animation.ANIMATIONTYPE_VECTOR3:
+                        this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
+                    default:
+                        break;
+                }
+
+                this._highLimitsCache[keyOffset] = toValue;
+            }
+
+            highLimitValue = this._highLimitsCache[keyOffset];
+            offsetValue = this._offsetsCache[keyOffset];
+        }
+
+        // Compute value
+        var repeatCount = (ratio / range) >> 0;
+        var currentFrame = from + ratio % range;
+        var currentValue = this._interpolate(currentFrame, repeatCount, this.loopMode, offsetValue, highLimitValue);
+
+        // Set value
+        if (this.targetPropertyPath.length > 1) {
+            var property = target[this.targetPropertyPath[0]];
+
+            for (var index = 1; index < this.targetPropertyPath.length - 1; index++) {
+                property = property[this.targetPropertyPath[index]];
+            }
+
+            property[this.targetPropertyPath[this.targetPropertyPath.length - 1]] = currentValue;
+        } else {
+            target[this.targetPropertyPath[0]] = currentValue;
+        }
+        return true;
+    };
+
+    // Statics
+    BABYLON.Animation.ANIMATIONTYPE_FLOAT = 0;
+    BABYLON.Animation.ANIMATIONTYPE_VECTOR3 = 1;
+    BABYLON.Animation.ANIMATIONTYPE_QUATERNION = 2;
+
+    BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE = 0;
+    BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE = 1;
+    BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT = 2;
+})();

+ 142 - 0
Cameras/babylon.arcRotateCamera.js

@@ -0,0 +1,142 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.ArcRotateCamera = function (name, alpha, beta, radius, target, scene) {
+        this.name = name;
+        this.id = name;
+        this.alpha = alpha;
+        this.beta = beta;
+        this.radius = radius;
+        this.target = target;
+
+        this._scene = scene;
+
+        scene.cameras.push(this);
+        
+        if (!scene.activeCamera) {
+            scene.activeCamera = this;
+        }
+
+        this.getViewMatrix();
+        
+        // Animations
+        this.animations = [];
+    };
+    
+    BABYLON.ArcRotateCamera.prototype = Object.create(BABYLON.Camera.prototype);
+
+    // Members
+    BABYLON.ArcRotateCamera.prototype.inertialAlphaOffset = 0;
+    BABYLON.ArcRotateCamera.prototype.inertialBetaOffset = 0;
+
+    // Methods
+    BABYLON.ArcRotateCamera.prototype.attachControl = function(canvas) {
+        var previousPosition;
+        var that = this;
+        
+        this._onPointerDown = function (evt) {
+            previousPosition = {
+                x: evt.clientX,
+                y: evt.clientY
+            };
+
+            evt.preventDefault();
+        };
+
+        this._onPointerUp = function (evt) {
+            previousPosition = null;
+            evt.preventDefault();
+        };
+
+        this._onPointerMove = function (evt) {
+            if (!previousPosition) {
+                return;
+            }
+
+            var offsetX = evt.clientX - previousPosition.x;
+            var offsetY = evt.clientY - previousPosition.y;
+
+            that.inertialAlphaOffset -= offsetX / 1000;
+            that.inertialBetaOffset -= offsetY / 1000;
+
+            previousPosition = {
+                x: evt.clientX,
+                y: evt.clientY
+            };
+
+            evt.preventDefault();
+        };
+
+        this._wheel = function(event) {
+            var delta = 0;
+            if (event.wheelDelta) {
+                delta = event.wheelDelta / 120;
+            } else if (event.detail) {
+                delta = -event.detail / 3;
+            }
+
+            if (delta)
+                that.radius -= delta;
+
+            if (event.preventDefault)
+                event.preventDefault();
+        };
+
+        canvas.addEventListener("pointerdown", this._onPointerDown);
+        canvas.addEventListener("pointerup", this._onPointerUp);
+        canvas.addEventListener("pointerout", this._onPointerUp);
+        canvas.addEventListener("pointermove", this._onPointerMove);
+        window.addEventListener('mousewheel', this._wheel);
+    };
+    
+    BABYLON.ArcRotateCamera.prototype.detachControl = function (canvas) {
+        canvas.removeEventListener("pointerdown", this._onPointerDown);
+        canvas.removeEventListener("pointerup", this._onPointerUp);
+        canvas.removeEventListener("pointerout", this._onPointerUp);
+        canvas.removeEventListener("pointermove", this._onPointerMove);
+        window.removeEventListener('mousewheel', this._wheel);
+    };
+
+    BABYLON.ArcRotateCamera.prototype._update = function() {
+        // Inertia
+        if (this.inertialAlphaOffset != 0 || this.inertialBetaOffset != 0) {
+
+            this.alpha += this.inertialAlphaOffset;
+            this.beta += this.inertialBetaOffset;
+
+            this.inertialAlphaOffset *= this.inertia;
+            this.inertialBetaOffset *= this.inertia;
+
+            if (Math.abs(this.inertialAlphaOffset) < BABYLON.Engine.epsilon)
+                this.inertialAlphaOffset = 0;
+
+            if (Math.abs(this.inertialBetaOffset) < BABYLON.Engine.epsilon)
+                this.inertialBetaOffset = 0;
+        }
+    };
+
+    BABYLON.ArcRotateCamera.prototype.setPosition = function(position) {
+        var radiusv3 = position.subtract(this.target.position ? this.target.position : this.target);
+        this.radius = radiusv3.length();
+
+        this.alpha = Math.atan(radiusv3.z / radiusv3.x);
+        this.beta = Math.acos(radiusv3.y / this.radius);
+    };
+
+    BABYLON.ArcRotateCamera.prototype.getViewMatrix = function () {
+        // Compute
+        if (this.beta > Math.PI)
+            this.beta = Math.PI;
+
+        if (this.beta <= 0)
+            this.beta = 0.01;
+
+        var cosa = Math.cos(this.alpha);
+        var sina = Math.sin(this.alpha);
+        var cosb = Math.cos(this.beta);
+        var sinb = Math.sin(this.beta);
+
+        this.position = this.target.add(new BABYLON.Vector3(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb));
+        return new BABYLON.Matrix.LookAtLH(this.position, this.target, BABYLON.Vector3.Up());
+    };
+})();

+ 42 - 0
Cameras/babylon.camera.js

@@ -0,0 +1,42 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Camera = function (name, position, scene) {
+        this.name = name;
+        this.id = name;
+        this.position = position;
+       
+        this._scene = scene;
+
+        scene.cameras.push(this);
+
+        if (!scene.activeCamera) {
+            scene.activeCamera = this;
+        }
+    };
+    
+    // Members
+    BABYLON.Camera.prototype.fov = 0.8;
+    BABYLON.Camera.prototype.minZ = 0.1;
+    BABYLON.Camera.prototype.maxZ = 1000.0;
+    BABYLON.Camera.prototype.inertia = 0.9;
+
+    // Methods
+    BABYLON.Camera.prototype.attachControl = function (canvas) {
+    };
+
+    BABYLON.Camera.prototype.detachControl = function (canvas) {
+    };
+
+    BABYLON.Camera.prototype._update = function () {
+    };
+
+    BABYLON.Camera.prototype.getViewMatrix = function () {
+        return BABYLON.Matrix.Identity();
+    };
+
+    BABYLON.Camera.prototype.getProjectionMatrix = function () {
+        return new BABYLON.Matrix.PerspectiveFovLH(this.fov, this._scene.getEngine().getAspectRatio(), this.minZ, this.maxZ);
+    };
+
+})();

+ 252 - 0
Cameras/babylon.freeCamera.js

@@ -0,0 +1,252 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.FreeCamera = function (name, position, scene) {
+        this.name = name;
+        this.id = name;
+
+        this._scene = scene;
+        this.position = position;
+        scene.cameras.push(this);
+        this.cameraDirection = new BABYLON.Vector3(0, 0, 0);
+        this.cameraRotation = new BABYLON.Vector2(0, 0);
+        this.rotation = new BABYLON.Vector3(0, 0, 0);
+        this.ellipsoid = new BABYLON.Vector3(0.5, 1, 0.5);
+
+        this._keys = [];
+        this.keysUp = [38];
+        this.keysDown = [40];
+        this.keysLeft = [37];
+        this.keysRight = [39];
+
+        if (!scene.activeCamera) {
+            scene.activeCamera = this;
+        }
+        // Collisions
+        this._collider = new BABYLON.Collider();
+        this._needMoveForGravity = true;
+        
+        // Animations
+        this.animations = [];
+    };
+
+    BABYLON.FreeCamera.prototype = Object.create(BABYLON.Camera.prototype);
+
+    // Members
+    BABYLON.FreeCamera.prototype.speed = 2.0;
+    BABYLON.FreeCamera.prototype.checkCollisions = false;
+    BABYLON.FreeCamera.prototype.applyGravity = false;
+
+    // Methods
+    BABYLON.FreeCamera.prototype._computeLocalCameraSpeed = function () {
+        return this.speed * ((BABYLON.Tools.GetDeltaTime() / (BABYLON.Tools.GetFps() * 10.0)));
+    };
+
+    // Target
+    BABYLON.FreeCamera.prototype.setTarget = function (target) {
+        var camMatrix = BABYLON.Matrix.LookAtLH(this.position, target, BABYLON.Vector3.Up());
+        camMatrix.invert();
+
+        this.rotation.x = Math.atan(camMatrix.m[6] / camMatrix.m[10]);
+
+        var vDir = target.subtract(this.position);
+
+        if (vDir.x >= 0.0) {
+            this.rotation.y = (-Math.atan(vDir.z / vDir.x) + Math.PI / 2.0);
+        } else {
+            this.rotation.y = (-Math.atan(vDir.z / vDir.x) - Math.PI / 2.0);
+        }
+
+        this.rotation.z = -Math.acos(BABYLON.Vector3.Dot(new BABYLON.Vector3(0, 1.0, 0), BABYLON.Vector3.Up()));
+
+        if (isNaN(this.rotation.x))
+            this.rotation.x = 0;
+
+        if (isNaN(this.rotation.y))
+            this.rotation.y = 0;
+
+        if (isNaN(this.rotation.z))
+            this.rotation.z = 0;
+    };
+
+    // Controls
+    BABYLON.FreeCamera.prototype.attachControl = function (canvas) {
+        var previousPosition;
+        var that = this;
+
+        this._onMouseDown = function (evt) {
+            previousPosition = {
+                x: evt.clientX,
+                y: evt.clientY
+            };
+
+            evt.preventDefault();
+        };
+
+        this._onMouseUp = function (evt) {
+            previousPosition = null;
+            evt.preventDefault();
+        };
+
+        this._onMouseOut = function (evt) {
+            previousPosition = null;
+            that._keys = [];
+            evt.preventDefault();
+        };
+
+        this._onMouseMove = function (evt) {
+            if (!previousPosition) {
+                return;
+            }
+
+            var offsetX = evt.clientX - previousPosition.x;
+            var offsetY = evt.clientY - previousPosition.y;
+
+            that.cameraRotation.y += offsetX / 2000.0;
+            that.cameraRotation.x += offsetY / 2000.0;
+
+            previousPosition = {
+                x: evt.clientX,
+                y: evt.clientY
+            };
+            evt.preventDefault();
+        };
+
+        this._onKeyDown = function (evt) {
+            if (that.keysUp.indexOf(evt.keyCode) !== -1 ||
+                that.keysDown.indexOf(evt.keyCode) !== -1 ||
+                that.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                that.keysRight.indexOf(evt.keyCode) !== -1) {
+                var index = that._keys.indexOf(evt.keyCode);
+
+                if (index === -1) {
+                    that._keys.push(evt.keyCode);
+                }
+                evt.preventDefault();
+            }
+        };
+
+        this._onKeyUp = function (evt) {
+            if (that.keysUp.indexOf(evt.keyCode) !== -1 ||
+                that.keysDown.indexOf(evt.keyCode) !== -1 ||
+                that.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                that.keysRight.indexOf(evt.keyCode) !== -1) {
+                var index = that._keys.indexOf(evt.keyCode);
+
+                if (index >= 0) {
+                    that._keys.splice(index, 1);
+                }
+                evt.preventDefault();
+            }
+        };
+
+        this._onLostFocus = function () {
+            that._keys = [];
+        };
+
+        canvas.addEventListener("mousedown", this._onMouseDown, true);
+        canvas.addEventListener("mouseup", this._onMouseUp, true);
+        canvas.addEventListener("mouseout", this._onMouseOut, true);
+        canvas.addEventListener("mousemove", this._onMouseMove, true);
+        window.addEventListener("keydown", this._onKeyDown, true);
+        window.addEventListener("keyup", this._onKeyUp, true);
+        window.addEventListener("blur", this._onLostFocus, true);
+    };
+
+    BABYLON.FreeCamera.prototype.detachControl = function (canvas) {
+        canvas.removeEventListener("mousedown", this._onMouseDown);
+        canvas.removeEventListener("mouseup", this._onMouseUp);
+        canvas.removeEventListener("mouseout", this._onMouseOut);
+        canvas.removeEventListener("mousemove", this._onMouseMove);
+        window.removeEventListener("keydown", this._onKeyDown);
+        window.removeEventListener("keyup", this._onKeyUp);
+        window.removeEventListener("blur", this._onLostFocus);
+    };
+
+    BABYLON.FreeCamera.prototype._collideWithWorld = function (velocity) {
+        var oldPosition = this.position.subtract(new BABYLON.Vector3(0, this.ellipsoid.y, 0));
+        this._collider.radius = this.ellipsoid;
+
+        var newPosition = this._scene._getNewPosition(oldPosition, velocity, this._collider, 3);
+        var diffPosition = newPosition.subtract(oldPosition);
+
+        if (diffPosition.length() > BABYLON.Engine.collisionsEpsilon) {
+            this.position = this.position.add(diffPosition);
+        }
+    };
+
+    BABYLON.FreeCamera.prototype._checkInputs = function () {
+        // Keyboard
+        for (var index = 0; index < this._keys.length; index++) {
+            var keyCode = this._keys[index];
+            var direction;
+            var speed = this._computeLocalCameraSpeed();
+
+            if (this.keysLeft.indexOf(keyCode) !== -1) {
+                direction = new BABYLON.Vector3(-speed, 0, 0);
+            } else if (this.keysUp.indexOf(keyCode) !== -1) {
+                direction = new BABYLON.Vector3(0, 0, speed);
+            } else if (this.keysRight.indexOf(keyCode) !== -1) {
+                direction = new BABYLON.Vector3(speed, 0, 0);
+            } else if (this.keysDown.indexOf(keyCode) !== -1) {
+                direction = new BABYLON.Vector3(0, 0, -speed);
+            }
+
+            var cameraTransform = BABYLON.Matrix.RotationYawPitchRoll(this.rotation.y, this.rotation.x, 0);
+            this.cameraDirection = this.cameraDirection.add(BABYLON.Vector3.TransformCoordinates(direction, cameraTransform));
+        }
+    };
+
+    BABYLON.FreeCamera.prototype._update = function () {
+        this._checkInputs();
+
+        var needToMove = this._needMoveForGravity || Math.abs(this.cameraDirection.x) > 0 || Math.abs(this.cameraDirection.y) > 0 || Math.abs(this.cameraDirection.z) > 0;
+        var needToRotate = Math.abs(this.cameraRotation.x) > 0 || Math.abs(this.cameraRotation.y) > 0;
+
+        // Move
+        if (needToMove) {
+            if (this.checkCollisions && this._scene.collisionsEnabled) {
+                this._collideWithWorld(this.cameraDirection);
+
+                if (this.applyGravity) {
+                    var oldPosition = this.position;
+                    this._collideWithWorld(this._scene.gravity);
+                    this._needMoveForGravity = (oldPosition.subtract(this.position).length() != 0);
+                }
+            } else {
+                this.position = this.position.add(this.cameraDirection);
+            }
+        }
+
+        // Rotate
+        if (needToRotate) {
+            this.rotation.x += this.cameraRotation.x;
+            this.rotation.y += this.cameraRotation.y;
+
+            var limit = (Math.PI / 2) * 0.95;
+
+            if (this.rotation.x > limit)
+                this.rotation.x = limit;
+            if (this.rotation.x < -limit)
+                this.rotation.x = -limit;
+        }
+
+        // Inertia
+        if (needToMove) {
+            this.cameraDirection = this.cameraDirection.scale(this.inertia);
+        }
+        if (needToRotate) {
+            this.cameraRotation = this.cameraRotation.scale(this.inertia);
+        }
+    };
+
+    BABYLON.FreeCamera.prototype.getViewMatrix = function () {
+        // Compute
+        var referencePoint = new BABYLON.Vector3(0, 0, 1);
+        var transform = BABYLON.Matrix.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
+
+        var currentTarget = this.position.add(BABYLON.Vector3.TransformCoordinates(referencePoint, transform));
+
+        return new BABYLON.Matrix.LookAtLH(this.position, currentTarget, BABYLON.Vector3.Up());
+    };
+})();

+ 126 - 0
Cameras/babylon.touchCamera.js

@@ -0,0 +1,126 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.TouchCamera = function (name, position, scene) {
+        this.name = name;
+        this.id = name;
+
+        this._scene = scene;
+        this.position = position;
+        scene.cameras.push(this);
+        this.cameraDirection = new BABYLON.Vector3(0, 0, 0);
+        this.cameraRotation = new BABYLON.Vector2(0, 0);
+        this.rotation = new BABYLON.Vector3(0, 0, 0);
+        this.ellipsoid = new BABYLON.Vector3(0.5, 1, 0.5);
+
+        if (!scene.activeCamera) {
+            scene.activeCamera = this;
+        }
+        // Collisions
+        this._collider = new BABYLON.Collider();
+        this._needMoveForGravity = true;
+        
+        // Offset
+        this._offsetX = null;
+        this._offsetY = null;
+        this._pointerCount = 0;
+        this._pointerPressed = [];
+        
+        // Animations
+        this.animations = [];
+    };
+
+    BABYLON.TouchCamera.prototype = Object.create(BABYLON.FreeCamera.prototype);
+
+    // Controls
+    BABYLON.TouchCamera.prototype.attachControl = function (canvas) {
+        var previousPosition;
+        var that = this;
+
+        this._onPointerDown = function (evt) {
+            evt.preventDefault();
+
+            that._pointerPressed.push(evt.pointerId);
+            
+            if (that._pointerPressed.length !== 1) {
+                return;
+            }
+
+            previousPosition = {
+                x: evt.clientX,
+                y: evt.clientY
+            };
+        };
+
+        this._onPointerUp = function (evt) {
+            evt.preventDefault();
+            
+            var index = that._pointerPressed.indexOf(evt.pointerId);
+
+            if (index === -1) {
+                return;
+            }
+            that._pointerPressed.splice(index, 1);
+            
+            if (index != 0) {
+                return;
+            }
+            previousPosition = null;
+            that._offsetX = null;
+            that._offsetY = null;
+        };
+
+        this._onPointerMove = function (evt) {
+            evt.preventDefault();
+            
+            if (!previousPosition) {
+                return;
+            }
+            
+            var index = that._pointerPressed.indexOf(evt.pointerId);
+            
+            if (index != 0) {
+                return;
+            }
+
+            that._offsetX = evt.clientX - previousPosition.x;
+            that._offsetY = -(evt.clientY - previousPosition.y);
+        };
+        
+        this._onLostFocus = function () {
+            that._offsetX = null;
+            that._offsetY = null;
+        };
+        
+        canvas.addEventListener("pointerdown", this._onPointerDown, true);
+        canvas.addEventListener("pointerup", this._onPointerUp, true);
+        canvas.addEventListener("pointerout", this._onPointerUp, true);
+        canvas.addEventListener("pointermove", this._onPointerMove, true);
+        window.addEventListener("blur", this._onLostFocus, true);
+    };
+
+    BABYLON.TouchCamera.prototype.detachControl = function (canvas) {
+        canvas.removeEventListener("pointerdown", this._onPointerDown);
+        canvas.removeEventListener("pointerup", this._onPointerUp);
+        canvas.removeEventListener("pointerout", this._onPointerUp);
+        canvas.removeEventListener("pointermove", this._onPointerMove);
+        window.removeEventListener("blur", this._onLostFocus);
+    };
+    
+    BABYLON.TouchCamera.prototype._checkInputs = function () {
+        if (!this._offsetX) {
+            return;
+        }
+        this.cameraRotation.y += this._offsetX / 100000.0;
+
+        if (this._pointerPressed.length > 1) {
+            this.cameraRotation.x += -this._offsetY / 100000.0;
+        } else {
+            var speed = this._computeLocalCameraSpeed();
+            var direction = new BABYLON.Vector3(0, 0, speed * this._offsetY / 500.0);
+
+            var cameraTransform = BABYLON.Matrix.RotationYawPitchRoll(this.rotation.y, this.rotation.x, 0);
+            this.cameraDirection = this.cameraDirection.add(BABYLON.Vector3.TransformCoordinates(direction, cameraTransform));
+        }
+    };
+})();

+ 307 - 0
Collisions/babylon.collider.js

@@ -0,0 +1,307 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Collider = function () {
+        this.radius = new BABYLON.Vector3(1, 1, 1);
+        this.retry = 0;
+    };
+
+    // Methods
+    BABYLON.Collider.prototype._initialize = function (source, dir, e) {
+        this.velocity = dir;
+        this.normalizedVelocity = BABYLON.Vector3.Normalize(dir);
+        this.basePoint = source;
+
+        this.basePointWorld = source.multiply(this.radius);
+        this.velocityWorld = dir.multiply(this.radius);
+
+        this.velocityWorldLength = this.velocityWorld.length();
+
+        this.epsilon = e;
+        this.collisionFound = false;
+    };
+
+    var checkPointInTriangle = function (point, pa, pb, pc, n) {
+        var e0 = pa.subtract(point);
+        var e1 = pb.subtract(point);
+
+        var d = BABYLON.Vector3.Dot(BABYLON.Vector3.Cross(e0, e1), n);
+        if (d < 0)
+            return false;
+
+        var e2 = pc.subtract(point);
+        d = BABYLON.Vector3.Dot(BABYLON.Vector3.Cross(e1, e2), n);
+        if (d < 0)
+            return false;
+
+        d = BABYLON.Vector3.Dot(BABYLON.Vector3.Cross(e2, e0), n);
+        return d >= 0;
+    };
+
+    var intersectBoxAASphere = function (boxMin, boxMax, sphereCenter, sphereRadius) {
+        var boxMinSphere = new BABYLON.Vector3(sphereCenter.X - sphereRadius, sphereCenter.Y - sphereRadius, sphereCenter.Z - sphereRadius);
+        var boxMaxSphere = new BABYLON.Vector3(sphereCenter.X + sphereRadius, sphereCenter.Y + sphereRadius, sphereCenter.Z + sphereRadius);
+
+        if (boxMin.X > boxMaxSphere.X)
+            return false;
+
+        if (boxMinSphere.X > boxMax.X)
+            return false;
+
+        if (boxMin.Y > boxMaxSphere.Y)
+            return false;
+
+        if (boxMinSphere.Y > boxMax.Y)
+            return false;
+
+        if (boxMin.Z > boxMaxSphere.Z)
+            return false;
+
+        if (boxMinSphere.Z > boxMax.Z)
+            return false;
+
+        return true;
+    };
+
+    var getLowestRoot = function (a, b, c, maxR) {
+        var determinant = b * b - 4.0 * a * c;
+        var result = { root: 0, found: false };
+
+        if (determinant < 0)
+            return result;
+
+        var sqrtD = Math.sqrt(determinant);
+        var r1 = (-b - sqrtD) / (2.0 * a);
+        var r2 = (-b + sqrtD) / (2.0 * a);
+
+        if (r1 > r2) {
+            var temp = r2;
+            r2 = r1;
+            r1 = temp;
+        }
+
+        if (r1 > 0 && r1 < maxR) {
+            result.root = r1;
+            result.found = true;
+            return result;
+        }
+
+        if (r2 > 0 && r2 < maxR) {
+            result.root = r2;
+            result.found = true;
+            return result;
+        }
+
+        return result;
+    };
+
+    BABYLON.Collider.prototype._canDoCollision = function (sphereCenter, sphereRadius, vecMin, vecMax) {
+        var vecTest = this.basePointWorld.subtract(sphereCenter);
+        var distance = vecTest.length();
+
+        var max = Math.max(this.radius.x, this.radius.y);
+        max = Math.max(max, this.radius.z);
+
+        if (distance > this.velocityWorldLength + max + sphereRadius) {
+            return false;
+        }
+
+        if (!intersectBoxAASphere(vecMin, vecMax, this.basePointWorld, this.velocityWorldLength + max))
+            return false;
+
+        return true;
+    };
+
+    BABYLON.Collider.prototype._testTriangle = function (subMesh, p1, p2, p3) {
+        var t0;
+        var embeddedInPlane = false;
+
+        var trianglePlane = BABYLON.CollisionPlane.CreateFromPoints(p1, p2, p3);
+
+        if ((!subMesh.getMaterial()) && !trianglePlane.isFrontFacingTo(this.normalizedVelocity, 0))
+            return;
+
+        var signedDistToTrianglePlane = trianglePlane.signedDistanceTo(this.basePoint);
+        var normalDotVelocity = BABYLON.Vector3.Dot(trianglePlane.normal, this.velocity);
+
+        if (normalDotVelocity == 0) {
+            if (Math.abs(signedDistToTrianglePlane) >= 1.0)
+                return;
+            embeddedInPlane = true;
+            t0 = 0;
+        }
+        else {
+            t0 = (-1.0 - signedDistToTrianglePlane) / normalDotVelocity;
+            var t1 = (1.0 - signedDistToTrianglePlane) / normalDotVelocity;
+
+            if (t0 > t1) {
+                var temp = t1;
+                t1 = t0;
+                t0 = temp;
+            }
+
+            if (t0 > 1.0 || t1 < 0.0)
+                return;
+
+            if (t0 < 0)
+                t0 = 0;
+            if (t0 > 1.0)
+                t0 = 1.0;
+        }
+
+        var collisionPoint = BABYLON.Vector3.Zero();
+
+        var found = false;
+        var t = 1.0;
+
+        if (!embeddedInPlane) {
+            var planeIntersectionPoint = (this.basePoint.subtract(trianglePlane.normal)).add(this.velocity.scale(t0));
+
+            if (checkPointInTriangle(planeIntersectionPoint, p1, p2, p3, trianglePlane.normal)) {
+                found = true;
+                t = t0;
+                collisionPoint = planeIntersectionPoint;
+            }
+        }
+
+        if (!found) {
+            var velocitySquaredLength = this.velocity.lengthSquared();
+
+            var a = velocitySquaredLength;
+
+            var b = 2.0 * (BABYLON.Vector3.Dot(this.velocity, this.basePoint.subtract(p1)));
+            var c = p1.subtract(this.basePoint).lengthSquared() - 1.0;
+
+            var lowestRoot = getLowestRoot(a, b, c, t);
+            if (lowestRoot.found) {
+                t = lowestRoot.root;
+                found = true;
+                collisionPoint = p1;
+            }
+
+            b = 2.0 * (BABYLON.Vector3.Dot(this.velocity, this.basePoint.subtract(p2)));
+            c = p2.subtract(this.basePoint).lengthSquared() - 1.0;
+
+            lowestRoot = getLowestRoot(a, b, c, t);
+            if (lowestRoot.found) {
+                t = lowestRoot.root;
+                found = true;
+                collisionPoint = p2;
+            }
+
+            b = 2.0 * (BABYLON.Vector3.Dot(this.velocity, this.basePoint.subtract(p3)));
+            c = p3.subtract(this.basePoint).lengthSquared() - 1.0;
+
+            lowestRoot = getLowestRoot(a, b, c, t);
+            if (lowestRoot.found) {
+                t = lowestRoot.root;
+                found = true;
+                collisionPoint = p3;
+            }
+
+            var edge = p2.subtract(p1);
+            var baseToVertex = p1.subtract(this.basePoint);
+            var edgeSquaredLength = edge.lengthSquared();
+            var edgeDotVelocity = BABYLON.Vector3.Dot(edge, this.velocity);
+            var edgeDotBaseToVertex = BABYLON.Vector3.Dot(edge, baseToVertex);
+
+            a = edgeSquaredLength * (-velocitySquaredLength) + edgeDotVelocity * edgeDotVelocity;
+            b = edgeSquaredLength * (2.0 * BABYLON.Vector3.Dot(this.velocity, baseToVertex)) - 2.0 * edgeDotVelocity * edgeDotBaseToVertex;
+            c = edgeSquaredLength * (1.0 - baseToVertex.lengthSquared()) + edgeDotBaseToVertex * edgeDotBaseToVertex;
+
+            lowestRoot = getLowestRoot(a, b, c, t);
+            if (lowestRoot.found) {
+                var f = (edgeDotVelocity * lowestRoot.root - edgeDotBaseToVertex) / edgeSquaredLength;
+
+                if (f >= 0.0 && f <= 1.0) {
+                    t = lowestRoot.root;
+                    found = true;
+                    collisionPoint = p1.add(edge.scale(f));
+                }
+            }
+
+            edge = p3.subtract(p2);
+            baseToVertex = p2.subtract(this.basePoint);
+            edgeSquaredLength = edge.lengthSquared();
+            edgeDotVelocity = BABYLON.Vector3.Dot(edge, this.velocity);
+            edgeDotBaseToVertex = BABYLON.Vector3.Dot(edge, baseToVertex);
+
+            a = edgeSquaredLength * (-velocitySquaredLength) + edgeDotVelocity * edgeDotVelocity;
+            b = edgeSquaredLength * (2.0 * BABYLON.Vector3.Dot(this.velocity, baseToVertex)) - 2.0 * edgeDotVelocity * edgeDotBaseToVertex;
+            c = edgeSquaredLength * (1.0 - baseToVertex.lengthSquared()) + edgeDotBaseToVertex * edgeDotBaseToVertex;
+            lowestRoot = getLowestRoot(a, b, c, t);
+            if (lowestRoot.found) {
+                var f = (edgeDotVelocity * lowestRoot.root - edgeDotBaseToVertex) / edgeSquaredLength;
+
+                if (f >= 0.0 && f <= 1.0) {
+                    t = lowestRoot.root;
+                    found = true;
+                    collisionPoint = p2.add(edge.scale(f));
+                }
+            }
+
+            edge = p1.subtract(p3);
+            baseToVertex = p3.subtract(this.basePoint);
+            edgeSquaredLength = edge.lengthSquared();
+            edgeDotVelocity = BABYLON.Vector3.Dot(edge, this.velocity);
+            edgeDotBaseToVertex = BABYLON.Vector3.Dot(edge, baseToVertex);
+
+            a = edgeSquaredLength * (-velocitySquaredLength) + edgeDotVelocity * edgeDotVelocity;
+            b = edgeSquaredLength * (2.0 * BABYLON.Vector3.Dot(this.velocity, baseToVertex)) - 2.0 * edgeDotVelocity * edgeDotBaseToVertex;
+            c = edgeSquaredLength * (1.0 - baseToVertex.lengthSquared()) + edgeDotBaseToVertex * edgeDotBaseToVertex;
+
+            lowestRoot = getLowestRoot(a, b, c, t);
+            if (lowestRoot.found) {
+                var f = (edgeDotVelocity * lowestRoot.root - edgeDotBaseToVertex) / edgeSquaredLength;
+
+                if (f >= 0.0 && f <= 1.0) {
+                    t = lowestRoot.root;
+                    found = true;
+                    collisionPoint = p3.add(edge.scale(f));
+                }
+            }
+        }
+
+        if (found) {
+            var distToCollision = t * this.velocity.length();
+
+            if (!this.collisionFound || distToCollision < this.nearestDistance) {
+                this.nearestDistance = distToCollision;
+                this.intersectionPoint = collisionPoint;
+                this.collisionFound = true;
+            }
+        }
+    };
+
+    BABYLON.Collider.prototype._collide = function (subMesh, pts, indices, indexStart, indexEnd, decal) {
+        for (var i = indexStart; i < indexEnd; i += 3) {
+            var p1 = pts[indices[i] - decal];
+            var p2 = pts[indices[i + 1] - decal];
+            var p3 = pts[indices[i + 2] - decal];
+
+            this._testTriangle(subMesh, p3, p2, p1);
+        }
+    };
+
+    BABYLON.Collider.prototype._getResponse = function(pos, vel) {
+        var destinationPoint = pos.add(vel);
+        var V = vel.scale((this.nearestDistance / vel.length()));
+
+        var newPos = this.basePoint.add(V);
+        var slidePlaneNormal = newPos.subtract(this.intersectionPoint);
+        slidePlaneNormal.normalize();
+        var displacementVector = slidePlaneNormal.scale(this.epsilon);
+
+        newPos = newPos.add(displacementVector);
+        this.intersectionPoint = this.intersectionPoint.add(displacementVector);
+
+        var slidePlaneOrigin = this.intersectionPoint;
+        var slidingPlane = new BABYLON.CollisionPlane(slidePlaneOrigin, slidePlaneNormal);
+        var newDestinationPoint = destinationPoint.subtract(slidePlaneNormal.scale(slidingPlane.signedDistanceTo(destinationPoint)));
+
+        var newVel = newDestinationPoint.subtract(this.intersectionPoint);
+
+        return { position: newPos, velocity: newVel };
+    };
+
+})();

+ 34 - 0
Collisions/babylon.collisionPlane.js

@@ -0,0 +1,34 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.CollisionPlane = function (origin, normal) {
+        this.normal = normal;
+        this.origin = origin;
+
+        normal.normalize();
+
+        this.equation = [];
+        this.equation[0] = normal.x;
+        this.equation[1] = normal.y;
+        this.equation[2] = normal.z;
+        this.equation[3] = -(normal.x * origin.x + normal.y * origin.y + normal.z * origin.z);
+    };
+
+    // Methods
+    BABYLON.CollisionPlane.prototype.isFrontFacingTo = function (direction, epsilon) {
+        var dot = BABYLON.Vector3.Dot(this.normal, direction);
+
+        return (dot <= epsilon);
+    };
+
+    BABYLON.CollisionPlane.prototype.signedDistanceTo = function (point) {
+        return BABYLON.Vector3.Dot(point, this.normal) + this.equation[3];
+    };
+
+    // Statics
+    BABYLON.CollisionPlane.CreateFromPoints = function (p1, p2, p3) {
+        var normal = BABYLON.Vector3.Cross(p2.subtract(p1), p3.subtract(p1));
+
+        return new BABYLON.CollisionPlane(p1, normal);
+    };
+})();

+ 121 - 0
Culling/babylon.boundingBox.js

@@ -0,0 +1,121 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.BoundingBox = function (vertices, stride, start, count) {
+        this.minimum = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+        this.maximum = new BABYLON.Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
+
+        for (var index = start; index < count; index += stride) {
+            var current = new BABYLON.Vector3(vertices[index], vertices[index + 1], vertices[index + 2]);
+
+            this.minimum = BABYLON.Vector3.Minimize(current, this.minimum);
+            this.maximum = BABYLON.Vector3.Maximize(current, this.maximum);
+        }
+        
+        // Bounding vectors
+        this.vectors = [];
+
+        this.vectors.push(this.minimum.clone());
+        this.vectors.push(this.maximum.clone());
+
+        this.vectors.push(this.minimum.clone());
+        this.vectors[2].x = this.maximum.x;
+
+        this.vectors.push(this.minimum.clone());
+        this.vectors[3].y = this.maximum.y;
+
+        this.vectors.push(this.minimum.clone());
+        this.vectors[4].z = this.maximum.z;
+
+        this.vectors.push(this.maximum.clone());
+        this.vectors[5].z = this.minimum.z;
+
+        this.vectors.push(this.maximum.clone());
+        this.vectors[6].x = this.minimum.x;
+
+        this.vectors.push(this.maximum.clone());
+        this.vectors[7].y = this.minimum.y;
+        
+        // OBB
+        this.center = this.maximum.add(this.minimum).scale(0.5);
+        this.extends = this.maximum.subtract(this.minimum).scale(0.5);
+        this.directions = [BABYLON.Vector3.Zero(), BABYLON.Vector3.Zero(), BABYLON.Vector3.Zero()];
+    };
+    
+    // Methods
+    BABYLON.BoundingBox.prototype._update = function (world) {
+        this.vectorsWorld = [];
+        this.minimumWorld = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+        this.maximumWorld = new BABYLON.Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
+
+        for (var index = 0; index < this.vectors.length; index++) {
+            var v = BABYLON.Vector3.TransformCoordinates(this.vectors[index], world);
+            this.vectorsWorld.push(v);
+
+            if (v.x < this.minimumWorld.x)
+                this.minimumWorld.x = v.x;
+            if (v.y < this.minimumWorld.y)
+                this.minimumWorld.y = v.y;
+            if (v.z < this.minimumWorld.z)
+                this.minimumWorld.z = v.z;
+
+            if (v.x > this.maximumWorld.x)
+                this.maximumWorld.x = v.x;
+            if (v.y > this.maximumWorld.y)
+                this.maximumWorld.y = v.y;
+            if (v.z > this.maximumWorld.z)
+                this.maximumWorld.z = v.z;
+        }
+
+        // OBB
+        this.center = this.maximumWorld.add(this.minimumWorld).scale(0.5);
+        this.directions[0] = BABYLON.Vector3.FromArray(world.m, 0);
+        this.directions[1] = BABYLON.Vector3.FromArray(world.m, 4);
+        this.directions[2] = BABYLON.Vector3.FromArray(world.m, 8);
+    };
+
+    BABYLON.BoundingBox.prototype.isInFrustrum = function (frustumPlanes) {
+        for (var p = 0; p < 6; p++) {
+            var inCount = 8;
+
+            for (var i = 0; i < 8; i++) {
+                if (frustumPlanes[p].dotCoordinate(this.vectorsWorld[i]) < 0) {
+                    --inCount;
+                } else {
+                    break;
+                }
+            }
+            if (inCount == 0)
+                return false;
+        }
+        return true;
+    };
+    
+    BABYLON.BoundingBox.prototype.intersectsPoint = function (point) {
+        if (this.maximumWorld.x < point.x || this.minimumWorld.x > point.x)
+            return false;
+
+        if (this.maximumWorld.y < point.y || this.minimumWorld.y > point.y)
+            return false;
+
+        if (this.maximumWorld.z < point.z || this.minimumWorld.z > point.z)
+            return false;
+
+        return true;
+    };
+
+    // Statics
+    BABYLON.BoundingBox.intersects = function (box0, box1) {
+        if (box0.maximumWorld.x < box1.minimumWorld.x || box0.minimumWorld.x > box1.maximumWorld.x)
+            return false;
+
+        if (box0.maximumWorld.y < box1.minimumWorld.y || box0.minimumWorld.y > box1.maximumWorld.y)
+            return false;
+
+        if (box0.maximumWorld.z < box1.minimumWorld.z || box0.minimumWorld.z > box1.maximumWorld.z)
+            return false;
+
+        return true;
+    };
+
+})();

+ 106 - 0
Culling/babylon.boundingInfo.js

@@ -0,0 +1,106 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.BoundingInfo = function (vertices, stride, verticesStart, verticesCount) {
+        this.boundingBox = new BABYLON.BoundingBox(vertices, stride, verticesStart, verticesCount);
+        this.boundingSphere = new BABYLON.BoundingSphere(vertices, stride, verticesStart, verticesCount);
+    };
+
+    // Methods
+    BABYLON.BoundingInfo.prototype._update = function (world, scale) {
+        this.boundingBox._update(world);
+        this.boundingSphere._update(world, scale);
+    };
+
+    var extentsOverlap = function (min0, max0, min1, max1) {
+        return !(min0 > max1 || min1 > max0);
+    };
+
+    var computeBoxExtents = function (axis, box) {
+        var p = BABYLON.Vector3.Dot(box.center, axis);
+
+        var r0 = Math.abs(BABYLON.Vector3.Dot(box.directions[0], axis)) * box.extends.x;
+        var r1 = Math.abs(BABYLON.Vector3.Dot(box.directions[1], axis)) * box.extends.y;
+        var r2 = Math.abs(BABYLON.Vector3.Dot(box.directions[2], axis)) * box.extends.z;
+
+        var r = r0 + r1 + r2;
+        return {
+            min: p - r,
+            max: p + r
+        };
+    };
+
+    var axisOverlap = function (axis, box0, box1) {
+        var result0 = computeBoxExtents(axis, box0);
+        var result1 = computeBoxExtents(axis, box1);
+
+        return extentsOverlap(result0.min, result0.max, result1.min, result1.max);
+    };
+
+    BABYLON.BoundingInfo.prototype.isInFrustrum = function (frustumPlanes) {
+        if (!this.boundingSphere.isInFrustrum(frustumPlanes))
+            return false;
+
+        return this.boundingBox.isInFrustrum(frustumPlanes);
+    };
+
+    BABYLON.BoundingInfo.prototype._checkCollision = function (collider) {
+        return collider._canDoCollision(this.boundingSphere.centerWorld, this.boundingSphere.radiusWorld, this.boundingBox.minimumWorld, this.boundingBox.maximumWorld);
+    };
+
+    BABYLON.BoundingInfo.prototype.intersectsPoint = function(point) {
+        if (!this.boundingSphere.centerWorld) {
+            return false;
+        }
+
+        if (!this.boundingSphere.intersectsPoint(point)) {
+            return false;
+        }
+
+        if (!this.boundingBox.intersectsPoint(point)) {
+            return false;
+        }
+
+        return true;
+    };
+
+    BABYLON.BoundingInfo.prototype.intersects = function (boundingInfo, precise) {
+        if (!this.boundingSphere.centerWorld || !boundingInfo.boundingSphere.centerWorld) {
+            return false;
+        }
+
+        if (!BABYLON.BoundingSphere.intersects(this.boundingSphere, boundingInfo.boundingSphere)) {
+            return false;
+        }
+
+        if (!BABYLON.BoundingBox.intersects(this.boundingBox, boundingInfo.boundingBox)) {
+            return false;
+        }
+
+        if (!precise) {
+            return true;
+        }
+
+        var box0 = this.boundingBox;
+        var box1 = boundingInfo.boundingBox;
+
+        if (!axisOverlap(box0.directions[0], box0, box1)) return false;
+        if (!axisOverlap(box0.directions[1], box0, box1)) return false;
+        if (!axisOverlap(box0.directions[2], box0, box1)) return false;
+        if (!axisOverlap(box1.directions[0], box0, box1)) return false;
+        if (!axisOverlap(box1.directions[1], box0, box1)) return false;
+        if (!axisOverlap(box1.directions[2], box0, box1)) return false;
+        if (!axisOverlap(BABYLON.Vector3.Cross(box0.directions[0], box1.directions[0]), box0, box1)) return false;
+        if (!axisOverlap(BABYLON.Vector3.Cross(box0.directions[0], box1.directions[1]), box0, box1)) return false;
+        if (!axisOverlap(BABYLON.Vector3.Cross(box0.directions[0], box1.directions[2]), box0, box1)) return false;
+        if (!axisOverlap(BABYLON.Vector3.Cross(box0.directions[1], box1.directions[0]), box0, box1)) return false;
+        if (!axisOverlap(BABYLON.Vector3.Cross(box0.directions[1], box1.directions[1]), box0, box1)) return false;
+        if (!axisOverlap(BABYLON.Vector3.Cross(box0.directions[1], box1.directions[2]), box0, box1)) return false;
+        if (!axisOverlap(BABYLON.Vector3.Cross(box0.directions[2], box1.directions[0]), box0, box1)) return false;
+        if (!axisOverlap(BABYLON.Vector3.Cross(box0.directions[2], box1.directions[1]), box0, box1)) return false;
+        if (!axisOverlap(BABYLON.Vector3.Cross(box0.directions[2], box1.directions[2]), box0, box1)) return false;
+
+        return true;
+    };
+
+})();

+ 63 - 0
Culling/babylon.boundingSphere.js

@@ -0,0 +1,63 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.BoundingSphere = function (vertices, stride, start, count) {
+        var minimum = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
+        var maximum = new BABYLON.Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
+
+        for (var index = start; index < count; index += stride) {
+            var current = new BABYLON.Vector3(vertices[index], vertices[index + 1], vertices[index + 2]);
+
+            minimum = BABYLON.Vector3.Minimize(current, minimum);
+            maximum = BABYLON.Vector3.Maximize(current, maximum);
+        }
+        
+        var distance = BABYLON.Vector3.Distance(minimum, maximum);
+        
+        this.center = BABYLON.Vector3.Lerp(minimum, maximum, 0.5);;
+        this.radius = distance * 0.5;
+    };
+    
+    // Methods
+    BABYLON.BoundingSphere.prototype._update = function (world, scale) {
+        this.centerWorld = BABYLON.Vector3.TransformCoordinates(this.center, world);
+        this.radiusWorld = this.radius * scale;
+    };
+    
+    BABYLON.BoundingSphere.prototype.isInFrustrum = function (frustumPlanes) {
+        for (var i = 0; i < 6; i++) {
+            if (frustumPlanes[i].dotCoordinate(this.centerWorld) <= -this.radiusWorld)
+                return false;
+        }
+
+        return true;
+    };
+
+    BABYLON.BoundingSphere.prototype.intersectsPoint = function(point) {
+        var x = this.centerWorld.x - point.x;
+        var y = this.centerWorld.y - point.y;
+        var z = this.centerWorld.z - point.z;
+
+        var distance = Math.sqrt((x * x) + (y * y) + (z * z));
+
+        if (this.radiusWorld < distance)
+            return false;
+
+        return true;
+    };
+
+    // Statics
+    BABYLON.BoundingSphere.intersects = function (sphere0, sphere1) {
+        var x = sphere0.centerWorld.x - sphere1.centerWorld.x;
+        var y = sphere0.centerWorld.y - sphere1.centerWorld.y;
+        var z = sphere0.centerWorld.z - sphere1.centerWorld.z;
+
+        var distance = Math.sqrt((x * x) + (y * y) + (z * z));
+
+        if (sphere0.radiusWorld + sphere1.radiusWorld < distance)
+            return false;
+
+        return true;
+    };
+
+})();

+ 94 - 0
Layer/babylon.layer.js

@@ -0,0 +1,94 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Layer = function (name, imgUrl, scene, isBackground) {
+        this.name = name;
+        this.texture = imgUrl ? new BABYLON.Texture(imgUrl, scene, true) : null;
+        this.isBackground = isBackground === undefined ? true : isBackground;
+
+        this._scene = scene;
+        this._scene.layers.push(this);
+        
+        // VBO
+        var vertices = [];
+        vertices.push(1, 1);
+        vertices.push(-1, 1);
+        vertices.push(-1, -1);
+        vertices.push(1, -1);
+        this._vertexDeclaration = [2];
+        this._vertexStrideSize = 2 * 4;
+        this._vertexBuffer = scene.getEngine().createVertexBuffer(vertices);
+
+        // Indices
+        var indices = [];
+        indices.push(0);
+        indices.push(1);
+        indices.push(2);
+
+        indices.push(0);
+        indices.push(2);
+        indices.push(3);
+
+        this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
+        
+        // Effects
+        this._effect = this._scene.getEngine().createEffect("layer",
+                    ["position"],
+                    ["textureMatrix"],
+                    ["textureSampler"], "");
+    };
+
+    // Members
+    BABYLON.Layer.prototype.onDispose = null;
+
+    // Methods
+    BABYLON.Layer.prototype.render = function () {
+        // Check
+        if (!this._effect.isReady() || !this.texture || !this.texture.isReady())
+            return 0;
+
+        var engine = this._scene.getEngine();
+       
+        // Render
+        engine.enableEffect(this._effect);
+
+        // Texture
+        this._effect.setTexture("textureSampler", this.texture);
+        this._effect.setMatrix("textureMatrix", this.texture._computeTextureMatrix());
+
+        // VBOs
+        engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, this._effect);
+
+        // Draw order
+        engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+        engine.draw(true, 0, 6);
+        engine.setAlphaMode(BABYLON.Engine.ALPHA_DISABLE);
+    };
+    
+    BABYLON.Layer.prototype.dispose = function () {
+        if (this._vertexBuffer) {
+            //this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
+            this._vertexBuffer = null;
+        }
+
+        if (this._indexBuffer) {
+            this._scene.getEngine()._releaseBuffer(this._indexBuffer);
+            this._indexBuffer = null;
+        }
+
+        if (this.texture) {
+            this.texture.dispose();
+            this.texture = null;
+        }
+
+        // Remove from scene
+        var index = this._scene.layers.indexOf(this);
+        this._scene.layers.splice(index, 1);
+        
+        // Callback
+        if (this.onDispose) {
+            this.onDispose();
+        }
+    };
+    
+})();

+ 19 - 0
Lights/babylon.directionalLight.js

@@ -0,0 +1,19 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.DirectionalLight = function (name, direction, scene) {
+        this.name = name;
+        this.id = name;
+        this.direction = direction;
+        this.diffuse = new BABYLON.Color3(1.0, 1.0, 1.0);
+        this.specular = new BABYLON.Color3(1.0, 1.0, 1.0);
+        this._scene = scene;
+
+        scene.lights.push(this);
+        
+        // Animations
+        this.animations = [];
+    };
+    
+    BABYLON.DirectionalLight.prototype = Object.create(BABYLON.Light.prototype);
+})();

+ 19 - 0
Lights/babylon.light.js

@@ -0,0 +1,19 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Light = function (name, scene) {
+        this.name = name;
+        this.id = name;
+        this.diffuse = new BABYLON.Color3(1.0, 1.0, 1.0);
+        this.specular = new BABYLON.Color3(1.0, 1.0, 1.0);
+
+        this._scene = scene;
+
+        scene.lights.push(this);
+    };
+    
+    // Members
+    BABYLON.Light.prototype.intensity = 1.0;
+    BABYLON.Light.prototype.isEnabled = true;
+    
+})();

+ 19 - 0
Lights/babylon.pointLight.js

@@ -0,0 +1,19 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.PointLight = function (name, position, scene) {
+        this.name = name;
+        this.id = name;
+        this.position = position;
+        this.diffuse = new BABYLON.Color3(1.0, 1.0, 1.0);
+        this.specular = new BABYLON.Color3(1.0, 1.0, 1.0);
+        this._scene = scene;
+
+        scene.lights.push(this);
+
+        // Animations
+        this.animations = [];
+    };
+    
+    BABYLON.PointLight.prototype = Object.create(BABYLON.Light.prototype);
+})();

+ 153 - 0
Materials/babylon.effect.js

@@ -0,0 +1,153 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+
+    BABYLON.Effect = function (baseName, attributesNames, uniformsNames, samplers, engine, defines) {
+        this._engine = engine;
+        this.name = baseName;
+        this.defines = defines;
+        this._uniformsNames = uniformsNames.concat(samplers);
+        this._samplers = samplers;
+        this._isReady = false;
+
+        var that = this;       
+
+        // Is in local store ?
+        if (BABYLON.Effect.ShadersStore[baseName + "VertexShader"]) {
+            this._prepareEffect(BABYLON.Effect.ShadersStore[baseName + "VertexShader"], BABYLON.Effect.ShadersStore[baseName + "PixelShader"], attributesNames, defines);
+        } else {
+            var shaderUrl = BABYLON.Engine.ShadersRepository + baseName;
+            // Vertex shader
+            BABYLON.Tools.LoadFile(shaderUrl + ".vertex.fx",
+                function(vertexSourceCode) {
+                    // Fragment shader
+                    BABYLON.Tools.LoadFile(shaderUrl + ".fragment.fx",
+                        function(fragmentSourceCode) {
+                            that._prepareEffect(vertexSourceCode, fragmentSourceCode, attributesNames, defines);
+                        });
+                }
+            );
+        }
+        
+        // Cache
+        this._valueCache = [];
+    };
+    
+    // Properties
+    BABYLON.Effect.prototype.isReady = function () {
+        return this._isReady;
+    };
+    
+    BABYLON.Effect.prototype.getProgram = function() {
+        return this._program;
+    };
+    
+    BABYLON.Effect.prototype.getAttribute = function (index) {
+        return this._attributes[index];
+    };
+    
+    BABYLON.Effect.prototype.getAttributesCount = function () {
+        return this._attributes.length;
+    };
+    
+    BABYLON.Effect.prototype.getUniformIndex = function (uniformName) {
+        return this._uniformsNames.indexOf(uniformName);
+    };
+    
+    BABYLON.Effect.prototype.getUniform = function (uniformName) {
+        return this._uniforms[this._uniformsNames.indexOf(uniformName)];
+    };
+    
+    BABYLON.Effect.prototype.getSamplers = function () {
+        return this._samplers;
+    };
+    
+    // Methods
+    BABYLON.Effect.prototype._prepareEffect = function (vertexSourceCode, fragmentSourceCode, attributesNames, defines) {
+        var engine = this._engine;
+        this._program = engine.createShaderProgram(vertexSourceCode, fragmentSourceCode, defines);
+
+        this._uniforms = engine.getUniforms(this._program, this._uniformsNames);
+        this._attributes = engine.getAttributes(this._program, attributesNames);
+
+        for (var index = 0; index < this._samplers.length; index++) {
+            var sampler = this.getUniform(this._samplers[index]);
+
+            if (sampler == null) {
+                this._samplers.splice(index, 1);
+                index--;
+            }
+        }
+
+        engine.bindSamplers(this);
+
+        this._isReady = true;
+    };
+
+    BABYLON.Effect.prototype.setTexture = function (channel, texture) {
+        this._engine.setTexture(this._samplers.indexOf(channel), texture);
+    };
+        
+    BABYLON.Effect.prototype.setMatrix = function (uniformName, matrix) {
+        if (this._valueCache[uniformName] && this._valueCache[uniformName].equals(matrix))
+            return;
+
+        this._valueCache[uniformName] = matrix;
+        this._engine.setMatrix(this.getUniform(uniformName), matrix);
+    };
+    
+    BABYLON.Effect.prototype.setBool = function (uniformName, bool) {
+        if (this._valueCache[uniformName] && this._valueCache[uniformName] === bool)
+            return;
+
+        this._valueCache[uniformName] = bool;
+
+        this._engine.setBool(this.getUniform(uniformName), bool);
+    };
+    
+    BABYLON.Effect.prototype.setVector2 = function (uniformName, x, y) {
+        if (this._valueCache[uniformName] && this._valueCache[uniformName][0] == x && this._valueCache[uniformName][1] == y)
+            return;
+
+        this._valueCache[uniformName] = [x, y];
+
+        this._engine.setVector2(this.getUniform(uniformName), x, y);
+    };
+    
+    BABYLON.Effect.prototype.setVector3 = function (uniformName, vector3) {
+        if (this._valueCache[uniformName] && this._valueCache[uniformName][0] == vector3.x && this._valueCache[uniformName][1] == vector3.y && this._valueCache[uniformName][2] == vector3.z)
+            return;
+
+        this._valueCache[uniformName] = [vector3.x, vector3.y, vector3.z];
+        
+        this._engine.setVector3(this.getUniform(uniformName), vector3);
+    };
+    
+    BABYLON.Effect.prototype.setVector4 = function (uniformName, x, y, z, w) {
+        if (this._valueCache[uniformName] && this._valueCache[uniformName][0] == x && this._valueCache[uniformName][1] == y && this._valueCache[uniformName][2] == z && this._valueCache[uniformName][3] == w)
+            return;
+
+        this._valueCache[uniformName] = [x, y, z, w];
+        this._engine.setVector4(this.getUniform(uniformName), x, y, z, w);
+    };
+    
+    BABYLON.Effect.prototype.setColor3 = function (uniformName, color3) {
+        if (this._valueCache[uniformName]  && this._valueCache[uniformName][0] == color3.r && this._valueCache[uniformName][1] == color3.g && this._valueCache[uniformName][2] == color3.b)
+            return;
+
+        this._valueCache[uniformName] = [color3.r, color3.g, color3.b];
+        this._engine.setColor3(this.getUniform(uniformName), color3);
+    };
+    
+    BABYLON.Effect.prototype.setColor4 = function (uniformName, color3, alpha) {
+        if (this._valueCache[uniformName] && this._valueCache[uniformName][0] == color3.r && this._valueCache[uniformName][1] == color3.g && this._valueCache[uniformName][2] == color3.b && this._valueCache[uniformName][3] == alpha)
+            return;
+
+        this._valueCache[uniformName] = [color3.r, color3.g, color3.b, alpha];
+        this._engine.setColor4(this.getUniform(uniformName), color3, alpha);
+    };
+    
+    // Statics
+    BABYLON.Effect.ShadersStore = {};
+
+})();

+ 61 - 0
Materials/babylon.material.js

@@ -0,0 +1,61 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Material = function (name, scene) {
+        this.name = name;
+        this.id = name;
+        
+        this._scene = scene;
+        scene.materials.push(this);
+    };
+    
+    // Members
+    BABYLON.Material.prototype.alpha = 1.0;
+    BABYLON.Material.prototype.wireframe = false;
+    BABYLON.Material.prototype.backFaceCulling = true;
+    BABYLON.Material.prototype._effect = null;
+
+    BABYLON.Material.prototype.onDispose = null;
+    
+    // Properties
+    BABYLON.Material.prototype.isReady = function () {
+        return true;
+    };
+
+    BABYLON.Material.prototype.getEffect = function () {
+        return this._effect;
+    };
+    
+    BABYLON.Material.prototype.needAlphaBlending = function () {
+        return (this.alpha < 1.0);
+    };
+    
+    BABYLON.Material.prototype.needAlphaTesting = function () {
+        return false;
+    };
+
+    // Methods   
+    BABYLON.Material.prototype._preBind = function () {
+        var engine = this._scene.getEngine();
+        
+        engine.enableEffect(this._effect);
+        engine.setState(this.backFaceCulling);
+    };
+
+    BABYLON.Material.prototype.bind = function (world, mesh) {       
+    };
+    
+    BABYLON.Material.prototype.unbind = function () {
+    };
+
+    BABYLON.Material.prototype.dispose = function () {
+        // Remove from scene
+        var index = this._scene.materials.indexOf(this);
+        this._scene.materials.splice(index, 1);
+        
+        // Callback
+        if (this.onDispose) {
+            this.onDispose();
+        }
+    };
+})();

+ 22 - 0
Materials/babylon.multiMaterial.js

@@ -0,0 +1,22 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.MultiMaterial = function (name, scene) {
+        this.name = name;
+        this.id = name;
+        
+        this._scene = scene;
+        scene.multiMaterials.push(this);
+
+        this.subMaterials = [];
+    };
+
+    // Properties
+    BABYLON.MultiMaterial.prototype.getSubMaterial = function (index) {
+        if (index < 0 || index >= this.subMaterials.length) {
+            return this._scene.defaultMaterial;
+        }
+
+        return this.subMaterials[index];
+    };
+})();

+ 359 - 0
Materials/babylon.standardMaterial.js

@@ -0,0 +1,359 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.StandardMaterial = function (name, scene) {
+        this.name = name;
+        this.id = name;
+
+        this._scene = scene;
+        scene.materials.push(this);
+
+        this.diffuseTexture = null;
+        this.ambientTexture = null;
+        this.opacityTexture = null;
+        this.reflectionTexture = null;
+        this.emissiveTexture = null;
+        this.specularTexture = null;
+
+        this.ambientColor = new BABYLON.Color3(0, 0, 0);
+        this.diffuseColor = new BABYLON.Color3(1, 1, 1);
+        this.specularColor = new BABYLON.Color3(1, 1, 1);
+        this.specularPower = 64;
+        this.emissiveColor = new BABYLON.Color3(0, 0, 0);
+
+        this._cachedDefines = null;
+    };
+
+    BABYLON.StandardMaterial.prototype = Object.create(BABYLON.Material.prototype);
+
+    // Properties   
+    BABYLON.StandardMaterial.prototype.needAlphaBlending = function () {
+        return (this.alpha < 1.0) || (this.opacityTexture != null);
+    };
+
+    BABYLON.StandardMaterial.prototype.needAlphaTesting = function () {
+        return this.diffuseTexture != null && this.diffuseTexture.hasAlpha;
+    };
+
+    // Methods   
+    BABYLON.StandardMaterial.prototype.isReady = function (mesh) {
+        var engine = this._scene.getEngine();
+
+        // Textures
+        if (this.diffuseTexture && !this.diffuseTexture.isReady()) {
+            return false;
+        }
+
+        if (this.ambientTexture && !this.ambientTexture.isReady()) {
+            return false;
+        }
+
+        if (this.opacityTexture && !this.opacityTexture.isReady()) {
+            return false;
+        }
+
+        if (this.reflectionTexture && !this.reflectionTexture.isReady()) {
+            return false;
+        }
+
+        if (this.emissiveTexture && !this.emissiveTexture.isReady()) {
+            return false;
+        }
+
+        if (this.specularTexture && !this.specularTexture.isReady()) {
+            return false;
+        }
+
+        // Effect
+        var defines = [];
+        if (this.diffuseTexture) {
+            defines.push("#define DIFFUSE");
+        }
+
+        if (this.ambientTexture) {
+            defines.push("#define AMBIENT");
+        }
+
+        if (this.opacityTexture) {
+            defines.push("#define OPACITY");
+        }
+
+        if (this.reflectionTexture) {
+            defines.push("#define REFLECTION");
+        }
+
+        if (this.emissiveTexture) {
+            defines.push("#define EMISSIVE");
+        }
+
+        if (this.specularTexture) {
+            defines.push("#define SPECULAR");
+        }
+
+        if (BABYLON.clipPlane) {
+            defines.push("#define CLIPPLANE");
+        }
+
+        if (engine.getAlphaTesting()) {
+            defines.push("#define ALPHATEST");
+        }
+        
+        var lightIndex = 0;
+        for (var index = 0; index < this._scene.lights.length; index++) {
+            var light = this._scene.lights[index];
+
+            if (!light.isEnabled) {
+                continue;
+            }
+
+            defines.push("#define LIGHT" + lightIndex++);
+
+            if (lightIndex == 4)
+                break;
+        }
+
+        var attribs = ["position", "normal"];
+        if (mesh) {
+            switch (mesh._uvCount) {
+                case 1:
+                    attribs = ["position", "normal", "uv"];
+                    defines.push("#define UV1");
+                    break;
+                case 2:
+                    attribs = ["position", "normal", "uv", "uv2"];
+                    defines.push("#define UV1");
+                    defines.push("#define UV2");
+                    break;
+            }
+        }
+
+        // Get correct effect      
+        var join = defines.join("\n");
+        if (this._cachedDefines != join) {
+            this._cachedDefines = join;
+            
+            // IE patch
+            var shaderName = "default";
+            if (window.ActiveXObject !== undefined) {
+                shaderName = "iedefault";
+            }
+
+            this._effect = this._scene.getEngine().createEffect(shaderName,
+                attribs,
+                ["world", "view", "worldViewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vDiffuseColor", "vSpecularColor", "vEmissiveColor",
+                 "vLightData0", "vLightDiffuse0", "vLightSpecular0",
+                 "vLightData1", "vLightDiffuse1", "vLightSpecular1",
+                 "vLightData2", "vLightDiffuse2", "vLightSpecular2",
+                 "vLightData3", "vLightDiffuse3", "vLightSpecular3",
+                 "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos",
+                 "vMisc", "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix"],
+                ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler"],
+                join);
+        }
+        if (!this._effect.isReady()) {
+            return false;
+        }
+
+        return true;
+    };
+
+    BABYLON.StandardMaterial.prototype.getRenderTargetTextures = function () {
+        var results = [];
+
+        if (this.reflectionTexture && this.reflectionTexture.isRenderTarget) {
+            results.push(this.reflectionTexture);
+        }
+
+        return results;
+    };
+
+    BABYLON.StandardMaterial.prototype.unbind = function () {
+        if (this.reflectionTexture && this.reflectionTexture.isRenderTarget) {
+            this._effect.setTexture("reflection2DSampler", null);
+        }
+    };
+
+    BABYLON.StandardMaterial.prototype.bind = function (world, mesh) {
+        var vMisc = [0, 0, 0, 0];
+        var baseColor = this.diffuseColor;
+
+        // Values
+        if (this.diffuseTexture) {
+            this._effect.setTexture("diffuseSampler", this.diffuseTexture);
+
+            this._effect.setVector2("vDiffuseInfos", this.diffuseTexture.coordinatesIndex, this.diffuseTexture.level);
+            this._effect.setMatrix("diffuseMatrix", this.diffuseTexture._computeTextureMatrix());
+
+            baseColor = new BABYLON.Color3(1, 1, 1);
+        }
+
+        if (this.ambientTexture) {
+            this._effect.setTexture("ambientSampler", this.ambientTexture);
+
+            this._effect.setVector2("vAmbientInfos", this.ambientTexture.coordinatesIndex, this.ambientTexture.level);
+            this._effect.setMatrix("ambientMatrix", this.ambientTexture._computeTextureMatrix());
+        }
+
+        if (this.opacityTexture) {
+            this._effect.setTexture("opacitySampler", this.opacityTexture);
+
+            this._effect.setVector2("vOpacityInfos", this.opacityTexture.coordinatesIndex, this.opacityTexture.level);
+            this._effect.setMatrix("opacityMatrix", this.opacityTexture._computeTextureMatrix());
+        }
+
+        if (this.reflectionTexture) {
+            if (this.reflectionTexture.isCube) {
+                this._effect.setTexture("reflectionCubeSampler", this.reflectionTexture);
+            } else {
+                this._effect.setTexture("reflection2DSampler", this.reflectionTexture);
+            }
+
+            // Matrix
+            var matrix = BABYLON.Matrix.Identity();
+
+            switch (this.reflectionTexture.coordinatesMode) {
+                case BABYLON.Texture.SPHERICAL_MODE:
+                    matrix.m[0] = -0.5 * this.reflectionTexture.uScale;
+                    matrix.m[5] = -0.5 * this.reflectionTexture.vScale;
+                    matrix.m[12] = 0.5 + this.reflectionTexture.uOffset;
+                    matrix.m[13] = 0.5 + this.reflectionTexture.vOffset;
+                    break;
+                case BABYLON.Texture.PLANAR_MODE:
+                    matrix.m[0] = this.reflectionTexture.uScale;
+                    matrix.m[5] = this.reflectionTexture.vScale;
+                    matrix.m[12] = this.reflectionTexture.uOffset;
+                    matrix.m[13] = this.reflectionTexture.vOffset;
+                    break;
+                case BABYLON.Texture.PROJECTION_MODE:
+                    matrix.m[0] = 0.5;
+                    matrix.m[5] = -0.5;
+                    matrix.m[10] = 0.0;
+                    matrix.m[12] = 0.5;
+                    matrix.m[13] = 0.5;
+                    matrix.m[14] = 1.0;
+                    matrix.m[15] = 1.0;
+
+                    matrix = this._scene.getProjectionMatrix().multiply(matrix);
+                    break;
+            }
+            this._effect.setMatrix("reflectionMatrix", matrix);
+            vMisc[0] = (this.reflectionTexture.isCube ? 1 : 0);
+            this._effect.setMatrix("view", this._scene.getViewMatrix());
+            this._effect.setVector2("vReflectionInfos", this.reflectionTexture.coordinatesMode, this.reflectionTexture.level);
+        }
+
+        if (this.emissiveTexture) {
+            this._effect.setTexture("emissiveSampler", this.emissiveTexture);
+
+            this._effect.setVector2("vEmissiveInfos", this.emissiveTexture.coordinatesIndex, this.emissiveTexture.level);
+            this._effect.setMatrix("emissiveMatrix", this.emissiveTexture._computeTextureMatrix());
+        }
+
+        if (this.specularTexture) {
+            this._effect.setTexture("specularSampler", this.specularTexture);
+
+            this._effect.setVector2("vSpecularInfos", this.specularTexture.coordinatesIndex, this.specularTexture.level);
+            this._effect.setMatrix("specularMatrix", this.specularTexture._computeTextureMatrix());
+        }
+
+        this._effect.setMatrix("world", world);
+        this._effect.setMatrix("worldViewProjection", world.multiply(this._scene.getTransformMatrix()));
+        this._effect.setVector3("vEyePosition", this._scene.activeCamera.position);
+        this._effect.setColor3("vAmbientColor", this._scene.ambientColor.multiply(this.ambientColor));
+        this._effect.setColor4("vDiffuseColor", baseColor, this.alpha * mesh.visibility);
+        this._effect.setColor4("vSpecularColor", this.specularColor, this.specularPower);
+        this._effect.setColor3("vEmissiveColor", this.emissiveColor);
+
+        var lightIndex = 0;
+        for (var index = 0; index < this._scene.lights.length; index++) {
+            var light = this._scene.lights[index];
+
+            if (!light.isEnabled) {
+                continue;
+            }
+            
+            if (light.position) {
+                this._effect.setVector4("vLightData" + lightIndex, light.position.x, light.position.y, light.position.z, 0);
+            } else {
+                this._effect.setVector4("vLightData" + lightIndex, light.direction.x, light.direction.y, light.direction.z, 1);
+            }
+            this._effect.setColor3("vLightDiffuse" + lightIndex, light.diffuse.scale(light.intensity));
+            this._effect.setColor3("vLightSpecular" + lightIndex, light.specular.scale(light.intensity));
+            lightIndex++;
+
+            if (lightIndex == 4)
+                break;
+        }
+
+        this._effect.setVector4("vMisc", vMisc[0], vMisc[1], vMisc[2], vMisc[3]);
+
+        if (BABYLON.clipPlane) {
+            this._effect.setVector4("vClipPlane", BABYLON.clipPlane.normal.x, BABYLON.clipPlane.normal.y, BABYLON.clipPlane.normal.z, BABYLON.clipPlane.d);
+        }
+    };
+
+    BABYLON.StandardMaterial.prototype.getAnimatables = function () {
+        var results = [];
+
+        if (this.diffuseTexture && this.diffuseTexture.animations && this.diffuseTexture.animations.length > 0) {
+            results.push(this.diffuseTexture);
+        }
+
+        if (this.ambientTexture && this.ambientTexture.animations && this.ambientTexture.animations.length > 0) {
+            results.push(this.ambientTexture);
+        }
+
+        if (this.opacityTexture && this.opacityTexture.animations && this.opacityTexture.animations.length > 0) {
+            results.push(this.opacityTexture);
+        }
+
+        if (this.reflectionTexture && this.reflectionTexture.animations && this.reflectionTexture.animations.length > 0) {
+            results.push(this.reflectionTexture);
+        }
+
+        if (this.emissiveTexture && this.emissiveTexture.animations && this.emissiveTexture.animations.length > 0) {
+            results.push(this.emissiveTexture);
+        }
+
+        if (this.specularTexture && this.specularTexture.animations && this.specularTexture.animations.length > 0) {
+            results.push(this.specularTexture);
+        }
+
+        return results;
+    };
+
+    BABYLON.StandardMaterial.prototype.dispose = function () {
+        if (this.diffuseTexture) {
+            this.diffuseTexture.dispose();
+        }
+
+        if (this.ambientTexture) {
+            this.ambientTexture.dispose();
+        }
+
+        if (this.opacityTexture) {
+            this.opacityTexture.dispose();
+        }
+
+        if (this.reflectionTexture) {
+            this.reflectionTexture.dispose();
+        }
+
+        if (this.emissiveTexture) {
+            this.emissiveTexture.dispose();
+        }
+
+        if (this.specularTexture) {
+            this.specularTexture.dispose();
+        }
+
+        // Remove from scene
+        var index = this._scene.materials.indexOf(this);
+        this._scene.materials.splice(index, 1);
+
+        // Callback
+        if (this.onDispose) {
+            this.onDispose();
+        }
+    };
+})();

+ 89 - 0
Materials/textures/babylon.baseTexture.js

@@ -0,0 +1,89 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.BaseTexture = function (url, scene) {
+        this._scene = scene;
+        this._scene.textures.push(this);
+    };
+    
+    // Members
+    BABYLON.BaseTexture.prototype.hasAlpha = false;
+    BABYLON.BaseTexture.prototype.level = 1;
+    BABYLON.BaseTexture.prototype._texture = null;
+    
+    BABYLON.BaseTexture.prototype.onDispose = null;
+
+    // Properties
+    BABYLON.BaseTexture.prototype.getInternalTexture = function () {
+        return this._texture;
+    };
+    
+    BABYLON.BaseTexture.prototype.isReady = function () {
+        return (this._texture !== undefined && this._texture.isReady);
+    };
+
+    // Methods
+    BABYLON.BaseTexture.prototype.getSize = function() {
+        if (this._texture._width) {
+            return { width: this._texture._width, height: this._texture._height };
+        }
+                    
+        if (this._texture._size) {
+            return { width: this._texture._size, height: this._texture._size };
+        }
+                        
+        return { width: 0, height: 0 };
+    };
+    
+    BABYLON.BaseTexture.prototype.getBaseSize = function () {
+        if (!this.isReady())
+            return { width: 0, height: 0 };
+
+        if (this._texture._size) {
+            return { width: this._texture._size, height: this._texture._size };
+        }
+
+        return { width: this._texture._baseWidth, height: this._texture._baseHeight };
+    };
+    
+    BABYLON.BaseTexture.prototype._getFromCache = function (url, noMipmap) {
+        var texturesCache = this._scene.getEngine().getLoadedTexturesCache();
+        for (var index = 0; index < texturesCache.length; index++) {
+            var texturesCacheEntry = texturesCache[index];
+
+            if (texturesCacheEntry.url === url && texturesCacheEntry.noMipmap === noMipmap) {
+                texturesCacheEntry.references++;
+                return texturesCacheEntry;
+            }
+        }
+
+        return null;
+    };
+
+    BABYLON.BaseTexture.prototype.dispose = function () {
+        if (this._texture === undefined) {
+            return;
+        }
+        var texturesCache = this._scene.getEngine().getLoadedTexturesCache();
+        this._texture.references--;
+        
+        // Remove from scene
+        var index = this._scene.textures.indexOf(this);
+        this._scene.textures.splice(index, 1);
+        
+        // Final reference ?
+        if (this._texture.references == 0) {
+            var index = texturesCache.indexOf(this._texture);
+            texturesCache.splice(index, 1);
+
+            this._scene.getEngine()._releaseTexture(this._texture);
+
+            delete this._texture;
+        }
+        
+        // Callback
+        if (this.onDispose) {
+            this.onDispose();
+        }
+    };
+})();

+ 20 - 0
Materials/textures/babylon.cubeTexture.js

@@ -0,0 +1,20 @@
+var BABYLON = BABYLON || {};
+
+(function () {    
+    BABYLON.CubeTexture = function (rootUrl, scene) {
+        this._scene = scene;
+        this._scene.textures.push(this);
+        
+        this.name = rootUrl;
+        this.hasAlpha = false;
+        this.coordinatesMode = BABYLON.Texture.CUBIC_MODE;
+
+        this._texture = this._getFromCache(rootUrl);
+        
+        if (!this._texture) {
+            this._texture = scene.getEngine().createCubeTexture(rootUrl);
+        }
+    };
+
+    BABYLON.CubeTexture.prototype = Object.create(BABYLON.BaseTexture.prototype);
+})();

+ 32 - 0
Materials/textures/babylon.dynamicTexture.js

@@ -0,0 +1,32 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.DynamicTexture = function (name, size, scene, generateMipMaps) {
+        this._scene = scene;
+        this._scene.textures.push(this);
+
+        this.name = name;
+
+        this.wrapU = false;
+        this.wrapV = false;
+
+        this._texture = scene.getEngine().createDynamicTexture(size, generateMipMaps);
+        var size = this.getSize();
+
+        this._canvas = document.createElement("canvas");
+        this._canvas.width = size.width;
+        this._canvas.height = size.height;
+        this._context = this._canvas.getContext("2d");
+    };
+
+    BABYLON.DynamicTexture.prototype = Object.create(BABYLON.Texture.prototype);
+    
+    // Methods
+    BABYLON.DynamicTexture.prototype.getContext = function() {
+        return this._context;
+    };
+    
+    BABYLON.DynamicTexture.prototype.update = function () {
+        this._scene.getEngine().updateDynamicTexture(this._texture, this._canvas);
+    };
+})();

+ 40 - 0
Materials/textures/babylon.mirrorTexture.js

@@ -0,0 +1,40 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.MirrorTexture = function (name, size, scene, generateMipMaps) {
+        this._scene = scene;
+        this._scene.textures.push(this);
+
+        this.name = name;        
+
+        this._texture = scene.getEngine().createRenderTargetTexture(size, generateMipMaps);
+    };
+
+    BABYLON.MirrorTexture.prototype = Object.create(BABYLON.RenderTargetTexture.prototype);
+    
+    // Members
+    BABYLON.MirrorTexture.prototype.mirrorPlane = new BABYLON.Plane(0, 1, 0, 1);
+    
+    // Method
+    BABYLON.MirrorTexture.prototype._onBeforeRender = function () {
+        var scene = this._scene;
+
+        var mirrorMatrix = BABYLON.Matrix.Reflection(this.mirrorPlane);
+        this._savedViewMatrix = scene.getViewMatrix();
+
+        scene.setTransformMatrix(mirrorMatrix.multiply(this._savedViewMatrix), scene.getProjectionMatrix());
+
+        BABYLON.clipPlane = this.mirrorPlane;
+
+        scene.getEngine().cullBackFaces = false;
+    };
+
+    BABYLON.MirrorTexture.prototype._onAfterRender = function () {
+        var scene = this._scene;
+
+        scene.setTransformMatrix(this._savedViewMatrix, scene.getProjectionMatrix());
+        scene.getEngine().cullBackFaces = true;
+
+        delete BABYLON.clipPlane;
+    };
+})();

+ 89 - 0
Materials/textures/babylon.renderTargetTexture.js

@@ -0,0 +1,89 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.RenderTargetTexture = function (name, size, scene, generateMipMaps) {
+        this._scene = scene;
+        this._scene.textures.push(this);
+
+        this.name = name;        
+
+        this._texture = scene.getEngine().createRenderTargetTexture(size, generateMipMaps);
+    };
+
+    BABYLON.RenderTargetTexture.prototype = Object.create(BABYLON.Texture.prototype);
+
+    // Members
+    BABYLON.RenderTargetTexture.prototype.renderList = [];
+    BABYLON.RenderTargetTexture.prototype.isRenderTarget = true;
+    BABYLON.RenderTargetTexture.prototype.coordinatesMode = BABYLON.Texture.PROJECTION_MODE;
+
+    // Methods  
+    BABYLON.RenderTargetTexture.prototype._onBeforeRender = null;
+    BABYLON.RenderTargetTexture.prototype._onAfterRender = null;
+    
+    BABYLON.RenderTargetTexture.prototype.render = function () {
+        if (this._onBeforeRender) {
+            this._onBeforeRender();
+        }
+
+        var scene = this._scene;
+        var engine = scene.getEngine();
+        
+        if (this._waitingRenderList) {
+            this.renderList = [];
+            for (var index = 0; index < this._waitingRenderList.length; index++) {
+                var id = this._waitingRenderList[index];
+                this.renderList.push(this._scene.getMeshByID(id));
+            }
+
+            delete this._waitingRenderList;
+        }
+
+        if (!this.renderList || this.renderList.length == 0) {
+            return;
+        }
+        
+        // Bind
+        engine.bindFramebuffer(this._texture);
+
+        // Clear
+        engine.clear(scene.clearColor, true, true);
+        
+        // Dispatch subMeshes
+        this._opaqueSubMeshes = [];
+        this._transparentSubMeshes = [];
+        this._alphaTestSubMeshes = [];
+        
+        for (var meshIndex = 0; meshIndex < this.renderList.length; meshIndex++) {
+            var mesh = this.renderList[meshIndex];
+
+            if (mesh.material && mesh.isEnabled() && mesh.isVisible) {
+                for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
+                    var subMesh = mesh.subMeshes[subIndex];
+                    var material = subMesh.getMaterial();
+                    
+                    if (material.needAlphaTesting()) { // Alpha test
+                        this._alphaTestSubMeshes.push(subMesh);
+                    } else if (material.needAlphaBlending()) { // Transparent
+                        if (material.alpha > 0) {
+                            this._transparentSubMeshes.push(subMesh); // Opaque
+                        }
+                    } else {
+                        this._opaqueSubMeshes.push(subMesh);
+                    }
+                }
+            }
+        }
+        
+        // Render
+        scene._localRender(this._opaqueSubMeshes, this._alphaTestSubMeshes, this._transparentSubMeshes, this.renderList);
+
+        // Unbind
+        engine.unBindFramebuffer(this._texture);
+        
+        if (this._onAfterRender) {
+            this._onAfterRender();
+        }
+    };
+
+})();

+ 89 - 0
Materials/textures/babylon.texture.js

@@ -0,0 +1,89 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Texture = function (url, scene, noMipmap, invertY) {
+        this._scene = scene;
+        this._scene.textures.push(this);
+
+        this.name = url;
+
+        this._texture = this._getFromCache(url, noMipmap);
+        
+        if (!this._texture) {
+            this._texture = scene.getEngine().createTexture(url, noMipmap, invertY);
+        }
+        
+        // Animations
+        this.animations = [];
+    };
+
+    BABYLON.Texture.prototype = Object.create(BABYLON.BaseTexture.prototype);
+
+    // Constants
+    BABYLON.Texture.EXPLICIT_MODE = 0;
+    BABYLON.Texture.SPHERICAL_MODE = 1;
+    BABYLON.Texture.PLANAR_MODE = 2;
+    BABYLON.Texture.CUBIC_MODE = 3;
+    BABYLON.Texture.PROJECTION_MODE = 4;
+    
+    // Members
+    BABYLON.Texture.prototype.uOffset = 0;
+    BABYLON.Texture.prototype.vOffset = 0;
+    BABYLON.Texture.prototype.uScale = 1.0;
+    BABYLON.Texture.prototype.vScale = 1.0;
+    BABYLON.Texture.prototype.uAng = 0;
+    BABYLON.Texture.prototype.vAng = 0;
+    BABYLON.Texture.prototype.wAng = 0;
+    BABYLON.Texture.prototype.wrapU = true;
+    BABYLON.Texture.prototype.wrapV = true;
+    BABYLON.Texture.prototype.coordinatesIndex = 0;
+    BABYLON.Texture.prototype.coordinatesMode = BABYLON.Texture.EXPLICIT_MODE;    
+
+    // Methods    
+    BABYLON.Texture.prototype._prepareRowForTextureGeneration = function(t) {
+        var matRot = BABYLON.Matrix.RotationYawPitchRoll(this.vAng, this.uAng, this.wAng);
+
+        t.x -= this.uOffset + 0.5;
+        t.y -= this.vOffset + 0.5;
+        t.z -= 0.5;
+
+        t = BABYLON.Vector3.TransformCoordinates(t, matRot);
+
+        if (this.uScale != 1.0)
+        {
+            t.x *= this.uScale;
+        }
+
+        if (this.vScale != 1.0)
+        {
+            t.y *= this.vScale;
+        }
+
+        t.x += 0.5;
+        t.y += 0.5;
+        t.z += 0.5;
+
+        return t;
+    };
+
+    BABYLON.Texture.prototype._computeTextureMatrix = function () {
+        var t0 = new BABYLON.Vector3(0, 0, 0);
+        var t1 = new BABYLON.Vector3(1.0, 0, 0);
+        var t2 = new BABYLON.Vector3(0, 1.0, 0);
+
+        var matTemp = BABYLON.Matrix.Identity();
+
+        t0 = this._prepareRowForTextureGeneration(t0);
+        t1 = this._prepareRowForTextureGeneration(t1);
+        t2 = this._prepareRowForTextureGeneration(t2);
+
+        t1 = t1.subtract(t0);
+        t2 = t2.subtract(t0);
+
+        matTemp.m[0] = t1.x; matTemp.m[1] = t1.y; matTemp.m[2] = t1.z;
+        matTemp.m[4] = t2.x; matTemp.m[5] = t2.y; matTemp.m[6] = t2.z;
+        matTemp.m[8] = t0.x; matTemp.m[9] = t0.y; matTemp.m[10] = t0.z;
+
+        return matTemp;
+    };
+})();

+ 703 - 0
Mesh/babylon.mesh.js

@@ -0,0 +1,703 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Mesh = function (name, vertexDeclaration, scene) {
+        this.name = name;
+        this.id = name;
+        this._scene = scene;
+        this._vertexDeclaration = vertexDeclaration;
+
+        this._vertexStrideSize = 0;
+        for (var index = 0; index < vertexDeclaration.length; index++) {
+            this._vertexStrideSize += vertexDeclaration[index];
+        }
+
+        this._vertexStrideSize *= 4; // sizeof(float)
+
+        this._totalVertices = 0;
+        this._worldMatrix = BABYLON.Matrix.Identity();
+
+        scene.meshes.push(this);
+
+        this.position = new BABYLON.Vector3(0, 0, 0);
+        this.rotation = new BABYLON.Vector3(0, 0, 0);
+        this.scaling = new BABYLON.Vector3(1, 1, 1);
+
+        this._vertices = [];
+        this._indices = [];
+        this.subMeshes = [];
+
+        // Animations
+        this.animations = [];
+
+        // Cache
+        this._positions = null;
+        this._cache = {
+            position: null,
+            scaling: null,
+            rotation: null
+        };
+
+        this._childrenFlag = false;
+    };
+
+    // Constants
+    BABYLON.Mesh.BILLBOARDMODE_NONE = 0;
+    BABYLON.Mesh.BILLBOARDMODE_X = 1;
+    BABYLON.Mesh.BILLBOARDMODE_Y = 2;
+    BABYLON.Mesh.BILLBOARDMODE_Z = 4;
+    BABYLON.Mesh.BILLBOARDMODE_ALL = 7;
+
+    // Members    
+    BABYLON.Mesh.prototype.material = null;
+    BABYLON.Mesh.prototype.parent = null;
+    BABYLON.Mesh.prototype._isEnabled = true;
+    BABYLON.Mesh.prototype.isVisible = true;
+    BABYLON.Mesh.prototype.visibility = 1.0;
+    BABYLON.Mesh.prototype.billboardMode = BABYLON.Mesh.BILLBOARDMODE_NONE;
+    BABYLON.Mesh.prototype.checkCollisions = false;
+
+    BABYLON.Mesh.prototype.onDispose = false;
+
+    // Properties
+    BABYLON.Mesh.prototype.getScene = function () {
+        return this._scene;
+    };
+
+    BABYLON.Mesh.prototype.getWorldMatrix = function () {
+        return this._worldMatrix;
+    };
+
+    BABYLON.Mesh.prototype.getTotalVertices = function () {
+        return this._totalVertices;
+    };
+
+    BABYLON.Mesh.prototype.getVertices = function () {
+        return this._vertices;
+    };
+
+    BABYLON.Mesh.prototype.getVertexStrideSize = function () {
+        return this._vertexStrideSize;
+    };
+
+    BABYLON.Mesh.prototype.getFloatVertexStrideSize = function () {
+        return this._vertexStrideSize / 4;
+    };
+
+    BABYLON.Mesh.prototype._needToSynchonizeChildren = function () {
+        return this._childrenFlag;
+    };
+
+    BABYLON.Mesh.prototype.isSynchronized = function () {
+        if (this.billboardMode !== BABYLON.Mesh.BILLBOARDMODE_NONE)
+            return false;
+
+        if (!this._cache.position || !this._cache.rotation || !this._cache.scaling) {
+            return false;
+        }
+
+        if (!this._cache.position.equals(this.position))
+            return false;
+
+        if (!this._cache.rotation.equals(this.rotation))
+            return false;
+
+        if (!this._cache.scaling.equals(this.scaling))
+            return false;
+
+        if (this.parent)
+            return !this.parent._needToSynchonizeChildren();
+
+        return true;
+    };
+
+    BABYLON.Mesh.prototype.isEnabled = function () {
+        if (!this._isEnabled) {
+            return false;
+        }
+
+        if (this.parent) {
+            return this.parent.isEnabled();
+        }
+
+        return true;
+    };
+
+    BABYLON.Mesh.prototype.setEnabled = function (value) {
+        this._isEnabled = value;
+    };
+
+    BABYLON.Mesh.prototype.isAnimated = function () {
+        return this._animationStarted;
+    };
+
+    // Methods
+    BABYLON.Mesh.prototype.computeWorldMatrix = function () {
+        if (this.isSynchronized()) {
+            this._childrenFlag = false;
+            return this._worldMatrix;
+        }
+
+        this._childrenFlag = true;
+        this._cache.position = this.position.clone();
+        this._cache.rotation = this.rotation.clone();
+        this._cache.scaling = this.scaling.clone();
+
+        var localWorld = BABYLON.Matrix.Scaling(this.scaling.x, this.scaling.y, this.scaling.z).multiply(
+            BABYLON.Matrix.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z));
+
+        // Billboarding
+        var matTranslation = BABYLON.Matrix.Translation(this.position.x, this.position.y, this.position.z);
+        if (this.billboardMode !== BABYLON.Mesh.BILLBOARDMODE_NONE) {
+            var localPosition = this.position.clone();
+            var zero = this._scene.activeCamera.position.clone();
+
+            if (this.parent) {
+                localPosition = localPosition.add(this.parent.position);
+                matTranslation = BABYLON.Matrix.Translation(localPosition.x, localPosition.y, localPosition.z);
+            }
+
+            if (this.billboardMode & BABYLON.Mesh.BILLBOARDMODE_ALL === BABYLON.Mesh.BILLBOARDMODE_ALL) {
+                zero = this._scene.activeCamera.position;
+            } else {
+                if (this.billboardMode & BABYLON.Mesh.BILLBOARDMODE_X)
+                    zero.x = localPosition.x + BABYLON.Engine.epsilon;
+                if (this.billboardMode & BABYLON.Mesh.BILLBOARDMODE_Y)
+                    zero.y = localPosition.y + BABYLON.Engine.epsilon;
+                if (this.billboardMode & BABYLON.Mesh.BILLBOARDMODE_Z)
+                    zero.z = localPosition.z + BABYLON.Engine.epsilon;
+            }
+
+            var matBillboard = BABYLON.Matrix.LookAtLH(localPosition, zero, BABYLON.Vector3.Up());
+            matBillboard.m[12] = matBillboard.m[13] = matBillboard.m[14] = 0;
+
+            matBillboard.invert();
+
+            localWorld = BABYLON.Matrix.RotationY(Math.PI).multiply(localWorld.multiply(matBillboard));
+        }
+
+        localWorld = localWorld.multiply(matTranslation);
+
+        // Parent
+        if (this.parent && this.billboardMode === BABYLON.Mesh.BILLBOARDMODE_NONE) {
+            var parentWorld = this.parent.getWorldMatrix();
+
+            localWorld = localWorld.multiply(parentWorld);
+        }
+
+        this._worldMatrix = localWorld;
+
+        // Bounding info
+        if (this._boundingInfo) {
+            this._scaleFactor = Math.max(this.scaling.x, this.scaling.y);
+            this._scaleFactor = Math.max(this._scaleFactor, this.scaling.z);
+
+            if (this.parent)
+                this._scaleFactor = this._scaleFactor * this.parent._scaleFactor;
+
+            this._boundingInfo._update(localWorld, this._scaleFactor);
+
+            for (var subIndex = 0; subIndex < this.subMeshes.length; subIndex++) {
+                var subMesh = this.subMeshes[subIndex];
+
+                subMesh.updateBoundingInfo(localWorld, this._scaleFactor);
+            }
+        }
+
+        return localWorld;
+    };
+
+    BABYLON.Mesh.prototype._createGlobalSubMesh = function () {
+        if (!this._totalVertices || !this._indices) {
+            return null;
+        }
+
+        this.subMeshes = [];
+        return new BABYLON.SubMesh(0, 0, this._totalVertices, 0, this._indices.length, this);
+    };
+
+    BABYLON.Mesh.prototype.setVertices = function (vertices, uvCount) {
+        if (this._vertexBuffer) {
+            this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
+        }
+
+        this._uvCount = uvCount;
+        this._vertexBuffer = this._scene.getEngine().createVertexBuffer(vertices);
+        this._vertices = vertices;
+
+        this._totalVertices = vertices.length / this.getFloatVertexStrideSize();
+
+        this._boundingInfo = new BABYLON.BoundingInfo(vertices, this.getFloatVertexStrideSize(), 0, vertices.length);
+
+        this._createGlobalSubMesh();
+        this._positions = null;
+    };
+
+    BABYLON.Mesh.prototype.setIndices = function (indices) {
+        if (this._indexBuffer) {
+            this._scene.getEngine()._releaseBuffer(this._indexBuffer);
+        }
+
+        this._indexBuffer = this._scene.getEngine().createIndexBuffer(indices);
+        this._indices = indices;
+
+        this._createGlobalSubMesh();
+    };
+
+    BABYLON.Mesh.prototype.render = function (subMesh) {
+        if (!this._vertexBuffer || !this._indexBuffer) {
+            return;
+        }
+
+        var engine = this._scene.getEngine();
+
+        // World
+        var world = this.getWorldMatrix();
+
+        // Material
+        var effectiveMaterial = subMesh.getMaterial();
+
+        if (!effectiveMaterial || !effectiveMaterial.isReady(this)) {
+            return;
+        }
+
+        effectiveMaterial._preBind();
+        effectiveMaterial.bind(world, this);
+
+        // Wireframe
+        var indexToBind = this._indexBuffer;
+        var useTriangles = true;
+
+        if (engine.forceWireframe || effectiveMaterial.wireframe) {
+            indexToBind = subMesh.getLinesIndexBuffer(this._indices, engine);
+            useTriangles = false;
+        }
+
+        // VBOs
+        engine.bindBuffers(this._vertexBuffer, indexToBind, this._vertexDeclaration, this._vertexStrideSize, effectiveMaterial.getEffect());
+
+        // Draw order
+        engine.draw(useTriangles, useTriangles ? subMesh.indexStart : 0, useTriangles ? subMesh.indexCount : subMesh.linesIndexCount);
+
+        // Unbind
+        effectiveMaterial.unbind();
+    };
+
+    BABYLON.Mesh.prototype.isDescendantOf = function (ancestor) {
+        if (this.parent) {
+            if (this.parent === ancestor) {
+                return true;
+            }
+
+            return this.parent.isDescendantOf(ancestor);
+        }
+        return false;
+    };
+
+    BABYLON.Mesh.prototype.getDescendants = function () {
+        var results = [];
+        for (var index = 0; index < this._scene.meshes.length; index++) {
+            var mesh = this._scene.meshes[index];
+            if (mesh.isDescendantOf(this)) {
+                results.push(mesh);
+            }
+        }
+
+        return results;
+    };
+
+    BABYLON.Mesh.prototype.getEmittedParticleSystems = function () {
+        var results = [];
+        for (var index = 0; index < this._scene.particleSystems.length; index++) {
+            var particleSystem = this._scene.particleSystems[index];
+            if (particleSystem.emitter === this) {
+                results.push(particleSystem);
+            }
+        }
+
+        return results;
+    };
+
+    BABYLON.Mesh.prototype.getHierarchyEmittedParticleSystems = function () {
+        var results = [];
+        var descendants = this.getDescendants();
+        descendants.push(this);
+
+        for (var index = 0; index < this._scene.particleSystems.length; index++) {
+            var particleSystem = this._scene.particleSystems[index];
+            if (descendants.indexOf(particleSystem.emitter) !== -1) {
+                results.push(particleSystem);
+            }
+        }
+
+        return results;
+    };
+
+    BABYLON.Mesh.prototype.getChildren = function () {
+        var results = [];
+        for (var index = 0; index < this._scene.meshes.length; index++) {
+            var mesh = this._scene.meshes[index];
+            if (mesh.parent == this) {
+                results.push(mesh);
+            }
+        }
+
+        return results;
+    };
+
+    BABYLON.Mesh.prototype.isInFrustrum = function (frustumPlanes) {
+        return this._boundingInfo.isInFrustrum(frustumPlanes);
+    };
+
+    BABYLON.Mesh.prototype.setMaterialByID = function (id) {
+        var materials = this._scene.materials;
+        for (var index = 0; index < materials.length; index++) {
+            if (materials[index].id == id) {
+                this.material = materials[index];
+                return;
+            }
+        }
+
+        // Multi
+        var multiMaterials = this._scene.multiMaterials;
+        for (var index = 0; index < multiMaterials.length; index++) {
+            if (multiMaterials[index].id == id) {
+                this.material = multiMaterials[index];
+                return;
+            }
+        }
+    };
+
+    BABYLON.Mesh.prototype.getAnimatables = function () {
+        var results = [];
+
+        if (this.material) {
+            results.push(this.material);
+        }
+
+        return results;
+    };
+
+    // Cache
+    BABYLON.Mesh.prototype._generatePointsArray = function () {
+        if (this._positions)
+            return;
+
+        this._positions = [];
+
+        var stride = this.getFloatVertexStrideSize();
+
+        for (var index = 0; index < this._vertices.length; index += stride) {
+            this._positions.push(BABYLON.Vector3.FromArray(this._vertices, index));
+        }
+    };
+
+    // Collisions
+    BABYLON.Mesh.prototype._collideForSubMesh = function (subMesh, transformMatrix, collider) {
+        this._generatePointsArray();
+        // Transformation
+        if (!subMesh._lastColliderWorldVertices || !subMesh._lastColliderTransformMatrix.equals(transformMatrix)) {
+            subMesh._lastColliderTransformMatrix = transformMatrix;
+            subMesh._lastColliderWorldVertices = [];
+            var start = subMesh.verticesStart;
+            var end = (subMesh.verticesStart + subMesh.verticesCount);
+            for (var i = start; i < end; i++) {
+                subMesh._lastColliderWorldVertices.push(BABYLON.Vector3.TransformCoordinates(this._positions[i], transformMatrix));
+            }
+        }
+        // Collide
+        collider._collide(subMesh, subMesh._lastColliderWorldVertices, this._indices, subMesh.indexStart, subMesh.indexStart + subMesh.indexCount, subMesh.verticesStart);
+    };
+
+    BABYLON.Mesh.prototype._processCollisionsForSubModels = function (collider, transformMatrix) {
+        for (var index = 0; index < this.subMeshes.length; index++) {
+            var subMesh = this.subMeshes[index];
+
+            // Bounding test
+            if (this.subMeshes.length > 1 && !subMesh._checkCollision(collider))
+                continue;
+
+            this._collideForSubMesh(subMesh, transformMatrix, collider);
+        }
+    };
+
+    BABYLON.Mesh.prototype._checkCollision = function (collider) {
+        // Bounding box test
+        if (!this._boundingInfo._checkCollision(collider))
+            return;
+
+        // Transformation matrix
+        var transformMatrix = this._worldMatrix.multiply(BABYLON.Matrix.Scaling(1.0 / collider.radius.x, 1.0 / collider.radius.y, 1.0 / collider.radius.z));
+
+        this._processCollisionsForSubModels(collider, transformMatrix);
+    };
+
+    BABYLON.Mesh.prototype.intersectsMesh = function (mesh, precise) {
+        if (!this._boundingInfo || !mesh._boundingInfo) {
+            return false;
+        }
+
+        return this._boundingInfo.intersects(mesh._boundingInfo, precise);
+    };
+
+    BABYLON.Mesh.prototype.intersectsPoint = function (point) {
+        if (!this._boundingInfo) {
+            return false;
+        }
+
+        return this._boundingInfo.intersectsPoint(point);
+    };
+
+    // Picking
+    BABYLON.Mesh.prototype.intersects = function (ray) {
+        if (!this._boundingInfo || !ray.intersectsSphere(this._boundingInfo.boundingSphere)) {
+            return { hit: false, distance: 0 };
+        }
+
+        this._generatePointsArray();
+
+        var distance = Number.MAX_VALUE;
+
+        for (var index = 0; index < this.subMeshes.length; index++) {
+            var subMesh = this.subMeshes[index];
+
+            // Bounding test
+            if (this.subMeshes.length > 1 && !subMesh.canIntersects(ray))
+                continue;
+
+            var result = subMesh.intersects(ray, this._positions, this._indices);
+
+            if (result.hit) {
+                if (result.distance < distance && result.distance >= 0) {
+                    distance = result.distance;
+                }
+            }
+        }
+
+        if (distance >= 0)
+            return { hit: true, distance: distance };
+
+        return { hit: false, distance: 0 };
+    };
+
+    // Clone
+    BABYLON.Mesh.prototype.clone = function (name, newParent) {
+        var result = new BABYLON.Mesh(name, this._vertexDeclaration, this._scene);
+
+        BABYLON.Tools.DeepCopy(this, result, ["name", "material"], ["_uvCount", "_vertices", "_indices", "_totalVertices"]);
+
+        // Bounding info
+        result._boundingInfo = new BABYLON.BoundingInfo(result._vertices, result.getFloatVertexStrideSize(), 0, result._vertices.length);
+
+        // Material
+        result.material = this.material;
+
+        // Buffers
+        result._vertexBuffer = this._vertexBuffer;
+        this._vertexBuffer.references++;
+
+        result._indexBuffer = this._indexBuffer;
+        this._indexBuffer.references++;
+
+        // Parent
+        if (newParent) {
+            result.parent = newParent;
+        }
+
+        // Children
+        for (var index = 0; index < this._scene.meshes.length; index++) {
+            var mesh = this._scene.meshes[index];
+
+            if (mesh.parent == this) {
+                mesh.clone(mesh.name, result);
+            }
+        }
+
+        // Particles
+        for (var index = 0; index < this._scene.particleSystems.length; index++) {
+            var system = this._scene.particleSystems[index];
+
+            if (system.emitter == this) {
+                system.clone(system.name, result);
+            }
+        }
+
+        return result;
+    };
+
+    // Dispose
+    BABYLON.Mesh.prototype.dispose = function (doNotRecurse) {
+        if (this._vertexBuffer) {
+            //this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
+            this._vertexBuffer = null;
+        }
+
+        if (this._indexBuffer) {
+            this._scene.getEngine()._releaseBuffer(this._indexBuffer);
+            this._indexBuffer = null;
+        }
+
+        // Remove from scene
+        var index = this._scene.meshes.indexOf(this);
+        this._scene.meshes.splice(index, 1);
+
+        if (doNotRecurse) {
+            return;
+        }
+
+        // Particles
+        for (var index = 0; index < this._scene.particleSystems.length; index++) {
+            if (this._scene.particleSystems[index].emitter == this) {
+                this._scene.particleSystems[index].dispose();
+                index--;
+            }
+        }
+
+        // Children
+        var objects = this._scene.meshes.slice(0);
+        for (var index = 0; index < objects.length; index++) {
+            if (objects[index].parent == this) {
+                objects[index].dispose();
+            }
+        }
+
+        // Callback
+        if (this.onDispose) {
+            this.onDispose();
+        }
+    };
+
+    // Statics
+    BABYLON.Mesh.createBox = function (name, size, scene) {
+        var box = new BABYLON.Mesh(name, [3, 3, 2], scene);
+
+        var normals = [
+            new BABYLON.Vector3(0, 0, 1),
+            new BABYLON.Vector3(0, 0, -1),
+            new BABYLON.Vector3(1, 0, 0),
+            new BABYLON.Vector3(-1, 0, 0),
+            new BABYLON.Vector3(0, 1, 0),
+            new BABYLON.Vector3(0, -1, 0)
+        ];
+
+        var indices = [];
+        var vertices = [];
+
+        // Create each face in turn.
+        for (var index = 0; index < normals.length; index++) {
+            var normal = normals[index];
+
+            // Get two vectors perpendicular to the face normal and to each other.
+            var side1 = new BABYLON.Vector3(normal.y, normal.z, normal.x);
+            var side2 = BABYLON.Vector3.Cross(normal, side1);
+
+            // Six indices (two triangles) per face.
+            var verticesLength = vertices.length / 8;
+            indices.push(verticesLength);
+            indices.push(verticesLength + 1);
+            indices.push(verticesLength + 2);
+
+            indices.push(verticesLength);
+            indices.push(verticesLength + 2);
+            indices.push(verticesLength + 3);
+
+            // Four vertices per face.
+            var vertex = normal.subtract(side1).subtract(side2).scale(size / 2);
+            vertices.push(vertex.x, vertex.y, vertex.z, normal.x, normal.y, normal.z, 1.0, 1.0);
+
+            vertex = normal.subtract(side1).add(side2).scale(size / 2);
+            vertices.push(vertex.x, vertex.y, vertex.z, normal.x, normal.y, normal.z, 0.0, 1.0);
+
+            vertex = normal.add(side1).add(side2).scale(size / 2);
+            vertices.push(vertex.x, vertex.y, vertex.z, normal.x, normal.y, normal.z, 0.0, 0.0);
+
+            vertex = normal.add(side1).subtract(side2).scale(size / 2);
+            vertices.push(vertex.x, vertex.y, vertex.z, normal.x, normal.y, normal.z, 1.0, 0.0);
+        }
+
+        box.setVertices(vertices, 1);
+        box.setIndices(indices);
+
+        return box;
+    };
+
+    BABYLON.Mesh.createSphere = function (name, segments, diameter, scene) {
+        var sphere = new BABYLON.Mesh(name, [3, 3, 2], scene);
+
+        var radius = diameter / 2;
+
+        var totalZRotationSteps = 2 + segments;
+        var totalYRotationSteps = 2 * totalZRotationSteps;
+
+        var indices = [];
+        var vertices = [];
+
+        for (var zRotationStep = 0; zRotationStep <= totalZRotationSteps; zRotationStep++) {
+            var normalizedZ = zRotationStep / totalZRotationSteps;
+            var angleZ = (normalizedZ * Math.PI);
+
+            for (var yRotationStep = 0; yRotationStep <= totalYRotationSteps; yRotationStep++) {
+                var normalizedY = yRotationStep / totalYRotationSteps;
+
+                var angleY = normalizedY * Math.PI * 2;
+
+                var rotationZ = BABYLON.Matrix.RotationZ(-angleZ);
+                var rotationY = BABYLON.Matrix.RotationY(angleY);
+                var afterRotZ = BABYLON.Vector3.TransformCoordinates(BABYLON.Vector3.Up(), rotationZ);
+                var complete = BABYLON.Vector3.TransformCoordinates(afterRotZ, rotationY);
+
+                var vertex = complete.scale(radius);
+                var normal = BABYLON.Vector3.Normalize(vertex);
+
+                vertices.push(vertex.x, vertex.y, vertex.z, normal.x, normal.y, normal.z, normalizedZ, normalizedY);
+            }
+
+            if (zRotationStep > 0) {
+                var verticesCount = vertices.length / 8;
+                for (var firstIndex = verticesCount - 2 * (totalYRotationSteps + 1) ; (firstIndex + totalYRotationSteps + 2) < verticesCount; firstIndex++) {
+                    indices.push((firstIndex));
+                    indices.push((firstIndex + 1));
+                    indices.push(firstIndex + totalYRotationSteps + 1);
+
+                    indices.push((firstIndex + totalYRotationSteps + 1));
+                    indices.push((firstIndex + 1));
+                    indices.push((firstIndex + totalYRotationSteps + 2));
+                }
+            }
+        }
+
+        sphere.setVertices(vertices, 1);
+        sphere.setIndices(indices);
+
+        return sphere;
+    };
+
+    // Plane
+    BABYLON.Mesh.createPlane = function (name, size, scene) {
+        var plane = new BABYLON.Mesh(name, [3, 3, 2], scene);
+
+        var indices = [];
+        var vertices = [];
+
+        // Vertices
+        var halfSize = size / 2.0;
+        vertices.push(-halfSize, -halfSize, 0, 0, 1.0, 0, 0.0, 0.0);
+        vertices.push(halfSize, -halfSize, 0, 0, 1.0, 0, 1.0, 0.0);
+        vertices.push(halfSize, halfSize, 0, 0, 1.0, 0, 1.0, 1.0);
+        vertices.push(-halfSize, halfSize, 0, 0, 1.0, 0, 0.0, 1.0);
+
+        // Indices
+        indices.push(0);
+        indices.push(1);
+        indices.push(2);
+
+        indices.push(0);
+        indices.push(2);
+        indices.push(3);
+
+        plane.setVertices(vertices, 1);
+        plane.setIndices(indices);
+
+        return plane;
+    };
+})();

+ 97 - 0
Mesh/babylon.subMesh.js

@@ -0,0 +1,97 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.SubMesh = function (materialIndex, verticesStart, verticesCount, indexStart, indexCount, mesh) {
+        this._mesh = mesh;
+        mesh.subMeshes.push(this);
+        this.materialIndex = materialIndex;
+        this.verticesStart = verticesStart;
+        this.verticesCount = verticesCount;
+        this.indexStart = indexStart;
+        this.indexCount = indexCount;
+
+        var stride = this._mesh.getFloatVertexStrideSize();
+        this._boundingInfo = new BABYLON.BoundingInfo(this._mesh.getVertices(), stride, verticesStart * stride, (verticesStart + verticesCount) * stride);
+    };
+    
+    //Properties
+    BABYLON.SubMesh.prototype.getMaterial = function () {
+        var rootMaterial = this._mesh.material;
+        
+        if (rootMaterial && rootMaterial.getSubMaterial) {
+            return rootMaterial.getSubMaterial(this.materialIndex);
+        }
+        
+        if (!rootMaterial) {
+            return this._mesh._scene.defaultMaterial;
+        }
+
+        return rootMaterial;
+    };
+
+    // Methods
+    BABYLON.SubMesh.prototype._checkCollision = function (collider) {
+        return this._boundingInfo._checkCollision(collider);
+    };
+    
+    BABYLON.SubMesh.prototype.updateBoundingInfo = function(world, scale) {
+        this._boundingInfo._update(world, scale);
+    };
+    
+    BABYLON.SubMesh.prototype.isInFrustrum = function (frustumPlanes) {
+        return this._boundingInfo.isInFrustrum(frustumPlanes);
+    };
+    
+    BABYLON.SubMesh.prototype.render = function() {
+        this._mesh.render(this);
+    };
+
+    BABYLON.SubMesh.prototype.getLinesIndexBuffer = function(indices, engine) {
+        if (!this._linesIndexBuffer) {
+            var linesIndices = [];
+
+            for (var index = this.indexStart; index < this.indexStart + this.indexCount; index += 3) {
+                linesIndices.push(  indices[index], indices[index + 1],
+                                    indices[index + 1], indices[index + 2],
+                                    indices[index + 2], indices[index]);
+            }
+
+            this._linesIndexBuffer = engine.createIndexBuffer(linesIndices);
+            this.linesIndexCount = linesIndices.length;
+        }
+        return this._linesIndexBuffer;
+    };
+
+    BABYLON.SubMesh.prototype.canIntersects = function(ray) {
+        return ray.intersectsSphere(this._boundingInfo.boundingSphere);
+    };
+
+    BABYLON.SubMesh.prototype.intersects = function (ray, positions, indices) {
+        var distance = Number.MAX_VALUE;
+        
+        // Triangles test
+        for (var index = this.indexStart; index < this.indexStart + this.indexCount; index += 3) {
+            var p0 = positions[indices[index]];
+            var p1 = positions[indices[index + 1]];
+            var p2 = positions[indices[index + 2]];
+
+            var result = ray.intersectsTriangle(p0, p1, p2);
+
+            if (result.hit) {
+                if (result.distance < distance && result.distance >= 0) {
+                    distance = result.distance;
+                }
+            }
+        }
+        
+        if (distance >= 0)
+            return { hit: true, distance: distance };
+
+        return { hit: false, distance: 0 };
+    };
+
+    // Clone    
+    BABYLON.SubMesh.prototype.clone = function(newMesh) {
+        return new BABYLON.SubMesh(this.materialIndex, this.verticesStart, this.verticesCount, this.indexStart, this.indexCount, newMesh);
+    };
+})();

+ 16 - 0
Particles/babylon.particle.js

@@ -0,0 +1,16 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Particle = function () {
+    };
+    
+    BABYLON.Particle.prototype.position = null;
+    BABYLON.Particle.prototype.direction = null;
+    BABYLON.Particle.prototype.lifeTime = 1.0;
+    BABYLON.Particle.prototype.age = 0;
+    BABYLON.Particle.prototype.size = 0;
+    BABYLON.Particle.prototype.angle = 0;
+    BABYLON.Particle.prototype.angularSpeed = 0;
+    BABYLON.Particle.prototype.color = null;
+    BABYLON.Particle.prototype.colorStep = null;
+})();

+ 359 - 0
Particles/babylon.particleSystem.js

@@ -0,0 +1,359 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    var appendParticleVertex = function (particle, vertices, offsetX, offsetY) {
+        vertices.push(particle.position.x);
+        vertices.push(particle.position.y);
+        vertices.push(particle.position.z);
+        vertices.push(particle.color.r);
+        vertices.push(particle.color.g);
+        vertices.push(particle.color.b);
+        vertices.push(particle.color.a);
+        vertices.push(particle.angle);
+        vertices.push(particle.size);
+        vertices.push(offsetX);
+        vertices.push(offsetY);
+    };
+
+    var randomNumber = function (min, max) {
+        if (min == max) {
+            return (min);
+        }
+
+        var random = Math.random();
+
+        return ((random * (max - min)) + min);
+    };
+
+    BABYLON.ParticleSystem = function (name, capacity, scene) {
+        this.name = name;
+        this.id = name;
+        this._capacity = capacity;
+
+        this._scene = scene;
+
+        scene.particleSystems.push(this);
+
+        // Vectors and colors
+        this.gravity = BABYLON.Vector3.Zero();
+        this.direction1 = new BABYLON.Vector3(0, 1.0, 0);
+        this.direction2 = new BABYLON.Vector3(0, 1.0, 0);
+        this.minEmitBox = new BABYLON.Vector3(-0.5, -0.5, -0.5);
+        this.maxEmitBox = new BABYLON.Vector3(0.5, 0.5, 0.5);
+        this.color1 = new BABYLON.Color4(1.0, 1.0, 1.0, 1.0);
+        this.color2 = new BABYLON.Color4(1.0, 1.0, 1.0, 1.0);
+        this.colorDead = new BABYLON.Color4(0, 0, 0, 1.0);
+        this.deadAlpha = 0;
+        this.textureMask = new BABYLON.Color4(1.0, 1.0, 1.0, 1.0);
+
+        // Particles
+        this.particles = [];
+        this._newPartsExcess = 0;
+
+        // VBO
+        this._vertexDeclaration = [3, 4, 4];
+        this._vertexStrideSize = 11 * 4; // 10 floats per particle (x, y, z, r, g, b, a, angle, size, offsetX, offsetY)
+        this._vertexBuffer = scene.getEngine().createDynamicVertexBuffer(capacity * this._vertexStrideSize * 4);
+
+        var indices = [];
+        var index = 0;
+        for (var count = 0; count < capacity; count++) {
+            indices.push(index);
+            indices.push(index + 1);
+            indices.push(index + 2);
+            indices.push(index);
+            indices.push(index + 2);
+            indices.push(index + 3);
+            index += 4;
+        }
+
+        this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
+
+        // Effects
+        this._baseEffect = this._scene.getEngine().createEffect("particles",
+                    ["position", "color", "options"],
+                    ["view", "projection", "textureMask"],
+                    ["diffuseSampler"], "");
+
+        this._clippedEffect = this._scene.getEngine().createEffect("particles",
+                    ["position", "color", "options"],
+                    ["invView", "view", "projection", "vClipPlane", "textureMask"],
+                    ["diffuseSampler"], "#define CLIPPLANE");
+    };
+
+    // Members
+    BABYLON.ParticleSystem.prototype.emitter = null;
+    BABYLON.ParticleSystem.prototype.emitRate = 10;
+    BABYLON.ParticleSystem.prototype.manualEmitCount = -1;
+    BABYLON.ParticleSystem.prototype.updateSpeed = 0.01;
+    BABYLON.ParticleSystem.prototype.targetStopDuration = 0;
+    BABYLON.ParticleSystem.prototype.disposeOnStop = false;
+
+    BABYLON.ParticleSystem.prototype.minEmitPower = 1;
+    BABYLON.ParticleSystem.prototype.maxEmitPower = 1;
+
+    BABYLON.ParticleSystem.prototype.minLifeTime = 1;
+    BABYLON.ParticleSystem.prototype.maxLifeTime = 1;
+
+    BABYLON.ParticleSystem.prototype.minSize = 1;
+    BABYLON.ParticleSystem.prototype.maxSize = 1;
+    BABYLON.ParticleSystem.prototype.minAngularSpeed = 0;
+    BABYLON.ParticleSystem.prototype.maxAngularSpeed = 0;
+
+    BABYLON.ParticleSystem.prototype.particleTexture = null;
+    
+    BABYLON.ParticleSystem.prototype.onDispose = null;
+
+    BABYLON.ParticleSystem.prototype.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;
+
+    // Methods   
+    BABYLON.ParticleSystem.prototype.isAlive = function () {
+        return this._alive;
+    };
+
+    BABYLON.ParticleSystem.prototype.start = function () {
+        this._started = true;
+        this._stopped = false;
+        this._actualFrame = 0;
+    };
+
+    BABYLON.ParticleSystem.prototype.stop = function () {
+        this._stopped = true;
+    };
+
+    BABYLON.ParticleSystem.prototype._update = function (newParticles) {
+        // Update current
+        this._alive = this.particles.length > 0;
+        for (var index = 0; index < this.particles.length; index++) {
+            var particle = this.particles[index];
+            particle.age += this._scaledUpdateSpeed;
+
+            if (particle.age >= particle.lifeTime) {
+                this.particles.splice(index, 1);
+                index--;
+                continue;
+            }
+            else {
+                particle.color = particle.color.add(particle.colorStep.scale(this._scaledUpdateSpeed));
+
+                if (particle.color.a < 0)
+                    particle.color.a = 0;
+
+                particle.position = particle.position.add(particle.direction.scale(this._scaledUpdateSpeed));
+
+                particle.angle += particle.angularSpeed * this._scaledUpdateSpeed;
+
+                particle.direction = particle.direction.add(this.gravity.scale(this._scaledUpdateSpeed));
+            }
+        }
+        
+        // Add new ones
+        var worldMatrix;
+
+        if (this.emitter.position) {
+            worldMatrix = this.emitter.getWorldMatrix();
+        } else {
+            worldMatrix = BABYLON.Matrix.Translation(this.emitter.x, this.emitter.y, this.emitter.z);
+        }
+
+        for (var index = 0; index < newParticles; index++) {
+            if (this.particles.length == this._capacity) {
+                break;
+            }
+
+            var particle = new BABYLON.Particle();
+            this.particles.push(particle);
+
+            var emitPower = randomNumber(this.minEmitPower, this.maxEmitPower);
+
+            var randX = randomNumber(this.direction1.x, this.direction2.x);
+            var randY = randomNumber(this.direction1.y, this.direction2.y);
+            var randZ = randomNumber(this.direction1.z, this.direction2.z);
+
+            particle.direction = BABYLON.Vector3.TransformNormal(new BABYLON.Vector3(randX, randY, randZ).scale(emitPower), worldMatrix);
+
+            particle.lifeTime = randomNumber(this.minLifeTime, this.maxLifeTime);
+
+            particle.size = randomNumber(this.minSize, this.maxSize);
+            particle.angularSpeed = randomNumber(this.minAngularSpeed, this.maxAngularSpeed);
+
+            randX = randomNumber(this.minEmitBox.x, this.maxEmitBox.x);
+            randY = randomNumber(this.minEmitBox.y, this.maxEmitBox.y);
+            randZ = randomNumber(this.minEmitBox.z, this.maxEmitBox.z);
+            var dispatch = new BABYLON.Vector3(randX, randY, randZ);
+
+            particle.position = BABYLON.Vector3.TransformCoordinates(dispatch, worldMatrix);
+
+            var step = randomNumber(0, 1.0);
+
+            var startColor = BABYLON.Color4.Lerp(this.color1, this.color2, step);
+            var deadColor = this.colorDead;
+            startColor.a = 1.0;
+            deadColor.a = this.deadAlpha;
+
+            particle.color = startColor;
+            var diff = deadColor.subtract(startColor);
+            particle.colorStep = diff.scale(1.0 / particle.lifeTime);
+        }
+    };
+
+    BABYLON.ParticleSystem.prototype.animate = function () {
+        if (!this._started)
+            return;
+
+        // Check
+        if (!this.emitter || !this._baseEffect.isReady() || !this._clippedEffect.isReady() || !this.particleTexture || !this.particleTexture.isReady())
+            return;
+
+        this._scaledUpdateSpeed = this.updateSpeed * this._scene.getAnimationRatio();
+
+        // determine the number of particles we need to create   
+        var emitCout;
+        
+        if (this.manualEmitCount > -1) {
+            emitCout = this.manualEmitCount;
+            this.manualEmitCount = 0;
+        } else {
+            emitCout = this.emitRate;
+        }
+
+        var newParticles = ((emitCout * this._scaledUpdateSpeed) >> 0);
+        this._newPartsExcess += emitCout * this._scaledUpdateSpeed - newParticles;
+
+        if (this._newPartsExcess > 1.0) {
+            newParticles += this._newPartsExcess >> 0;
+            this._newPartsExcess -= this._newPartsExcess >> 0;
+        }
+
+        this._alive = false;
+
+        if (!this._stopped) {
+            this._actualFrame += this._scaledUpdateSpeed;
+
+            if (this.targetStopDuration && this._actualFrame >= this.targetStopDuration)
+                this.stop();
+        } else {
+            newParticles = 0;
+        }
+
+        this._update(newParticles);
+
+        // Stopped?
+        if (this._stopped) {
+            if (!this._alive) {
+                this._started = false;
+                if (this.disposeOnStop) {
+                    this._scene._toBeDisposed.push(this);
+                }
+            }
+        }
+
+        // Update VBO
+        var vertices = [];
+        for (var index = 0; index < this.particles.length; index++) {
+            var particle = this.particles[index];
+
+            appendParticleVertex(particle, vertices, 0, 0);
+            appendParticleVertex(particle, vertices, 1, 0);
+            appendParticleVertex(particle, vertices, 1, 1);
+            appendParticleVertex(particle, vertices, 0, 1);
+        }
+        var engine = this._scene.getEngine();
+        engine.updateDynamicVertexBuffer(this._vertexBuffer, vertices);
+    };
+
+    BABYLON.ParticleSystem.prototype.render = function () {
+        // Effect
+        if (BABYLON.clipPlane) {
+            this._effect = this._clippedEffect;
+        } else {
+            this._effect = this._baseEffect;
+        }
+
+        // Check
+        if (!this.emitter || !this._effect.isReady() || !this.particleTexture || !this.particleTexture.isReady() || !this.particles.length)
+            return 0;
+
+        var engine = this._scene.getEngine();
+
+        // Render
+        engine.enableEffect(this._effect);
+
+        var viewMatrix = this._scene.getViewMatrix();
+        this._effect.setTexture("diffuseSampler", this.particleTexture);
+        this._effect.setMatrix("view", viewMatrix);
+        this._effect.setMatrix("projection", this._scene.getProjectionMatrix());
+        this._effect.setVector4("textureMask", this.textureMask.r, this.textureMask.g, this.textureMask.b, this.textureMask.a);
+
+        if (BABYLON.clipPlane) {
+            var invView = viewMatrix.clone();
+            invView.invert();
+            this._effect.setMatrix("invView", invView);
+            this._effect.setVector4("vClipPlane", BABYLON.clipPlane.normal.x, BABYLON.clipPlane.normal.y, BABYLON.clipPlane.normal.z, BABYLON.clipPlane.d);
+        }
+
+        // VBOs
+        engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, this._effect);
+
+        // Draw order
+        if (this.blendMode === BABYLON.ParticleSystem.BLENDMODE_ONEONE) {
+            engine.setAlphaMode(BABYLON.Engine.ALPHA_ADD);
+        } else {
+            engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+        }
+        engine.draw(true, 0, this.particles.length * 6);
+        engine.setAlphaMode(BABYLON.Engine.ALPHA_DISABLE);
+
+        return this.particles.length;
+    };
+
+    BABYLON.ParticleSystem.prototype.dispose = function () {
+        if (this._vertexBuffer) {
+            //this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
+            this._vertexBuffer = null;
+        }
+
+        if (this._indexBuffer) {
+            this._scene.getEngine()._releaseBuffer(this._indexBuffer);
+            this._indexBuffer = null;
+        }
+
+        if (this.particleTexture) {
+            this.particleTexture.dispose();
+            this.particleTexture = null;
+        }
+
+        // Remove from scene
+        var index = this._scene.particleSystems.indexOf(this);
+        this._scene.particleSystems.splice(index, 1);
+        
+        // Callback
+        if (this.onDispose) {
+            this.onDispose();
+        }
+    };
+
+    // Clone
+    BABYLON.ParticleSystem.prototype.clone = function(name, newEmitter) {
+        var result = new BABYLON.ParticleSystem(name, this._capacity, this._scene);
+
+        BABYLON.Tools.DeepCopy(this, result, ["particles"], ["_vertexDeclaration", "_vertexStrideSize"]);
+
+        if (newEmitter === undefined) {
+            newEmitter = this.emitter;
+        }
+
+        result.emitter = newEmitter;
+        if (this.particleTexture) {
+            result.particleTexture = new BABYLON.Texture(this.particleTexture.name, this._scene);
+        }
+
+        result.start();
+
+        return result;
+    };
+    
+    // Statics
+    BABYLON.ParticleSystem.BLENDMODE_ONEONE = 0;
+    BABYLON.ParticleSystem.BLENDMODE_STANDARD = 1;
+})();

+ 221 - 0
Shaders/default.fragment.fx

@@ -0,0 +1,221 @@
+#ifdef GL_ES
+precision mediump float;
+#endif
+
+#define MAP_PROJECTION	4.
+
+// Constants
+uniform vec3 vEyePosition;
+uniform vec3 vAmbientColor;
+uniform vec4 vDiffuseColor;
+uniform vec4 vSpecularColor;
+uniform vec3 vEmissiveColor;
+uniform vec4 vMisc;
+
+// Lights
+#ifdef LIGHT0
+uniform vec4 vLightData0;
+uniform vec3 vLightDiffuse0;
+uniform vec3 vLightSpecular0;
+#endif
+#ifdef LIGHT1
+uniform vec4 vLightData1;
+uniform vec3 vLightDiffuse1;
+uniform vec3 vLightSpecular1;
+#endif
+#ifdef LIGHT2
+uniform vec4 vLightData2;
+uniform vec3 vLightDiffuse2;
+uniform vec3 vLightSpecular2;
+#endif
+#ifdef LIGHT3
+uniform vec4 vLightData3;
+uniform vec3 vLightDiffuse3;
+uniform vec3 vLightSpecular3;
+#endif
+
+// Samplers
+#ifdef DIFFUSE
+varying vec2 vDiffuseUV;
+uniform sampler2D diffuseSampler;
+uniform vec2 vDiffuseInfos;
+#endif
+
+#ifdef AMBIENT
+varying vec2 vAmbientUV;
+uniform sampler2D ambientSampler;
+uniform vec2 vAmbientInfos;
+#endif
+
+#ifdef OPACITY	
+varying vec2 vOpacityUV;
+uniform sampler2D opacitySampler;
+uniform vec2 vOpacityInfos;
+#endif
+
+#ifdef REFLECTION
+varying vec3 vReflectionUVW;
+uniform samplerCube reflectionCubeSampler;
+uniform sampler2D reflection2DSampler;
+uniform vec2 vReflectionInfos;
+#endif
+
+#ifdef EMISSIVE
+varying vec2 vEmissiveUV;
+uniform vec2 vEmissiveInfos;
+uniform sampler2D emissiveSampler;
+#endif
+
+#ifdef SPECULAR
+varying vec2 vSpecularUV;
+uniform vec2 vSpecularInfos;
+uniform sampler2D specularSampler;
+#endif
+
+// Input
+varying vec3 vPositionW;
+varying vec3 vNormalW;
+
+#ifdef CLIPPLANE
+varying float fClipDistance;
+#endif
+
+// Light Computing
+struct lightingInfo
+{ 
+	vec3 diffuse; 
+	vec3 specular; 
+}; 
+
+lightingInfo computeLighting(vec3 viewDirectionW, vec4 lightData, vec3 diffuseColor, vec3 specularColor) {	
+	lightingInfo result;
+
+	vec3 lightVectorW;
+	if (lightData.w == 0.)
+	{
+		lightVectorW = normalize(lightData.xyz - vPositionW);
+	}
+	else 
+	{
+		lightVectorW = normalize(-lightData.xyz);
+	}
+
+	// diffuse
+	float ndl = max(0., dot(vNormalW, lightVectorW));
+
+	// Specular
+	vec3 angleW = normalize(viewDirectionW + lightVectorW);
+	float specComp = dot(normalize(vNormalW), angleW);
+	specComp = pow(specComp, vSpecularColor.a);
+
+	result.diffuse = ndl * diffuseColor;
+	result.specular = specComp * specularColor;
+
+	return result;
+}
+
+void main(void) {
+	// Clip plane
+#ifdef CLIPPLANE
+	if (fClipDistance > 0.0)
+		discard;
+#endif
+
+	vec3 viewDirectionW = normalize(vEyePosition - vPositionW);
+
+	// Base color
+	vec4 baseColor = vec4(1., 1., 1., 1.);
+	vec3 diffuseColor = vDiffuseColor.rgb;
+
+#ifdef DIFFUSE
+	baseColor = texture2D(diffuseSampler, vDiffuseUV);
+
+#ifdef ALPHATEST
+	if (baseColor.a < 0.4)
+		discard;
+#endif
+
+	baseColor.rgb *= vDiffuseInfos.y;
+#endif
+
+	// Ambient color
+	vec3 baseAmbientColor = vec3(1., 1., 1.);
+
+#ifdef AMBIENT
+	baseAmbientColor = texture2D(ambientSampler, vAmbientUV).rgb * vAmbientInfos.y;
+#endif
+
+	// Lighting
+	vec3 diffuseBase = vec3(0., 0., 0.);
+	vec3 specularBase = vec3(0., 0., 0.);
+
+#ifdef LIGHT0
+	lightingInfo info = computeLighting(viewDirectionW, vLightData0, vLightDiffuse0, vLightSpecular0);
+	diffuseBase += info.diffuse;
+	specularBase += info.specular;
+#endif
+#ifdef LIGHT1
+	info = computeLighting(viewDirectionW, vLightData1, vLightDiffuse1, vLightSpecular1);
+	diffuseBase += info.diffuse;
+	specularBase += info.specular;
+#endif
+#ifdef LIGHT2
+	info = computeLighting(viewDirectionW, vLightData2, vLightDiffuse2, vLightSpecular2);
+	diffuseBase += info.diffuse;
+	specularBase += info.specular;
+#endif
+#ifdef LIGHT3
+	info = computeLighting(viewDirectionW, vLightData3, vLightDiffuse3, vLightSpecular3);
+	diffuseBase += info.diffuse;
+	specularBase += info.specular;
+#endif
+
+	// Reflection
+	vec3 reflectionColor = vec3(0., 0., 0.);
+
+#ifdef REFLECTION
+	if (vMisc.x != 0.0)
+	{
+		reflectionColor = textureCube(reflectionCubeSampler, vReflectionUVW).rgb * vReflectionInfos.y;
+	}
+	else
+	{
+		vec2 coords = vReflectionUVW.xy;
+
+		if (vReflectionInfos.x == MAP_PROJECTION)
+		{
+			coords /= vReflectionUVW.z;
+		}
+
+		coords.y = 1.0 - coords.y;
+
+		reflectionColor = texture2D(reflection2DSampler, coords).rgb * vReflectionInfos.y;
+	}	
+#endif
+
+	// Alpha
+	float alpha = vDiffuseColor.a;
+
+#ifdef OPACITY
+	vec3 opacityMap = texture2D(opacitySampler, vOpacityUV).rgb * vec3(0.3, 0.59, 0.11);
+	alpha *= (opacityMap.x + opacityMap.y + opacityMap.z )* vOpacityInfos.y;
+#endif
+
+	// Emissive
+	vec3 emissiveColor = vEmissiveColor;
+#ifdef EMISSIVE
+	emissiveColor += texture2D(emissiveSampler, vEmissiveUV).rgb * vEmissiveInfos.y;
+#endif
+
+	// Specular map
+	vec3 specularColor = vSpecularColor.rgb;
+#ifdef SPECULAR
+	specularColor = texture2D(specularSampler, vSpecularUV).rgb * vSpecularInfos.y;	
+#endif
+
+	// Composition
+	vec3 finalDiffuse = clamp(diffuseBase * diffuseColor + emissiveColor + vAmbientColor, 0.0, 1.0) * baseColor.rgb;
+	vec3 finalSpecular = specularBase * specularColor;
+
+	gl_FragColor = vec4(finalDiffuse * baseAmbientColor + finalSpecular + reflectionColor, alpha);
+}

+ 179 - 0
Shaders/default.vertex.fx

@@ -0,0 +1,179 @@
+#define MAP_EXPLICIT	0.
+#define MAP_SPHERICAL	1.
+#define MAP_PLANAR		2.
+#define MAP_CUBIC		3.
+#define MAP_PROJECTION	4.
+
+// Attributes
+attribute vec3 position;
+attribute vec3 normal;
+#ifdef UV1
+attribute vec2 uv;
+#endif
+#ifdef UV2
+attribute vec2 uv2;
+#endif
+
+// Uniforms
+uniform mat4 world;
+uniform mat4 worldViewProjection;
+
+#ifdef DIFFUSE
+varying vec2 vDiffuseUV;
+uniform mat4 diffuseMatrix;
+uniform vec2 vDiffuseInfos;
+#endif
+
+#ifdef AMBIENT
+varying vec2 vAmbientUV;
+uniform mat4 ambientMatrix;
+uniform vec2 vAmbientInfos;
+#endif
+
+#ifdef OPACITY
+varying vec2 vOpacityUV;
+uniform mat4 opacityMatrix;
+uniform vec2 vOpacityInfos;
+#endif
+
+#ifdef REFLECTION
+uniform vec3 vEyePosition;
+uniform mat4 view;
+varying vec3 vReflectionUVW;
+
+uniform vec2 vReflectionInfos;
+uniform mat4 reflectionMatrix;
+#endif
+
+#ifdef EMISSIVE
+varying vec2 vEmissiveUV;
+uniform vec2 vEmissiveInfos;
+uniform mat4 emissiveMatrix;
+#endif
+
+#ifdef SPECULAR
+varying vec2 vSpecularUV;
+uniform vec2 vSpecularInfos;
+uniform mat4 specularMatrix;
+#endif
+
+// Output
+varying vec3 vPositionW;
+varying vec3 vNormalW;
+
+#ifdef CLIPPLANE
+uniform vec4 vClipPlane;
+varying float fClipDistance;
+#endif
+
+#ifdef REFLECTION
+vec3 computeReflectionCoords(float mode, vec4 worldPos, vec3 worldNormal)
+{	
+	if (mode == MAP_SPHERICAL)
+	{
+		vec3 coords = vec3(view * vec4(worldNormal, 0.0));	
+
+		return vec3(reflectionMatrix * vec4(coords, 1.0));
+	}
+	else if (mode == MAP_PLANAR)
+	{
+		vec3 viewDir = worldPos.xyz - vEyePosition;
+		vec3 coords = normalize(reflect(viewDir, worldNormal));
+
+		return vec3(reflectionMatrix * vec4(coords, 1));
+	}
+	else if (mode == MAP_CUBIC)
+	{
+		vec3 viewDir = worldPos.xyz - vEyePosition;
+		vec3 coords = reflect(viewDir, worldNormal);
+
+		return vec3(reflectionMatrix * vec4(coords, 0));	
+	}
+	else if (mode == MAP_PROJECTION)
+	{
+		return vec3(reflectionMatrix * (view * worldPos));
+	}
+
+	return vec3(0, 0, 0);
+}
+#endif
+
+void main(void) {
+	gl_Position = worldViewProjection * vec4(position, 1.0);   
+
+	vec4 worldPos = world * vec4(position, 1.0);
+	vPositionW = vec3(worldPos);
+	vNormalW = normalize(vec3(world * vec4(normal, 0.0)));
+
+	// Texture coordinates
+#ifndef UV1
+	vec2 uv = vec2(0., 0.);
+#endif
+#ifndef UV2
+	vec2 uv2 = vec2(0., 0.);
+#endif
+
+#ifdef DIFFUSE
+	if (vDiffuseInfos.x == 0.)
+	{
+		vDiffuseUV = vec2(diffuseMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vDiffuseUV = vec2(diffuseMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+#ifdef AMBIENT
+	if (vAmbientInfos.x == 0.)
+	{
+		vAmbientUV = vec2(ambientMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vAmbientUV = vec2(ambientMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+#ifdef OPACITY
+	if (vOpacityInfos.x == 0.)
+	{
+		vOpacityUV = vec2(opacityMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vOpacityUV = vec2(opacityMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+#ifdef REFLECTION
+	vReflectionUVW = computeReflectionCoords(vReflectionInfos.x, vec4(vPositionW, 1.0), vNormalW);
+#endif
+
+#ifdef EMISSIVE
+	if (vEmissiveInfos.x == 0.)
+	{
+		vEmissiveUV = vec2(emissiveMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vEmissiveUV = vec2(emissiveMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+#ifdef SPECULAR
+	if (vSpecularInfos.x == 0.)
+	{
+		vSpecularUV = vec2(specularMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vSpecularUV = vec2(specularMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+	// Clip plane
+#ifdef CLIPPLANE
+		fClipDistance = dot(worldPos, vClipPlane);
+#endif
+}

+ 175 - 0
Shaders/iedefault.fragment.fx

@@ -0,0 +1,175 @@
+#ifdef GL_ES
+precision mediump float;
+#endif
+
+#define MAP_PROJECTION	4.
+
+// Constants
+uniform vec3 vEyePosition;
+uniform vec3 vAmbientColor;
+uniform vec4 vDiffuseColor;
+uniform vec4 vSpecularColor;
+uniform vec3 vEmissiveColor;
+uniform vec4 vMisc;
+uniform vec4 vLightsType;
+
+// Lights
+#ifdef LIGHT0
+uniform vec3 vLightData0;
+uniform vec3 vLightDiffuse0;
+uniform vec3 vLightSpecular0;
+#endif
+
+// Samplers
+#ifdef DIFFUSE
+varying vec2 vDiffuseUV;
+uniform sampler2D diffuseSampler;
+uniform vec2 vDiffuseInfos;
+#endif
+
+#ifdef AMBIENT
+varying vec2 vAmbientUV;
+uniform sampler2D ambientSampler;
+uniform vec2 vAmbientInfos;
+#endif
+
+#ifdef OPACITY	
+varying vec2 vOpacityUV;
+uniform sampler2D opacitySampler;
+uniform vec2 vOpacityInfos;
+#endif
+
+#ifdef REFLECTION
+varying vec3 vReflectionUVW;
+uniform samplerCube reflectionCubeSampler;
+uniform sampler2D reflection2DSampler;
+uniform vec2 vReflectionInfos;
+#endif
+
+#ifdef EMISSIVE
+varying vec2 vEmissiveUV;
+uniform vec2 vEmissiveInfos;
+uniform sampler2D emissiveSampler;
+#endif
+
+#ifdef SPECULAR
+varying vec2 vSpecularUV;
+uniform vec2 vSpecularInfos;
+uniform sampler2D specularSampler;
+#endif
+
+// Input
+varying vec3 vPositionW;
+varying vec3 vNormalW;
+
+#ifdef CLIPPLANE
+varying float fClipDistance;
+#endif
+
+void main(void) {
+	// Clip plane
+#ifdef CLIPPLANE
+	if (fClipDistance > 0.0)
+		discard;
+#endif
+
+	vec3 viewDirectionW = normalize(vEyePosition - vPositionW);
+
+	// Base color
+	vec4 baseColor = vec4(1., 1., 1., 1.);
+	vec3 diffuseColor = vDiffuseColor.rgb;
+
+#ifdef DIFFUSE
+	baseColor = texture2D(diffuseSampler, vDiffuseUV);
+
+#ifdef ALPHATEST
+	if (baseColor.a < 0.4)
+		discard;
+#endif
+
+	baseColor.rgb *= vDiffuseInfos.y;
+#endif
+
+	// Ambient color
+	vec3 baseAmbientColor = vec3(1., 1., 1.);
+
+#ifdef AMBIENT
+	baseAmbientColor = texture2D(ambientSampler, vAmbientUV).rgb * vAmbientInfos.y;
+#endif
+
+	// Lighting
+	vec3 diffuseBase = vec3(0., 0., 0.);
+	vec3 specularBase = vec3(0., 0., 0.);
+
+#ifdef LIGHT0
+	vec3 lightVectorW;
+	if (vLightsType.x == 0.)
+	{
+		lightVectorW = normalize(vLightData0 - vPositionW);
+	}
+	else 
+	{
+		lightVectorW = normalize(-vLightData0);
+	}
+
+	// diffuse
+	float ndl = max(0., dot(vNormalW, lightVectorW));
+
+	// Specular
+	vec3 angleW = normalize(viewDirectionW + lightVectorW);
+	float specComp = dot(normalize(vNormalW), angleW);
+	specComp = pow(specComp, vSpecularColor.a);
+
+	specularBase += ndl * vLightDiffuse0;
+	diffuseBase += specComp * vLightSpecular0;
+#endif
+
+	// Reflection
+	vec3 reflectionColor = vec3(0., 0., 0.);
+
+#ifdef REFLECTION
+	if (vMisc.x != 0.0)
+	{
+		reflectionColor = textureCube(reflectionCubeSampler, vReflectionUVW).rgb * vReflectionInfos.y;
+	}
+	else
+	{
+		vec2 coords = vReflectionUVW.xy;
+
+		if (vReflectionInfos.x == MAP_PROJECTION)
+		{
+			coords /= vReflectionUVW.z;
+		}
+
+		coords.y = 1.0 - coords.y;
+
+		reflectionColor = texture2D(reflection2DSampler, coords).rgb * vReflectionInfos.y;
+	}	
+#endif
+
+	// Alpha
+	float alpha = vDiffuseColor.a;
+
+#ifdef OPACITY
+	vec3 opacityMap = texture2D(opacitySampler, vOpacityUV).rgb * vec3(0.3, 0.59, 0.11);
+	alpha *= (opacityMap.x + opacityMap.y + opacityMap.z )* vOpacityInfos.y;
+#endif
+
+	// Emissive
+	vec3 emissiveColor = vEmissiveColor;
+#ifdef EMISSIVE
+	emissiveColor += texture2D(emissiveSampler, vEmissiveUV).rgb * vEmissiveInfos.y;
+#endif
+
+	// Specular map
+	vec3 specularColor = vSpecularColor.rgb;
+#ifdef SPECULAR
+	specularColor = texture2D(specularSampler, vSpecularUV).rgb * vSpecularInfos.y;	
+#endif
+
+	// Composition
+	vec3 finalDiffuse = clamp(diffuseBase * diffuseColor + emissiveColor + vAmbientColor, 0.0, 1.0) * baseColor.rgb;
+	vec3 finalSpecular = specularBase * specularColor;
+
+	gl_FragColor = vec4(finalDiffuse * baseAmbientColor + finalSpecular + reflectionColor, alpha);
+}

+ 179 - 0
Shaders/iedefault.vertex.fx

@@ -0,0 +1,179 @@
+#define MAP_EXPLICIT	0.
+#define MAP_SPHERICAL	1.
+#define MAP_PLANAR		2.
+#define MAP_CUBIC		3.
+#define MAP_PROJECTION	4.
+
+// Attributes
+attribute vec3 position;
+attribute vec3 normal;
+#ifdef UV1
+attribute vec2 uv;
+#endif
+#ifdef UV2
+attribute vec2 uv2;
+#endif
+
+// Uniforms
+uniform mat4 world;
+uniform mat4 worldViewProjection;
+
+#ifdef DIFFUSE
+varying vec2 vDiffuseUV;
+uniform mat4 diffuseMatrix;
+uniform vec2 vDiffuseInfos;
+#endif
+
+#ifdef AMBIENT
+varying vec2 vAmbientUV;
+uniform mat4 ambientMatrix;
+uniform vec2 vAmbientInfos;
+#endif
+
+#ifdef OPACITY
+varying vec2 vOpacityUV;
+uniform mat4 opacityMatrix;
+uniform vec2 vOpacityInfos;
+#endif
+
+#ifdef REFLECTION
+uniform vec3 vEyePosition;
+uniform mat4 view;
+varying vec3 vReflectionUVW;
+
+uniform vec2 vReflectionInfos;
+uniform mat4 reflectionMatrix;
+#endif
+
+#ifdef EMISSIVE
+varying vec2 vEmissiveUV;
+uniform vec2 vEmissiveInfos;
+uniform mat4 emissiveMatrix;
+#endif
+
+#ifdef SPECULAR
+varying vec2 vSpecularUV;
+uniform vec2 vSpecularInfos;
+uniform mat4 specularMatrix;
+#endif
+
+// Output
+varying vec3 vPositionW;
+varying vec3 vNormalW;
+
+#ifdef CLIPPLANE
+uniform vec4 vClipPlane;
+varying float fClipDistance;
+#endif
+
+#ifdef REFLECTION
+vec3 computeReflectionCoords(float mode, vec4 worldPos, vec3 worldNormal)
+{	
+	if (mode == MAP_SPHERICAL)
+	{
+		vec3 coords = vec3(view * vec4(worldNormal, 0.0));	
+
+		return vec3(reflectionMatrix * vec4(coords, 1.0));
+	}
+	else if (mode == MAP_PLANAR)
+	{
+		vec3 viewDir = worldPos.xyz - vEyePosition;
+		vec3 coords = normalize(reflect(viewDir, worldNormal));
+
+		return vec3(reflectionMatrix * vec4(coords, 1));
+	}
+	else if (mode == MAP_CUBIC)
+	{
+		vec3 viewDir = worldPos.xyz - vEyePosition;
+		vec3 coords = reflect(viewDir, worldNormal);
+
+		return vec3(reflectionMatrix * vec4(coords, 0));	
+	}
+	else if (mode == MAP_PROJECTION)
+	{
+		return vec3(reflectionMatrix * (view * worldPos));
+	}
+
+	return vec3(0, 0, 0);
+}
+#endif
+
+void main(void) {
+	gl_Position = worldViewProjection * vec4(position, 1.0);   
+
+	vec4 worldPos = world * vec4(position, 1.0);
+	vPositionW = vec3(worldPos);
+	vNormalW = normalize(vec3(world * vec4(normal, 0.0)));
+
+	// Texture coordinates
+#ifndef UV1
+	vec2 uv = vec2(0., 0.);
+#endif
+#ifndef UV2
+	vec2 uv2 = vec2(0., 0.);
+#endif
+
+#ifdef DIFFUSE
+	if (vDiffuseInfos.x == 0.)
+	{
+		vDiffuseUV = vec2(diffuseMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vDiffuseUV = vec2(diffuseMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+#ifdef AMBIENT
+	if (vAmbientInfos.x == 0.)
+	{
+		vAmbientUV = vec2(ambientMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vAmbientUV = vec2(ambientMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+#ifdef OPACITY
+	if (vOpacityInfos.x == 0.)
+	{
+		vOpacityUV = vec2(opacityMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vOpacityUV = vec2(opacityMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+#ifdef REFLECTION
+	vReflectionUVW = computeReflectionCoords(vReflectionInfos.x, vec4(vPositionW, 1.0), vNormalW);
+#endif
+
+#ifdef EMISSIVE
+	if (vEmissiveInfos.x == 0.)
+	{
+		vEmissiveUV = vec2(emissiveMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vEmissiveUV = vec2(emissiveMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+#ifdef SPECULAR
+	if (vSpecularInfos.x == 0.)
+	{
+		vSpecularUV = vec2(specularMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vSpecularUV = vec2(specularMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
+	// Clip plane
+#ifdef CLIPPLANE
+		fClipDistance = dot(worldPos, vClipPlane);
+#endif
+}

+ 14 - 0
Shaders/layer.fragment.fx

@@ -0,0 +1,14 @@
+#ifdef GL_ES
+precision mediump float;
+#endif
+
+// Samplers
+varying vec2 vUV;
+uniform sampler2D textureSampler;
+
+
+void main(void) {
+	vec4 baseColor = texture2D(textureSampler, vUV);
+
+	gl_FragColor = baseColor;
+}

+ 16 - 0
Shaders/layer.vertex.fx

@@ -0,0 +1,16 @@
+// Attributes
+attribute vec2 position;
+
+// Uniforms
+uniform mat4 textureMatrix;
+
+// Output
+varying vec2 vUV;
+
+const vec2 madd = vec2(0.5, 0.5);
+
+void main(void) {	
+
+	vUV = vec2(textureMatrix * vec4(position * madd + madd, 1.0, 0.0));
+	gl_Position = vec4(position, 0.0, 1.0);
+}

+ 23 - 0
Shaders/particles.fragment.fx

@@ -0,0 +1,23 @@
+#ifdef GL_ES
+precision mediump float;
+#endif
+
+// Samplers
+varying vec2 vUV;
+varying vec4 vColor;
+uniform vec4 textureMask;
+uniform sampler2D diffuseSampler;
+
+#ifdef CLIPPLANE
+varying float fClipDistance;
+#endif
+
+void main(void) {
+#ifdef CLIPPLANE
+	if (fClipDistance > 0.0)
+		discard;
+#endif
+	vec4 baseColor = texture2D(diffuseSampler, vUV);
+
+	gl_FragColor = (baseColor * textureMask + (vec4(1., 1., 1., 1.) - textureMask)) * vColor;
+}

+ 47 - 0
Shaders/particles.vertex.fx

@@ -0,0 +1,47 @@
+// Attributes
+attribute vec3 position;
+attribute vec4 color;
+attribute vec4 options;
+
+// Uniforms
+uniform mat4 view;
+uniform mat4 projection;
+
+// Output
+varying vec2 vUV;
+varying vec4 vColor;
+
+#ifdef CLIPPLANE
+uniform vec4 vClipPlane;
+uniform mat4 invView;
+varying float fClipDistance;
+#endif
+
+void main(void) {	
+	vec3 viewPos = (view * vec4(position, 1.0)).xyz; 
+	vec3 cornerPos;
+	float size = options.y;
+	float angle = options.x;
+	vec2 offset = options.zw;
+
+	cornerPos = vec3(offset.x - 0.5, offset.y  - 0.5, 0.) * size;
+
+	// Rotate
+	vec3 rotatedCorner;
+	rotatedCorner.x = cornerPos.x * cos(angle) - cornerPos.y * sin(angle);
+	rotatedCorner.y = cornerPos.x * sin(angle) + cornerPos.y * cos(angle);
+	rotatedCorner.z = 0.;
+
+	// Position
+	viewPos += rotatedCorner;
+	gl_Position = projection * vec4(viewPos, 1.0);   
+	
+	vColor = color;
+	vUV = offset;
+
+	// Clip plane
+#ifdef CLIPPLANE
+	vec4 worldPos = invView * vec4(viewPos, 1.0);
+	fClipDistance = dot(worldPos, vClipPlane);
+#endif
+}

+ 22 - 0
Shaders/sprites.fragment.fx

@@ -0,0 +1,22 @@
+#ifdef GL_ES
+precision mediump float;
+#endif
+
+uniform bool alphaTest;
+
+// Samplers
+varying vec2 vUV;
+uniform sampler2D diffuseSampler;
+
+
+void main(void) {
+	vec4 baseColor = texture2D(diffuseSampler, vUV);
+
+	if (alphaTest) 
+	{
+		if (baseColor.a < 0.95)
+			discard;
+	}
+
+	gl_FragColor = baseColor;
+}

+ 39 - 0
Shaders/sprites.vertex.fx

@@ -0,0 +1,39 @@
+// Attributes
+attribute vec3 position;
+attribute vec4 options;
+attribute vec4 cellInfo;
+
+// Uniforms
+uniform vec2 textureInfos;
+uniform mat4 view;
+uniform mat4 projection;
+
+// Output
+varying vec2 vUV;
+
+void main(void) {	
+	vec3 viewPos = (view * vec4(position, 1.0)).xyz; 
+	vec3 cornerPos;
+	
+	float angle = options.x;
+	float size = options.y;
+	vec2 offset = options.zw;
+	vec2 uvScale = textureInfos.xy;
+
+	cornerPos = vec3(offset.x - 0.5, offset.y  - 0.5, 0.) * size;
+
+	// Rotate
+	vec3 rotatedCorner;
+	rotatedCorner.x = cornerPos.x * cos(angle) - cornerPos.y * sin(angle);
+	rotatedCorner.y = cornerPos.x * sin(angle) + cornerPos.y * cos(angle);
+	rotatedCorner.z = 0.;
+
+	// Position
+	viewPos += rotatedCorner;
+	gl_Position = projection * vec4(viewPos, 1.0);   
+	
+	// Texture
+	vec2 uvOffset = vec2(abs(offset.x - cellInfo.x), 1.0 - abs(offset.y - cellInfo.y));
+
+	vUV = (uvOffset + cellInfo.zw) * uvScale;
+}

+ 65 - 0
Sprites/babylon.sprite.js

@@ -0,0 +1,65 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Sprite = function (name, manager) {
+        this.name = name;
+        this._manager = manager;
+
+        this._manager.sprites.push(this);
+
+        this.position = BABYLON.Vector3.Zero();
+
+        this._frameCount = 0;
+    };
+
+    // Members
+    BABYLON.Sprite.prototype.position = null;
+    BABYLON.Sprite.prototype.size = 1.0;
+    BABYLON.Sprite.prototype.angle = 0;
+    BABYLON.Sprite.prototype.cellIndex = 0;
+    BABYLON.Sprite.prototype.invertU = 0;
+    BABYLON.Sprite.prototype.invertV = 0;
+
+    BABYLON.Sprite.prototype._animationStarted = false;
+    BABYLON.Sprite.prototype._loopAnimation = false;
+    BABYLON.Sprite.prototype._fromIndex = false;
+    BABYLON.Sprite.prototype._toIndex = false;
+    BABYLON.Sprite.prototype._delay = false;
+    BABYLON.Sprite.prototype._direction = 1;
+
+    // Methods
+    BABYLON.Sprite.prototype.playAnimation = function (from, to, loop, delay) {
+        this._fromIndex = from;
+        this._toIndex = to;
+        this._loopAnimation = loop;
+        this._delay = delay;
+        this._animationStarted = true;
+
+        this._direction = from < to ? 1 : -1;
+
+        this.cellIndex = from;
+        this._time = 0;
+    };
+
+    BABYLON.Sprite.prototype.stopAnimation = function() {
+        this._animationStarted = false;
+    };
+
+    BABYLON.Sprite.prototype._animate = function (deltaTime) {
+        if (!this._animationStarted)
+            return;
+
+        this._time += deltaTime;
+        if (this._time > this._delay) {
+            this._time = this._time % this._delay;
+            this.cellIndex += this._direction;
+            if (this.cellIndex == this._toIndex) {
+                if (this._loopAnimation) {
+                    this.cellIndex = this._fromIndex;
+                } else {
+                    this._animationStarted = false;
+                }
+            }
+        }
+    }
+})();

+ 150 - 0
Sprites/babylon.spriteManager.js

@@ -0,0 +1,150 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    var appendSpriteVertex = function (sprite, vertices, offsetX, offsetY, rowSize, epsilon) {
+        if (offsetX == 0)
+            offsetX = epsilon;
+        else if (offsetX == 1)
+            offsetX = 1 - epsilon;
+        
+        if (offsetY == 0)
+            offsetY = epsilon;
+        else if (offsetY == 1)
+            offsetY = 1 - epsilon;
+
+        vertices.push(sprite.position.x);
+        vertices.push(sprite.position.y);
+        vertices.push(sprite.position.z);
+        vertices.push(sprite.angle);
+        vertices.push(sprite.size);
+        vertices.push(offsetX);
+        vertices.push(offsetY);
+        vertices.push(sprite.invertU ? 1 : 0);
+        vertices.push(sprite.invertV ? 1 : 0);
+        var offset = (sprite.cellIndex / rowSize) >> 0;
+        vertices.push(sprite.cellIndex - offset * rowSize);
+        vertices.push(offset);
+    };
+
+    BABYLON.SpriteManager = function (name, imgUrl, capacity, cellSize, scene, epsilon) {
+        this.name = name;
+        this._capacity = capacity;
+        this.cellSize = cellSize;
+        this._spriteTexture = new BABYLON.Texture(imgUrl, scene, true, false);
+        this._spriteTexture.wrapU = false;
+        this._spriteTexture.wrapV = false;
+        this._epsilon = epsilon === undefined ? 0.01 : epsilon;
+
+        this._scene = scene;
+        this._scene.spriteManagers.push(this);
+        
+        // VBO
+        this._vertexDeclaration = [3, 4, 4];
+        this._vertexStrideSize = 11 * 4; // 11 floats per sprite (x, y, z, angle, size, offsetX, offsetY, invertU, invertV, cellIndexX, cellIndexY)
+        this._vertexBuffer = scene.getEngine().createDynamicVertexBuffer(capacity * this._vertexStrideSize * 4);
+
+        var indices = [];
+        var index = 0;
+        for (var count = 0; count < capacity; count++) {
+            indices.push(index);
+            indices.push(index + 1);
+            indices.push(index + 2);
+            indices.push(index);
+            indices.push(index + 2);
+            indices.push(index + 3);
+            index += 4;
+        }
+
+        this._indexBuffer = scene.getEngine().createIndexBuffer(indices);
+        
+        // Sprites
+        this.sprites = [];
+        
+        // Effects
+        this._effect = this._scene.getEngine().createEffect("sprites",
+                    ["position", "options", "cellInfo"],
+                    ["view", "projection", "textureInfos", "alphaTest"],
+                    ["diffuseSampler"], "");
+    };
+    
+    // Members
+    BABYLON.SpriteManager.prototype.onDispose = null;
+
+    // Methods
+    BABYLON.SpriteManager.prototype.render = function() {
+        // Check
+        if (!this._effect.isReady() || !this._spriteTexture || !this._spriteTexture.isReady())
+            return 0;
+
+        var engine = this._scene.getEngine();
+        var baseSize = this._spriteTexture.getBaseSize();
+
+        // Sprites
+        var deltaTime = BABYLON.Tools.GetDeltaTime();
+        var vertices = [];
+        var max = Math.min(this._capacity, this.sprites.length);
+        var rowSize = baseSize.width / this.cellSize;
+        for (var index = 0; index < max; index++) {
+            var sprite = this.sprites[index];
+
+            sprite._animate(deltaTime);
+
+            appendSpriteVertex(sprite, vertices, 0, 0, rowSize, this._epsilon);
+            appendSpriteVertex(sprite, vertices, 1, 0, rowSize, this._epsilon);
+            appendSpriteVertex(sprite, vertices, 1, 1, rowSize, this._epsilon);
+            appendSpriteVertex(sprite, vertices, 0, 1, rowSize, this._epsilon);
+        }
+        engine.updateDynamicVertexBuffer(this._vertexBuffer, vertices);
+       
+        // Render
+        engine.enableEffect(this._effect);
+
+        var viewMatrix = this._scene.getViewMatrix();
+        this._effect.setTexture("diffuseSampler", this._spriteTexture);
+        this._effect.setMatrix("view", viewMatrix);
+        this._effect.setMatrix("projection", this._scene.getProjectionMatrix());
+
+        this._effect.setVector2("textureInfos", this.cellSize / baseSize.width, this.cellSize / baseSize.height);
+
+        // VBOs
+        engine.bindBuffers(this._vertexBuffer, this._indexBuffer, this._vertexDeclaration, this._vertexStrideSize, this._effect);
+
+        // Draw order
+        this._effect.setBool("alphaTest", true);
+        engine.setColorWrite(false);
+        engine.draw(true, 0, max * 6);
+        engine.setColorWrite(true);
+        this._effect.setBool("alphaTest", false);
+        
+        engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+        engine.draw(true, 0, max * 6);
+        engine.setAlphaMode(BABYLON.Engine.ALPHA_DISABLE);
+    };
+    
+    BABYLON.SpriteManager.prototype.dispose = function () {
+        if (this._vertexBuffer) {
+            //this._scene.getEngine()._releaseBuffer(this._vertexBuffer);
+            this._vertexBuffer = null;
+        }
+
+        if (this._indexBuffer) {
+            this._scene.getEngine()._releaseBuffer(this._indexBuffer);
+            this._indexBuffer = null;
+        }
+
+        if (this._spriteTexture) {
+            this._spriteTexture.dispose();
+            this._spriteTexture = null;
+        }
+
+        // Remove from scene
+        var index = this._scene.spriteManagers.indexOf(this);
+        this._scene.spriteManagers.splice(index, 1);
+        
+        // Callback
+        if (this.onDispose) {
+            this.onDispose();
+        }
+    };
+    
+})();

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1192 - 0
Tools/babylon.math.js


+ 446 - 0
Tools/babylon.sceneLoader.js

@@ -0,0 +1,446 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    var loadCubeTexture = function (rootUrl, parsedTexture, scene) {
+        var texture = new BABYLON.CubeTexture(rootUrl + parsedTexture.name, scene);
+
+        texture.name = parsedTexture.name;
+        texture.hasAlpha = parsedTexture.hasAlpha;
+        texture.level = parsedTexture.level;
+        texture.coordinatesMode = parsedTexture.coordinatesMode;
+        texture.isCube = true;
+
+        return texture;
+    };
+
+    var loadTexture = function (rootUrl, parsedTexture, scene) {
+        if (!parsedTexture.name && !parsedTexture.isRenderTarget) {
+            return null;
+        }
+
+        if (parsedTexture.isCube) {
+            return loadCubeTexture(rootUrl, parsedTexture, scene);
+        }
+
+        var texture;
+
+        if (parsedTexture.mirrorPlane) {
+            texture = new BABYLON.MirrorTexture(parsedTexture.name, parsedTexture.renderTargetSize, scene);
+            texture._waitingRenderList = parsedTexture.renderList;
+            texture.mirrorPlane = BABYLON.Plane.FromArray(parsedTexture.mirrorPlane);
+        } else if (parsedTexture.isRenderTarget) {
+            texture = new BABYLON.RenderTargetTexture(parsedTexture.name, parsedTexture.renderTargetSize, scene);
+            texture._waitingRenderList = parsedTexture.renderList;
+        } else {
+            texture = new BABYLON.Texture(rootUrl + parsedTexture.name, scene);
+        }
+
+        texture.name = parsedTexture.name;
+        texture.hasAlpha = parsedTexture.hasAlpha;
+        texture.level = parsedTexture.level;
+
+        texture.coordinatesIndex = parsedTexture.coordinatesIndex;
+        texture.coordinatesMode = parsedTexture.coordinatesMode;
+        texture.uOffset = parsedTexture.uOffset;
+        texture.vOffset = parsedTexture.vOffset;
+        texture.uScale = parsedTexture.uScale;
+        texture.vScale = parsedTexture.vScale;
+        texture.uAng = parsedTexture.uAng;
+        texture.vAng = parsedTexture.vAng;
+        texture.wAng = parsedTexture.wAng;
+
+        texture.wrapU = parsedTexture.wrapU;
+        texture.wrapV = parsedTexture.wrapV;
+        
+        // Animations
+        if (parsedTexture.animations) {
+            for (var animationIndex = 0; animationIndex < parsedTexture.animations.length; animationIndex++) {
+                var parsedAnimation = parsedTexture.animations[animationIndex];
+
+                texture.animations.push(parseAnimation(parsedAnimation));
+            }
+        }
+
+        return texture;
+    };
+
+    var parseMaterial = function (parsedMaterial, scene, rootUrl) {
+        var material;
+        material = new BABYLON.StandardMaterial(parsedMaterial.name, scene);
+
+        material.ambientColor = BABYLON.Color3.FromArray(parsedMaterial.ambient);
+        material.diffuseColor = BABYLON.Color3.FromArray(parsedMaterial.diffuse);
+        material.specularColor = BABYLON.Color3.FromArray(parsedMaterial.specular);
+        material.specularPower = parsedMaterial.specularPower;
+        material.emissiveColor = BABYLON.Color3.FromArray(parsedMaterial.emissive);
+
+        material.alpha = parsedMaterial.alpha;
+
+        material.id = parsedMaterial.id;
+        material.backFaceCulling = parsedMaterial.backFaceCulling;
+
+        if (parsedMaterial.diffuseTexture) {
+            material.diffuseTexture = loadTexture(rootUrl, parsedMaterial.diffuseTexture, scene);
+        }
+
+        if (parsedMaterial.ambientTexture) {
+            material.ambientTexture = loadTexture(rootUrl, parsedMaterial.ambientTexture, scene);
+        }
+
+        if (parsedMaterial.opacityTexture) {
+            material.opacityTexture = loadTexture(rootUrl, parsedMaterial.opacityTexture, scene);
+        }
+
+        if (parsedMaterial.reflectionTexture) {
+            material.reflectionTexture = loadTexture(rootUrl, parsedMaterial.reflectionTexture, scene);
+        }
+        
+        if (parsedMaterial.emissiveTexture) {
+            material.emissiveTexture = loadTexture(rootUrl, parsedMaterial.emissiveTexture, scene);
+        }
+        
+        if (parsedMaterial.specularTexture) {
+            material.specularTexture = loadTexture(rootUrl, parsedMaterial.specularTexture, scene);
+        }
+
+        return material;
+    };
+
+    var parseMaterialById = function (id, parsedData, scene, rootUrl) {
+        for (var index = 0; index < parsedData.materials.length; index++) {
+            var parsedMaterial = parsedData.materials[index];
+            if (parsedMaterial.id === id) {
+                return parseMaterial(parsedMaterial, scene, rootUrl);
+            }
+        }
+
+        return null;
+    };
+
+    var parseMultiMaterial = function (parsedMultiMaterial, scene) {
+        var multiMaterial = new BABYLON.MultiMaterial(parsedMultiMaterial.name, scene);
+
+        multiMaterial.id = parsedMultiMaterial.id;
+
+        for (var matIndex = 0; matIndex < parsedMultiMaterial.materials.length; matIndex++) {
+            var subMatId = parsedMultiMaterial.materials[matIndex];
+
+            if (subMatId) {
+                multiMaterial.subMaterials.push(scene.getMaterialByID(subMatId));
+            } else {
+                multiMaterial.subMaterials.push(null);
+            }
+        }
+
+        return multiMaterial;
+    };
+    
+    var parseParticleSystem = function (parsedParticleSystem, scene, rootUrl) {
+        var emitter = scene.getLastMeshByID(parsedParticleSystem.emitterId);
+
+        var particleSystem = new BABYLON.ParticleSystem("particles#" + emitter.name, parsedParticleSystem.capacity, scene);
+        if (parsedParticleSystem.textureName) {
+            particleSystem.particleTexture = new BABYLON.Texture(rootUrl + parsedParticleSystem.textureName, scene);
+        }
+        particleSystem.minAngularSpeed = parsedParticleSystem.minAngularSpeed;
+        particleSystem.maxAngularSpeed = parsedParticleSystem.maxAngularSpeed;
+        particleSystem.minSize = parsedParticleSystem.minSize;
+        particleSystem.maxSize = parsedParticleSystem.maxSize;
+        particleSystem.minLifeTime = parsedParticleSystem.minLifeTime;
+        particleSystem.maxLifeTime = parsedParticleSystem.maxLifeTime;
+        particleSystem.emitter = emitter;
+        particleSystem.emitRate = parsedParticleSystem.emitRate;
+        particleSystem.minEmitBox = BABYLON.Vector3.FromArray(parsedParticleSystem.minEmitBox);
+        particleSystem.maxEmitBox = BABYLON.Vector3.FromArray(parsedParticleSystem.maxEmitBox);
+        particleSystem.gravity = BABYLON.Vector3.FromArray(parsedParticleSystem.gravity);
+        particleSystem.direction1 = BABYLON.Vector3.FromArray(parsedParticleSystem.direction1);
+        particleSystem.direction2 = BABYLON.Vector3.FromArray(parsedParticleSystem.direction2);
+        particleSystem.color1 = BABYLON.Color4.FromArray(parsedParticleSystem.color1);
+        particleSystem.color2 = BABYLON.Color4.FromArray(parsedParticleSystem.color2);
+        particleSystem.colorDead = BABYLON.Color4.FromArray(parsedParticleSystem.colorDead);
+        particleSystem.deadAlpha = parsedParticleSystem.deadAlpha;
+        particleSystem.updateSpeed = parsedParticleSystem.updateSpeed;
+        particleSystem.targetStopDuration = parsedParticleSystem.targetStopFrame;
+        particleSystem.textureMask = BABYLON.Color4.FromArray(parsedParticleSystem.textureMask);
+        particleSystem.blendMode = parsedParticleSystem.blendMode;
+        particleSystem.start();
+
+        return particleSystem;
+    };
+
+    var parseAnimation = function (parsedAnimation) {
+        var animation = new BABYLON.Animation(parsedAnimation.name, parsedAnimation.property, parsedAnimation.framePerSecond, parsedAnimation.dataType, parsedAnimation.loopBehavior);
+
+        var dataType = parsedAnimation.dataType;
+        var keys = [];
+        for (var index = 0; index < parsedAnimation.keys.length; index++) {
+            var key = parsedAnimation.keys[index];
+
+            var data;
+
+            switch (dataType) {
+                case BABYLON.Animation.ANIMATIONTYPE_FLOAT:
+                    data = key.values[0];
+                    break;
+                case BABYLON.Animation.ANIMATIONTYPE_QUATERNION:
+                    data = BABYLON.Quaternion.FromArray(key.values);
+                    break;
+                case BABYLON.Animation.ANIMATIONTYPE_VECTOR3:
+                default:
+                    data = BABYLON.Vector3.FromArray(key.values);
+                    break;
+            }
+
+            keys.push({
+                frame: key.frame,
+                value: data
+            });
+        }
+
+        animation.setKeys(keys);
+
+        return animation;
+    };
+
+    var parseMesh = function (parsedMesh, scene) {
+        var declaration = null;
+        
+        switch (parsedMesh.uvCount) {
+            case 0:
+                declaration = [3, 3];
+                break;
+            case 1:
+                declaration = [3, 3, 2];
+                break;
+            case 2:
+                declaration = [3, 3, 2, 2];
+                break;
+        }
+
+        var mesh = new BABYLON.Mesh(parsedMesh.name, declaration, scene);
+        mesh.id = parsedMesh.id;
+
+        mesh.position = BABYLON.Vector3.FromArray(parsedMesh.position);
+        mesh.rotation = BABYLON.Vector3.FromArray(parsedMesh.rotation);
+        mesh.scaling = BABYLON.Vector3.FromArray(parsedMesh.scaling);
+
+        mesh.setEnabled(parsedMesh.isEnabled);
+        mesh.isVisible = parsedMesh.isVisible;
+
+        mesh.billboardMode = parsedMesh.billboardMode;
+
+        if (parsedMesh.visibility !== undefined) {
+            mesh.visibility = parsedMesh.visibility;
+        }
+
+        mesh.checkCollisions = parsedMesh.checkCollisions;
+
+        if (parsedMesh.vertices && parsedMesh.indices) {
+            mesh.setVertices(parsedMesh.vertices, parsedMesh.uvCount);
+            mesh.setIndices(parsedMesh.indices);
+        }
+
+        if (parsedMesh.parentId) {
+            mesh.parent = scene.getLastMeshByID(parsedMesh.parentId);
+        }
+        if (parsedMesh.materialId) {
+            mesh.setMaterialByID(parsedMesh.materialId);
+        } else {
+            mesh.material = null;
+        }
+
+        // SubMeshes
+        if (parsedMesh.subMeshes) {
+            mesh.subMeshes = [];
+            for (var subIndex = 0; subIndex < parsedMesh.subMeshes.length; subIndex++) {
+                var parsedSubMesh = parsedMesh.subMeshes[subIndex];
+
+                var subMesh = new BABYLON.SubMesh(parsedSubMesh.materialIndex, parsedSubMesh.verticesStart, parsedSubMesh.verticesCount, parsedSubMesh.indexStart, parsedSubMesh.indexCount, mesh);
+            }
+        }
+        
+        // Animations
+        if (parsedMesh.animations) {
+            for (var animationIndex = 0; animationIndex < parsedMesh.animations.length; animationIndex++) {
+                var parsedAnimation = parsedMesh.animations[animationIndex];
+
+                mesh.animations.push(parseAnimation(parsedAnimation));
+            }
+        }
+        
+        if (parsedMesh.autoAnimate) {
+            scene.beginAnimation(mesh, parsedMesh.autoAnimateFrom, parsedMesh.autoAnimateTo, parsedMesh.autoAnimateLoop, 1.0);
+        }
+
+        return mesh;
+    };
+
+    var isDescendantOf = function (mesh, name, hierarchyIds) {
+        if (mesh.name === name) {
+            hierarchyIds.push(mesh.id);
+            return true;
+        }
+        
+        if (mesh.parentId && hierarchyIds.indexOf(mesh.parentId) !== -1) {
+            hierarchyIds.push(mesh.id);
+            return true;
+        }
+
+        return false;
+    };
+
+    BABYLON.SceneLoader = {
+        ImportMesh: function (meshName, rootUrl, sceneFilename, scene, then) {
+            BABYLON.Tools.LoadFile(rootUrl + sceneFilename, function (data) {
+                var parsedData = JSON.parse(data);
+
+                // Meshes
+                var meshes = [];
+                var particleSystems = [];
+                var hierarchyIds = [];                
+                for (var index = 0; index < parsedData.meshes.length; index++) {
+                    var parsedMesh = parsedData.meshes[index];
+
+                    if (!meshName || isDescendantOf(parsedMesh, meshName, hierarchyIds)) {
+                        // Material ?
+                        if (parsedMesh.materialId) {
+                            var materialFound = (scene.getMaterialByID(parsedMesh.materialId) !== null);
+                            
+                            if (!materialFound) {
+                                for (var multimatIndex = 0; multimatIndex < parsedData.multiMaterials.length; multimatIndex++) {
+                                    var parsedMultiMaterial = parsedData.multiMaterials[multimatIndex];
+                                    if (parsedMultiMaterial.id == parsedMesh.materialId) {
+                                        for (var matIndex = 0; matIndex < parsedMultiMaterial.materials.length; matIndex++) {
+                                            var subMatId = parsedMultiMaterial.materials[matIndex];
+
+                                            parseMaterialById(subMatId, parsedData, scene, rootUrl);
+                                        }
+
+                                        parseMultiMaterial(parsedMultiMaterial, scene);
+                                        materialFound = true;
+                                        break;
+                                    }
+                                }
+                            }
+
+                            if (!materialFound) {
+                                parseMaterialById(parsedMesh.materialId, parsedData, scene, rootUrl);
+                            }
+                        }
+
+                        meshes.push(parseMesh(parsedMesh, scene));
+                    }
+                }
+                
+                // Particles
+                if (parsedData.particleSystems) {
+                    for (var index = 0; index < parsedData.particleSystems.length; index++) {
+                        var parsedParticleSystem = parsedData.particleSystems[index];
+
+                        if (hierarchyIds.indexOf(parsedParticleSystem.emitterId) !== -1) {
+                            particleSystems.push(parseParticleSystem(parsedParticleSystem, scene, rootUrl));
+                        }
+                    }
+                }
+
+                if (then) {
+                    then(meshes, particleSystems);
+                }
+            });
+        },
+        Load: function (rootUrl, sceneFilename, engine, then, progressCallBack) {
+            BABYLON.Tools.LoadFile(rootUrl + sceneFilename, function (data) {
+                var parsedData = JSON.parse(data);
+                var scene = new BABYLON.Scene(engine);
+
+                // Scene
+                scene.autoClear = parsedData.autoClear;
+                scene.clearColor = BABYLON.Color3.FromArray(parsedData.clearColor);
+                scene.ambientColor = BABYLON.Color3.FromArray(parsedData.ambientColor);
+                scene.gravity = BABYLON.Vector3.FromArray(parsedData.gravity);
+
+                // Lights
+                for (var index = 0; index < parsedData.lights.length; index++) {
+                    var parsedLight = parsedData.lights[index];
+                    var light;
+                    if (parsedLight.type === 0) {
+                        light = new BABYLON.PointLight(parsedLight.name, BABYLON.Vector3.FromArray(parsedLight.data), scene);
+                    } else {
+                        light = new BABYLON.DirectionalLight(parsedLight.name, BABYLON.Vector3.FromArray(parsedLight.data), scene);
+                    }
+                    light.diffuse = BABYLON.Color3.FromArray(parsedLight.diffuse);
+                    light.specular = BABYLON.Color3.FromArray(parsedLight.specular);
+                    light.id = parsedLight.id;
+
+                    if (parsedLight.intensity) {
+                        light.intensity = parsedLight.intensity;
+                    }
+                }
+
+                // Cameras
+                for (var index = 0; index < parsedData.cameras.length; index++) {
+                    var parsedCamera = parsedData.cameras[index];
+                    var camera = new BABYLON.FreeCamera(parsedCamera.name, BABYLON.Vector3.FromArray(parsedCamera.position), scene);
+                    camera.id = parsedCamera.id;
+
+                    if (parsedCamera.target) {
+                        camera.setTarget(BABYLON.Vector3.FromArray(parsedCamera.target));
+                    } else {
+                        camera.rotation = BABYLON.Vector3.FromArray(parsedCamera.rotation);
+                    }
+
+                    camera.fov = parsedCamera.fov;
+                    camera.minZ = parsedCamera.minZ;
+                    camera.maxZ = parsedCamera.maxZ;
+
+                    camera.speed = parsedCamera.speed;
+                    camera.inertia = parsedCamera.inertia;
+
+                    camera.checkCollisions = parsedCamera.checkCollisions;
+                    camera.applyGravity = parsedCamera.applyGravity;
+                    if (parsedCamera.ellipsoid) {
+                        camera.ellipsoid = BABYLON.Vector3.FromArray(parsedCamera.ellipsoid);
+                    }
+                }
+
+                if (parsedData.activeCameraID) {
+                    scene.activeCameraByID(parsedData.activeCameraID);
+                }
+
+                // Materials
+                if (parsedData.materials) {
+                    for (var index = 0; index < parsedData.materials.length; index++) {
+                        var parsedMaterial = parsedData.materials[index];
+                        parseMaterial(parsedMaterial, scene, rootUrl);
+                    }
+                }
+
+                if (parsedData.multiMaterials) {
+                    for (var index = 0; index < parsedData.multiMaterials.length; index++) {
+                        var parsedMultiMaterial = parsedData.multiMaterials[index];
+                        parseMultiMaterial(parsedMultiMaterial, scene);
+                    }
+                }
+
+                // Meshes
+                for (var index = 0; index < parsedData.meshes.length; index++) {
+                    var parsedMesh = parsedData.meshes[index];
+                    parseMesh(parsedMesh, scene);
+                }
+
+                // Particles Systems
+                if (parsedData.particleSystems) {
+                    for (var index = 0; index < parsedData.particleSystems.length; index++) {
+                        var parsedParticleSystem = parsedData.particleSystems[index];
+                        parseParticleSystem(parsedParticleSystem, scene, rootUrl);
+                    }
+                }
+
+                // Finish
+                if (then) {
+                    then(scene);
+                }
+            }, progressCallBack);
+        }
+    };
+})();

+ 220 - 0
Tools/babylon.tools.dds.js

@@ -0,0 +1,220 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Tools = BABYLON.Tools || {};
+
+    // Based on https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
+
+    // All values and structures referenced from:
+    // http://msdn.microsoft.com/en-us/library/bb943991.aspx/
+    var DDS_MAGIC = 0x20534444;
+
+    var DDSD_CAPS = 0x1,
+        DDSD_HEIGHT = 0x2,
+        DDSD_WIDTH = 0x4,
+        DDSD_PITCH = 0x8,
+        DDSD_PIXELFORMAT = 0x1000,
+        DDSD_MIPMAPCOUNT = 0x20000,
+        DDSD_LINEARSIZE = 0x80000,
+        DDSD_DEPTH = 0x800000;
+
+    var DDSCAPS_COMPLEX = 0x8,
+        DDSCAPS_MIPMAP = 0x400000,
+        DDSCAPS_TEXTURE = 0x1000;
+
+    var DDSCAPS2_CUBEMAP = 0x200,
+        DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
+        DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
+        DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
+        DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
+        DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
+        DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
+        DDSCAPS2_VOLUME = 0x200000;
+
+    var DDPF_ALPHAPIXELS = 0x1,
+        DDPF_ALPHA = 0x2,
+        DDPF_FOURCC = 0x4,
+        DDPF_RGB = 0x40,
+        DDPF_YUV = 0x200,
+        DDPF_LUMINANCE = 0x20000;
+
+    function fourCCToInt32(value) {
+        return value.charCodeAt(0) +
+            (value.charCodeAt(1) << 8) +
+            (value.charCodeAt(2) << 16) +
+            (value.charCodeAt(3) << 24);
+    }
+
+    function int32ToFourCC(value) {
+        return String.fromCharCode(
+            value & 0xff,
+            (value >> 8) & 0xff,
+            (value >> 16) & 0xff,
+            (value >> 24) & 0xff
+        );
+    }
+
+    var FOURCC_DXT1 = fourCCToInt32("DXT1");
+    var FOURCC_DXT3 = fourCCToInt32("DXT3");
+    var FOURCC_DXT5 = fourCCToInt32("DXT5");
+
+    var headerLengthInt = 31; // The header length in 32 bit ints
+
+    // Offsets into the header array
+    var off_magic = 0;
+
+    var off_size = 1;
+    var off_flags = 2;
+    var off_height = 3;
+    var off_width = 4;
+
+    var off_mipmapCount = 7;
+
+    var off_pfFlags = 20;
+    var off_pfFourCC = 21;
+
+    function dxtToRgb565(src, src16Offset, width, height) {
+        var c = new Uint16Array(4);
+        var dst = new Uint16Array(width * height);
+        var nWords = (width * height) / 4;
+        var m = 0;
+        var dstI = 0;
+        var i = 0;
+        var r0 = 0, g0 = 0, b0 = 0, r1 = 0, g1 = 0, b1 = 0;
+
+        var blockWidth = width / 4;
+        var blockHeight = height / 4;
+        for (var blockY = 0; blockY < blockHeight; blockY++) {
+            for (var blockX = 0; blockX < blockWidth; blockX++) {
+                i = src16Offset + 4 * (blockY * blockWidth + blockX);
+                c[0] = src[i];
+                c[1] = src[i + 1];
+                r0 = c[0] & 0x1f;
+                g0 = c[0] & 0x7e0;
+                b0 = c[0] & 0xf800;
+                r1 = c[1] & 0x1f;
+                g1 = c[1] & 0x7e0;
+                b1 = c[1] & 0xf800;
+                // Interpolate between c0 and c1 to get c2 and c3.
+                // Note that we approximate 1/3 as 3/8 and 2/3 as 5/8 for
+                // speed.  This also appears to be what the hardware DXT
+                // decoder in many GPUs does :)
+                c[2] = ((5 * r0 + 3 * r1) >> 3)
+                    | (((5 * g0 + 3 * g1) >> 3) & 0x7e0)
+                    | (((5 * b0 + 3 * b1) >> 3) & 0xf800);
+                c[3] = ((5 * r1 + 3 * r0) >> 3)
+                    | (((5 * g1 + 3 * g0) >> 3) & 0x7e0)
+                    | (((5 * b1 + 3 * b0) >> 3) & 0xf800);
+                m = src[i + 2];
+                dstI = (blockY * 4) * width + blockX * 4;
+                dst[dstI] = c[m & 0x3];
+                dst[dstI + 1] = c[(m >> 2) & 0x3];
+                dst[dstI + 2] = c[(m >> 4) & 0x3];
+                dst[dstI + 3] = c[(m >> 6) & 0x3];
+                dstI += width;
+                dst[dstI] = c[(m >> 8) & 0x3];
+                dst[dstI + 1] = c[(m >> 10) & 0x3];
+                dst[dstI + 2] = c[(m >> 12) & 0x3];
+                dst[dstI + 3] = c[(m >> 14)];
+                m = src[i + 3];
+                dstI += width;
+                dst[dstI] = c[m & 0x3];
+                dst[dstI + 1] = c[(m >> 2) & 0x3];
+                dst[dstI + 2] = c[(m >> 4) & 0x3];
+                dst[dstI + 3] = c[(m >> 6) & 0x3];
+                dstI += width;
+                dst[dstI] = c[(m >> 8) & 0x3];
+                dst[dstI + 1] = c[(m >> 10) & 0x3];
+                dst[dstI + 2] = c[(m >> 12) & 0x3];
+                dst[dstI + 3] = c[(m >> 14)];
+            }
+        }
+        return dst;
+    }
+
+    function uploadDDSLevels(gl, ext, arrayBuffer) {
+        var header = new Int32Array(arrayBuffer, 0, headerLengthInt),
+            fourCC, blockBytes, internalFormat,
+            width, height, dataLength, dataOffset,
+            rgb565Data, byteArray, mipmapCount, i;
+
+
+        if (header[off_magic] != DDS_MAGIC) {
+            console.error("Invalid magic number in DDS header");
+            return 0;
+        }
+
+        if (!header[off_pfFlags] & DDPF_FOURCC) {
+            console.error("Unsupported format, must contain a FourCC code");
+            return 0;
+        }
+
+
+        fourCC = header[off_pfFourCC];
+        switch (fourCC) {
+            case FOURCC_DXT1:
+                blockBytes = 8;
+                internalFormat = ext ? ext.COMPRESSED_RGB_S3TC_DXT1_EXT : null;
+                break;
+
+
+            case FOURCC_DXT3:
+                blockBytes = 16;
+                internalFormat = ext ? ext.COMPRESSED_RGBA_S3TC_DXT3_EXT : null;
+                break;
+
+
+            case FOURCC_DXT5:
+                blockBytes = 16;
+                internalFormat = ext ? ext.COMPRESSED_RGBA_S3TC_DXT5_EXT : null;
+                break;
+
+
+            default:
+                console.error("Unsupported FourCC code:", int32ToFourCC(fourCC));
+                return null;
+        }
+
+
+        mipmapCount = 1;
+        if (header[off_flags] & DDSD_MIPMAPCOUNT && loadMipmaps !== false) {
+            mipmapCount = Math.max(1, header[off_mipmapCount]);
+        }
+
+
+        width = header[off_width];
+        height = header[off_height];
+        dataOffset = header[off_size] + 4;
+
+
+        if (ext) {
+            for (i = 0; i < mipmapCount; ++i) {
+                dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
+                byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);
+                gl.compressedTexImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, byteArray);
+                dataOffset += dataLength;
+                width *= 0.5;
+                height *= 0.5;
+            }
+        } else {
+            if (fourCC == FOURCC_DXT1) {
+                dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
+                byteArray = new Uint16Array(arrayBuffer);
+                rgb565Data = dxtToRgb565(byteArray, dataOffset / 2, width, height);
+                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, rgb565Data);
+                if (loadMipmaps) {
+                    gl.generateMipmap(gl.TEXTURE_2D);
+                }
+            } else {
+                console.error("No manual decoder for", int32ToFourCC(fourCC), "and no native support");
+                return 0;
+            }
+        }
+
+        return mipmapCount;
+    }
+
+    BABYLON.Tools.LoadDDSTexture = function(gl, ext, data) {
+        return uploadDDSLevels(gl, ext, data);
+    };
+})();

+ 173 - 0
Tools/babylon.tools.js

@@ -0,0 +1,173 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Tools = {};
+    BABYLON.Tools.QueueNewFrame = function (func) {
+        if (window.requestAnimationFrame)
+            window.requestAnimationFrame(func);
+        else if (window.msRequestAnimationFrame)
+            window.msRequestAnimationFrame(func);
+        else if (window.webkitRequestAnimationFrame)
+            window.webkitRequestAnimationFrame(func);
+        else if (window.mozRequestAnimationFrame)
+            window.mozRequestAnimationFrame(func);
+        else if (window.oRequestAnimationFrame)
+            window.oRequestAnimationFrame(func);
+        else {
+            window.setTimeout(func, 16);
+        }
+    };
+
+    BABYLON.Tools.RequestFullscreen = function (element) {
+        if (element.requestFullscreen)
+            element.requestFullscreen();
+        else if (element.msRequestFullscreen)
+            element.msRequestFullscreen();
+        else if (element.webkitRequestFullscreen)
+            element.webkitRequestFullscreen();
+        else if (element.mozRequestFullscreen)
+            element.mozRequestFullscreen();
+    };
+
+    BABYLON.Tools.ExitFullscreen = function () {
+        if (document.exitFullscreen) {
+            document.exitFullscreen();
+        }
+        else if (document.mozCancelFullScreen) {
+            document.mozCancelFullScreen();
+        }
+        else if (document.webkitCancelFullScreen) {
+            document.webkitCancelFullScreen();
+        }
+        else if (document.msCancelFullScreen) {
+            document.msCancelFullScreen();
+        }
+    };
+
+    // External files
+    BABYLON.Tools.BaseUrl = "";
+    
+    BABYLON.Tools.LoadFile = function (url, callback, progressCallBack) {
+        var request = new XMLHttpRequest();
+        var loadUrl = BABYLON.Tools.BaseUrl + url;
+        request.open('GET', loadUrl, true);
+
+        request.onprogress = progressCallBack;
+
+        request.onreadystatechange = function () {
+            if (request.readyState == 4) {
+                if (request.status == 200) {
+                    callback(request.responseText);
+                } else { // Failed
+                    throw new Error(request.status, "Unable to load " + loadUrl);
+                }
+            }
+        };
+
+        request.send(null);
+    };
+
+    // Misc.    
+    BABYLON.Tools.WithinEpsilon = function (a, b) {
+        var num = a - b;
+        return -1.401298E-45 <= num && num <= 1.401298E-45;
+    };
+
+    var cloneValue = function (source, destinationObject) {
+        if (!source)
+            return null;
+
+        if (source instanceof BABYLON.Mesh) {
+            return null;
+        }
+
+        if (source instanceof BABYLON.SubMesh) {
+            return source.clone(destinationObject);
+        } else if (source.clone) {
+            return source.clone();
+        }
+        return null;
+    };
+
+    BABYLON.Tools.DeepCopy = function (source, destination, doNotCopyList, mustCopyList) {
+        for (var prop in source) {
+
+            if (prop[0] === "_" && (!mustCopyList || mustCopyList.indexOf(prop) === -1)) {
+                continue;
+            }
+
+            if (doNotCopyList && doNotCopyList.indexOf(prop) !== -1) {
+                continue;
+            }
+            var sourceValue = source[prop];
+            var typeOfSourceValue = typeof sourceValue;
+
+            if (typeOfSourceValue == "function") {
+                continue;
+            }
+
+            if (typeOfSourceValue == "object") {
+                if (sourceValue instanceof Array) {
+                    destination[prop] = [];
+
+                    if (sourceValue.length > 0) {
+                        if (typeof sourceValue[0] == "object") {
+                            for (var index = 0; index < sourceValue.length; index++) {
+                                var clonedValue = cloneValue(sourceValue[index], destination);
+
+                                if (destination[prop].indexOf(clonedValue) === -1) { // Test if auto inject was not done
+                                    destination[prop].push(clonedValue);
+                                }
+                            }
+                        } else {
+                            destination[prop] = sourceValue.slice(0);
+                        }
+                    }
+                } else {
+                    destination[prop] = cloneValue(sourceValue, destination);
+                }
+            } else {
+                destination[prop] = sourceValue;
+            }
+        }
+    };
+
+    // FPS
+    var fpsRange = 60;
+    var previousFramesDuration = [];
+    var fps = 60;
+    var deltaTime = 0;
+
+    BABYLON.Tools.GetFps = function () {
+        return fps;
+    };
+
+    BABYLON.Tools.GetDeltaTime = function () {
+        return deltaTime;
+    };
+
+    BABYLON.Tools._MeasureFps = function () {
+        previousFramesDuration.push((new Date).getTime());
+        var length = previousFramesDuration.length;
+
+        if (length >= 2) {
+            deltaTime = previousFramesDuration[length - 1] - previousFramesDuration[length - 2];
+        }
+
+        if (length >= fpsRange) {
+
+            if (length > fpsRange) {
+                previousFramesDuration.splice(0, 1);
+                length = previousFramesDuration.length;
+            }
+
+            var sum = 0;
+            for (var id = 0; id < length - 1; id++) {
+                sum += previousFramesDuration[id + 1] - previousFramesDuration[id];
+            }
+
+            fps = 1000.0 / (sum / (length - 1));
+        }
+    };
+
+})();

+ 763 - 0
babylon.engine.js

@@ -0,0 +1,763 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Engine = function (canvas, antialias) {
+        this._renderingCanvas = canvas;
+
+        // GL
+        try {
+            this._gl = canvas.getContext("webgl", { antialias: antialias }) || canvas.getContext("experimental-webgl", { antialias: antialias });
+        } catch (e) {
+            throw new Error("WebGL not supported");
+        }
+
+        if (this._gl === undefined) {
+            throw new Error("WebGL not supported");
+        }
+
+        // Options
+        this.forceWireframe = false;
+        this.cullBackFaces = true;
+
+        // Scenes
+        this.scenes = [];
+
+        // Textures
+        this._workingCanvas = document.createElement("canvas");
+        this._workingContext = this._workingCanvas.getContext("2d");
+
+        // Viewport
+        this._hardwareScalingLevel = 1.0;
+        this.resize();
+
+        // Caps
+        this._caps = {};
+        this._caps.maxTexturesImageUnits = this._gl.getParameter(this._gl.MAX_TEXTURE_IMAGE_UNITS);
+        this._caps.maxTextureSize = this._gl.getParameter(this._gl.MAX_TEXTURE_SIZE);
+        this._caps.maxCubemapTextureSize = this._gl.getParameter(this._gl.MAX_CUBE_MAP_TEXTURE_SIZE);
+        this._caps.maxRenderTextureSize = this._gl.getParameter(this._gl.MAX_RENDERBUFFER_SIZE);
+
+        // Cache
+        this._loadedTexturesCache = [];
+        this._activeTexturesCache = [];
+        this._buffersCache = {
+            vertexBuffer: null,
+            indexBuffer: null
+        };
+        this._currentEffect = null;
+        this._currentState = {
+            culling: null
+        };
+
+        this._compiledEffects = {};
+
+        this._gl.enable(this._gl.DEPTH_TEST);
+        this._gl.depthFunc(this._gl.LEQUAL);
+
+        // Fullscreen
+        this.isFullscreen = false;
+        var that = this;
+        document.addEventListener("fullscreenchange", function () {
+            that.isFullscreen = document.fullscreen;
+        }, false);
+
+        document.addEventListener("mozfullscreenchange", function () {
+            that.isFullscreen = document.mozFullScreen;
+        }, false);
+
+        document.addEventListener("webkitfullscreenchange", function () {
+            that.isFullscreen = document.webkitIsFullScreen;
+        }, false);
+
+        document.addEventListener("msfullscreenchange", function () {
+            that.isFullscreen = document.msIsFullScreen;
+        }, false);
+    };
+
+    // Properties
+    BABYLON.Engine.prototype.getAspectRatio = function () {
+        return this._aspectRatio;
+    };
+
+    BABYLON.Engine.prototype.getRenderWidth = function () {
+        return this._renderingCanvas.width;
+    };
+
+    BABYLON.Engine.prototype.getRenderHeight = function () {
+        return this._renderingCanvas.height;
+    };
+
+    BABYLON.Engine.prototype.getRenderingCanvas = function () {
+        return this._renderingCanvas;
+    };
+
+    BABYLON.Engine.prototype.setHardwareScalingLevel = function (level) {
+        this._hardwareScalingLevel = level;
+        this.resize();
+    };
+
+    BABYLON.Engine.prototype.getLoadedTexturesCache = function () {
+        return this._loadedTexturesCache;
+    };
+
+    BABYLON.Engine.prototype.getCaps = function () {
+        return this._caps;
+    };
+
+    // Methods
+    BABYLON.Engine.prototype.switchFullscreen = function (element) {
+        if (this.isFullscreen) {
+            BABYLON.Tools.ExitFullscreen();
+        } else {
+            BABYLON.Tools.RequestFullscreen(element ? element : this._renderingCanvas);
+        }
+    };
+
+    BABYLON.Engine.prototype.clear = function (color, backBuffer, depthStencil) {
+        this._gl.clearColor(color.r, color.g, color.b, 1.0);
+        this._gl.clearDepth(1.0);
+        var mode = 0;
+
+        if (backBuffer || this.forceWireframe)
+            mode |= this._gl.COLOR_BUFFER_BIT;
+
+        if (depthStencil)
+            mode |= this._gl.DEPTH_BUFFER_BIT;
+
+        this._gl.clear(mode);
+    };
+
+    BABYLON.Engine.prototype.beginFrame = function () {
+        BABYLON.Tools._MeasureFps();
+
+        this._gl.viewport(0, 0, this._renderingCanvas.width, this._renderingCanvas.height);
+    };
+
+    BABYLON.Engine.prototype.endFrame = function () {
+        this.flushFramebuffer();
+    };
+
+    BABYLON.Engine.prototype.resize = function () {
+        this._renderingCanvas.width = this._renderingCanvas.clientWidth / this._hardwareScalingLevel;
+        this._renderingCanvas.height = this._renderingCanvas.clientHeight / this._hardwareScalingLevel;
+        this._aspectRatio = this._renderingCanvas.width / this._renderingCanvas.height;
+    };
+
+    BABYLON.Engine.prototype.bindFramebuffer = function (texture) {
+        var gl = this._gl;
+        gl.bindFramebuffer(gl.FRAMEBUFFER, texture._framebuffer);
+        gl.viewport(0.0, 0.0, texture._size, texture._size);
+
+        this.wipeCaches();
+    };
+
+    BABYLON.Engine.prototype.unBindFramebuffer = function (texture) {
+        if (texture.generateMipMaps) {
+            var gl = this._gl;
+            gl.bindTexture(gl.TEXTURE_2D, texture);
+            gl.generateMipmap(gl.TEXTURE_2D);
+            gl.bindTexture(gl.TEXTURE_2D, null);
+        }
+    };
+
+    BABYLON.Engine.prototype.flushFramebuffer = function () {
+        this._gl.flush();
+    };
+
+    BABYLON.Engine.prototype.restoreDefaultFramebuffer = function () {
+        this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, null);
+        this._gl.viewport(0, 0, this._renderingCanvas.width, this._renderingCanvas.height);
+
+        this.wipeCaches();
+    };
+
+    // VBOs
+    BABYLON.Engine.prototype.createVertexBuffer = function (vertices) {
+        var vbo = this._gl.createBuffer();
+        this._gl.bindBuffer(this._gl.ARRAY_BUFFER, vbo);
+        this._gl.bufferData(this._gl.ARRAY_BUFFER, new Float32Array(vertices), this._gl.STATIC_DRAW);
+        this._gl.bindBuffer(this._gl.ARRAY_BUFFER, null);
+        this._buffersCache.vertexBuffer = null;
+        vbo.references = 1;
+        return vbo;
+    };
+
+    BABYLON.Engine.prototype.createDynamicVertexBuffer = function (capacity) {
+        var vbo = this._gl.createBuffer();
+        this._gl.bindBuffer(this._gl.ARRAY_BUFFER, vbo);
+        this._gl.bufferData(this._gl.ARRAY_BUFFER, capacity, this._gl.DYNAMIC_DRAW);
+        this._gl.bindBuffer(this._gl.ARRAY_BUFFER, null);
+        this._buffersCache.vertexBuffer = null;
+        vbo.references = 1;
+        return vbo;
+    };
+
+    BABYLON.Engine.prototype.updateDynamicVertexBuffer = function (vertexBuffer, vertices) {
+        this._gl.bindBuffer(this._gl.ARRAY_BUFFER, vertexBuffer);
+        this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, new Float32Array(vertices));
+        this._gl.bindBuffer(this._gl.ARRAY_BUFFER, null);
+    };
+
+    BABYLON.Engine.prototype.createIndexBuffer = function (indices, is32Bits) {
+        var vbo = this._gl.createBuffer();
+        this._gl.bindBuffer(this._gl.ELEMENT_ARRAY_BUFFER, vbo);
+        this._gl.bufferData(this._gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), this._gl.STATIC_DRAW);
+        this._gl.bindBuffer(this._gl.ELEMENT_ARRAY_BUFFER, null);
+        this._buffersCache.indexBuffer = null;
+        vbo.references = 1;
+        vbo.is32Bits = is32Bits;
+        return vbo;
+    };
+
+    BABYLON.Engine.prototype.bindBuffers = function (vertexBuffer, indexBuffer, vertexDeclaration, vertexStrideSize, effect) {
+        if (this._buffersCache.vertexBuffer != vertexBuffer) {
+            this._gl.bindBuffer(this._gl.ARRAY_BUFFER, vertexBuffer);
+            this._buffersCache.vertexBuffer = vertexBuffer;
+
+            var offset = 0;
+            for (var index = 0; index < vertexDeclaration.length; index++) {
+                var order = effect.getAttribute(index);
+
+                if (order >= 0) {
+                    this._gl.vertexAttribPointer(order, vertexDeclaration[index], this._gl.FLOAT, false, vertexStrideSize, offset);
+                    offset += vertexDeclaration[index] * 4;
+                }
+            }
+        }
+
+        if (this._buffersCache.indexBuffer != indexBuffer) {
+            this._gl.bindBuffer(this._gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
+            this._buffersCache.indexBuffer = indexBuffer;
+        }
+    };
+
+    BABYLON.Engine.prototype._releaseBuffer = function (buffer) {
+        buffer.references--;
+
+        if (buffer.references === 0) {
+            this._gl.deleteBuffer(buffer);
+        }
+    };
+
+    BABYLON.Engine.prototype.draw = function (useTriangles, indexStart, indexCount) {
+        this._gl.drawElements(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, indexCount, this._gl.UNSIGNED_SHORT, indexStart * 2);
+    };
+
+    // Shaders
+    BABYLON.Engine.prototype.createEffect = function (baseName, attributesNames, uniformsNames, samplers, defines) {
+        var name = baseName + "@" + defines;
+        if (this._compiledEffects[name]) {
+            return this._compiledEffects[name];
+        }
+
+        var effect = new BABYLON.Effect(baseName, attributesNames, uniformsNames, samplers, this, defines);
+        this._compiledEffects[name] = effect;
+
+        return effect;
+    };
+
+    var compileShader = function (gl, source, type, defines) {
+        var shader = gl.createShader(type === "vertex" ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER);
+
+        gl.shaderSource(shader, (defines ? defines + "\n" : "") + source);
+        gl.compileShader(shader);
+
+        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+            throw new Error(gl.getShaderInfoLog(shader));
+        }
+        return shader;
+    };
+
+    BABYLON.Engine.prototype.createShaderProgram = function (vertexCode, fragmentCode, defines) {
+        var vertexShader = compileShader(this._gl, vertexCode, "vertex", defines);
+        var fragmentShader = compileShader(this._gl, fragmentCode, "fragment", defines);
+
+        var shaderProgram = this._gl.createProgram();
+        this._gl.attachShader(shaderProgram, vertexShader);
+        this._gl.attachShader(shaderProgram, fragmentShader);
+
+        this._gl.linkProgram(shaderProgram);
+
+        this._gl.deleteShader(vertexShader);
+        this._gl.deleteShader(fragmentShader);
+
+        return shaderProgram;
+    };
+
+    BABYLON.Engine.prototype.getUniforms = function (shaderProgram, uniformsNames) {
+        var results = [];
+
+        for (var index = 0; index < uniformsNames.length; index++) {
+            results.push(this._gl.getUniformLocation(shaderProgram, uniformsNames[index]));
+        }
+
+        return results;
+    };
+
+    BABYLON.Engine.prototype.getAttributes = function (shaderProgram, attributesNames) {
+        var results = [];
+
+        for (var index = 0; index < attributesNames.length; index++) {
+            try {
+                results.push(this._gl.getAttribLocation(shaderProgram, attributesNames[index]));
+            } catch(e) {
+                results.push(-1);
+            } 
+        }
+
+        return results;
+    };
+
+    BABYLON.Engine.prototype.enableEffect = function (effect) {
+        if (!effect || !effect.getAttributesCount() || this._currentEffect === effect) {
+            return;
+        }
+        this._buffersCache.vertexBuffer = null;
+
+        // Use program
+        this._gl.useProgram(effect.getProgram());
+
+        for (var index = 0; index < effect.getAttributesCount() ; index++) {
+            // Attributes
+            var order = effect.getAttribute(index);
+
+            if (order >= 0) {
+                this._gl.enableVertexAttribArray(effect.getAttribute(index));
+            }
+        }
+
+        this._currentEffect = effect;
+    };
+
+    BABYLON.Engine.prototype.setMatrix = function (uniform, matrix) {
+        if (!uniform)
+            return;
+
+        this._gl.uniformMatrix4fv(uniform, false, matrix.toArray());
+    };
+
+    BABYLON.Engine.prototype.setVector2 = function (uniform, x, y) {
+        if (!uniform)
+            return;
+
+        this._gl.uniform2f(uniform, x, y);
+    };
+
+    BABYLON.Engine.prototype.setVector3 = function (uniform, vector3) {
+        if (!uniform)
+            return;
+
+        this._gl.uniform3f(uniform, vector3.x, vector3.y, vector3.z);
+    };
+
+    BABYLON.Engine.prototype.setBool = function (uniform, bool) {
+        if (!uniform)
+            return;
+
+        this._gl.uniform1i(uniform, bool);
+    };
+
+    BABYLON.Engine.prototype.setVector4 = function (uniform, x, y, z, w) {
+        if (!uniform)
+            return;
+
+        this._gl.uniform4f(uniform, x, y, z, w);
+    };
+
+    BABYLON.Engine.prototype.setColor3 = function (uniform, color3) {
+        if (!uniform)
+            return;
+
+        this._gl.uniform3f(uniform, color3.r, color3.g, color3.b);
+    };
+
+    BABYLON.Engine.prototype.setColor4 = function (uniform, color3, alpha) {
+        if (!uniform)
+            return;
+
+        this._gl.uniform4f(uniform, color3.r, color3.g, color3.b, alpha);
+    };
+
+    // States
+    BABYLON.Engine.prototype.setState = function (culling) {
+        // Culling        
+        if (this._currentState.culling !== culling) {
+            if (culling) {
+                this._gl.cullFace(this.cullBackFaces ? this._gl.BACK : this._gl.FRONT);
+                this._gl.enable(this._gl.CULL_FACE);
+            } else {
+                this._gl.disable(this._gl.CULL_FACE);
+            }
+
+            this._currentState.culling = culling;
+        }
+    };
+    
+    BABYLON.Engine.prototype.setDepthBuffer = function (enable) {
+        if (enable) {
+            this._gl.enable(this._gl.DEPTH_TEST);
+        } else {
+            this._gl.disable(this._gl.DEPTH_TEST);
+        }
+    };
+
+    BABYLON.Engine.prototype.setDepthWrite = function (enable) {
+        this._gl.depthMask(enable);
+    };
+
+    BABYLON.Engine.prototype.setColorWrite = function (enable) {
+        this._gl.colorMask(enable, enable, enable, enable);
+    };
+
+    BABYLON.Engine.prototype.setAlphaMode = function (mode) {
+
+        switch (mode) {
+            case BABYLON.Engine.ALPHA_DISABLE:
+                this.setDepthWrite(true);
+                this._gl.disable(this._gl.BLEND);
+                break;
+            case BABYLON.Engine.ALPHA_COMBINE:
+                this.setDepthWrite(false);
+                this._gl.blendFuncSeparate(this._gl.SRC_ALPHA, this._gl.ONE_MINUS_SRC_ALPHA, this._gl.ZERO, this._gl.ONE);
+                this._gl.enable(this._gl.BLEND);
+                break;
+            case BABYLON.Engine.ALPHA_ADD:
+                this.setDepthWrite(false);
+                this._gl.blendFuncSeparate(this._gl.ONE, this._gl.ONE, this._gl.ZERO, this._gl.ONE);
+                this._gl.enable(this._gl.BLEND);
+                break;
+        }
+    };
+
+    BABYLON.Engine.prototype.setAlphaTesting = function (enable) {
+        this._alphaTest = enable;
+    };
+
+    BABYLON.Engine.prototype.getAlphaTesting = function () {
+        return this._alphaTest;
+    };
+
+    // Textures
+    BABYLON.Engine.prototype.wipeCaches = function () {
+        this._activeTexturesCache = [];
+        this._currentEffect = null;
+        this._currentState = {
+            culling: null
+        };
+        this._buffersCache = {
+            vertexBuffer: null,
+            indexBuffer: null
+        };
+    };
+
+    var getExponantOfTwo = function (value, max) {
+        var count = 1;
+
+        do {
+            count *= 2;
+        } while (count < value);
+
+        if (count > max)
+            count = max;
+
+        return count;
+    };
+
+    BABYLON.Engine.prototype.createTexture = function (url, noMipmap, invertY) {
+        var texture = this._gl.createTexture();
+        var that = this;
+        var img = new Image();
+
+        img.onload = function () {
+            that._workingCanvas.width = getExponantOfTwo(img.width, that._caps.maxTextureSize);
+            that._workingCanvas.height = getExponantOfTwo(img.height, that._caps.maxTextureSize);
+
+            that._workingContext.drawImage(img, 0, 0, img.width, img.height, 0, 0, that._workingCanvas.width, that._workingCanvas.height);
+
+            that._gl.bindTexture(that._gl.TEXTURE_2D, texture);
+            that._gl.pixelStorei(that._gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? true : invertY);
+            that._gl.texImage2D(that._gl.TEXTURE_2D, 0, that._gl.RGBA, that._gl.RGBA, that._gl.UNSIGNED_BYTE, that._workingCanvas);
+            that._gl.texParameteri(that._gl.TEXTURE_2D, that._gl.TEXTURE_MAG_FILTER, that._gl.LINEAR);
+
+            if (noMipmap) {
+                that._gl.texParameteri(that._gl.TEXTURE_2D, that._gl.TEXTURE_MIN_FILTER, that._gl.LINEAR);
+            } else {
+                that._gl.texParameteri(that._gl.TEXTURE_2D, that._gl.TEXTURE_MIN_FILTER, that._gl.LINEAR_MIPMAP_LINEAR);
+                that._gl.generateMipmap(that._gl.TEXTURE_2D);
+            }
+            that._gl.bindTexture(that._gl.TEXTURE_2D, null);
+
+            that._activeTexturesCache = [];
+            texture._baseWidth = img.width;
+            texture._baseHeight = img.height;
+            texture._width = that._workingCanvas.width;
+            texture._height = that._workingCanvas.height;
+            texture.isReady = true;
+        };
+
+        img.src = url;
+
+        texture.url = url;
+        texture.noMipmap = noMipmap;
+        texture.references = 1;
+        this._loadedTexturesCache.push(texture);
+
+        return texture;
+    };
+
+    BABYLON.Engine.prototype.createDynamicTexture = function (size, noMipmap) {
+        var texture = this._gl.createTexture();
+
+        var width = getExponantOfTwo(size, this._caps.maxTextureSize);
+        var height = getExponantOfTwo(size, this._caps.maxTextureSize);
+
+        this._gl.bindTexture(this._gl.TEXTURE_2D, texture);
+        this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, this._gl.LINEAR);
+
+        if (noMipmap) {
+            this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, this._gl.LINEAR);
+        } else {
+            this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, this._gl.LINEAR_MIPMAP_LINEAR);
+        }
+        this._gl.bindTexture(this._gl.TEXTURE_2D, null);
+
+        this._activeTexturesCache = [];
+        texture._baseWidth = width;
+        texture._baseHeight = height;
+        texture._width = width;
+        texture._height = height;
+        texture.isReady = false;
+        texture.noMipmap = noMipmap;
+        texture.references = 1;
+
+        this._loadedTexturesCache.push(texture);
+
+        return texture;
+    };
+
+    BABYLON.Engine.prototype.updateDynamicTexture = function (texture, canvas) {
+        this._gl.bindTexture(this._gl.TEXTURE_2D, texture);
+        this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, true);
+        this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, this._gl.RGBA, this._gl.UNSIGNED_BYTE, canvas);
+        if (!texture.noMipmap) {
+            this._gl.generateMipmap(this._gl.TEXTURE_2D);
+        }
+        this._gl.bindTexture(this._gl.TEXTURE_2D, null);
+        this._activeTexturesCache = [];
+        texture.isReady = true;
+    };
+
+    BABYLON.Engine.prototype.createRenderTargetTexture = function (size, generateMipMaps) {
+        var gl = this._gl;
+
+        var texture = gl.createTexture();
+        gl.bindTexture(gl.TEXTURE_2D, texture);
+
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, generateMipMaps ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+
+        // Create the depth buffer
+        var depthBuffer = gl.createRenderbuffer();
+        gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+        gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, size, size);
+
+        // Create the framebuffer
+        var framebuffer = gl.createFramebuffer();
+        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
+        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+
+        // Unbind
+        gl.bindTexture(gl.TEXTURE_2D, null);
+        gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+
+        texture._framebuffer = framebuffer;
+        texture._depthBuffer = depthBuffer;
+        texture._size = size;
+        texture.isReady = true;
+        texture.generateMipMaps = generateMipMaps;
+        texture.references = 1;
+        this._activeTexturesCache = [];
+
+        this._loadedTexturesCache.push(texture);
+
+        return texture;
+    };
+
+    var extensions = ["_px.jpg", "_py.jpg", "_pz.jpg", "_nx.jpg", "_ny.jpg", "_nz.jpg"];
+
+    var cascadeLoad = function (rootUrl, index, loadedImages, onfinish) {
+        var img = new Image();
+        img.onload = function () {
+            loadedImages.push(this);
+
+            if (index != extensions.length - 1) {
+                cascadeLoad(rootUrl, index + 1, loadedImages, onfinish);
+            } else {
+                onfinish(loadedImages);
+            }
+        };
+
+        img.src = rootUrl + extensions[index];
+    };
+
+    BABYLON.Engine.prototype.createCubeTexture = function (rootUrl) {
+        var gl = this._gl;
+
+        var texture = gl.createTexture();
+        texture.isCube = true;
+        texture.url = rootUrl;
+        texture.references = 1;
+        this._loadedTexturesCache.push(texture);
+
+        var that = this;
+        cascadeLoad(rootUrl, 0, [], function (imgs) {
+            var width = getExponantOfTwo(imgs[0].width);
+            var height = width;
+
+            that._workingCanvas.width = width;
+            that._workingCanvas.height = height;
+
+            var faces = [
+                gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
+                gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
+            ];
+
+            gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
+
+            for (var index = 0; index < faces.length; index++) {
+                that._workingContext.drawImage(imgs[index], 0, 0, imgs[index].width, imgs[index].height, 0, 0, width, height);
+                gl.texImage2D(faces[index], 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, that._workingCanvas);
+            }
+
+            gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
+            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
+            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+            gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+
+            that._activeTexturesCache = [];
+
+            texture._width = width;
+            texture._height = height;
+            texture.isReady = true;
+        });
+
+        return texture;
+    };
+
+    BABYLON.Engine.prototype._releaseTexture = function (texture) {
+        var gl = this._gl;
+
+        if (texture._framebuffer) {
+            gl.deleteFramebuffer(texture._framebuffer);
+        }
+
+        if (texture._depthBuffer) {
+            gl.deleteRenderbuffer(texture._depthBuffer);
+        }
+
+        gl.deleteTexture(texture);
+
+        // Unbind channels
+        for (var channel = 0; channel < this._caps.maxTexturesImageUnits; channel++) {
+            this._gl.activeTexture(this._gl["TEXTURE" + channel]);
+            this._gl.bindTexture(this._gl.TEXTURE_2D, null);
+            this._gl.bindTexture(this._gl.TEXTURE_CUBE_MAP, null);
+            this._activeTexturesCache[channel] = null;
+        }
+    };
+
+    BABYLON.Engine.prototype.bindSamplers = function (effect) {
+        this._gl.useProgram(effect.getProgram());
+        var samplers = effect.getSamplers();
+        for (var index = 0; index < samplers.length; index++) {
+            var uniform = effect.getUniform(samplers[index]);
+            this._gl.uniform1i(uniform, index);
+        }
+        this._currentEffect = null;
+    };
+
+    BABYLON.Engine.prototype.setTexture = function (channel, texture) {
+        if (!texture || !texture.isReady()) {
+            if (this._activeTexturesCache[channel] != null) {
+                this._gl.activeTexture(this._gl["TEXTURE" + channel]);
+                this._gl.bindTexture(this._gl.TEXTURE_2D, null);
+                this._gl.bindTexture(this._gl.TEXTURE_CUBE_MAP, null);
+                this._activeTexturesCache[channel] = null;
+            }
+            return;
+        }
+
+        if (this._activeTexturesCache[channel] == texture) {
+            return;
+        }
+        this._activeTexturesCache[channel] = texture;
+
+        var internalTexture = texture.getInternalTexture();
+        this._gl.activeTexture(this._gl["TEXTURE" + channel]);
+
+        if (internalTexture.isCube) {
+            this._gl.bindTexture(this._gl.TEXTURE_CUBE_MAP, internalTexture);
+
+            if (internalTexture._cachedCoordinatesMode !== texture.coordinatesMode) {
+                internalTexture._cachedCoordinatesMode = texture.coordinatesMode;
+                this._gl.texParameteri(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_WRAP_S, texture.coordinatesMode !== BABYLON.CubeTexture.CUBIC_MODE ? this._gl.REPEAT : this._gl.CLAMP_TO_EDGE);
+                this._gl.texParameteri(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_WRAP_T, texture.coordinatesMode !== BABYLON.CubeTexture.CUBIC_MODE ? this._gl.REPEAT : this._gl.CLAMP_TO_EDGE);
+            }
+        } else {
+            this._gl.bindTexture(this._gl.TEXTURE_2D, internalTexture);
+
+            if (internalTexture._cachedWrapU !== texture.wrapU) {
+                internalTexture._cachedWrapU = texture.wrapU;
+                this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_S, texture.wrapU ? this._gl.REPEAT : this._gl.CLAMP_TO_EDGE);
+            }
+
+            if (internalTexture._cachedWrapV !== texture.wrapV) {
+                internalTexture._cachedWrapV = texture.wrapV;
+                this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_T, texture.wrapV ? this._gl.REPEAT : this._gl.CLAMP_TO_EDGE);
+            }
+        }
+    };
+
+    // Dispose
+    BABYLON.Engine.prototype.dispose = function () {
+        // Release scenes
+        while (this.scenes.length) {
+            this.scenes[0].dispose();
+        }
+
+        // Release effects
+        for (var name in this._compiledEffects.length) {
+            this._gl.deleteProgram(this._compiledEffects[name]._program);
+        }
+    };
+
+    // Statics
+    BABYLON.Engine.ShadersRepository = "Babylon/Shaders/";
+
+    BABYLON.Engine.ALPHA_DISABLE = 0;
+    BABYLON.Engine.ALPHA_ADD = 1;
+    BABYLON.Engine.ALPHA_COMBINE = 2;
+
+    BABYLON.Engine.epsilon = 0.001;
+    BABYLON.Engine.collisionsEpsilon = 0.001;
+
+    BABYLON.Engine.isSupported = function () {
+        try {
+            var tempcanvas = document.createElement("canvas");
+            var gl = tempcanvas.getContext("webgl") || tempcanvas.getContext("experimental-webgl");
+
+            return gl != null && !!window.WebGLRenderingContext;
+        } catch (e) {
+            return false;
+        }
+    };
+})();

+ 619 - 0
babylon.scene.js

@@ -0,0 +1,619 @@
+var BABYLON = BABYLON || {};
+
+(function () {
+    BABYLON.Scene = function (engine) {
+        this._engine = engine;
+        this.autoClear = true;
+        this.clearColor = new BABYLON.Color3(0.2, 0.2, 0.3);
+        this.ambientColor = new BABYLON.Color3(0, 0, 0);
+
+        engine.scenes.push(this);
+
+        this._totalVertices = 0;
+        this._activeVertices = 0;
+        this._activeParticles = 0;
+        this._lastFrameDuration = 0;
+        this._evaluateActiveMeshesDuration = 0;
+        this._renderTargetsDuration = 0;
+        this._renderDuration = 0;
+
+        this._toBeDisposed = [];
+
+        // Lights
+        this.lights = [];
+
+        // Cameras
+        this.cameras = [];
+        this.activeCamera = null;
+
+        // Meshes
+        this.meshes = [];
+        this._activeMeshes = [];
+
+        // Materials
+        this.materials = [];
+        this.multiMaterials = [];
+        this.defaultMaterial = new BABYLON.StandardMaterial("default material", this);
+
+        // Textures
+        this.textures = [];
+
+        // Particles
+        this.particleSystems = [];
+
+        // Sprites
+        this.spriteManagers = [];
+        
+        // Layers
+        this.layers = [];
+
+        // Collisions
+        this.collisionsEnabled = true;
+        this.gravity = new BABYLON.Vector3(0, 0, -9);
+        
+        // Animations
+        this._activeAnimatables = [];
+    };
+
+    // Properties   
+    BABYLON.Scene.prototype.getEngine = function () {
+        return this._engine;
+    };
+
+    BABYLON.Scene.prototype.getTotalVertices = function () {
+        return this._totalVertices;
+    };
+
+    BABYLON.Scene.prototype.getActiveVertices = function () {
+        return this._activeVertices;
+    };
+
+    BABYLON.Scene.prototype.getTotalVertices = function () {
+        return this._totalVertices;
+    };
+
+    BABYLON.Scene.prototype.getActiveParticles = function () {
+        return this._activeParticles;
+    };
+
+    // Stats
+    BABYLON.Scene.prototype.getLastFrameDuration = function () {
+        return this._lastFrameDuration;
+    };
+
+    BABYLON.Scene.prototype.getEvaluateActiveMeshesDuration = function () {
+        return this._evaluateActiveMeshesDuration;
+    };
+
+    BABYLON.Scene.prototype.getRenderTargetsDuration = function () {
+        return this._renderTargetsDuration;
+    };
+
+    BABYLON.Scene.prototype.getRenderDuration = function () {
+        return this._renderDuration;
+    };
+
+    BABYLON.Scene.prototype.getParticlesDuration = function () {
+        return this._particlesDuration;
+    };
+    
+    BABYLON.Scene.prototype.getSpritesDuration = function () {
+        return this._spritesDuration;
+    };
+    
+    BABYLON.Scene.prototype.getAnimationRatio = function () {
+        return this._animationRatio;
+    };
+    
+    // Ready
+    BABYLON.Scene.prototype.isReady = function () {
+        for (var index = 0; index < this.materials.length; index++) {
+            if (!this.materials[index].isReady()) {
+                return false;
+            }
+        }
+
+        return true;
+    };
+
+    BABYLON.Scene.prototype.executeWhenReady = function(func) {
+        if (this.isReady()) {
+            func();
+            return;
+        }
+        
+        var that = this;
+        var checkState = function() {
+            if (that.isReady()) {
+                func();
+                return;
+            }            
+
+            setTimeout(checkState, 150);
+        };
+
+        setTimeout(checkState, 150);
+    };
+    
+    // Animations
+    BABYLON.Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio) {
+        if (speedRatio === undefined) {
+            speedRatio = 1.0;
+        }
+
+        // Local animations
+        if (target.animations) {
+            this.stopAnimation(target);
+            
+            var animatable = new BABYLON._Animatable(target, from, to, loop, speedRatio);
+
+            this._activeAnimatables.push(animatable);
+        }
+        
+        // Children animations
+        if (target.getAnimatables) {
+            var animatables = target.getAnimatables();
+            for (var index = 0; index < animatables.length; index++) {
+                this.beginAnimation(animatables[index], from, to, loop, speedRatio);
+            }
+        }
+    };
+
+    BABYLON.Scene.prototype.stopAnimation = function(target) {
+        for (var index = 0; index < this._activeAnimatables.length; index++) {
+            if (this._activeAnimatables[index].target === target) {
+                this._activeAnimatables.splice(index, 1);
+                return;
+            }
+        }
+    };
+
+    BABYLON.Scene.prototype._animate = function () {
+        for (var index = 0; index < this._activeAnimatables.length; index++) {
+            if (!this._activeAnimatables[index]._animate()) {
+                this._activeAnimatables.splice(index, 1);
+                index--;
+            }
+        }
+    };
+    
+    // Matrix
+    BABYLON.Scene.prototype.getViewMatrix = function () {
+        return this._viewMatrix;
+    };
+
+    BABYLON.Scene.prototype.getProjectionMatrix = function () {
+        return this._projectionMatrix;
+    };
+
+    BABYLON.Scene.prototype.getTransformMatrix = function () {
+        return this._transformMatrix;
+    };
+
+    BABYLON.Scene.prototype.setTransformMatrix = function (view, projection) {
+        this._viewMatrix = view;
+        this._projectionMatrix = projection;
+
+        this._transformMatrix = this._viewMatrix.multiply(this._projectionMatrix);
+    };
+
+    // Methods
+    BABYLON.Scene.prototype.activeCameraByID = function (id) {
+        for (var index = 0; index < this.cameras.length; index++) {
+            if (this.cameras[index].id == id) {
+                this.activeCamera = this.cameras[index];
+                return;
+            }
+        }
+    };
+
+    BABYLON.Scene.prototype.getMaterialByID = function (id) {
+        for (var index = 0; index < this.materials.length; index++) {
+            if (this.materials[index].id == id) {
+                return this.materials[index];
+            }
+        }
+
+        return null;
+    };
+
+    BABYLON.Scene.prototype.getMeshByID = function (id) {
+        for (var index = 0; index < this.meshes.length; index++) {
+            if (this.meshes[index].id == id) {
+                return this.meshes[index];
+            }
+        }
+
+        return null;
+    };
+
+    BABYLON.Scene.prototype.getLastMeshByID = function (id) {
+        var result = null;
+        for (var index = 0; index < this.meshes.length; index++) {
+            if (this.meshes[index].id == id) {
+                result = this.meshes[index];
+            }
+        }
+
+        return result;
+    };
+    
+    BABYLON.Scene.prototype.getMeshByName = function (name) {
+        for (var index = 0; index < this.meshes.length; index++) {
+            if (this.meshes[index].name == name) {
+                return this.meshes[index];
+            }
+        }
+
+        return null;
+    };
+
+    BABYLON.Scene.prototype.isActiveMesh = function (mesh) {
+        return (this._activeMeshes.indexOf(mesh) !== -1);
+    };
+
+    BABYLON.Scene.prototype._evaluateActiveMeshes = function () {
+        this._activeMeshes = [];
+        this._opaqueSubMeshes = [];
+        this._transparentSubMeshes = [];
+        this._alphaTestSubMeshes = [];
+        this._processedMaterials = [];
+        this._renderTargets = [];
+        this._activeParticleSystems = [];
+
+        var frustumPlanes = BABYLON.Frustum.GetPlanes(this._transformMatrix);
+
+        this._totalVertices = 0;
+        this._activeVertices = 0;
+
+        // meshes
+        for (var meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
+            var mesh = this.meshes[meshIndex];
+
+            this._totalVertices += mesh.getTotalVertices();
+            mesh.computeWorldMatrix();
+
+            if (mesh.isEnabled() && mesh.isVisible && mesh.visibility > 0 && mesh.isInFrustrum(frustumPlanes)) {
+                for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
+                    var subMesh = mesh.subMeshes[subIndex];
+
+                    if (mesh.subMeshes.length == 1 || subMesh.isInFrustrum(frustumPlanes)) {
+                        var material = subMesh.getMaterial();
+
+                        if (this._activeMeshes.indexOf(mesh) === -1) {
+                            this._activeMeshes.push(mesh);
+                        }
+
+                        if (material) {
+                            // Render targets
+                            if (material.getRenderTargetTextures) {
+                                if (this._processedMaterials.indexOf(material) === -1) {
+                                    this._processedMaterials.push(material);
+
+                                    this._renderTargets = this._renderTargets.concat(material.getRenderTargetTextures());
+                                }
+                            }
+
+                            // Dispatch
+                            if (material.needAlphaBlending() || mesh.visibility < 1.0) { // Transparent
+                                if (material.alpha > 0 || mesh.visibility < 1.0) {
+                                    this._transparentSubMeshes.push(subMesh); // Opaque
+                                }
+                            } else if (material.needAlphaTesting()) { // Alpha test
+                                this._alphaTestSubMeshes.push(subMesh);
+                            } else {
+                                this._opaqueSubMeshes.push(subMesh);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // Particle systems
+        var beforeParticlesDate = new Date();
+        for (var particleIndex = 0; particleIndex < this.particleSystems.length; particleIndex++) {
+            var particleSystem = this.particleSystems[particleIndex];
+
+            if (!particleSystem.emitter.position || (particleSystem.emitter && particleSystem.emitter.isEnabled())) {
+                this._activeParticleSystems.push(particleSystem);
+                particleSystem.animate();
+            }
+        }
+        this._particlesDuration += new Date() - beforeParticlesDate;
+    };
+
+    BABYLON.Scene.prototype._localRender = function (opaqueSubMeshes, alphaTestSubMeshes, transparentSubMeshes, activeMeshes) {
+        var engine = this._engine;
+        // Opaque
+        for (var subIndex = 0; subIndex < opaqueSubMeshes.length; subIndex++) {
+            var submesh = opaqueSubMeshes[subIndex];
+
+            this._activeVertices += submesh.verticesCount;
+            
+            submesh.render();
+        }
+
+        // Alpha test
+        engine.setAlphaTesting(true);
+        for (var subIndex = 0; subIndex < alphaTestSubMeshes.length; subIndex++) {
+            var submesh = alphaTestSubMeshes[subIndex];
+
+            this._activeVertices += submesh.verticesCount;
+
+            submesh.render();
+        }
+        engine.setAlphaTesting(false);
+
+        if (!activeMeshes) {
+            // Sprites
+            var beforeSpritessDate = new Date();
+            for (var index = 0; index < this.spriteManagers.length; index++) {
+                var spriteManager = this.spriteManagers[index];
+
+                spriteManager.render();
+            }
+            this._spritesDuration = new Date() - beforeSpritessDate;
+        }
+        
+        // Transparent
+        engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+        for (var subIndex = 0; subIndex < transparentSubMeshes.length; subIndex++) {
+            var submesh = transparentSubMeshes[subIndex];
+
+            this._activeVertices += submesh.verticesCount;
+            
+            submesh.render();
+        }
+        engine.setAlphaMode(BABYLON.Engine.ALPHA_DISABLE);
+
+        // Particles
+        var beforeParticlesDate = new Date();
+        for (var particleIndex = 0; particleIndex < this._activeParticleSystems.length; particleIndex++) {
+            var particleSystem = this._activeParticleSystems[particleIndex];
+
+            if (!activeMeshes || activeMeshes.indexOf(particleSystem.emitter) !== -1) {
+                this._activeParticles += particleSystem.render();
+            }
+        }
+        this._particlesDuration += new Date() - beforeParticlesDate;
+    };
+
+    BABYLON.Scene.prototype.render = function () {
+        var startDate = new Date();
+        this._particlesDuration = 0;
+        this._activeParticles = 0;
+        var engine = this._engine;
+
+        // Before render
+        if (this.beforeRender) {
+            this.beforeRender();
+        }
+
+        // Camera
+        if (!this.activeCamera)
+            throw new Error("Active camera not set");
+
+        this.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix());
+
+        // Animations
+        this._animationRatio = BABYLON.Tools.GetDeltaTime() * (60.0 / 1000.0);
+        this._animate();
+
+        // Meshes
+        var beforeEvaluateActiveMeshesDate = new Date();
+        this._evaluateActiveMeshes();
+        this._evaluateActiveMeshesDuration = new Date() - beforeEvaluateActiveMeshesDate;
+
+        // Render targets
+        var beforeRenderTargetDate = new Date();
+        for (var renderIndex = 0; renderIndex < this._renderTargets.length; renderIndex++) {
+            var renderTarget = this._renderTargets[renderIndex];
+
+            renderTarget.render();
+        }
+
+        if (this._renderTargets.length > 0) { // Restore back buffer
+            engine.restoreDefaultFramebuffer();
+        }
+        this._renderTargetsDuration = new Date() - beforeRenderTargetDate;
+
+        // Clear
+        var beforeRenderDate = new Date();
+        engine.clear(this.clearColor, this.autoClear, true);
+
+        // Backgrounds
+        engine.setDepthBuffer(false);
+        for (var layerIndex = 0; layerIndex < this.layers.length; layerIndex++) {
+            var layer = this.layers[layerIndex];
+
+            if (layer.isBackground) {
+                layer.render();
+            }
+        }
+        engine.setDepthBuffer(true);
+
+        // Render
+        this._localRender(this._opaqueSubMeshes, this._alphaTestSubMeshes, this._transparentSubMeshes);
+
+        // Foregrounds
+        engine.setDepthBuffer(false);
+        for (var layerIndex = 0; layerIndex < this.layers.length; layerIndex++) {
+            var layer = this.layers[layerIndex];
+
+            if (!layer.isBackground) {
+                layer.render();
+            }
+        }
+        engine.setDepthBuffer(true);
+        
+        this._renderDuration = new Date() - beforeRenderDate;
+
+        // Update camera
+        this.activeCamera._update();
+
+        // After render
+        if (this.afterRender) {
+            this.afterRender();
+        }
+
+        // Cleaning
+        for (var index = 0; index < this._toBeDisposed.length; index++) {
+            this._toBeDisposed[index].dispose();
+        }
+
+        this._toBeDisposed = [];
+
+        this._lastFrameDuration = new Date() - startDate;
+    };
+
+    BABYLON.Scene.prototype.dispose = function () {
+        this.beforeRender = null;
+        this.afterRender = null;
+
+        // Detach cameras
+        var canvas = this._engine.getRenderingCanvas();
+        for (var index = 0; index < this.cameras.length; index++) {
+            this.cameras[index].detachControl(canvas);
+        }
+
+        // Release meshes
+        while (this.meshes.length) {
+            this.meshes[0].dispose(true);
+        }
+
+        // Release materials
+        while (this.materials.length) {
+            this.materials[0].dispose();
+        }
+
+        // Release particles
+        while (this.particleSystems.length) {
+            this.particleSystems[0].dispose();
+        }
+
+        // Release sprites
+        while (this.spriteManagers.length) {
+            this.spriteManagers[0].dispose();
+        }
+        
+        // Release layers
+        while (this.layers.length) {
+            this.layers[0].dispose();
+        }
+
+        // Release textures
+        while (this.textures.length) {
+            this.textures[index].dispose();
+        }
+
+        // Remove from engine
+        var index = this._engine.scenes.indexOf(this);
+        this._engine.scenes.splice(index, 1);
+
+        this._engine.wipeCaches();
+    };
+
+    // Collisions
+    BABYLON.Scene.prototype._getNewPosition = function (position, velocity, collider, maximumRetry) {
+        var scaledPosition = position.divide(collider.radius);
+        var scaledVelocity = velocity.divide(collider.radius);
+
+        collider.retry = 0;
+        collider.initialVelocity = scaledVelocity;
+        collider.initialPosition = scaledPosition;
+        var finalPosition = this._collideWithWorld(scaledPosition, scaledVelocity, collider, maximumRetry);
+
+        finalPosition = finalPosition.multiply(collider.radius);
+
+        return finalPosition;
+    };
+
+    BABYLON.Scene.prototype._collideWithWorld = function (position, velocity, collider, maximumRetry) {
+        var closeDistance = BABYLON.Engine.collisionsEpsilon * 10.0;
+
+        if (collider.retry >= maximumRetry) {
+            return position;
+        }
+
+        collider._initialize(position, velocity, closeDistance);
+
+        // Check all meshes
+        for (var index = 0; index < this.meshes.length; index++) {
+            var mesh = this.meshes[index];
+            if (mesh.isEnabled() && mesh.checkCollisions) {
+                mesh._checkCollision(collider);
+            }
+        }
+
+        if (!collider.collisionFound) {
+            return position.add(velocity);
+        }
+
+        if (velocity.x != 0 || velocity.y != 0 || velocity.z != 0) {
+            var response = collider._getResponse(position, velocity);
+            position = response.position;
+            velocity = response.velocity;
+        }
+
+        if (velocity.length() <= closeDistance) {
+            return position;
+        }
+
+        collider.retry++;
+        return this._collideWithWorld(position, velocity, collider, maximumRetry);
+    };
+
+    // Picking
+    BABYLON.Scene.prototype.createPickingRay = function(x, y, world) {        
+        var engine = this._engine;
+        
+        if (!this._viewMatrix) {
+            if (!this.activeCamera)
+                throw new Error("Active camera not set");
+
+            this.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix());
+        }
+
+        return BABYLON.Ray.CreateNew(x, y, engine.getRenderWidth(), engine.getRenderHeight(), world ? world : BABYLON.Matrix.Identity(), this._viewMatrix, this._projectionMatrix);
+    };
+
+    BABYLON.Scene.prototype.pick = function (x, y) {
+        var distance = Number.MAX_VALUE;
+        var pickedPoint = null;
+        var pickedMesh = null;
+
+        for (var meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
+            var mesh = this.meshes[meshIndex];
+
+            if (!mesh.isEnabled() || !mesh.isVisible) {
+                continue;
+            }
+            
+            var world = mesh.getWorldMatrix();
+            var ray = this.createPickingRay(x, y, world);
+
+            var result = mesh.intersects(ray);
+            if (!result.hit)
+                continue;
+
+            if (result.distance >= distance)
+                continue;
+
+            distance = result.distance;
+            pickedMesh = mesh;
+
+            // Get picked point
+            var worldOrigin = BABYLON.Vector3.TransformCoordinates(ray.origin, world);
+            var direction = ray.direction.clone();
+            direction.normalize();
+            direction = direction.scale(result.distance);
+            var worldDirection = BABYLON.Vector3.TransformNormal(direction, world);
+
+            pickedPoint = worldOrigin.add(worldDirection);
+        }
+
+        return { hit: distance != Number.MAX_VALUE, distance: distance, pickedMesh: pickedMesh, pickedPoint: pickedPoint };
+    };
+
+})();