David Catuhe 6 年之前
父節點
當前提交
1902a59f89

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

@@ -27,6 +27,7 @@
 
 ### Inspector
 - Added support for Euler edition only for angles (can be turned off in the new inspector settings) ([Deltakosh](https://github.com/deltakosh/))
+- Added an option to ignore backfaces for picking (can be turned on and off in the new inspector settings) ([Deltakosh](https://github.com/deltakosh/))
 - Added support for `ShadowGenerator` ([Deltakosh](https://github.com/deltakosh/))
 - Added support for scene normalization ([Deltakosh](https://github.com/deltakosh/))
 - Added support for morph targets ([Deltakosh](https://github.com/deltakosh/))

+ 5 - 1
inspector/src/components/actionTabs/tabs/settingsTabComponent.tsx

@@ -1,6 +1,7 @@
 import * as React from "react";
 import { PaneComponent, IPaneComponentProps } from "../paneComponent";
 import { CheckBoxLineComponent } from '../lines/checkBoxLineComponent';
+import { LineContainerComponent } from '../lineContainerComponent';
 
 export class SettingsTabComponent extends PaneComponent {
 
@@ -13,7 +14,10 @@ export class SettingsTabComponent extends PaneComponent {
 
         return (
             <div className="pane">
-                <CheckBoxLineComponent label="Only display Euler values" target={state} propertyName="onlyUseEulers" />
+                <LineContainerComponent globalState={this.props.globalState} title="UI">
+                    <CheckBoxLineComponent label="Only display Euler values" target={state} propertyName="onlyUseEulers" />
+                    <CheckBoxLineComponent label="Ignore backfaces when picking" target={state} propertyName="ignoreBackfacesForPicking" />
+                </LineContainerComponent>
             </div>
         );
     }

+ 16 - 0
inspector/src/components/globalState.ts

@@ -46,6 +46,22 @@ export class GlobalState {
         Tools.StoreLocalBooleanSettings("settings_onlyUseEulers", value);
     }
 
+    private _ignoreBackfacesForPicking: Nullable<boolean> = null;
+
+    public get ignoreBackfacesForPicking(): boolean {
+        if (this._ignoreBackfacesForPicking === null) {
+            this._ignoreBackfacesForPicking = Tools.ReadLocalBooleanSettings("settings_ignoreBackfacesForPicking", false);
+        }
+
+        return this._ignoreBackfacesForPicking!;
+    }
+
+    public set ignoreBackfacesForPicking(value: boolean) {
+        this._ignoreBackfacesForPicking = value;
+
+        Tools.StoreLocalBooleanSettings("settings_ignoreBackfacesForPicking", value);
+    }
+
     public init(propertyChangedObservable: Observable<PropertyChangedEvent>) {
         this.onPropertyChangedObservable = propertyChangedObservable;
 

+ 54 - 36
inspector/src/components/sceneExplorer/entities/sceneTreeItemComponent.tsx

@@ -14,6 +14,7 @@ import { GlobalState } from "../../globalState";
 import { UtilityLayerRenderer } from "babylonjs/Rendering/utilityLayerRenderer";
 import { PropertyChangedEvent } from '../../../components/propertyChangedEvent';
 import { LightGizmo } from 'babylonjs/Gizmos/lightGizmo';
+import { Tmp, Vector3 } from 'babylonjs/Maths/math';
 
 interface ISceneTreeItemComponentProps {
     scene: Scene;
@@ -80,16 +81,16 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
                 const manager: GizmoManager = scene.reservedDataStore.gizmoManager;
 
                 const className = entity.getClassName();
-                
+
                 if (className === "TransformNode" || className.indexOf("Mesh") !== -1) {
                     manager.attachToMesh(entity);
-                }else if (className.indexOf("Light") !== -1) {
+                } else if (className.indexOf("Light") !== -1) {
                     if (!this._selectedEntity.reservedDataStore || !this._selectedEntity.reservedDataStore.lightGizmo) {
                         this.props.globalState.enableLightGizmo(this._selectedEntity, true);
                         this.forceUpdate();
                     }
                     manager.attachToMesh(this._selectedEntity.reservedDataStore.lightGizmo.attachedMesh);
-                }else{
+                } else {
                     manager.attachToMesh(null);
                 }
             }
@@ -133,14 +134,31 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
         if (!this.state.isInPickingMode) {
             this._onPointerObserver = scene.onPointerObservable.add(() => {
                 const pickPosition = scene.unTranslatedPointer;
-                const pickInfo = scene.pick(pickPosition.x, pickPosition.y, (mesh) => mesh.isEnabled() && mesh.isVisible && mesh.getTotalVertices() > 0);
-                
+                const pickInfo = scene.pick(pickPosition.x, pickPosition.y, (mesh) => mesh.isEnabled() && mesh.isVisible && mesh.getTotalVertices() > 0, false,
+                    undefined, (p0, p1, p2, ray) => {
+                        if (!this.props.globalState.ignoreBackfacesForPicking) {
+                            return true;
+                        }
+
+                        let p0p1 = Tmp.Vector3[0];
+                        let p1p2 = Tmp.Vector3[1];
+                        let normal = Tmp.Vector3[2];
+
+                        p1.subtractToRef(p0, p0p1);
+                        p2.subtractToRef(p1, p1p2);
+
+                        normal = Vector3.Cross(p0p1, p1p2);
+
+
+                        return Vector3.Dot(normal, ray.direction) < 0;
+                    });
+
                 // Pick light gizmos first
-                if(this.props.globalState.lightGizmos.length > 0){
+                if (this.props.globalState.lightGizmos.length > 0) {
                     var gizmoScene = this.props.globalState.lightGizmos[0].gizmoLayer.utilityLayerScene;
-                    let pickInfo = gizmoScene.pick(pickPosition.x, pickPosition.y, (m:any)=>{
-                        for(var g of (this.props.globalState.lightGizmos as any)){
-                            if(g.attachedMesh == m){
+                    let pickInfo = gizmoScene.pick(pickPosition.x, pickPosition.y, (m: any) => {
+                        for (var g of (this.props.globalState.lightGizmos as any)) {
+                            if (g.attachedMesh == m) {
                                 return true;
                             }
                         }
@@ -179,7 +197,7 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
 
         const manager: GizmoManager = scene.reservedDataStore.gizmoManager;
         // Allow picking of light gizmo when a gizmo mode is selected
-        this._gizmoLayerOnPointerObserver = UtilityLayerRenderer.DefaultUtilityLayer.utilityLayerScene.onPointerObservable.add((pointerInfo)=>{
+        this._gizmoLayerOnPointerObserver = UtilityLayerRenderer.DefaultUtilityLayer.utilityLayerScene.onPointerObservable.add((pointerInfo) => {
             if (pointerInfo.type == PointerEventTypes.POINTERDOWN) {
                 if (pointerInfo.pickInfo && pointerInfo.pickInfo.pickedMesh) {
                     var node: Nullable<any> = pointerInfo.pickInfo.pickedMesh;
@@ -187,8 +205,8 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
                     while (node && node.parent != null) {
                         node = node.parent;
                     }
-                    for(var gizmo of this.props.globalState.lightGizmos){
-                        if(gizmo._rootMesh == node){
+                    for (var gizmo of this.props.globalState.lightGizmos) {
+                        if (gizmo._rootMesh == node) {
                             manager.attachToMesh(gizmo.attachedMesh);
                         }
                     }
@@ -209,13 +227,13 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
             switch (mode) {
                 case 1:
                     manager.positionGizmoEnabled = true;
-                    if(!this._posDragEnd){
+                    if (!this._posDragEnd) {
                         // Record movement for generating replay code
-                        this._posDragEnd = manager.gizmos.positionGizmo!.onDragEndObservable.add(()=>{
+                        this._posDragEnd = manager.gizmos.positionGizmo!.onDragEndObservable.add(() => {
                             if (manager.gizmos.positionGizmo && manager.gizmos.positionGizmo.attachedMesh) {
-                                var lightGizmo:Nullable<LightGizmo> =  manager.gizmos.positionGizmo.attachedMesh.reservedDataStore.lightGizmo;
-                                var obj:any = (lightGizmo && lightGizmo.light) ? lightGizmo.light : manager.gizmos.positionGizmo.attachedMesh;
-                                
+                                var lightGizmo: Nullable<LightGizmo> = manager.gizmos.positionGizmo.attachedMesh.reservedDataStore.lightGizmo;
+                                var obj: any = (lightGizmo && lightGizmo.light) ? lightGizmo.light : manager.gizmos.positionGizmo.attachedMesh;
+
                                 if (obj.position) {
                                     var e = new PropertyChangedEvent();
                                     e.object = obj
@@ -223,53 +241,53 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
                                     e.value = obj.position;
                                     this.props.globalState.onPropertyChangedObservable.notifyObservers(e)
                                 }
-                            }                            
+                            }
                         })
                     }
-                    
+
                     break;
                 case 2:
                     manager.rotationGizmoEnabled = true;
-                    if(!this._rotateDragEnd){
+                    if (!this._rotateDragEnd) {
                         // Record movement for generating replay code
-                        this._rotateDragEnd = manager.gizmos.rotationGizmo!.onDragEndObservable.add(()=>{
+                        this._rotateDragEnd = manager.gizmos.rotationGizmo!.onDragEndObservable.add(() => {
                             if (manager.gizmos.rotationGizmo && manager.gizmos.rotationGizmo.attachedMesh) {
-                                var lightGizmo:Nullable<LightGizmo> =  manager.gizmos.rotationGizmo.attachedMesh.reservedDataStore.lightGizmo;
-                                var obj:any = (lightGizmo && lightGizmo.light) ? lightGizmo.light : manager.gizmos.rotationGizmo.attachedMesh;
-                                
+                                var lightGizmo: Nullable<LightGizmo> = manager.gizmos.rotationGizmo.attachedMesh.reservedDataStore.lightGizmo;
+                                var obj: any = (lightGizmo && lightGizmo.light) ? lightGizmo.light : manager.gizmos.rotationGizmo.attachedMesh;
+
                                 if (obj.rotationQuaternion) {
                                     var e = new PropertyChangedEvent();
                                     e.object = obj;
                                     e.property = "rotationQuaternion";
                                     e.value = obj.rotationQuaternion;
                                     this.props.globalState.onPropertyChangedObservable.notifyObservers(e);
-                                } else if(obj.rotation) {
+                                } else if (obj.rotation) {
                                     var e = new PropertyChangedEvent();
                                     e.object = obj;
                                     e.property = "rotation";
                                     e.value = obj.rotation;
                                     this.props.globalState.onPropertyChangedObservable.notifyObservers(e);
-                                } else if(obj.direction) {
+                                } else if (obj.direction) {
                                     var e = new PropertyChangedEvent();
                                     e.object = obj;
                                     e.property = "direction";
                                     e.value = obj.direction;
                                     this.props.globalState.onPropertyChangedObservable.notifyObservers(e);
                                 }
-                            }                            
+                            }
                         })
                     }
 
                     break;
                 case 3:
                     manager.scaleGizmoEnabled = true;
-                    if(!this._scaleDragEnd){
+                    if (!this._scaleDragEnd) {
                         // Record movement for generating replay code
-                        this._scaleDragEnd = manager.gizmos.scaleGizmo!.onDragEndObservable.add(()=>{
+                        this._scaleDragEnd = manager.gizmos.scaleGizmo!.onDragEndObservable.add(() => {
                             if (manager.gizmos.scaleGizmo && manager.gizmos.scaleGizmo.attachedMesh) {
-                                var lightGizmo:Nullable<LightGizmo> =  manager.gizmos.scaleGizmo.attachedMesh.reservedDataStore.lightGizmo;
-                                var obj:any = (lightGizmo && lightGizmo.light) ? lightGizmo.light : manager.gizmos.scaleGizmo.attachedMesh;
-                                
+                                var lightGizmo: Nullable<LightGizmo> = manager.gizmos.scaleGizmo.attachedMesh.reservedDataStore.lightGizmo;
+                                var obj: any = (lightGizmo && lightGizmo.light) ? lightGizmo.light : manager.gizmos.scaleGizmo.attachedMesh;
+
                                 if (obj.scaling) {
                                     var e = new PropertyChangedEvent();
                                     e.object = obj;
@@ -277,7 +295,7 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
                                     e.value = obj.scaling;
                                     this.props.globalState.onPropertyChangedObservable.notifyObservers(e);
                                 }
-                            }                            
+                            }
                         })
                     }
 
@@ -295,8 +313,8 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
 
                 if (className === "TransformNode" || className.indexOf("Mesh") !== -1) {
                     manager.attachToMesh(this._selectedEntity);
-                } else if(className.indexOf("Light") !== -1){
-                    if(!this._selectedEntity.reservedDataStore || !this._selectedEntity.reservedDataStore.lightGizmo){
+                } else if (className.indexOf("Light") !== -1) {
+                    if (!this._selectedEntity.reservedDataStore || !this._selectedEntity.reservedDataStore.lightGizmo) {
                         this.props.globalState.enableLightGizmo(this._selectedEntity, true);
                         this.forceUpdate();
                     }
@@ -326,7 +344,7 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
                     </div>
                     <div className={this.state.gizmoMode === 4 ? "bounding selected icon" : "bounding icon"} onClick={() => this.setGizmoMode(4)} title="Enable/Disable bounding box mode">
                         <FontAwesomeIcon icon={faVectorSquare} />
-                    </div>                    
+                    </div>
                     <div className="separator" />
                     <div className={this.state.isInPickingMode ? "pickingMode selected icon" : "pickingMode icon"} onClick={() => this.onPickingMode()} title="Turn picking mode on/off">
                         <FontAwesomeIcon icon={faCrosshairs} />

+ 2 - 2
src/scene.ts

@@ -370,7 +370,7 @@ export class Scene extends AbstractScene implements IAnimatable {
     /**
     * An event triggered after rendering the scene for an active camera (When scene.render is called this will be called after each camera)
     */
-   public onAfterRenderCameraObservable = new Observable<Camera>();
+    public onAfterRenderCameraObservable = new Observable<Camera>();
 
     private _onAfterRenderObserver: Nullable<Observer<Scene>> = null;
     /** Sets a function to be executed after rendering this scene */
@@ -4200,7 +4200,7 @@ export class Scene extends AbstractScene implements IAnimatable {
      */
     public pick(x: number, y: number, predicate?: (mesh: AbstractMesh) => boolean,
         fastCheck?: boolean, camera?: Nullable<Camera>,
-        trianglePredicate?: (p0: Vector3, p1: Vector3, p2: Vector3) => boolean
+        trianglePredicate?: TrianglePickingPredicate
     ): Nullable<PickingInfo> {
         // Dummy info if picking as not been imported
         const pi = new PickingInfo();