瀏覽代碼

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js into inspector-linearcolor

Popov72 5 年之前
父節點
當前提交
a9459a42b5
共有 31 個文件被更改,包括 687 次插入197 次删除
  1. 46 1
      dist/preview release/babylon.d.ts
  2. 2 2
      dist/preview release/babylon.js
  3. 74 13
      dist/preview release/babylon.max.js
  4. 1 1
      dist/preview release/babylon.max.js.map
  5. 93 2
      dist/preview release/babylon.module.d.ts
  6. 46 1
      dist/preview release/documentation.d.ts
  7. 10 16
      dist/preview release/serializers/babylon.glTF2Serializer.js
  8. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.js.map
  9. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  10. 9 15
      dist/preview release/serializers/babylonjs.serializers.js
  11. 1 1
      dist/preview release/serializers/babylonjs.serializers.js.map
  12. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  13. 93 2
      dist/preview release/viewer/babylon.module.d.ts
  14. 8 8
      dist/preview release/viewer/babylon.viewer.js
  15. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  16. 7 0
      dist/preview release/what's new.md
  17. 30 19
      inspector/src/components/actionTabs/lines/optionsLineComponent.tsx
  18. 11 2
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/commonMaterialPropertyGridComponent.tsx
  19. 1 0
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/standardMaterialPropertyGridComponent.tsx
  20. 1 0
      inspector/src/components/propertyChangedEvent.ts
  21. 19 17
      inspector/src/components/replayRecorder.ts
  22. 10 19
      serializers/src/glTF/2.0/Extensions/KHR_texture_transform.ts
  23. 1 1
      src/Materials/Node/Blocks/Dual/textureBlock.ts
  24. 5 53
      src/Materials/PBR/pbrBaseMaterial.ts
  25. 92 10
      src/Materials/material.ts
  26. 19 4
      src/Materials/standardMaterial.ts
  27. 10 1
      src/Particles/solidParticle.ts
  28. 69 2
      src/Particles/solidParticleSystem.ts
  29. 12 1
      src/Shaders/default.fragment.fx
  30. 5 1
      src/Shaders/pbr.fragment.fx
  31. 8 1
      src/XR/features/WebXRControllerPointerSelection.ts

+ 46 - 1
dist/preview release/babylon.d.ts

