Browse Source

Merge pull request #8156 from bghgary/gltf-gpu-instance

Hide the source mesh in glTF instancing extension and some code clean up
mergify[bot] 5 years ago
parent
commit
bd1c74ca39

+ 1 - 0
dist/preview release/what's new.md

@@ -31,6 +31,7 @@
 - Allow logging of shader code when a compilation error occurs ([Popov72](https://github.com/Popov72))
 - Add back support for selecting textures based on engine capabilities ([bghgary](https://github.com/bghgary))
 - Fix Draco decoder when running on IE11 ([bghgary](https://github.com/bghgary))
+- Change default camera calculations to only include visible and enabled meshes ([bghgary](https://github.com/bghgary))
 
 ### NME
 

+ 57 - 51
loaders/src/glTF/2.0/Extensions/EXT_mesh_gpu_instancing.ts

@@ -7,6 +7,7 @@ import { Nullable } from "babylonjs/types";
 import { GLTFLoader, ArrayItem } from "../glTFLoader";
 import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
 import { INode } from "../glTFLoaderInterfaces";
+import { AbstractMesh } from 'babylonjs/Meshes/abstractMesh';
 
 const NAME = "EXT_mesh_gpu_instancing";
 
@@ -47,61 +48,66 @@ export class EXT_mesh_gpu_instancing implements IGLTFLoaderExtension {
     /** @hidden */
     public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
         return GLTFLoader.LoadExtensionAsync<IEXTMeshGpuInstancing, TransformNode>(context, node, this.name, (extensionContext, extension) => {
-            return this._loader.loadNodeAsync(`#/nodes/${node.index}`, node, (babylonTransformNode) => {
-                const promises = new Array<Promise<any>>();
-                let instanceCount: Nullable<number> = null;
-                const loadAttribute = (attribute: string, assignBufferFunc: (data: Float32Array) => void) => {
-                    if (extension.attributes[attribute] == undefined) {
-                        return;
-                    }
-                    const accessor = ArrayItem.Get(`${extensionContext}/attributes/${attribute}`, this._loader.gltf.accessors, extension.attributes[attribute]);
-                    if (instanceCount === null) {
-                        instanceCount = accessor.count;
-                    } else if (instanceCount !== accessor.count) {
-                        throw new Error(`${extensionContext}/attributes: Instance buffer accessors do not have the same count.`);
-                    }
-                    promises.push(this._loader._loadFloatAccessorAsync(`/accessors/${accessor.bufferView}`, accessor).then((data) => {
-                        assignBufferFunc(data);
-                    }));
-                };
-                let translationBuffer: Nullable<Float32Array> = null;
-                let rotationBuffer: Nullable<Float32Array> = null;
-                let scaleBuffer: Nullable<Float32Array> = null;
-
-                loadAttribute("TRANSLATION", (data) => { translationBuffer = data; });
-                loadAttribute("ROTATION", (data) => { rotationBuffer = data; });
-                loadAttribute("SCALE", (data) => { scaleBuffer = data; });
-
-                return Promise.all(promises).then(() => {
-                    if (instanceCount) {
-                        let instanceName: string = "";
-                        let instance: Nullable<TransformNode> = null;
-                        const digitLength = instanceCount.toString().length;
+            const promise = this._loader.loadNodeAsync(`#/nodes/${node.index}`, node, assign);
+
+            if (!node._primitiveBabylonMeshes) {
+                return promise;
+            }
+
+            // Hide the source meshes.
+            for (const babylonMesh of node._primitiveBabylonMeshes) {
+                babylonMesh.isVisible = false;
+            }
+
+            const promises = new Array<Promise<Nullable<Float32Array>>>();
+            let instanceCount = 0;
+
+            const loadAttribute = (attribute: string) => {
+                if (extension.attributes[attribute] == undefined) {
+                    promises.push(Promise.resolve(null));
+                    return;
+                }
 
+                const accessor = ArrayItem.Get(`${extensionContext}/attributes/${attribute}`, this._loader.gltf.accessors, extension.attributes[attribute]);
+                promises.push(this._loader._loadFloatAccessorAsync(`/accessors/${accessor.bufferView}`, accessor));
+
+                if (instanceCount === 0) {
+                    instanceCount = accessor.count;
+                } else if (instanceCount !== accessor.count) {
+                    throw new Error(`${extensionContext}/attributes: Instance buffer accessors do not have the same count.`);
+                }
+            };
+
+            loadAttribute("TRANSLATION");
+            loadAttribute("ROTATION");
+            loadAttribute("SCALE");
+
+            if (instanceCount == 0) {
+                return promise;
+            }
+
+            const digitLength = instanceCount.toString().length;
+            for (let i = 0; i < instanceCount; ++i) {
+                for (const babylonMesh of node._primitiveBabylonMeshes!) {
+                    const instanceName = `${babylonMesh.name || babylonMesh.id}_${StringTools.PadNumber(i, digitLength)}`;
+                    const babylonInstancedMesh = (babylonMesh as (InstancedMesh | Mesh)).createInstance(instanceName);
+                    babylonInstancedMesh.setParent(babylonMesh);
+                }
+            }
+
+            return promise.then((babylonTransformNode) => {
+                return Promise.all(promises).then(([translationBuffer, rotationBuffer, scaleBuffer]) => {
+                    for (const babylonMesh of node._primitiveBabylonMeshes!) {
+                        const babylonInstancedMeshes = babylonMesh.getChildMeshes(true, (node) => (node as AbstractMesh).isAnInstance);
                         for (let i = 0; i < instanceCount; ++i) {
-                            if (node._primitiveBabylonMeshes) {
-                                for (let j = 0; j < node._primitiveBabylonMeshes.length; ++j) {
-                                    const babylonMeshPrimitive = node._primitiveBabylonMeshes[j];
-                                    instanceName = (babylonMeshPrimitive.name || babylonMeshPrimitive.id) + "_" + StringTools.PadNumber(i, digitLength);
-                                    if (babylonMeshPrimitive.isAnInstance) {
-                                        instance = (babylonMeshPrimitive as InstancedMesh).sourceMesh.createInstance(instanceName);
-                                    } else if ((babylonMeshPrimitive as Mesh).createInstance) {
-                                        instance = (babylonMeshPrimitive as Mesh).createInstance(instanceName);
-                                    }
-                                    if (instance) {
-                                        instance.setParent(babylonMeshPrimitive);
-                                        translationBuffer ? Vector3.FromArrayToRef(translationBuffer, i * 3, instance.position)
-                                            : instance.position.set(0, 0, 0);
-                                        rotationBuffer ? Quaternion.FromArrayToRef(rotationBuffer, i * 4, instance.rotationQuaternion!)
-                                            : instance.rotationQuaternion!.set(0, 0, 0, 1);
-                                        scaleBuffer ? Vector3.FromArrayToRef(scaleBuffer, i * 3, instance.scaling)
-                                            : instance.scaling.set(1, 1, 1);
-                                    }
-                                }
-                            }
+                            const babylonInstancedMesh = babylonInstancedMeshes[i];
+                            translationBuffer && Vector3.FromArrayToRef(translationBuffer, i * 3, babylonInstancedMesh.position);
+                            rotationBuffer && Quaternion.FromArrayToRef(rotationBuffer, i * 4, babylonInstancedMesh.rotationQuaternion!);
+                            scaleBuffer && Vector3.FromArrayToRef(scaleBuffer, i * 3, babylonInstancedMesh.scaling);
+                            babylonInstancedMesh.refreshBoundingInfo();
                         }
                     }
-                    assign(babylonTransformNode);
+
                     return babylonTransformNode;
                 });
             });

+ 4 - 1
sandbox/index.js

@@ -165,8 +165,11 @@ if (BABYLON.Engine.isSupported()) {
                 framingBehavior.elevationReturnTime = -1;
 
                 if (currentScene.meshes.length) {
-                    var worldExtends = currentScene.getWorldExtends();
                     currentScene.activeCamera.lowerRadiusLimit = null;
+
+                    var worldExtends = currentScene.getWorldExtends(function (mesh) {
+                        return mesh.isVisible && mesh.isEnabled();
+                    });
                     framingBehavior.zoomOnBoundingInfo(worldExtends.min, worldExtends.max);
                 }
             }

+ 1 - 1
src/Helpers/sceneHelpers.ts

@@ -115,7 +115,7 @@ Scene.prototype.createDefaultCamera = function(createArcRotateCamera = false, re
 
     // Camera
     if (!this.activeCamera) {
-        var worldExtends = this.getWorldExtends();
+        var worldExtends = this.getWorldExtends((mesh) => mesh.isVisible && mesh.isEnabled());
         var worldSize = worldExtends.max.subtract(worldExtends.min);
         var worldCenter = worldExtends.min.add(worldSize.scale(0.5));
 

+ 10 - 0
src/Meshes/instancedMesh.ts

@@ -160,6 +160,16 @@ export class InstancedMesh extends AbstractMesh {
     }
 
     /**
+     * Creates a new InstancedMesh object from the mesh model.
+     * @see http://doc.babylonjs.com/how_to/how_to_use_instances
+     * @param name defines the name of the new instance
+     * @returns a new InstancedMesh
+     */
+    public createInstance(name: string): InstancedMesh {
+        return this._sourceMesh.createInstance(name);
+    }
+
+    /**
      * Is this node ready to be used/rendered
      * @param completeCheck defines if a complete check (including materials and lights) has to be done (false by default)
      * @return {boolean} is it ready

BIN
tests/validation/ReferenceImages/gltfExtensionExtMeshGpuInstancingTest.png


+ 6 - 0
tests/validation/config.json

@@ -827,6 +827,12 @@
             "title": "Visibility",
             "playgroundId": "#PXC9CF#2",
             "referenceImage": "visibility.png"
+        },
+        {
+            "title": "GLTF Extension EXT_mesh_gpu_instancing",
+            "playgroundId": "#QFIGLW#9",
+            "renderCount": 50,
+            "referenceImage": "gltfExtensionExtMeshGpuInstancingTest.png"
         }
     ]
 }