Bläddra i källkod

Instances hardware support

David Catuhe 11 år sedan
förälder
incheckning
a2605bc6e4

+ 9 - 4
Babylon/Animations/babylon.animatable.js

@@ -16,15 +16,20 @@
             this._paused = false;
             this.animationStarted = false;
             if (animations) {
-                this.appendAnimations(animations);
+                this.appendAnimations(target, animations);
             }
 
             this._scene = scene;
             scene._activeAnimatables.push(this);
         }
         // Methods
-        Animatable.prototype.appendAnimations = function (animations) {
-            this._animations = this._animations.concat(animations);
+        Animatable.prototype.appendAnimations = function (target, animations) {
+            for (var index = 0; index < animations.length; index++) {
+                var animation = animations[index];
+
+                animation._target = target;
+                this._animations.push(animation);
+            }
         };
 
         Animatable.prototype.getAnimationByTargetProperty = function (property) {
@@ -74,7 +79,7 @@
 
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
-                var isRunning = animation.animate(this.target, delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this.speedRatio);
+                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this.speedRatio);
                 running = running || isRunning;
             }
 

+ 9 - 4
Babylon/Animations/babylon.animatable.ts

@@ -9,7 +9,7 @@
 
         constructor(scene: Scene, public target, public fromFrame: number = 0, public toFrame: number = 100, public loopAnimation: boolean = false, public speedRatio: number = 1.0, public onAnimationEnd?, animations?: any) {
             if (animations) {
-                this.appendAnimations(animations);
+                this.appendAnimations(target, animations);
             }
 
             this._scene = scene;
@@ -17,8 +17,13 @@
         }
 
         // Methods
-        public appendAnimations(animations: Animation[]): void {
-            this._animations = this._animations.concat(animations);
+        public appendAnimations(target: any, animations: Animation[]): void {
+            for (var index = 0; index < animations.length; index++) {
+                var animation = animations[index];
+
+                animation._target = target;
+                this._animations.push(animation);    
+            }            
         }
 
         public getAnimationByTargetProperty(property: string) {
@@ -68,7 +73,7 @@
 
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
-                var isRunning = animation.animate(this.target, delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this.speedRatio);
+                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this.speedRatio);
                 running = running || isRunning;
             }
 

+ 5 - 5
Babylon/Animations/babylon.animation.js

@@ -125,7 +125,7 @@
             return this._keys[this._keys.length - 1].value;
         };
 