@@ -26978,6 +26978,9 @@ declare module BABYLON {
          * Each element of this array is an object `{idx: int, faceId: int}`.
          * `idx` is the picked particle index in the `SPS.particles` array
          * `faceId` is the picked face index counted within this particle.
+         * This array is the first element of the pickedBySubMesh array : sps.pickBySubMesh[0].
+         * It's not pertinent to use it when using a SPS with the support for MultiMaterial enabled.
+         * Use the method SPS.pickedParticle(pickingInfo) instead.
          * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
          */
         pickedParticles: {
@@ -26985,6 +26988,21 @@ declare module BABYLON {
             faceId: number;
         }[];
         /**
+         * This array is populated when the SPS is set as 'pickable'
+         * Each key of this array is a submesh index.
+         * Each element of this array is a second array defined like this :
+         * Each key of this second array is a `faceId` value that you can get from a pickResult object.
+         * Each element of this second array is an object `{idx: int, faceId: int}`.
+         * `idx` is the picked particle index in the `SPS.particles` array
+         * `faceId` is the picked face index counted within this particle.
+         * It's better to use the method SPS.pickedParticle(pickingInfo) rather than using directly this array.
+         * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
+         */
+        pickedBySubMesh: {
+            idx: number;
+            faceId: number;
+        }[][];
+        /**
          * This array is populated when `enableDepthSort` is set to true.
          * Each element of this array is an instance of the class DepthSortedParticle.
          */
@@ -27230,6 +27248,17 @@ declare module BABYLON {
         * Disposes the SPS.
         */
         dispose(): void;
+        /** Returns an object {idx: numbern faceId: number} for the picked particle from the passed pickingInfo object.
+         * idx is the particle index in the SPS
+         * faceId is the picked face index counted within this particle.
+         * Returns null if the pickInfo can't identify a picked particle.
+         * @param pickingInfo (PickingInfo object)
+         * @returns {idx: number, faceId: number} or null
+         */
+        pickedParticle(pickingInfo: PickingInfo): Nullable<{
+            idx: number;
+            faceId: number;
+        }>;
         /**
          * Returns a SolidParticle object from its identifier : particle.id
          * @param id (integer) the particle Id
@@ -27572,6 +27601,10 @@ declare module BABYLON {
          */
         materialIndex: Nullable<number>;
         /**
+         * Custom object or properties.
+         */
+        props: Nullable<any>;
+        /**
          * The culling strategy to use to check whether the solid particle must be culled or not when using isInFrustum().
          * The possible values are :
          * - AbstractMesh.CULLINGSTRATEGY_STANDARD
@@ -27711,6 +27744,10 @@ declare module BABYLON {
      */
     export class DepthSortedParticle {
         /**
+         * Particle index
+         */
+        idx: number;
+        /**
          * Index of the particle in the "indices" array
          */
         ind: number;
@@ -27730,7 +27767,7 @@ declare module BABYLON {
          * Creates a new sorted particle
          * @param materialIndex
          */
-        constructor(ind: number, indLength: number, materialIndex: number);
+        constructor(idx: number, ind: number, indLength: number, materialIndex: number);
     }
 }
 declare module BABYLON {
@@ -44584,6 +44621,11 @@ declare module BABYLON {
          */
         selectionMeshPickedColor: Color3;
         /**
+         * Optional filter to be used for ray selection.  This predicate shares behavior with
+         * scene.pointerMovePredicate which takes priority if it is also assigned.
+         */
+        raySelectionPredicate: (mesh: AbstractMesh) => boolean;
+        /**
          * constructs a new background remover module
          * @param _xrSessionManager the session manager for this module
          * @param _options read-only options to be used in this module
@@ -49913,6 +49955,9 @@ declare module BABYLON {
      * Class used to host texture specific utilities
      */
     export class BRDFTextureTools {
+        /**
+         * Prevents texture cache collision
+         */
         private static _instanceNumber;
         /**
          * Gets a default environment BRDF for MS-BRDF Height Correlated BRDF

File diff suppressed because it is too large
+ 2 - 2
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 74 - 13
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/babylon.max.js.map


+ 93 - 2
dist/preview release/babylon.module.d.ts

@@ -27772,6 +27772,7 @@ declare module "babylonjs/Particles/solidParticleSystem" {
     import { DepthSortedParticle, SolidParticle } from "babylonjs/Particles/solidParticle";
     import { Material } from "babylonjs/Materials/material";
     import { MultiMaterial } from "babylonjs/Materials/multiMaterial";
+    import { PickingInfo } from "babylonjs/Collisions/pickingInfo";
     /**
      * The SPS is a single updatable mesh. The solid particles are simply separate parts or faces fo this big mesh.
      *As it is just a mesh, the SPS has all the same properties than any other BJS mesh : not more, not less. It can be scaled, rotated, translated, enlighted, textured, moved, etc.
@@ -27822,6 +27823,9 @@ declare module "babylonjs/Particles/solidParticleSystem" {
          * Each element of this array is an object `{idx: int, faceId: int}`.
          * `idx` is the picked particle index in the `SPS.particles` array
          * `faceId` is the picked face index counted within this particle.
+         * This array is the first element of the pickedBySubMesh array : sps.pickBySubMesh[0].
+         * It's not pertinent to use it when using a SPS with the support for MultiMaterial enabled.
+         * Use the method SPS.pickedParticle(pickingInfo) instead.
          * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
          */
         pickedParticles: {
@@ -27829,6 +27833,21 @@ declare module "babylonjs/Particles/solidParticleSystem" {
             faceId: number;
         }[];
         /**
+         * This array is populated when the SPS is set as 'pickable'
+         * Each key of this array is a submesh index.
+         * Each element of this array is a second array defined like this :
+         * Each key of this second array is a `faceId` value that you can get from a pickResult object.
+         * Each element of this second array is an object `{idx: int, faceId: int}`.
+         * `idx` is the picked particle index in the `SPS.particles` array
+         * `faceId` is the picked face index counted within this particle.
+         * It's better to use the method SPS.pickedParticle(pickingInfo) rather than using directly this array.
+         * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
+         */
+        pickedBySubMesh: {
+            idx: number;
+            faceId: number;
+        }[][];
+        /**
          * This array is populated when `enableDepthSort` is set to true.
          * Each element of this array is an instance of the class DepthSortedParticle.
          */
@@ -28074,6 +28093,17 @@ declare module "babylonjs/Particles/solidParticleSystem" {
         * Disposes the SPS.
         */
         dispose(): void;
+        /** Returns an object {idx: numbern faceId: number} for the picked particle from the passed pickingInfo object.
+         * idx is the particle index in the SPS
+         * faceId is the picked face index counted within this particle.
+         * Returns null if the pickInfo can't identify a picked particle.
+         * @param pickingInfo (PickingInfo object)
+         * @returns {idx: number, faceId: number} or null
+         */
+        pickedParticle(pickingInfo: PickingInfo): Nullable<{
+            idx: number;
+            faceId: number;
+        }>;
         /**
          * Returns a SolidParticle object from its identifier : particle.id
          * @param id (integer) the particle Id
@@ -28424,6 +28454,10 @@ declare module "babylonjs/Particles/solidParticle" {
          */
         materialIndex: Nullable<number>;
         /**
+         * Custom object or properties.
+         */
+        props: Nullable<any>;
+        /**
          * The culling strategy to use to check whether the solid particle must be culled or not when using isInFrustum().
          * The possible values are :
          * - AbstractMesh.CULLINGSTRATEGY_STANDARD
@@ -28563,6 +28597,10 @@ declare module "babylonjs/Particles/solidParticle" {
      */
     export class DepthSortedParticle {
         /**
+         * Particle index
+         */
+        idx: number;
+        /**
          * Index of the particle in the "indices" array
          */
         ind: number;
@@ -28582,7 +28620,7 @@ declare module "babylonjs/Particles/solidParticle" {
          * Creates a new sorted particle
          * @param materialIndex
          */
-        constructor(ind: number, indLength: number, materialIndex: number);
+        constructor(idx: number, ind: number, indLength: number, materialIndex: number);
     }
 }
 declare module "babylonjs/Collisions/meshCollisionData" {
@@ -46148,6 +46186,11 @@ declare module "babylonjs/XR/features/WebXRControllerPointerSelection" {
          */
         selectionMeshPickedColor: Color3;
         /**
+         * Optional filter to be used for ray selection.  This predicate shares behavior with
+         * scene.pointerMovePredicate which takes priority if it is also assigned.
+         */
+        raySelectionPredicate: (mesh: AbstractMesh) => boolean;
+        /**
          * constructs a new background remover module
          * @param _xrSessionManager the session manager for this module
          * @param _options read-only options to be used in this module
@@ -52011,6 +52054,9 @@ declare module "babylonjs/Misc/brdfTextureTools" {
      * Class used to host texture specific utilities
      */
     export class BRDFTextureTools {
+        /**
+         * Prevents texture cache collision
+         */
         private static _instanceNumber;
         /**
          * Gets a default environment BRDF for MS-BRDF Height Correlated BRDF
@@ -100434,6 +100480,9 @@ declare module BABYLON {
          * Each element of this array is an object `{idx: int, faceId: int}`.
          * `idx` is the picked particle index in the `SPS.particles` array
          * `faceId` is the picked face index counted within this particle.
+         * This array is the first element of the pickedBySubMesh array : sps.pickBySubMesh[0].
+         * It's not pertinent to use it when using a SPS with the support for MultiMaterial enabled.
+         * Use the method SPS.pickedParticle(pickingInfo) instead.
          * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
          */
         pickedParticles: {
@@ -100441,6 +100490,21 @@ declare module BABYLON {
             faceId: number;
         }[];
         /**
+         * This array is populated when the SPS is set as 'pickable'
+         * Each key of this array is a submesh index.
+         * Each element of this array is a second array defined like this :
+         * Each key of this second array is a `faceId` value that you can get from a pickResult object.
+         * Each element of this second array is an object `{idx: int, faceId: int}`.
+         * `idx` is the picked particle index in the `SPS.particles` array
+         * `faceId` is the picked face index counted within this particle.
+         * It's better to use the method SPS.pickedParticle(pickingInfo) rather than using directly this array.
+         * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
+         */
+        pickedBySubMesh: {
+            idx: number;
+            faceId: number;
+        }[][];
+        /**
          * This array is populated when `enableDepthSort` is set to true.
          * Each element of this array is an instance of the class DepthSortedParticle.
          */
@@ -100686,6 +100750,17 @@ declare module BABYLON {
         * Disposes the SPS.
         */
         dispose(): void;
+        /** Returns an object {idx: numbern faceId: number} for the picked particle from the passed pickingInfo object.
+         * idx is the particle index in the SPS
+         * faceId is the picked face index counted within this particle.
+         * Returns null if the pickInfo can't identify a picked particle.
+         * @param pickingInfo (PickingInfo object)
+         * @returns {idx: number, faceId: number} or null
+         */
+        pickedParticle(pickingInfo: PickingInfo): Nullable<{
+            idx: number;
+            faceId: number;
+        }>;
         /**
          * Returns a SolidParticle object from its identifier : particle.id
          * @param id (integer) the particle Id
@@ -101028,6 +101103,10 @@ declare module BABYLON {
          */
         materialIndex: Nullable<number>;
         /**
+         * Custom object or properties.
+         */
+        props: Nullable<any>;
+        /**
          * The culling strategy to use to check whether the solid particle must be culled or not when using isInFrustum().
          * The possible values are :
          * - AbstractMesh.CULLINGSTRATEGY_STANDARD
@@ -101167,6 +101246,10 @@ declare module BABYLON {
      */
     export class DepthSortedParticle {
         /**
+         * Particle index
+         */
+        idx: number;
+        /**
          * Index of the particle in the "indices" array
          */
         ind: number;
@@ -101186,7 +101269,7 @@ declare module BABYLON {
          * Creates a new sorted particle
          * @param materialIndex
          */
-        constructor(ind: number, indLength: number, materialIndex: number);
+        constructor(idx: number, ind: number, indLength: number, materialIndex: number);
     }
 }
 declare module BABYLON {
@@ -118040,6 +118123,11 @@ declare module BABYLON {
          */
         selectionMeshPickedColor: Color3;
         /**
+         * Optional filter to be used for ray selection.  This predicate shares behavior with
+         * scene.pointerMovePredicate which takes priority if it is also assigned.
+         */
+        raySelectionPredicate: (mesh: AbstractMesh) => boolean;
+        /**
          * constructs a new background remover module
          * @param _xrSessionManager the session manager for this module
          * @param _options read-only options to be used in this module
@@ -123369,6 +123457,9 @@ declare module BABYLON {
      * Class used to host texture specific utilities
      */
     export class BRDFTextureTools {
+        /**
+         * Prevents texture cache collision
+         */
         private static _instanceNumber;
         /**
          * Gets a default environment BRDF for MS-BRDF Height Correlated BRDF

+ 46 - 1
dist/preview release/documentation.d.ts

@@ -26978,6 +26978,9 @@ declare module BABYLON {
          * Each element of this array is an object `{idx: int, faceId: int}`.
          * `idx` is the picked particle index in the `SPS.particles` array
          * `faceId` is the picked face index counted within this particle.
+         * This array is the first element of the pickedBySubMesh array : sps.pickBySubMesh[0].
+         * It's not pertinent to use it when using a SPS with the support for MultiMaterial enabled.
+         * Use the method SPS.pickedParticle(pickingInfo) instead.
          * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
          */
         pickedParticles: {
@@ -26985,6 +26988,21 @@ declare module BABYLON {
             faceId: number;
         }[];
         /**
+         * This array is populated when the SPS is set as 'pickable'
+         * Each key of this array is a submesh index.
+         * Each element of this array is a second array defined like this :
+         * Each key of this second array is a `faceId` value that you can get from a pickResult object.
+         * Each element of this second array is an object `{idx: int, faceId: int}`.
+         * `idx` is the picked particle index in the `SPS.particles` array
+         * `faceId` is the picked face index counted within this particle.
+         * It's better to use the method SPS.pickedParticle(pickingInfo) rather than using directly this array.
+         * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
+         */
+        pickedBySubMesh: {
+            idx: number;
+            faceId: number;
+        }[][];
+        /**
          * This array is populated when `enableDepthSort` is set to true.
          * Each element of this array is an instance of the class DepthSortedParticle.
          */
@@ -27230,6 +27248,17 @@ declare module BABYLON {
         * Disposes the SPS.
         */
         dispose(): void;
+        /** Returns an object {idx: numbern faceId: number} for the picked particle from the passed pickingInfo object.
+         * idx is the particle index in the SPS
+         * faceId is the picked face index counted within this particle.
+         * Returns null if the pickInfo can't identify a picked particle.
+         * @param pickingInfo (PickingInfo object)
+         * @returns {idx: number, faceId: number} or null
+         */
+        pickedParticle(pickingInfo: PickingInfo): Nullable<{
+            idx: number;
+            faceId: number;
+        }>;
         /**
          * Returns a SolidParticle object from its identifier : particle.id
          * @param id (integer) the particle Id
@@ -27572,6 +27601,10 @@ declare module BABYLON {
          */
         materialIndex: Nullable<number>;
         /**
+         * Custom object or properties.
+         */
+        props: Nullable<any>;
+        /**
          * The culling strategy to use to check whether the solid particle must be culled or not when using isInFrustum().
          * The possible values are :
          * - AbstractMesh.CULLINGSTRATEGY_STANDARD
@@ -27711,6 +27744,10 @@ declare module BABYLON {
      */
     export class DepthSortedParticle {
         /**
+         * Particle index
+         */
+        idx: number;
+        /**
          * Index of the particle in the "indices" array
          */
         ind: number;
@@ -27730,7 +27767,7 @@ declare module BABYLON {
          * Creates a new sorted particle
          * @param materialIndex
          */
-        constructor(ind: number, indLength: number, materialIndex: number);
+        constructor(idx: number, ind: number, indLength: number, materialIndex: number);
     }
 }
 declare module BABYLON {
@@ -44584,6 +44621,11 @@ declare module BABYLON {
          */
         selectionMeshPickedColor: Color3;
         /**
+         * Optional filter to be used for ray selection.  This predicate shares behavior with
+         * scene.pointerMovePredicate which takes priority if it is also assigned.
+         */
+        raySelectionPredicate: (mesh: AbstractMesh) => boolean;
+        /**
          * constructs a new background remover module
          * @param _xrSessionManager the session manager for this module
          * @param _options read-only options to be used in this module
@@ -49913,6 +49955,9 @@ declare module BABYLON {
      * Class used to host texture specific utilities
      */
     export class BRDFTextureTools {
+        /**
+         * Prevents texture cache collision
+         */
         private static _instanceNumber;
         /**
          * Gets a default environment BRDF for MS-BRDF Height Correlated BRDF

+ 10 - 16
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -740,22 +740,16 @@ var KHR_texture_transform = /** @class */ (function () {
                 reject(context + ": \"scene\" is not defined for Babylon texture " + babylonTexture.name + "!");
                 return;
             }
-            var transformIsRequired = false;
-            if (babylonTexture.uOffset !== 0 || babylonTexture.vOffset !== 0) {
-                transformIsRequired = true;
-            }
-            if (babylonTexture.uScale !== 1 || babylonTexture.vScale !== 1) {
-                transformIsRequired = true;
-            }
-            if (babylonTexture.wAng !== 0) {
-                transformIsRequired = true;
-            }
-            if (!transformIsRequired) {
-                resolve(babylonTexture);
-                return;
-            }
-            // Do we need to flatten the transform?
-            if (babylonTexture.uRotationCenter === 0 && babylonTexture.vRotationCenter === 0) {
+            var bakeTextureTransform = false;
+            /*
+            * The KHR_texture_transform schema only supports rotation around the origin.
+            * the texture must be baked to preserve appearance.
+            * see: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform#gltf-schema-updates
+            */
+            if ((babylonTexture.uAng !== 0 || babylonTexture.wAng !== 0 || babylonTexture.vAng !== 0) && (babylonTexture.uRotationCenter !== 0 || babylonTexture.vRotationCenter !== 0)) {
+                bakeTextureTransform = true;
+            }
+            if (!bakeTextureTransform) {
                 resolve(babylonTexture);
                 return;
             }

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.js.map


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 9 - 15
dist/preview release/serializers/babylonjs.serializers.js

@@ -918,22 +918,16 @@ var KHR_texture_transform = /** @class */ (function () {
                 reject(context + ": \"scene\" is not defined for Babylon texture " + babylonTexture.name + "!");
                 return;
             }
-            var transformIsRequired = false;
-            if (babylonTexture.uOffset !== 0 || babylonTexture.vOffset !== 0) {
-                transformIsRequired = true;
-            }
-            if (babylonTexture.uScale !== 1 || babylonTexture.vScale !== 1) {
-                transformIsRequired = true;
-            }
-            if (babylonTexture.wAng !== 0) {
-                transformIsRequired = true;
-            }
-            if (!transformIsRequired) {
-                resolve(babylonTexture);
-                return;
+            var bakeTextureTransform = false;
+            /*
+            * The KHR_texture_transform schema only supports rotation around the origin.
+            * the texture must be baked to preserve appearance.
+            * see: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform#gltf-schema-updates
+            */
+            if ((babylonTexture.uAng !== 0 || babylonTexture.wAng !== 0 || babylonTexture.vAng !== 0) && (babylonTexture.uRotationCenter !== 0 || babylonTexture.vRotationCenter !== 0)) {
+                bakeTextureTransform = true;
             }
-            // Do we need to flatten the transform?
-            if (babylonTexture.uRotationCenter === 0 && babylonTexture.vRotationCenter === 0) {
+            if (!bakeTextureTransform) {
                 resolve(babylonTexture);
                 return;
             }

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.js.map


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


+ 93 - 2
dist/preview release/viewer/babylon.module.d.ts

@@ -27772,6 +27772,7 @@ declare module "babylonjs/Particles/solidParticleSystem" {
     import { DepthSortedParticle, SolidParticle } from "babylonjs/Particles/solidParticle";
     import { Material } from "babylonjs/Materials/material";
     import { MultiMaterial } from "babylonjs/Materials/multiMaterial";
+    import { PickingInfo } from "babylonjs/Collisions/pickingInfo";
     /**
      * The SPS is a single updatable mesh. The solid particles are simply separate parts or faces fo this big mesh.
      *As it is just a mesh, the SPS has all the same properties than any other BJS mesh : not more, not less. It can be scaled, rotated, translated, enlighted, textured, moved, etc.
@@ -27822,6 +27823,9 @@ declare module "babylonjs/Particles/solidParticleSystem" {
          * Each element of this array is an object `{idx: int, faceId: int}`.
          * `idx` is the picked particle index in the `SPS.particles` array
          * `faceId` is the picked face index counted within this particle.
+         * This array is the first element of the pickedBySubMesh array : sps.pickBySubMesh[0].
+         * It's not pertinent to use it when using a SPS with the support for MultiMaterial enabled.
+         * Use the method SPS.pickedParticle(pickingInfo) instead.
          * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
          */
         pickedParticles: {
@@ -27829,6 +27833,21 @@ declare module "babylonjs/Particles/solidParticleSystem" {
             faceId: number;
         }[];
         /**
+         * This array is populated when the SPS is set as 'pickable'
+         * Each key of this array is a submesh index.
+         * Each element of this array is a second array defined like this :
+         * Each key of this second array is a `faceId` value that you can get from a pickResult object.
+         * Each element of this second array is an object `{idx: int, faceId: int}`.
+         * `idx` is the picked particle index in the `SPS.particles` array
+         * `faceId` is the picked face index counted within this particle.
+         * It's better to use the method SPS.pickedParticle(pickingInfo) rather than using directly this array.
+         * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
+         */
+        pickedBySubMesh: {
+            idx: number;
+            faceId: number;
+        }[][];
+        /**
          * This array is populated when `enableDepthSort` is set to true.
          * Each element of this array is an instance of the class DepthSortedParticle.
          */
@@ -28074,6 +28093,17 @@ declare module "babylonjs/Particles/solidParticleSystem" {
         * Disposes the SPS.
         */
         dispose(): void;
+        /** Returns an object {idx: numbern faceId: number} for the picked particle from the passed pickingInfo object.
+         * idx is the particle index in the SPS
+         * faceId is the picked face index counted within this particle.
+         * Returns null if the pickInfo can't identify a picked particle.
+         * @param pickingInfo (PickingInfo object)
+         * @returns {idx: number, faceId: number} or null
+         */
+        pickedParticle(pickingInfo: PickingInfo): Nullable<{
+            idx: number;
+            faceId: number;
+        }>;
         /**
          * Returns a SolidParticle object from its identifier : particle.id
          * @param id (integer) the particle Id
@@ -28424,6 +28454,10 @@ declare module "babylonjs/Particles/solidParticle" {
          */
         materialIndex: Nullable<number>;
         /**
+         * Custom object or properties.
+         */
+        props: Nullable<any>;
+        /**
          * The culling strategy to use to check whether the solid particle must be culled or not when using isInFrustum().
          * The possible values are :
          * - AbstractMesh.CULLINGSTRATEGY_STANDARD
@@ -28563,6 +28597,10 @@ declare module "babylonjs/Particles/solidParticle" {
      */
     export class DepthSortedParticle {
         /**
+         * Particle index
+         */
+        idx: number;
+        /**
          * Index of the particle in the "indices" array
          */
         ind: number;
@@ -28582,7 +28620,7 @@ declare module "babylonjs/Particles/solidParticle" {
          * Creates a new sorted particle
          * @param materialIndex
          */
-        constructor(ind: number, indLength: number, materialIndex: number);
+        constructor(idx: number, ind: number, indLength: number, materialIndex: number);
     }
 }
 declare module "babylonjs/Collisions/meshCollisionData" {
@@ -46148,6 +46186,11 @@ declare module "babylonjs/XR/features/WebXRControllerPointerSelection" {
          */
         selectionMeshPickedColor: Color3;
         /**
+         * Optional filter to be used for ray selection.  This predicate shares behavior with
+         * scene.pointerMovePredicate which takes priority if it is also assigned.
+         */
+        raySelectionPredicate: (mesh: AbstractMesh) => boolean;
+        /**
          * constructs a new background remover module
          * @param _xrSessionManager the session manager for this module
          * @param _options read-only options to be used in this module
@@ -52011,6 +52054,9 @@ declare module "babylonjs/Misc/brdfTextureTools" {
      * Class used to host texture specific utilities
      */
     export class BRDFTextureTools {
+        /**
+         * Prevents texture cache collision
+         */
         private static _instanceNumber;
         /**
          * Gets a default environment BRDF for MS-BRDF Height Correlated BRDF
@@ -100434,6 +100480,9 @@ declare module BABYLON {
          * Each element of this array is an object `{idx: int, faceId: int}`.
          * `idx` is the picked particle index in the `SPS.particles` array
          * `faceId` is the picked face index counted within this particle.
+         * This array is the first element of the pickedBySubMesh array : sps.pickBySubMesh[0].
+         * It's not pertinent to use it when using a SPS with the support for MultiMaterial enabled.
+         * Use the method SPS.pickedParticle(pickingInfo) instead.
          * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
          */
         pickedParticles: {
@@ -100441,6 +100490,21 @@ declare module BABYLON {
             faceId: number;
         }[];
         /**
+         * This array is populated when the SPS is set as 'pickable'
+         * Each key of this array is a submesh index.
+         * Each element of this array is a second array defined like this :
+         * Each key of this second array is a `faceId` value that you can get from a pickResult object.
+         * Each element of this second array is an object `{idx: int, faceId: int}`.
+         * `idx` is the picked particle index in the `SPS.particles` array
+         * `faceId` is the picked face index counted within this particle.
+         * It's better to use the method SPS.pickedParticle(pickingInfo) rather than using directly this array.
+         * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
+         */
+        pickedBySubMesh: {
+            idx: number;
+            faceId: number;
+        }[][];
+        /**
          * This array is populated when `enableDepthSort` is set to true.
          * Each element of this array is an instance of the class DepthSortedParticle.
          */
@@ -100686,6 +100750,17 @@ declare module BABYLON {
         * Disposes the SPS.
         */
         dispose(): void;
+        /** Returns an object {idx: numbern faceId: number} for the picked particle from the passed pickingInfo object.
+         * idx is the particle index in the SPS
+         * faceId is the picked face index counted within this particle.
+         * Returns null if the pickInfo can't identify a picked particle.
+         * @param pickingInfo (PickingInfo object)
+         * @returns {idx: number, faceId: number} or null
+         */
+        pickedParticle(pickingInfo: PickingInfo): Nullable<{
+            idx: number;
+            faceId: number;
+        }>;
         /**
          * Returns a SolidParticle object from its identifier : particle.id
          * @param id (integer) the particle Id
@@ -101028,6 +101103,10 @@ declare module BABYLON {
          */
         materialIndex: Nullable<number>;
         /**
+         * Custom object or properties.
+         */
+        props: Nullable<any>;
+        /**
          * The culling strategy to use to check whether the solid particle must be culled or not when using isInFrustum().
          * The possible values are :
          * - AbstractMesh.CULLINGSTRATEGY_STANDARD
@@ -101167,6 +101246,10 @@ declare module BABYLON {
      */
     export class DepthSortedParticle {
         /**
+         * Particle index
+         */
+        idx: number;
+        /**
          * Index of the particle in the "indices" array
          */
         ind: number;
@@ -101186,7 +101269,7 @@ declare module BABYLON {
          * Creates a new sorted particle
          * @param materialIndex
          */
-        constructor(ind: number, indLength: number, materialIndex: number);
+        constructor(idx: number, ind: number, indLength: number, materialIndex: number);
     }
 }
 declare module BABYLON {
@@ -118040,6 +118123,11 @@ declare module BABYLON {
          */
         selectionMeshPickedColor: Color3;
         /**
+         * Optional filter to be used for ray selection.  This predicate shares behavior with
+         * scene.pointerMovePredicate which takes priority if it is also assigned.
+         */
+        raySelectionPredicate: (mesh: AbstractMesh) => boolean;
+        /**
          * constructs a new background remover module
          * @param _xrSessionManager the session manager for this module
          * @param _options read-only options to be used in this module
@@ -123369,6 +123457,9 @@ declare module BABYLON {
      * Class used to host texture specific utilities
      */
     export class BRDFTextureTools {
+        /**
+         * Prevents texture cache collision
+         */
         private static _instanceNumber;
         /**
          * Gets a default environment BRDF for MS-BRDF Height Correlated BRDF

File diff suppressed because it is too large
+ 8 - 8
dist/preview release/viewer/babylon.viewer.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -34,13 +34,20 @@
 
 ### Materials
 - Added the `roughness` and `albedoScaling` parameters to PBR sheen ([Popov72](https://github.com/Popov72))
+- Updated the energy conservation factor for the clear coat layer in PBR materials ([Popov72](https://github.com/Popov72))
+- Added the `transparencyMode` property to the `StandardMaterial` class ([Popov72](https://github.com/Popov72))
+
+### WebXR
+- Added optional ray and mesh selection predicates to `WebXRControllerPointerSelection` ([Exolun](https://github.com/Exolun))
 
 ## Bugs
 
 - Fix infinite loop in `GlowLayer.unReferenceMeshFromUsingItsOwnMaterial` ([Popov72](https://github.com/Popov72)
+- Fix picking issue in the Solid Particle System when MultiMaterial is enabled ([jerome](https://github.com/jbousquie))
 - `QuadraticErrorSimplification` was not exported ([RaananW](https://github.com/Raananw)
 - Fix NME Frames bug where collapsing and moving a frame removed the nodes inside ([Kyle Belfort](https://github.com/belfortk)
 - Fix moving / disappearing controls when freezing/unfreezing the ScrollViewer ([Popov72](https://github.com/Popov72)
 - Fix: when using instances, master mesh (if displayed) does not have correct instance buffer values ([Popov72](https://github.com/Popov72)
+- Fix improper baking of transformed textures in `KHR_texture_transform` serializer. ([drigax](https://github.com/Drigax))
 
 ## Breaking changes

+ 30 - 19
inspector/src/components/actionTabs/lines/optionsLineComponent.tsx

@@ -3,29 +3,40 @@ import * as React from "react";
 import { Observable } from "babylonjs/Misc/observable";
 import { PropertyChangedEvent } from "../../propertyChangedEvent";
 
+export const Null_Value = Number.MAX_SAFE_INTEGER;
+
 class ListLineOption {
     public label: string;
     public value: number;
 }
 
 interface IOptionsLineComponentProps {
-    label: string,
-    target: any,
-    propertyName: string,
-    options: ListLineOption[],
-    noDirectUpdate?: boolean,
-    onSelect?: (value: number) => void,
-    extractValue?: () => number,
-    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+    label: string;
+    target: any;
+    propertyName: string;
+    options: ListLineOption[];
+    noDirectUpdate?: boolean;
+    onSelect?: (value: number) => void;
+    extractValue?: () => number;
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    allowNullValue?: boolean;
 }
 
 export class OptionsLineComponent extends React.Component<IOptionsLineComponentProps, { value: number }> {
     private _localChange = false;
 
+    private remapValueIn(value: number | null): number {
+        return this.props.allowNullValue && value === null ? Null_Value : value!;
+    }
+
+    private remapValueOut(value: number): number | null {
+        return this.props.allowNullValue && value === Null_Value ? null : value;
+    }
+
     constructor(props: IOptionsLineComponentProps) {
         super(props);
 
-        this.state = { value: this.props.extractValue ? this.props.extractValue() : props.target[props.propertyName] };
+        this.state = { value: this.remapValueIn(this.props.extractValue ? this.props.extractValue() : props.target[props.propertyName]) };
     }
 
     shouldComponentUpdate(nextProps: IOptionsLineComponentProps, nextState: { value: number }) {
@@ -34,7 +45,7 @@ export class OptionsLineComponent extends React.Component<IOptionsLineComponentP
             return true;
         }
 
-        const newValue = nextProps.extractValue ? nextProps.extractValue() : nextProps.target[nextProps.propertyName];
+        let newValue = this.remapValueIn(nextProps.extractValue ? nextProps.extractValue() : nextProps.target[nextProps.propertyName]);
         if (newValue != null && newValue !== nextState.value) {
             nextState.value = newValue;
             return true;
@@ -51,7 +62,8 @@ export class OptionsLineComponent extends React.Component<IOptionsLineComponentP
             object: this.props.target,
             property: this.props.propertyName,
             value: newValue,
-            initialValue: previousValue
+            initialValue: previousValue,
+            allowNullValue: this.props.allowNullValue,
         });
     }
 
@@ -59,21 +71,20 @@ export class OptionsLineComponent extends React.Component<IOptionsLineComponentP
         const value = parseInt(valueString);
         this._localChange = true;
 
-        const store = this.props.extractValue ? this.props.extractValue() : this.props.target[this.props.propertyName]
+        const store = this.props.extractValue ? this.props.extractValue() : this.props.target[this.props.propertyName];
 
         if (!this.props.noDirectUpdate) {
-            this.props.target[this.props.propertyName] = value;
+            this.props.target[this.props.propertyName] = this.remapValueOut(value);
         }
         this.setState({ value: value });
-        
+
         if (this.props.onSelect) {
             this.props.onSelect(value);
         }
 
-        const newValue = this.props.extractValue ? this.props.extractValue() : this.props.target[this.props.propertyName]
+        const newValue = this.props.extractValue ? this.props.extractValue() : this.props.target[this.props.propertyName];
 
         this.raiseOnPropertyChanged(newValue, store);
-
     }
 
     render() {
@@ -84,12 +95,12 @@ export class OptionsLineComponent extends React.Component<IOptionsLineComponentP
 
                 </div>
                 <div className="options">
-                    <select onChange={evt => this.updateValue(evt.target.value)} value={this.state.value ?? ""}>
+                    <select onChange={(evt) => this.updateValue(evt.target.value)} value={this.state.value ?? ""}>
                         {
-                            this.props.options.map(option => {
+                            this.props.options.map((option) => {
                                 return (
                                     <option key={option.label} value={option.value}>{option.label}</option>
-                                )
+                                );
                             })
                         }
                     </select>

+ 11 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/materials/commonMaterialPropertyGridComponent.tsx

@@ -11,7 +11,7 @@ import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
 import { SliderLineComponent } from "../../../lines/sliderLineComponent";
 import { LineContainerComponent } from "../../../lineContainerComponent";
 import { TextLineComponent } from "../../../lines/textLineComponent";
-import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
+import { OptionsLineComponent, Null_Value } from "../../../lines/optionsLineComponent";
 import { LockObject } from "../lockObject";
 import { GlobalState } from '../../../../globalState';
 import { CustomPropertyGridComponent } from '../customPropertyGridComponent';
@@ -40,6 +40,7 @@ export class CommonMaterialPropertyGridComponent extends React.Component<ICommon
         ];
 
         var transparencyModeOptions = [
+            { label: "<Not Defined>", value: Null_Value },
             { label: "Opaque", value: PBRMaterial.PBRMATERIAL_OPAQUE },
             { label: "Alpha test", value: PBRMaterial.PBRMATERIAL_ALPHATEST },
             { label: "Alpha blend", value: PBRMaterial.PBRMATERIAL_ALPHABLEND },
@@ -96,9 +97,17 @@ export class CommonMaterialPropertyGridComponent extends React.Component<ICommon
                     <SliderLineComponent label="Alpha" target={material} propertyName="alpha" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     {
                         (material as any).transparencyMode !== undefined &&
-                        <OptionsLineComponent label="Transparency mode" options={transparencyModeOptions} target={material} propertyName="transparencyMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={(value) => this.setState({ transparencyMode: value })} />
+                        <OptionsLineComponent allowNullValue={true} label="Transparency mode" options={transparencyModeOptions} target={material} propertyName="transparencyMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={(value) => this.setState({ transparencyMode: value })} />
                     }
                     <OptionsLineComponent label="Alpha mode" options={alphaModeOptions} target={material} propertyName="alphaMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={(value) => this.setState({ alphaMode: value })} />
+                    {
+                        (material as any).useAlphaFromDiffuseTexture !== undefined &&
+                        <CheckBoxLineComponent label="Use alpha from diffuse texture" target={material} propertyName="useAlphaFromDiffuseTexture" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        (material as any).useAlphaFromAlbedoTexture !== undefined &&
+                        <CheckBoxLineComponent label="Use alpha from albedo texture" target={material} propertyName="useAlphaFromAlbedoTexture" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
                     <CheckBoxLineComponent label="Separate culling pass" target={material} propertyName="separateCullingPass" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
             </div>

+ 1 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/materials/standardMaterialPropertyGridComponent.tsx

@@ -62,6 +62,7 @@ export class StandardMaterialPropertyGridComponent extends React.Component<IStan
                     <SliderLineComponent label="Specular power" target={material} propertyName="specularPower" minimum={0} maximum={128} step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Color3LineComponent label="Emissive" target={material} propertyName="emissiveColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Color3LineComponent label="Ambient" target={material} propertyName="ambientColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Use specular over alpha" target={material} propertyName="useSpecularOverAlpha" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="LEVELS" closed={true}>
                     {

+ 1 - 0
inspector/src/components/propertyChangedEvent.ts

@@ -3,4 +3,5 @@ export class PropertyChangedEvent {
     public property: string;
     public value: any;
     public initialValue: any;
+    public allowNullValue?: boolean;
 }

+ 19 - 17
inspector/src/components/replayRecorder.ts

@@ -53,24 +53,26 @@ export class ReplayRecorder {
 
         let value = event.value;
 
-        if (value.w !== undefined) { // Quaternion
-            value = `new BABYLON.Quaternion(${value.x}, ${value.y}, ${value.z}, ${value.w})`;
-        } else if (value.z !== undefined) { // Vector3
-            value = `new BABYLON.Vector3(${value.x}, ${value.y}, ${value.z})`;
-        } else if (value.y !== undefined) { // Vector2
-            value = `new BABYLON.Vector2(${value.x}, ${value.y})`;
-        } else if (value.a !== undefined) { // Color4
-            value = `new BABYLON.Color4(${value.r}, ${value.g}, ${value.b}, ${value.a})`;
-            if (event.object._isLinearColor) {
-                value += '.toLinearSpace()';
+        if (!event.allowNullValue || event.allowNullValue && value !== null) {
+            if (value.w !== undefined) { // Quaternion
+                value = `new BABYLON.Quaternion(${value.x}, ${value.y}, ${value.z}, ${value.w})`;
+            } else if (value.z !== undefined) { // Vector3
+                value = `new BABYLON.Vector3(${value.x}, ${value.y}, ${value.z})`;
+            } else if (value.y !== undefined) { // Vector2
+                value = `new BABYLON.Vector2(${value.x}, ${value.y})`;
+            } else if (value.a !== undefined) { // Color4
+                value = `new BABYLON.Color4(${value.r}, ${value.g}, ${value.b}, ${value.a})`;
+                if (event.object._isLinearColor) {
+                    value += '.toLinearSpace()';
+                }
+            } else if (value.b !== undefined) { // Color3
+                value = `new BABYLON.Color3(${value.r}, ${value.g}, ${value.b})`;
+                if (event.object._isLinearColor) {
+                    value += '.toLinearSpace()';
+                }
+            } else if (value.getClassName) {
+                value = this._getIndirectData(value);
             }
-        } else if (value.b !== undefined) { // Color3
-            value = `new BABYLON.Color3(${value.r}, ${value.g}, ${value.b})`;
-            if (event.object._isLinearColor) {
-                value += '.toLinearSpace()';
-            }
-        } else if (value.getClassName) {
-            value = this._getIndirectData(value);
         }
 
         let target = this._getIndirectData(event.object);

+ 10 - 19
serializers/src/glTF/2.0/Extensions/KHR_texture_transform.ts

@@ -99,27 +99,18 @@ export class KHR_texture_transform implements IGLTFExporterExtensionV2 {
                 return;
             }
 
-            let transformIsRequired = false;
-
-            if (babylonTexture.uOffset !== 0 || babylonTexture.vOffset !== 0) {
-                transformIsRequired = true;
-            }
-
-            if (babylonTexture.uScale !== 1 || babylonTexture.vScale !== 1) {
-                transformIsRequired = true;
-            }
-
-            if (babylonTexture.wAng !== 0) {
-                transformIsRequired = true;
-            }
-
-            if (!transformIsRequired) {
-                resolve(babylonTexture);
-                return;
+            let bakeTextureTransform = false;
+
+            /*
+            * The KHR_texture_transform schema only supports rotation around the origin.
+            * the texture must be baked to preserve appearance.
+            * see: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform#gltf-schema-updates
+            */
+            if ((babylonTexture.uAng !== 0 || babylonTexture.wAng !== 0 || babylonTexture.vAng !== 0) && (babylonTexture.uRotationCenter !== 0 || babylonTexture.vRotationCenter !== 0)) {
+                bakeTextureTransform = true;
             }
 
-            // Do we need to flatten the transform?
-            if (babylonTexture.uRotationCenter === 0 && babylonTexture.vRotationCenter === 0) {
+            if (!bakeTextureTransform) {
                 resolve(babylonTexture);
                 return;
             }

+ 1 - 1
src/Materials/Node/Blocks/Dual/textureBlock.ts

@@ -376,7 +376,7 @@ export class TextureBlock extends NodeMaterialBlock {
             return "";
         }
 
-        var codeString = `${this._codeVariableName}.texture = new BABYLON.Texture("${this.texture.name}");\r\n`;
+        var codeString = `${this._codeVariableName}.texture = new BABYLON.Texture("${this.texture.name}", null);\r\n`;
         codeString += `${this._codeVariableName}.texture.wrapU = ${this.texture.wrapU};\r\n`;
         codeString += `${this._codeVariableName}.texture.wrapV = ${this.texture.wrapV};\r\n`;
         codeString += `${this._codeVariableName}.texture.uAng = ${this.texture.uAng};\r\n`;

+ 5 - 53
src/Materials/PBR/pbrBaseMaterial.ts

@@ -269,23 +269,23 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     /**
      * PBRMaterialTransparencyMode: No transparency mode, Alpha channel is not use.
      */
-    public static readonly PBRMATERIAL_OPAQUE = 0;
+    public static readonly PBRMATERIAL_OPAQUE = Material.MATERIAL_OPAQUE;
 
     /**
      * PBRMaterialTransparencyMode: Alpha Test mode, pixel are discarded below a certain threshold defined by the alpha cutoff value.
      */
-    public static readonly PBRMATERIAL_ALPHATEST = 1;
+    public static readonly PBRMATERIAL_ALPHATEST = Material.MATERIAL_ALPHATEST;
 
     /**
      * PBRMaterialTransparencyMode: Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
      */
-    public static readonly PBRMATERIAL_ALPHABLEND = 2;
+    public static readonly PBRMATERIAL_ALPHABLEND = Material.MATERIAL_ALPHABLEND;
 
     /**
      * PBRMaterialTransparencyMode: Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
      * They are also discarded below the alpha cutoff threshold to improve performances.
      */
-    public static readonly PBRMATERIAL_ALPHATESTANDBLEND = 3;
+    public static readonly PBRMATERIAL_ALPHATESTANDBLEND = Material.MATERIAL_ALPHATESTANDBLEND;
 
     /**
      * Defines the default value of how much AO map is occluding the analytical lights
@@ -608,11 +608,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     protected _useLinearAlphaFresnel = false;
 
     /**
-     * The transparency mode of the material.
-     */
-    protected _transparencyMode: Nullable<number> = null;
-
-    /**
      * Specifies the environment BRDF texture used to comput the scale and offset roughness values
      * from cos thetav and roughness:
      * http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
@@ -818,40 +813,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     }
 
     /**
-     * Gets the current transparency mode.
-     */
-    @serialize()
-    public get transparencyMode(): Nullable<number> {
-        return this._transparencyMode;
-    }
-
-    /**
-     * Sets the transparency mode of the material.
-     *
-     * | Value | Type                                | Description |
-     * | ----- | ----------------------------------- | ----------- |
-     * | 0     | OPAQUE                              |             |
-     * | 1     | ALPHATEST                           |             |
-     * | 2     | ALPHABLEND                          |             |
-     * | 3     | ALPHATESTANDBLEND                   |             |
-     *
-     */
-    public set transparencyMode(value: Nullable<number>) {
-        if (this._transparencyMode === value) {
-            return;
-        }
-
-        this._transparencyMode = value;
-
-        this._forceAlphaTest = (value === PBRBaseMaterial.PBRMATERIAL_ALPHATESTANDBLEND);
-
-        this._markAllSubMeshesAsTexturesAndMiscDirty();
-    }
-
-    /**
      * Returns true if alpha blending should be disabled.
      */
-    private get _disableAlphaBlending(): boolean {
+    protected get _disableAlphaBlending(): boolean {
         return (this.subSurface.disableAlphaBlending ||
             this._transparencyMode === PBRBaseMaterial.PBRMATERIAL_OPAQUE ||
             this._transparencyMode === PBRBaseMaterial.PBRMATERIAL_ALPHATEST);
@@ -869,18 +833,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     }
 
     /**
-     * Specifies if the mesh will require alpha blending.
-     * @param mesh - BJS mesh.
-     */
-    public needAlphaBlendingForMesh(mesh: AbstractMesh): boolean {
-        if (this._disableAlphaBlending && mesh.visibility >= 1.0) {
-            return false;
-        }
-
-        return super.needAlphaBlendingForMesh(mesh);
-    }
-
-    /**
      * Specifies whether or not this material should be rendered in alpha test mode.
      */
     public needAlphaTesting(): boolean {

+ 92 - 10
src/Materials/material.ts

@@ -123,6 +123,27 @@ export class Material implements IAnimatable {
     public static readonly AllDirtyFlag = Constants.MATERIAL_AllDirtyFlag;
 
     /**
+     * MaterialTransparencyMode: No transparency mode, Alpha channel is not use.
+     */
+    public static readonly MATERIAL_OPAQUE = 0;
+
+    /**
+     * MaterialTransparencyMode: Alpha Test mode, pixel are discarded below a certain threshold defined by the alpha cutoff value.
+     */
+    public static readonly MATERIAL_ALPHATEST = 1;
+
+    /**
+     * MaterialTransparencyMode: Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
+     */
+    public static readonly MATERIAL_ALPHABLEND = 2;
+
+    /**
+     * MaterialTransparencyMode: Pixels are blended (according to the alpha mode) with the already drawn pixels in the current frame buffer.
+     * They are also discarded below the alpha cutoff threshold to improve performances.
+     */
+    public static readonly MATERIAL_ALPHATESTANDBLEND = 3;
+
+    /**
      * The ID of the material
      */
     @serialize()
@@ -668,10 +689,63 @@ export class Material implements IAnimatable {
     }
 
     /**
-     * Specifies if the material will require alpha blending
+     * Enforces alpha test in opaque or blend mode in order to improve the performances of some situations.
+     */
+    protected _forceAlphaTest = false;
+
+    /**
+     * The transparency mode of the material.
+     */
+    protected _transparencyMode: Nullable<number> = null;
+
+    /**
+     * Gets the current transparency mode.
+     */
+    @serialize()
+    public get transparencyMode(): Nullable<number> {
+        return this._transparencyMode;
+    }
+
+    /**
+     * Sets the transparency mode of the material.
+     *
+     * | Value | Type                                | Description |
+     * | ----- | ----------------------------------- | ----------- |
+     * | 0     | OPAQUE                              |             |
+     * | 1     | ALPHATEST                           |             |
+     * | 2     | ALPHABLEND                          |             |
+     * | 3     | ALPHATESTANDBLEND                   |             |
+     *
+     */
+    public set transparencyMode(value: Nullable<number>) {
+        if (this._transparencyMode === value) {
+            return;
+        }
+
+        this._transparencyMode = value;
+
+        this._forceAlphaTest = (value === Material.MATERIAL_ALPHATESTANDBLEND);
+
+        this._markAllSubMeshesAsTexturesAndMiscDirty();
+    }
+
+    /**
+     * Returns true if alpha blending should be disabled.
+     */
+    protected get _disableAlphaBlending(): boolean {
+        return (this._transparencyMode === Material.MATERIAL_OPAQUE ||
+                this._transparencyMode === Material.MATERIAL_ALPHATEST);
+    }
+
+    /**
+     * Specifies whether or not this material should be rendered in alpha blend mode.
      * @returns a boolean specifying if alpha blending is needed
      */
     public needAlphaBlending(): boolean {
+        if (this._disableAlphaBlending) {
+            return false;
+        }
+
         return (this.alpha < 1.0);
     }
 
@@ -681,18 +755,34 @@ export class Material implements IAnimatable {
      * @returns a boolean specifying if alpha blending is needed for the mesh
      */
     public needAlphaBlendingForMesh(mesh: AbstractMesh): boolean {
+        if (this._disableAlphaBlending && mesh.visibility >= 1.0) {
+            return false;
+        }
+
         return this.needAlphaBlending() || (mesh.visibility < 1.0) || mesh.hasVertexAlpha;
     }
 
     /**
-     * Specifies if this material should be rendered in alpha test mode
+     * Specifies whether or not this material should be rendered in alpha test mode.
      * @returns a boolean specifying if an alpha test is needed.
      */
     public needAlphaTesting(): boolean {
+        if (this._forceAlphaTest) {
+            return true;
+        }
+
         return false;
     }
 
     /**
+     * Specifies if material alpha testing should be turned on for the mesh
+     * @param mesh defines the mesh to check
+     */
+    protected _shouldTurnAlphaTestOn(mesh: AbstractMesh): boolean {
+        return (!this.needAlphaBlendingForMesh(mesh) && this.needAlphaTesting());
+    }
+
+    /**
      * Gets the texture used for the alpha test
      * @returns the texture to use for alpha testing
      */
@@ -794,14 +884,6 @@ export class Material implements IAnimatable {
     }
 
     /**
-     * Specifies if material alpha testing should be turned on for the mesh
-     * @param mesh defines the mesh to check
-     */
-    protected _shouldTurnAlphaTestOn(mesh: AbstractMesh): boolean {
-        return (!this.needAlphaBlendingForMesh(mesh) && this.needAlphaTesting());
-    }
-
-    /**
      * Processes to execute after binding the material to a mesh
      * @param mesh defines the rendered mesh
      */

+ 19 - 4
src/Materials/standardMaterial.ts

@@ -15,6 +15,7 @@ import { Mesh } from "../Meshes/mesh";
 import { ImageProcessingConfiguration, IImageProcessingConfigurationDefines } from "./imageProcessingConfiguration";
 import { ColorCurves } from "./colorCurves";
 import { FresnelParameters } from "./fresnelParameters";
+import { Material } from "../Materials/material";
 import { MaterialDefines } from "../Materials/materialDefines";
 import { PushMaterial } from "./pushMaterial";
 import { MaterialHelper } from "./materialHelper";
@@ -114,6 +115,8 @@ export class StandardMaterialDefines extends MaterialDefines implements IImagePr
     public NUM_MORPH_INFLUENCERS = 0;
     public NONUNIFORMSCALING = false; // https://playground.babylonjs.com#V6DWIH
     public PREMULTIPLYALPHA = false; // https://playground.babylonjs.com#LNVJJ7
+    public ALPHATEST_AFTERALLALPHACOMPUTATIONS = false;
+    public ALPHABLEND = true;
 
     public IMAGEPROCESSING = false;
     public VIGNETTE = false;
@@ -286,7 +289,7 @@ export class StandardMaterial extends PushMaterial {
     /**
      * Does the transparency come from the diffuse texture alpha channel.
      */
-    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    @expandToProperty("_markAllSubMeshesAsTexturesAndMiscDirty")
     public useAlphaFromDiffuseTexture: boolean;
 
     @serialize("useEmissiveAsIllumination")
@@ -744,6 +747,10 @@ export class StandardMaterial extends PushMaterial {
      * @returns a boolean specifying if alpha blending is needed
      */
     public needAlphaBlending(): boolean {
+        if (this._disableAlphaBlending) {
+            return false;
+        }
+
         return (this.alpha < 1.0) || (this._opacityTexture != null) || this._shouldUseAlphaFromDiffuseTexture() || this._opacityFresnelParameters && this._opacityFresnelParameters.isEnabled;
     }
 
@@ -752,11 +759,15 @@ export class StandardMaterial extends PushMaterial {
      * @returns a boolean specifying if an alpha test is needed.
      */
     public needAlphaTesting(): boolean {
-        return this._diffuseTexture != null && this._diffuseTexture.hasAlpha;
+        if (this._forceAlphaTest) {
+            return true;
+        }
+
+        return this._diffuseTexture != null && this._diffuseTexture.hasAlpha && (this._transparencyMode == null || this._transparencyMode === Material.MATERIAL_ALPHATEST);
     }
 
     protected _shouldUseAlphaFromDiffuseTexture(): boolean {
-        return this._diffuseTexture != null && this._diffuseTexture.hasAlpha && this._useAlphaFromDiffuseTexture;
+        return this._diffuseTexture != null && this._diffuseTexture.hasAlpha && this._useAlphaFromDiffuseTexture && this._transparencyMode !== Material.MATERIAL_OPAQUE;
     }
 
     /**
@@ -971,6 +982,10 @@ export class StandardMaterial extends PushMaterial {
             defines.SPECULAROVERALPHA = this._useSpecularOverAlpha;
 
             defines.PREMULTIPLYALPHA = (this.alphaMode === Constants.ALPHA_PREMULTIPLIED || this.alphaMode === Constants.ALPHA_PREMULTIPLIED_PORTERDUFF);
+
+            defines.ALPHATEST_AFTERALLALPHACOMPUTATIONS = this.transparencyMode !== null;
+
+            defines.ALPHABLEND = this.transparencyMode === null || this.needAlphaBlendingForMesh(mesh); // check on null for backward compatibility
         }
 
         if (defines._areImageProcessingDirty && this._imageProcessingConfiguration) {
@@ -1012,7 +1027,7 @@ export class StandardMaterial extends PushMaterial {
         }
 
         // Misc.
-        MaterialHelper.PrepareDefinesForMisc(mesh, scene, this._useLogarithmicDepth, this.pointsCloud, this.fogEnabled, this._shouldTurnAlphaTestOn(mesh), defines);
+        MaterialHelper.PrepareDefinesForMisc(mesh, scene, this._useLogarithmicDepth, this.pointsCloud, this.fogEnabled, this._shouldTurnAlphaTestOn(mesh) || this._forceAlphaTest, defines);
 
         // Attribs
         MaterialHelper.PrepareDefinesForAttributes(mesh, defines, true, true, true);

+ 10 - 1
src/Particles/solidParticle.ts

@@ -117,6 +117,10 @@ export class SolidParticle {
      */
     public materialIndex: Nullable<number> = null;
     /**
+     * Custom object or properties.
+     */
+    public props: Nullable<any> = null;
+    /**
      * The culling strategy to use to check whether the solid particle must be culled or not when using isInFrustum().
      * The possible values are :
      * - AbstractMesh.CULLINGSTRATEGY_STANDARD
@@ -358,6 +362,10 @@ export class ModelShape {
  */
 export class DepthSortedParticle {
     /**
+     * Particle index
+     */
+    public idx: number = 0;
+    /**
      * Index of the particle in the "indices" array
      */
     public ind: number = 0;
@@ -378,7 +386,8 @@ export class DepthSortedParticle {
      * Creates a new sorted particle
      * @param materialIndex
      */
-    constructor(ind: number, indLength: number, materialIndex: number) {
+    constructor(idx: number, ind: number, indLength: number, materialIndex: number) {
+        this.idx = idx;
         this.ind = ind;
         this.indicesLength = indLength;
         this.materialIndex = materialIndex;

+ 69 - 2
src/Particles/solidParticleSystem.ts

@@ -15,6 +15,7 @@ import { SubMesh } from '../Meshes/subMesh';
 import { Material } from '../Materials/material';
 import { StandardMaterial } from '../Materials/standardMaterial';
 import { MultiMaterial } from '../Materials/multiMaterial';
+import { PickingInfo } from '../Collisions/pickingInfo';
 
 /**
  * The SPS is a single updatable mesh. The solid particles are simply separate parts or faces fo this big mesh.
@@ -66,10 +67,25 @@ export class SolidParticleSystem implements IDisposable {
      * Each element of this array is an object `{idx: int, faceId: int}`.
      * `idx` is the picked particle index in the `SPS.particles` array
      * `faceId` is the picked face index counted within this particle.
+     * This array is the first element of the pickedBySubMesh array : sps.pickBySubMesh[0].
+     * It's not pertinent to use it when using a SPS with the support for MultiMaterial enabled.
+     * Use the method SPS.pickedParticle(pickingInfo) instead.
      * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
      */
     public pickedParticles: { idx: number; faceId: number }[];
     /**
+     * This array is populated when the SPS is set as 'pickable'
+     * Each key of this array is a submesh index.
+     * Each element of this array is a second array defined like this :
+     * Each key of this second array is a `faceId` value that you can get from a pickResult object.
+     * Each element of this second array is an object `{idx: int, faceId: int}`.
+     * `idx` is the picked particle index in the `SPS.particles` array
+     * `faceId` is the picked face index counted within this particle.
+     * It's better to use the method SPS.pickedParticle(pickingInfo) rather than using directly this array.
+     * Please read : http://doc.babylonjs.com/how_to/Solid_Particle_System#pickable-particles
+     */
+    public pickedBySubMesh: { idx: number; faceId: number}[][];
+    /**
      * This array is populated when `enableDepthSort` is set to true.
      * Each element of this array is an instance of the class DepthSortedParticle.
      */
@@ -169,7 +185,8 @@ export class SolidParticleSystem implements IDisposable {
             this._updatable = true;
         }
         if (this._pickable) {
-            this.pickedParticles = [];
+            this.pickedBySubMesh = [[]];
+            this.pickedParticles = this.pickedBySubMesh[0];
         }
         if (this._depthSort || this._multimaterialEnabled) {
             this.depthSortedParticles = [];
@@ -430,6 +447,7 @@ export class SolidParticleSystem implements IDisposable {
         copy.uvs.copyFromFloats(0.0, 0.0, 1.0, 1.0);
         copy.color = null;
         copy.translateFromPivot = false;
+        copy.shapeId = 0;
         copy.materialIndex = null;
     }
 
@@ -464,6 +482,7 @@ export class SolidParticleSystem implements IDisposable {
         const storeApart = (options && options.storage) ? true : false;
         copy.idx = idx;
         copy.idxInShape = idxInShape;
+        copy.shapeId = model.shapeID;
         if (this._useModelMaterial) {
             var materialId = model._material!.uniqueId;
             const materialIndexesById = this._materialIndexesById;
@@ -563,7 +582,7 @@ export class SolidParticleSystem implements IDisposable {
 
         if (this._depthSort || this._multimaterialEnabled) {
             var matIndex = (copy.materialIndex !== null) ? copy.materialIndex : 0;
-            this.depthSortedParticles.push(new DepthSortedParticle(ind, meshInd.length, matIndex));
+            this.depthSortedParticles.push(new DepthSortedParticle(idx, ind, meshInd.length, matIndex));
         }
 
         return copy;
@@ -992,6 +1011,7 @@ export class SolidParticleSystem implements IDisposable {
             // camera-particle distance for depth sorting
             if (this._depthSort && this._depthSortParticles) {
                 var dsp = this.depthSortedParticles[p];
+                dsp.idx = particle.idx;
                 dsp.ind = particle._ind;
                 dsp.indicesLength = particle._model._indicesLength;
                 dsp.sqDistance = Vector3.DistanceSquared(particle.position, camInvertedPosition);
@@ -1291,6 +1311,29 @@ export class SolidParticleSystem implements IDisposable {
         (<any>this._uvs32) = null;
         (<any>this._colors32) = null;
         (<any>this.pickedParticles) = null;
+        (<any>this.pickedBySubMesh) = null;
+        (<any>this._materials) = null;
+        (<any>this._materialIndexes) = null;
+        (<any>this._indicesByMaterial) = null;
+        (<any>this._idxOfId) = null;
+    }
+    /** Returns an object {idx: numbern faceId: number} for the picked particle from the passed pickingInfo object.
+     * idx is the particle index in the SPS
+     * faceId is the picked face index counted within this particle.
+     * Returns null if the pickInfo can't identify a picked particle.
+     * @param pickingInfo (PickingInfo object)
+     * @returns {idx: number, faceId: number} or null
+     */
+    public pickedParticle(pickingInfo: PickingInfo): Nullable<{idx: number, faceId: number}> {
+        if (pickingInfo.hit) {
+            const subMesh = pickingInfo.subMeshId;
+            const faceId = pickingInfo.faceId;
+            const picked = this.pickedBySubMesh;
+            if (picked[subMesh] && picked[subMesh][faceId]) {
+                return picked[subMesh][faceId];
+            }
+        }
+        return null;
     }
 
     /**
@@ -1367,6 +1410,7 @@ export class SolidParticleSystem implements IDisposable {
                 sortedPart.materialIndex = part.materialIndex;
                 sortedPart.ind = part._ind;
                 sortedPart.indicesLength = part._model._indicesLength;
+                sortedPart.idx = part.idx;
             }
         }
         this._sortParticlesByMaterial();
@@ -1401,9 +1445,16 @@ export class SolidParticleSystem implements IDisposable {
         const length = depthSortedParticles.length;
         const indices32 = this._indices32;
         const indices = this._indices;
+
+        let subMeshIndex = 0;
+        let subMeshFaceId = 0;
         let sid = 0;
         let lastMatIndex = depthSortedParticles[0].materialIndex;
         materialIndexes.push(lastMatIndex);
+        if (this._pickable) {
+            this.pickedBySubMesh = [[]];
+            this.pickedParticles = this.pickedBySubMesh[0];
+        }
         for (let sorted = 0; sorted < length; sorted++) {
             let sortedPart = depthSortedParticles[sorted];
             let lind = sortedPart.indicesLength;
@@ -1412,12 +1463,28 @@ export class SolidParticleSystem implements IDisposable {
                 lastMatIndex = sortedPart.materialIndex;
                 indicesByMaterial.push(sid);
                 materialIndexes.push(lastMatIndex);
+                if (this._pickable) {
+                    subMeshIndex++;
+                    this.pickedBySubMesh[subMeshIndex] = [];
+                    subMeshFaceId = 0;
+                }
             }
+            let faceId = 0;
             for (let i = 0; i < lind; i++) {
                 indices32[sid] = indices[sind + i];
+                if (this._pickable) {
+                    let f = i % 3;
+                    if (f == 0) {
+                        const pickedData = {idx: sortedPart.idx, faceId: faceId};
+                        this.pickedBySubMesh[subMeshIndex][subMeshFaceId] = pickedData;
+                        subMeshFaceId++;
+                        faceId++;
+                    }
+                }
                 sid++;
             }
         }
+
         indicesByMaterial.push(indices32.length);   // add the last number to ease the indices start/count values for subMeshes creation
         if (this._updatable) {
             this.mesh.updateIndices(indices32);

+ 12 - 1
src/Shaders/default.fragment.fx

@@ -195,7 +195,7 @@ void main(void) {
 #ifdef DIFFUSE
 	baseColor = texture2D(diffuseSampler, vDiffuseUV + uvOffset);
 
-	#ifdef ALPHATEST
+	#if defined(ALPHATEST) && !defined(ALPHATEST_AFTERALLALPHACOMPUTATIONS)
 		if (baseColor.a < alphaCutOff)
 			discard;
 	#endif
@@ -363,6 +363,17 @@ vec3 reflectionColor = vec3(0., 0., 0.);
 	alpha += opacityParts.x * (1.0 - opacityFresnelTerm) + opacityFresnelTerm * opacityParts.y;
 #endif
 
+#ifdef ALPHATEST
+    #ifdef ALPHATEST_AFTERALLALPHACOMPUTATIONS
+        if (alpha < alphaCutOff)
+            discard;
+    #endif
+    #ifndef ALPHABLEND
+        // Prevent to blend with the canvas.
+        alpha = 1.0;
+    #endif
+#endif
+
 	// Emissive
 	vec3 emissiveColor = vEmissiveColor;
 #ifdef EMISSIVE

+ 5 - 1
src/Shaders/pbr.fragment.fx

@@ -983,6 +983,10 @@ void main(void) {
         #endif
 
         specularEnvironmentReflectance *= conservationFactor;
+
+        #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION)
+            vec3 energyConservationFactorClearCoat = getEnergyConservationFactor(specularEnvironmentR0, environmentClearCoatBrdf);
+        #endif
     #endif
 
     // _____________________________ Transmittance + Tint ________________________________
@@ -1144,7 +1148,7 @@ void main(void) {
         // Full value needed for alpha.
         vec3 finalClearCoatScaled = finalClearCoat * vLightingIntensity.x * vLightingIntensity.w;
         #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION)
-            finalClearCoatScaled *= energyConservationFactor;
+            finalClearCoatScaled *= energyConservationFactorClearCoat;
         #endif
 
     // ____________________________ Clear Coat Radiance _______________________________

+ 8 - 1
src/XR/features/WebXRControllerPointerSelection.ts

@@ -163,6 +163,12 @@ export class WebXRControllerPointerSelection extends WebXRAbstractFeature {
     public selectionMeshPickedColor: Color3 = new Color3(0.3, 0.3, 1.0);
 
     /**
+     * Optional filter to be used for ray selection.  This predicate shares behavior with
+     * scene.pointerMovePredicate which takes priority if it is also assigned.
+     */
+    public raySelectionPredicate: (mesh: AbstractMesh) => boolean;
+
+    /**
      * constructs a new background remover module
      * @param _xrSessionManager the session manager for this module
      * @param _options read-only options to be used in this module
@@ -248,7 +254,8 @@ export class WebXRControllerPointerSelection extends WebXRAbstractFeature {
 
             // Every frame check collisions/input
             controllerData.xrController.getWorldPointerRayToRef(controllerData.tmpRay);
-            controllerData.pick = this._scene.pickWithRay(controllerData.tmpRay);
+            controllerData.pick = this._scene.pickWithRay(controllerData.tmpRay,
+                this._scene.pointerMovePredicate || this.raySelectionPredicate);
 
             const pick = controllerData.pick;