-        Animation.prototype.animate = function (target, delay, from, to, loop, speedRatio) {
+        Animation.prototype.animate = function (delay, from, to, loop, speedRatio) {
             if (!this.targetPropertyPath || this.targetPropertyPath.length < 1) {
                 this._stopped = true;
                 return false;
@@ -201,7 +201,7 @@
 
             // Set value
             if (this.targetPropertyPath.length > 1) {
-                var property = target[this.targetPropertyPath[0]];
+                var property = this._target[this.targetPropertyPath[0]];
 
                 for (var index = 1; index < this.targetPropertyPath.length - 1; index++) {
                     property = property[this.targetPropertyPath[index]];
@@ -209,11 +209,11 @@
 
                 property[this.targetPropertyPath[this.targetPropertyPath.length - 1]] = currentValue;
             } else {
-                target[this.targetPropertyPath[0]] = currentValue;
+                this._target[this.targetPropertyPath[0]] = currentValue;
             }
 
-            if (target.markAsDirty) {
-                target.markAsDirty(this.targetProperty);
+            if (this._target.markAsDirty) {
+                this._target.markAsDirty(this.targetProperty);
             }
 
             if (!returnValue) {

+ 6 - 5
Babylon/Animations/babylon.animation.ts

@@ -4,6 +4,7 @@
         private _offsetsCache = {};
         private _highLimitsCache = {};
         private _stopped = false;
+        public _target;
 
         public targetPropertyPath: string[];
         public currentFrame: number;
@@ -126,7 +127,7 @@
             return this._keys[this._keys.length - 1].value;
         }
 
-        public animate(target, delay: number, from: number, to: number, loop: boolean, speedRatio: number): boolean {
+        public animate(delay: number, from: number, to: number, loop: boolean, speedRatio: number): boolean {
             if (!this.targetPropertyPath || this.targetPropertyPath.length < 1) {
                 this._stopped = true;
                 return false;
@@ -202,7 +203,7 @@
 
             // Set value
             if (this.targetPropertyPath.length > 1) {
-                var property = target[this.targetPropertyPath[0]];
+                var property = this._target[this.targetPropertyPath[0]];
 
                 for (var index = 1; index < this.targetPropertyPath.length - 1; index++) {
                     property = property[this.targetPropertyPath[index]];
@@ -210,11 +211,11 @@
 
                 property[this.targetPropertyPath[this.targetPropertyPath.length - 1]] = currentValue;
             } else {
-                target[this.targetPropertyPath[0]] = currentValue;
+                this._target[this.targetPropertyPath[0]] = currentValue;
             }
 
-            if (target.markAsDirty) {
-                target.markAsDirty(this.targetProperty);
+            if (this._target.markAsDirty) {
+                this._target.markAsDirty(this.targetProperty);
             }
 
             if (!returnValue) {

+ 35 - 31
Babylon/Lights/Shadows/babylon.shadowGenerator.js

@@ -22,37 +22,27 @@
             this._shadowMap.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
             this._shadowMap.renderParticles = false;
 
-            var effectiveRender = function (useBones, mesh, renderMesh, subMesh) {
-                // World
-                var world = mesh.getWorldMatrix();
-                if (useBones) {
-                    _this._effect.setMatrix("world", world);
-                } else {
-                    world.multiplyToRef(_this.getTransformMatrix(), _this._worldViewProjection);
-                    _this._effect.setMatrix("worldViewProjection", _this._worldViewProjection);
-                }
-
-                // Draw
-                renderMesh._draw(subMesh, true);
-            };
-
             // Custom render function
             var renderSubMesh = function (subMesh) {
                 var mesh = subMesh.getRenderingMesh();
                 var scene = _this._scene;
                 var engine = scene.getEngine();
 
-                if (_this.isReady(mesh)) {
-                    // Managing instances
-                    var batch = mesh._getInstancesRenderList();
+                // Managing instances
+                var batch = mesh._getInstancesRenderList();
 
-                    if (batch.mustReturn) {
-                        return;
-                    }
+                if (batch.mustReturn) {
+                    return;
+                }
+
+                var hardwareInstancedRendering = (engine.getCaps().instancedArrays !== null) && (batch.visibleInstances !== null);
 
+                if (_this.isReady(mesh, hardwareInstancedRendering)) {
                     engine.enableEffect(_this._effect);
                     mesh._bind(subMesh, _this._effect, false);
 
+                    _this._effect.setMatrix("viewProjection", _this.getTransformMatrix());
+
                     // Alpha test
                     if (mesh.material && mesh.material.needAlphaTesting()) {
                         var alphaTexture = mesh.material.getAlphaTestTexture();
@@ -64,20 +54,28 @@
                     var useBones = mesh.skeleton && mesh.isVerticesDataPresent(BABYLON.VertexBuffer.MatricesIndicesKind) && mesh.isVerticesDataPresent(BABYLON.VertexBuffer.MatricesWeightsKind);
 
                     if (useBones) {
-                        _this._effect.setMatrix("viewProjection", _this.getTransformMatrix());
-
                         _this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices());
                     }
 
-                    if (batch.renderSelf) {
-                        effectiveRender(useBones, mesh, mesh, subMesh);
-                    }
+                    if (hardwareInstancedRendering) {
+                        mesh._renderWithInstances(subMesh, false, batch, _this._effect, engine);
+                    } else {
+                        if (batch.renderSelf) {
+                            _this._effect.setMatrix("world", mesh.getWorldMatrix());
+
+                            // Draw
+                            mesh._draw(subMesh, true);
+                        }
+
+                        if (batch.visibleInstances) {
+                            for (var instanceIndex = 0; instanceIndex < batch.visibleInstances.length; instanceIndex++) {
+                                var instance = batch.visibleInstances[instanceIndex];
 
-                    if (batch.visibleInstances) {
-                        for (var instanceIndex = 0; instanceIndex < batch.visibleInstances.length; instanceIndex++) {
-                            var instance = batch.visibleInstances[instanceIndex];
+                                _this._effect.setMatrix("world", instance.getWorldMatrix());
 
-                            effectiveRender(useBones, instance, mesh, subMesh);
+                                // Draw
+                                mesh._draw(subMesh, true);
+                            }
                         }
                     }
                 } else {
@@ -104,7 +102,7 @@
                 }
             };
         }
-        ShadowGenerator.prototype.isReady = function (mesh) {
+        ShadowGenerator.prototype.isReady = function (mesh, useInstances) {
             var defines = [];
 
             if (this.useVarianceShadowMap) {
@@ -134,11 +132,17 @@
                 defines.push("#define BonesPerMesh " + mesh.skeleton.bones.length);
             }
 
+            // Instances
+            if (useInstances) {
+                defines.push("#define INSTANCES");
+                attribs.push("world");
+            }
+
             // Get correct effect
             var join = defines.join("\n");
             if (this._cachedDefines != join) {
                 this._cachedDefines = join;
-                this._effect = this._scene.getEngine().createEffect("shadowMap", attribs, ["world", "mBones", "viewProjection", "worldViewProjection", "diffuseMatrix"], ["diffuseSampler"], join);
+                this._effect = this._scene.getEngine().createEffect("shadowMap", attribs, ["world", "mBones", "viewProjection", "diffuseMatrix"], ["diffuseSampler"], join);
             }
 
             return this._effect.isReady();

+ 35 - 32
Babylon/Lights/Shadows/babylon.shadowGenerator.ts

@@ -30,38 +30,27 @@
             this._shadowMap.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
             this._shadowMap.renderParticles = false;
 
-            var effectiveRender = (useBones: boolean, mesh: AbstractMesh, renderMesh:Mesh, subMesh: SubMesh): void => {
-                // World
-                var world = mesh.getWorldMatrix();
-                if (useBones) {
-                    this._effect.setMatrix("world", world);
-                } else {
-                    world.multiplyToRef(this.getTransformMatrix(), this._worldViewProjection);
-                    this._effect.setMatrix("worldViewProjection", this._worldViewProjection);
-                }
-
-                // Draw
-                renderMesh._draw(subMesh, true);
-            }
-
             // Custom render function
             var renderSubMesh = (subMesh: SubMesh): void => {
                 var mesh = subMesh.getRenderingMesh();
                 var scene = this._scene;
                 var engine = scene.getEngine();
 
-                if (this.isReady(mesh)) {
+                // Managing instances
+                var batch = mesh._getInstancesRenderList();
 
-                    // Managing instances
-                    var batch = mesh._getInstancesRenderList();
+                if (batch.mustReturn) {
+                    return;
+                }
 
-                    if (batch.mustReturn) {
-                        return;
-                    }
+                var hardwareInstancedRendering = (engine.getCaps().instancedArrays !== null) && (batch.visibleInstances !== null);
 
+                if (this.isReady(mesh, hardwareInstancedRendering)) {
                     engine.enableEffect(this._effect);
                     mesh._bind(subMesh, this._effect, false);
 
+                    this._effect.setMatrix("viewProjection", this.getTransformMatrix());
+
                     // Alpha test
                     if (mesh.material && mesh.material.needAlphaTesting()) {
                         var alphaTexture = mesh.material.getAlphaTestTexture();
@@ -73,20 +62,28 @@
                     var useBones = mesh.skeleton && mesh.isVerticesDataPresent(BABYLON.VertexBuffer.MatricesIndicesKind) && mesh.isVerticesDataPresent(BABYLON.VertexBuffer.MatricesWeightsKind);
 
                     if (useBones) {
-                        this._effect.setMatrix("viewProjection", this.getTransformMatrix());
-
                         this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices());
                     }
 
-                    if (batch.renderSelf) {
-                        effectiveRender(useBones, mesh, mesh, subMesh);
-                    }
+                    if (hardwareInstancedRendering) {
+                        mesh._renderWithInstances(subMesh, false, batch, this._effect, engine);
+                    } else {
+                        if (batch.renderSelf) {
+                            this._effect.setMatrix("world", mesh.getWorldMatrix());
+
+                            // Draw
+                            mesh._draw(subMesh, true);
+                        }
 
-                    if (batch.visibleInstances) {
-                        for (var instanceIndex = 0; instanceIndex < batch.visibleInstances.length; instanceIndex++) {
-                            var instance = batch.visibleInstances[instanceIndex];
+                        if (batch.visibleInstances) {
+                            for (var instanceIndex = 0; instanceIndex < batch.visibleInstances.length; instanceIndex++) {
+                                var instance = batch.visibleInstances[instanceIndex];
 
-                            effectiveRender(useBones, instance, mesh, subMesh);
+                                this._effect.setMatrix("world", instance.getWorldMatrix());
+
+                                // Draw
+                                mesh._draw(subMesh, true);
+                            }
                         }
                     }
                 } else {
@@ -115,7 +112,7 @@
 
         }
 
-        public isReady(mesh: Mesh): boolean {
+        public isReady(mesh: Mesh, useInstances: boolean): boolean {
             var defines = [];
 
             if (this.useVarianceShadowMap) {
@@ -145,13 +142,19 @@
                 defines.push("#define BonesPerMesh " + mesh.skeleton.bones.length);
             }
 
+            // Instances
+            if (useInstances) {
+                defines.push("#define INSTANCES");
+                attribs.push("world");
+            }
+
             // Get correct effect      
             var join = defines.join("\n");
             if (this._cachedDefines != join) {
                 this._cachedDefines = join;
                 this._effect = this._scene.getEngine().createEffect("shadowMap",
                     attribs,
-                    ["world", "mBones", "viewProjection", "worldViewProjection", "diffuseMatrix"],
+                    ["world", "mBones", "viewProjection", "diffuseMatrix"],
                     ["diffuseSampler"], join);
             }
 
@@ -162,7 +165,7 @@
             return this._shadowMap;
         }
 
-        public getLight() : DirectionalLight{
+        public getLight(): DirectionalLight {
             return this._light;
         }
 

+ 7 - 1
Babylon/Materials/babylon.effect.js

@@ -46,7 +46,13 @@
             return this._attributesNames;
         };
 
-        Effect.prototype.getAttribute = function (index) {
+        Effect.prototype.getAttributeLocation = function (index) {
+            return this._attributes[index];
+        };
+
+        Effect.prototype.getAttributeLocationByName = function (name) {
+            var index = this._attributesNames.indexOf(name);
+
             return this._attributes[index];
         };
 

+ 7 - 1
Babylon/Materials/babylon.effect.ts

@@ -60,7 +60,13 @@
             return this._attributesNames;
         }
 
-        public getAttribute(index: number): number {
+        public getAttributeLocation(index: number): number {
+            return this._attributes[index];
+        }
+
+        public getAttributeLocationByName(name: string): number {
+            var index = this._attributesNames.indexOf(name);
+
             return this._attributes[index];
         }
 

+ 4 - 1
Babylon/Materials/babylon.material.js

@@ -18,7 +18,7 @@
                 scene.materials.push(this);
             }
         }
-        Material.prototype.isReady = function (mesh) {
+        Material.prototype.isReady = function (mesh, useInstances) {
             return true;
         };
 
@@ -55,6 +55,9 @@
         Material.prototype.bind = function (world, mesh) {
         };
 
+        Material.prototype.bindOnlyWorldMatrix = function (world) {
+        };
+
         Material.prototype.unbind = function () {
         };
 

+ 4 - 1
Babylon/Materials/babylon.material.ts

@@ -26,7 +26,7 @@
             }
         }
 
-        public isReady(mesh?: AbstractMesh): boolean {
+        public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
             return true;
         }
 
@@ -63,6 +63,9 @@
         public bind(world: Matrix, mesh: Mesh): void {
         }
 
+        public bindOnlyWorldMatrix(world: Matrix): void {
+        }
+
         public unbind(): void {
         }
 

+ 13 - 5
Babylon/Materials/babylon.standardMaterial.js

@@ -22,7 +22,6 @@ var BABYLON;
             this._cachedDefines = null;
             this._renderTargets = new BABYLON.SmartArray(16);
             this._worldViewProjectionMatrix = BABYLON.Matrix.Zero();
-            this._lightMatrix = BABYLON.Matrix.Zero();
             this._globalAmbientColor = new BABYLON.Color3(0, 0, 0);
             this._baseColor = new BABYLON.Color3();
             this._scaledDiffuse = new BABYLON.Color3();
@@ -55,7 +54,7 @@ var BABYLON;
         };
 
         // Methods
-        StandardMaterial.prototype.isReady = function (mesh) {
+        StandardMaterial.prototype.isReady = function (mesh, useInstances) {
             if (this.checkReadyOnlyOnce) {
                 if (this._wasPreviouslyReady) {
                     return true;
@@ -238,6 +237,12 @@ var BABYLON;
                     defines.push("#define BONES4");
                     optionalDefines.push(defines[defines.length - 1]);
                 }
+
+                // Instances
+                if (useInstances) {
+                    defines.push("#define INSTANCES");
+                    attribs.push("world");
+                }
             }
 
             // Get correct effect
@@ -281,12 +286,16 @@ var BABYLON;
             }
         };
 
+        StandardMaterial.prototype.bindOnlyWorldMatrix = function (world) {
+            this._effect.setMatrix("world", world);
+        };
+
         StandardMaterial.prototype.bind = function (world, mesh) {
             var scene = this.getScene();
             this._baseColor.copyFrom(this.diffuseColor);
 
             // Matrices
-            this._effect.setMatrix("world", world);
+            this.bindOnlyWorldMatrix(world);
             this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
 
             // Bones
@@ -394,8 +403,7 @@ var BABYLON;
                     // Shadows
                     var shadowGenerator = light.getShadowGenerator();
                     if (mesh.receiveShadows && shadowGenerator) {
-                        world.multiplyToRef(shadowGenerator.getTransformMatrix(), this._lightMatrix);
-                        this._effect.setMatrix("lightMatrix" + lightIndex, this._lightMatrix);
+                        this._effect.setMatrix("lightMatrix" + lightIndex, shadowGenerator.getTransformMatrix());
                         this._effect.setTexture("shadowSampler" + lightIndex, shadowGenerator.getShadowMap());
                         this._effect.setFloat("darkness" + lightIndex, shadowGenerator.getDarkness());
                     }

+ 13 - 5
Babylon/Materials/babylon.standardMaterial.ts

@@ -20,7 +20,6 @@
         private _cachedDefines = null;
         private _renderTargets = new BABYLON.SmartArray<RenderTargetTexture>(16);
         private _worldViewProjectionMatrix = BABYLON.Matrix.Zero();
-        private _lightMatrix = BABYLON.Matrix.Zero();
         private _globalAmbientColor = new BABYLON.Color3(0, 0, 0);
         private _baseColor = new BABYLON.Color3();
         private _scaledDiffuse = new BABYLON.Color3();
@@ -58,7 +57,7 @@
         }
 
         // Methods   
-        public isReady(mesh?: AbstractMesh): boolean {
+        public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
             if (this.checkReadyOnlyOnce) {
                 if (this._wasPreviouslyReady) {
                     return true;
@@ -241,6 +240,12 @@
                     defines.push("#define BONES4");
                     optionalDefines.push(defines[defines.length - 1]);
                 }
+
+                // Instances
+                if (useInstances) {
+                    defines.push("#define INSTANCES");
+                    attribs.push("world");
+                }
             }
 
             // Get correct effect      
@@ -287,12 +292,16 @@
             }
         }
 
+        public bindOnlyWorldMatrix(world: Matrix): void {
+            this._effect.setMatrix("world", world);
+        }
+
         public bind(world: Matrix, mesh: Mesh): void {
             var scene = this.getScene();
             this._baseColor.copyFrom(this.diffuseColor);
 
             // Matrices        
-            this._effect.setMatrix("world", world);
+            this.bindOnlyWorldMatrix(world);
             this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
 
             // Bones
@@ -400,8 +409,7 @@
                     // Shadows
                     var shadowGenerator = light.getShadowGenerator();
                     if (mesh.receiveShadows && shadowGenerator) {
-                        world.multiplyToRef(shadowGenerator.getTransformMatrix(), this._lightMatrix);
-                        this._effect.setMatrix("lightMatrix" + lightIndex, this._lightMatrix);
+                        this._effect.setMatrix("lightMatrix" + lightIndex, shadowGenerator.getTransformMatrix());
                         this._effect.setTexture("shadowSampler" + lightIndex, shadowGenerator.getShadowMap());
                         this._effect.setFloat("darkness" + lightIndex, shadowGenerator.getDarkness());
                     }

+ 7 - 0
Babylon/Math/babylon.math.js

@@ -1145,6 +1145,13 @@
             }
         };
 
+        Matrix.prototype.copyToArray = function (array, offset) {
+            if (typeof offset === "undefined") { offset = 0; }
+            for (var index = 0; index < 16; index++) {
+                array[offset + index] = this.m[index];
+            }
+        };
+
         Matrix.prototype.multiplyToRef = function (other, result) {
             this.multiplyToArray(other, result.m, 0);
         };

+ 6 - 0
Babylon/Math/babylon.math.ts

@@ -1119,6 +1119,12 @@
             }
         }
 
+        public copyToArray(array: Float32Array, offset: number = 0): void {
+            for (var index = 0; index < 16; index++) {
+                array[offset + index] = this.m[index];
+            }
+        }
+
         public multiplyToRef(other: Matrix, result: Matrix): void {
             this.multiplyToArray(other, result.m, 0);
         }

+ 58 - 26
Babylon/Mesh/babylon.mesh.js

@@ -230,7 +230,7 @@ var BABYLON;
             engine.bindMultiBuffers(this._geometry.getVertexBuffers(), indexToBind, effect);
         };
 
-        Mesh.prototype._draw = function (subMesh, useTriangles) {
+        Mesh.prototype._draw = function (subMesh, useTriangles, instancesCount) {
             if (!this._geometry || !this._geometry.getVertexBuffers() || !this._geometry.getIndexBuffer()) {
                 return;
             }
@@ -238,13 +238,7 @@ var BABYLON;
             var engine = this.getScene().getEngine();
 
             // Draw order
-            engine.draw(useTriangles, useTriangles ? subMesh.indexStart : 0, useTriangles ? subMesh.indexCount : subMesh.linesIndexCount);
-        };
-
-        Mesh.prototype.bindAndDraw = function (subMesh, effect) {
-            this._bind(subMesh, effect, false);
-
-            this._draw(subMesh, true);
+            engine.draw(useTriangles, useTriangles ? subMesh.indexStart : 0, useTriangles ? subMesh.indexCount : subMesh.linesIndexCount, instancesCount);
         };
 
         Mesh.prototype.registerBeforeRender = function (func) {
@@ -292,6 +286,38 @@ var BABYLON;
             return this._batchCache;
         };
 
+        Mesh.prototype._renderWithInstances = function (subMesh, wireFrame, batch, effect, engine) {
+            if (!this._worldMatricesInstancesBuffer) {
+                var matricesCount = this.instances.length + 1;
+                this._worldMatricesInstancesBuffer = engine.createInstancesBuffer(matricesCount * 16 * 4);
+                this._worldMatricesInstancesArray = new Float32Array(16 * matricesCount);
+            }
+
+            var offset = 0;
+            var instancesCount = 0;
+
+            var world = this.getWorldMatrix();
+            if (batch.renderSelf) {
+                world.copyToArray(this._worldMatricesInstancesArray, offset);
+                offset += 16;
+                instancesCount++;
+            }
+
+            for (var instanceIndex = 0; instanceIndex < batch.visibleInstances.length; instanceIndex++) {
+                var instance = batch.visibleInstances[instanceIndex];
+                instance.getWorldMatrix().copyToArray(this._worldMatricesInstancesArray, offset);
+                offset += 16;
+                instancesCount++;
+            }
+
+            var offsetLocation = effect.getAttributeLocationByName("world");
+            engine.updateAndBindInstancesBuffer(this._worldMatricesInstancesBuffer, this._worldMatricesInstancesArray, offsetLocation);
+
+            this._draw(subMesh, !wireFrame, instancesCount);
+
+            engine.unBindInstancesBuffer(this._worldMatricesInstancesBuffer, offsetLocation);
+        };
+
         Mesh.prototype.render = function (subMesh) {
             var scene = this.getScene();
 
@@ -311,40 +337,46 @@ var BABYLON;
                 this._onBeforeRenderCallbacks[callbackIndex]();
             }
 
+            var engine = scene.getEngine();
+            var hardwareInstancedRendering = (engine.getCaps().instancedArrays !== null) && (batch.visibleInstances !== null);
+
             // Material
             var effectiveMaterial = subMesh.getMaterial();
 
-            if (!effectiveMaterial || !effectiveMaterial.isReady(this)) {
+            if (!effectiveMaterial || !effectiveMaterial.isReady(this, hardwareInstancedRendering)) {
                 return;
             }
 
-            var engine = scene.getEngine();
+            effectiveMaterial._preBind();
             var effect = effectiveMaterial.getEffect();
 
             // Bind
             var wireFrame = engine.forceWireframe || effectiveMaterial.wireframe;
             this._bind(subMesh, effect, wireFrame);
-            effectiveMaterial._preBind();
 
-            if (batch.renderSelf) {
-                // World
-                var world = this.getWorldMatrix();
-                effectiveMaterial.bind(world, this);
+            var world = this.getWorldMatrix();
+            effectiveMaterial.bind(world, this);
 
-                // Draw
-                this._draw(subMesh, !wireFrame);
-            }
+            // Instances rendering
+            if (hardwareInstancedRendering) {
+                this._renderWithInstances(subMesh, wireFrame, batch, effect, engine);
+            } else {
+                if (batch.renderSelf) {
+                    // Draw
+                    this._draw(subMesh, !wireFrame);
+                }
 
-            if (batch.visibleInstances) {
-                for (var instanceIndex = 0; instanceIndex < batch.visibleInstances.length; instanceIndex++) {
-                    var instance = batch.visibleInstances[instanceIndex];
+                if (batch.visibleInstances) {
+                    for (var instanceIndex = 0; instanceIndex < batch.visibleInstances.length; instanceIndex++) {
+                        var instance = batch.visibleInstances[instanceIndex];
 
-                    // World
-                    world = instance.getWorldMatrix();
-                    effectiveMaterial.bind(world, this);
+                        // World
+                        world = instance.getWorldMatrix();
+                        effectiveMaterial.bindOnlyWorldMatrix(world);
 
-                    // Draw
-                    this._draw(subMesh, !wireFrame);
+                        // Draw
+                        this._draw(subMesh, !wireFrame);
+                    }
                 }
             }
 

+ 60 - 27
Babylon/Mesh/babylon.mesh.ts

@@ -19,6 +19,8 @@
         public _visibleInstances: any = {};
         private _renderIdForInstances = -1;
         private _batchCache = new _InstancesBatch();
+        private _worldMatricesInstancesBuffer: WebGLBuffer;
+        private _worldMatricesInstancesArray: Float32Array;
 
         constructor(name: string, scene: Scene) {
             super(name, scene);
@@ -231,7 +233,7 @@
             engine.bindMultiBuffers(this._geometry.getVertexBuffers(), indexToBind, effect);
         }
 
-        public _draw(subMesh: SubMesh, useTriangles: boolean): void {
+        public _draw(subMesh: SubMesh, useTriangles: boolean, instancesCount?: number): void {
             if (!this._geometry || !this._geometry.getVertexBuffers() || !this._geometry.getIndexBuffer()) {
                 return;
             }
@@ -239,13 +241,7 @@
             var engine = this.getScene().getEngine();
 
             // Draw order
-            engine.draw(useTriangles, useTriangles ? subMesh.indexStart : 0, useTriangles ? subMesh.indexCount : subMesh.linesIndexCount);
-        }
-
-        public bindAndDraw(subMesh: SubMesh, effect: Effect): void {
-            this._bind(subMesh, effect, false);
-
-            this._draw(subMesh, true);
+            engine.draw(useTriangles, useTriangles ? subMesh.indexStart : 0, useTriangles ? subMesh.indexCount : subMesh.linesIndexCount, instancesCount);
         }
 
         public registerBeforeRender(func: () => void): void {
@@ -294,6 +290,38 @@
             return this._batchCache;
         }
 
+        public _renderWithInstances(subMesh: SubMesh, wireFrame: boolean, batch: _InstancesBatch, effect: Effect, engine: Engine): void {
+            if (!this._worldMatricesInstancesBuffer) {
+                var matricesCount = this.instances.length + 1;
+                this._worldMatricesInstancesBuffer = engine.createInstancesBuffer(matricesCount * 16 * 4);
+                this._worldMatricesInstancesArray = new Float32Array(16 * matricesCount);
+            }
+
+            var offset = 0;
+            var instancesCount = 0;
+
+            var world = this.getWorldMatrix();
+            if (batch.renderSelf) {
+                world.copyToArray(this._worldMatricesInstancesArray, offset);
+                offset += 16;
+                instancesCount++;
+            }
+
+            for (var instanceIndex = 0; instanceIndex < batch.visibleInstances.length; instanceIndex++) {
+                var instance = batch.visibleInstances[instanceIndex];
+                instance.getWorldMatrix().copyToArray(this._worldMatricesInstancesArray, offset);
+                offset += 16;
+                instancesCount++;
+            }
+
+            var offsetLocation = effect.getAttributeLocationByName("world");
+            engine.updateAndBindInstancesBuffer(this._worldMatricesInstancesBuffer, this._worldMatricesInstancesArray, offsetLocation);
+
+            this._draw(subMesh, !wireFrame, instancesCount);
+
+            engine.unBindInstancesBuffer(this._worldMatricesInstancesBuffer, offsetLocation);
+        }
+
         public render(subMesh: SubMesh): void {
             var scene = this.getScene();
 
@@ -313,43 +341,48 @@
                 this._onBeforeRenderCallbacks[callbackIndex]();
             }
 
+            var engine = scene.getEngine();
+            var hardwareInstancedRendering = (engine.getCaps().instancedArrays !== null) && (batch.visibleInstances !== null); 
+
             // Material
             var effectiveMaterial = subMesh.getMaterial();
 
-            if (!effectiveMaterial || !effectiveMaterial.isReady(this)) {
+            if (!effectiveMaterial || !effectiveMaterial.isReady(this, hardwareInstancedRendering)) {
                 return;
             }
 
-            var engine = scene.getEngine();
+            effectiveMaterial._preBind();
             var effect = effectiveMaterial.getEffect();
 
             // Bind
             var wireFrame = engine.forceWireframe || effectiveMaterial.wireframe;
             this._bind(subMesh, effect, wireFrame);
-            effectiveMaterial._preBind();
 
-            if (batch.renderSelf) {
-                // World
-                var world = this.getWorldMatrix();
-                effectiveMaterial.bind(world, this);
+            var world = this.getWorldMatrix();
+            effectiveMaterial.bind(world, this);
 
-                // Draw
-                this._draw(subMesh, !wireFrame);
-            }
+            // Instances rendering
+            if (hardwareInstancedRendering) {
+                this._renderWithInstances(subMesh, wireFrame, batch, effect, engine);
+            } else {
+                if (batch.renderSelf) {
+                    // Draw
+                    this._draw(subMesh, !wireFrame);
+                }
 
-            if (batch.visibleInstances) {
-                for (var instanceIndex = 0; instanceIndex < batch.visibleInstances.length; instanceIndex++) {
-                    var instance = batch.visibleInstances[instanceIndex];
+                if (batch.visibleInstances) {
+                    for (var instanceIndex = 0; instanceIndex < batch.visibleInstances.length; instanceIndex++) {
+                        var instance = batch.visibleInstances[instanceIndex];
 
-                    // World
-                    world = instance.getWorldMatrix();
-                    effectiveMaterial.bind(world, this);
+                        // World
+                        world = instance.getWorldMatrix();
+                        effectiveMaterial.bindOnlyWorldMatrix(world);
 
-                    // Draw
-                    this._draw(subMesh, !wireFrame);
+                        // Draw
+                        this._draw(subMesh, !wireFrame);
+                    }
                 }
             }
-
             // Unbind
             effectiveMaterial.unbind();
         }

+ 10 - 4
Babylon/Shaders/default.vertex.fx

@@ -20,7 +20,13 @@ attribute vec4 matricesWeights;
 #endif
 
 // Uniforms
+
+#ifdef INSTANCES
+attribute mat4 world;
+#else
 uniform mat4 world;
+#endif
+
 uniform mat4 view;
 uniform mat4 viewProjection;
 
@@ -219,16 +225,16 @@ void main(void) {
 	// Shadows
 #ifdef SHADOWS
 #ifdef LIGHT0
-	vPositionFromLight0 = lightMatrix0 * vec4(position, 1.0);
+	vPositionFromLight0 = lightMatrix0 * worldPos;
 #endif
 #ifdef LIGHT1
-	vPositionFromLight1 = lightMatrix1 * vec4(position, 1.0);
+	vPositionFromLight1 = lightMatrix1 * worldPos;
 #endif
 #ifdef LIGHT2
-	vPositionFromLight2 = lightMatrix2 * vec4(position, 1.0);
+	vPositionFromLight2 = lightMatrix2 * worldPos;
 #endif
 #ifdef LIGHT3
-	vPositionFromLight3 = lightMatrix3 * vec4(position, 1.0);
+	vPositionFromLight3 = lightMatrix3 * worldPos;
 #endif
 #endif
 

+ 4 - 4
Babylon/Shaders/legacydefault.vertex.fx

@@ -266,16 +266,16 @@ void main(void) {
 	// Shadows
 #ifdef SHADOWS
 #ifdef LIGHT0
-	vPositionFromLight0 = lightMatrix0 * vec4(position, 1.0);
+	vPositionFromLight0 = lightMatrix0 * worldPos;
 #endif
 #ifdef LIGHT1
-	vPositionFromLight1 = lightMatrix1 * vec4(position, 1.0);
+	vPositionFromLight1 = lightMatrix1 * worldPos;
 #endif
 #ifdef LIGHT2
-	vPositionFromLight2 = lightMatrix2 * vec4(position, 1.0);
+	vPositionFromLight2 = lightMatrix2 * worldPos;
 #endif
 #ifdef LIGHT3
-	vPositionFromLight3 = lightMatrix3 * vec4(position, 1.0);
+	vPositionFromLight3 = lightMatrix3 * worldPos;
 #endif
 #endif
 

+ 9 - 6
Babylon/Shaders/shadowMap.vertex.fx

@@ -10,12 +10,15 @@ attribute vec4 matricesWeights;
 #endif
 
 // Uniform
-#ifdef BONES
+#ifdef INSTANCES
+attribute mat4 world;
+#else
 uniform mat4 world;
-uniform mat4 mBones[BonesPerMesh];
+#endif
+
 uniform mat4 viewProjection;
-#else
-uniform mat4 worldViewProjection;
+#ifdef BONES
+uniform mat4 mBones[BonesPerMesh];
 #endif
 
 #ifndef VSM
@@ -44,9 +47,9 @@ void main(void)
 	gl_Position = viewProjection * finalWorld * vec4(position, 1.0);
 #else
 #ifndef VSM
-	vPosition = worldViewProjection * vec4(position, 1.0);
+	vPosition = viewProjection * world * vec4(position, 1.0);
 #endif
-	gl_Position = worldViewProjection * vec4(position, 1.0);
+	gl_Position = viewProjection * world * vec4(position, 1.0);
 #endif
 
 #ifdef ALPHATEST

+ 40 - 4
Babylon/babylon.engine.js

@@ -17,6 +17,7 @@
 
         do {
             count *= 2;
+            count *= 2;
         } while(count < value);
 
         if (count > max)
@@ -152,6 +153,7 @@
             this._caps.textureFloat = (this._gl.getExtension('OES_texture_float') !== null);
             this._caps.textureAnisotropicFilterExtension = this._gl.getExtension('EXT_texture_filter_anisotropic') || this._gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') || this._gl.getExtension('MOZ_EXT_texture_filter_anisotropic');
             this._caps.maxAnisotropy = this._caps.textureAnisotropicFilterExtension ? this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0;
+            this._caps.instancedArrays = this._gl.getExtension('ANGLE_instanced_arrays');
 
             // Depth buffer
             this.setDepthBuffer(true);
@@ -505,7 +507,7 @@
 
                 var offset = 0;
                 for (var index = 0; index < vertexDeclaration.length; index++) {
-                    var order = effect.getAttribute(index);
+                    var order = effect.getAttributeLocation(index);
 
                     if (order >= 0) {
                         this._gl.vertexAttribPointer(order, vertexDeclaration[index], this._gl.FLOAT, false, vertexStrideSize, offset);
@@ -528,10 +530,13 @@
                 var attributes = effect.getAttributesNames();
 
                 for (var index = 0; index < attributes.length; index++) {
-                    var order = effect.getAttribute(index);
+                    var order = effect.getAttributeLocation(index);
 
                     if (order >= 0) {
                         var vertexBuffer = vertexBuffers[attributes[index]];
+                        if (!vertexBuffer) {
+                            continue;
+                        }
                         var stride = vertexBuffer.getStrideSize();
                         this._gl.bindBuffer(this._gl.ARRAY_BUFFER, vertexBuffer.getBuffer());
                         this._gl.vertexAttribPointer(order, stride, this._gl.FLOAT, false, stride * 4, 0);
@@ -556,7 +561,38 @@
             return false;
         };
 
-        Engine.prototype.draw = function (useTriangles, indexStart, indexCount) {
+        Engine.prototype.createInstancesBuffer = function (capacity) {
+            var buffer = this._gl.createBuffer();
+            this._gl.bindBuffer(this._gl.ARRAY_BUFFER, buffer);
+            this._gl.bufferData(this._gl.ARRAY_BUFFER, capacity, this._gl.DYNAMIC_DRAW);
+            return buffer;
+        };
+
+        Engine.prototype.updateAndBindInstancesBuffer = function (instancesBuffer, data, offsetLocation) {
+            this._gl.bindBuffer(this._gl.ARRAY_BUFFER, instancesBuffer);
+            this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, data);
+
+            for (var index = 0; index < 4; index++) {
+                this._gl.enableVertexAttribArray(offsetLocation + index);
+                this._gl.vertexAttribPointer(offsetLocation + index, 4, this._gl.FLOAT, false, 64, index * 16);
+                this._caps.instancedArrays.vertexAttribDivisorANGLE(offsetLocation + index, 1);
+            }
+        };
+
+        Engine.prototype.unBindInstancesBuffer = function (instancesBuffer, offsetLocation) {
+            this._gl.bindBuffer(this._gl.ARRAY_BUFFER, instancesBuffer);
+            for (var index = 0; index < 4; index++) {
+                this._gl.disableVertexAttribArray(offsetLocation + index);
+                this._caps.instancedArrays.vertexAttribDivisorANGLE(offsetLocation + index, 0);
+            }
+        };
+
+        Engine.prototype.draw = function (useTriangles, indexStart, indexCount, instancesCount) {
+            if (instancesCount) {
+                this._caps.instancedArrays.drawElementsInstancedANGLE(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, indexCount, this._gl.UNSIGNED_SHORT, indexStart * 2, instancesCount);
+                return;
+            }
+
             this._gl.drawElements(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, indexCount, this._gl.UNSIGNED_SHORT, indexStart * 2);
         };
 
@@ -655,7 +691,7 @@
             var attributesCount = effect.getAttributesCount();
             for (var index = 0; index < attributesCount; index++) {
                 // Attributes
-                var order = effect.getAttribute(index);
+                var order = effect.getAttributeLocation(index);
 
                 if (order >= 0) {
                     this._vertexAttribArrays[order] = true;

+ 48 - 10
Babylon/babylon.engine.ts

@@ -16,6 +16,7 @@
 
         do {
             count *= 2;
+            count *= 2;
         } while (count < value);
 
         if (count > max)
@@ -92,6 +93,7 @@
         public textureFloat: boolean;
         public textureAnisotropicFilterExtension;
         public maxAnisotropy: number;
+        public instancedArrays;
     }
 
     export class Engine {
@@ -232,6 +234,7 @@
             this._caps.textureFloat = (this._gl.getExtension('OES_texture_float') !== null);
             this._caps.textureAnisotropicFilterExtension = this._gl.getExtension('EXT_texture_filter_anisotropic') || this._gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') || this._gl.getExtension('MOZ_EXT_texture_filter_anisotropic');
             this._caps.maxAnisotropy = this._caps.textureAnisotropicFilterExtension ? this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0;
+            this._caps.instancedArrays = this._gl.getExtension('ANGLE_instanced_arrays');
 
             // Depth buffer
             this.setDepthBuffer(true);
@@ -502,12 +505,12 @@
             //if (length && length != vertices.length) {
             //    this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, new Float32Array(vertices, 0, length));
             //} else {
-                if (vertices instanceof Float32Array) {
-                    this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, vertices);
-                } else {
-                    this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, new Float32Array(vertices));
-                }
-          //  }
+            if (vertices instanceof Float32Array) {
+                this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, vertices);
+            } else {
+                this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, new Float32Array(vertices));
+            }
+            //  }
 
             this._resetVertexBufferBinding();
         }
@@ -535,7 +538,7 @@
 
                 var offset = 0;
                 for (var index = 0; index < vertexDeclaration.length; index++) {
-                    var order = effect.getAttribute(index);
+                    var order = effect.getAttributeLocation(index);
 
                     if (order >= 0) {
                         this._gl.vertexAttribPointer(order, vertexDeclaration[index], this._gl.FLOAT, false, vertexStrideSize, offset);
@@ -558,10 +561,13 @@
                 var attributes = effect.getAttributesNames();
 
                 for (var index = 0; index < attributes.length; index++) {
-                    var order = effect.getAttribute(index);
+                    var order = effect.getAttributeLocation(index);
 
                     if (order >= 0) {
                         var vertexBuffer = vertexBuffers[attributes[index]];
+                        if (!vertexBuffer) {
+                            continue;
+                        }
                         var stride = vertexBuffer.getStrideSize();
                         this._gl.bindBuffer(this._gl.ARRAY_BUFFER, vertexBuffer.getBuffer());
                         this._gl.vertexAttribPointer(order, stride, this._gl.FLOAT, false, stride * 4, 0);
@@ -586,7 +592,39 @@
             return false;
         }
 
-        public draw(useTriangles: boolean, indexStart: number, indexCount: number): void {
+        public createInstancesBuffer(capacity: number): WebGLBuffer {
+            var buffer = this._gl.createBuffer();
+            this._gl.bindBuffer(this._gl.ARRAY_BUFFER, buffer);
+            this._gl.bufferData(this._gl.ARRAY_BUFFER, capacity, this._gl.DYNAMIC_DRAW);
+            return buffer;
+        }
+
+
+        public updateAndBindInstancesBuffer(instancesBuffer: WebGLBuffer, data: Float32Array, offsetLocation: number): void {
+            this._gl.bindBuffer(this._gl.ARRAY_BUFFER, instancesBuffer);
+            this._gl.bufferSubData(this._gl.ARRAY_BUFFER, 0, data);
+
+            for (var index = 0; index < 4; index++) {
+                this._gl.enableVertexAttribArray(offsetLocation + index);
+                this._gl.vertexAttribPointer(offsetLocation + index, 4, this._gl.FLOAT, false, 64, index * 16);                
+                this._caps.instancedArrays.vertexAttribDivisorANGLE(offsetLocation + index, 1);
+            }
+        }
+
+        public unBindInstancesBuffer(instancesBuffer: WebGLBuffer, offsetLocation: number): void {
+            this._gl.bindBuffer(this._gl.ARRAY_BUFFER, instancesBuffer);
+            for (var index = 0; index < 4; index++) {
+                this._gl.disableVertexAttribArray(offsetLocation + index);
+                this._caps.instancedArrays.vertexAttribDivisorANGLE(offsetLocation + index, 0);
+            }
+        }
+
+        public draw(useTriangles: boolean, indexStart: number, indexCount: number, instancesCount?: number): void {
+            if (instancesCount) {
+                this._caps.instancedArrays.drawElementsInstancedANGLE(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, indexCount, this._gl.UNSIGNED_SHORT, indexStart * 2, instancesCount);
+                return;
+            }
+
             this._gl.drawElements(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, indexCount, this._gl.UNSIGNED_SHORT, indexStart * 2);
         }
 
@@ -686,7 +724,7 @@
             var attributesCount = effect.getAttributesCount();
             for (var index = 0; index < attributesCount; index++) {
                 // Attributes
-                var order = effect.getAttribute(index);
+                var order = effect.getAttributeLocation(index);
 
                 if (order >= 0) {
                     this._vertexAttribArrays[order] = true;

+ 1 - 1
Babylon/babylon.scene.js

@@ -334,7 +334,7 @@
 
             // Local animations
             if (target.animations) {
-                animatable.appendAnimations(target.animations);
+                animatable.appendAnimations(target, target.animations);
             }
 
             // Children animations

+ 1 - 1
Babylon/babylon.scene.ts

@@ -396,7 +396,7 @@
 
             // Local animations
             if (target.animations) {
-                animatable.appendAnimations(target.animations);
+                animatable.appendAnimations(target, target.animations);
             }
 
             // Children animations

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 9 - 9
babylon.1.12-beta.js