@@ -406,28 +406,32 @@ internal static float MouseRayHitTest(
406406 return FaceRaycast ( mousePosition , pickerOptions , allowUnselected , selection , 0 , true ) ;
407407 }
408408
409+ static List < ( ProBuilderMesh mesh , Face face , float dist , int hash ) > s_PbHits = new List < ( ProBuilderMesh , Face , float , int ) > ( ) ;
410+
409411 static float FaceRaycast ( Vector3 mousePosition ,
410412 ScenePickerPreferences pickerOptions ,
411413 bool allowUnselected ,
412414 SceneSelection selection ,
413415 int deepClickOffset = 0 ,
414416 bool isPreview = true )
415417 {
416- GameObject pickedGo = null ;
417- ProBuilderMesh pickedPb = null ;
418- Face pickedFace = null ;
419-
420- int newHash = 0 ;
418+ GameObject candidateGo = null ;
419+ ProBuilderMesh candidatePb = null ;
420+ Face candidateFace = null ;
421+ float candidateDistance = Mathf . Infinity ;
422+ float resultDistance = Mathf . Infinity ;
421423
422424 // If any event modifiers are engaged don't cycle the deep click
423- EventModifiers em = Event . current . modifiers ;
425+ EventModifiers em = EventModifiers . None ;
426+ if ( Event . current != null )
427+ em = Event . current . modifiers ;
424428
425429 // Reset cycle if we used an event modifier previously.
426430 // Move state back to single selection.
427431 if ( ( em != EventModifiers . None ) != s_AppendModifierPreviousState )
428432 {
429433 s_AppendModifierPreviousState = ( em != EventModifiers . None ) ;
430- s_DeepSelectionPrevious = newHash ;
434+ s_DeepSelectionPrevious = 0 ;
431435 }
432436
433437 if ( isPreview || em != EventModifiers . None )
@@ -437,13 +441,11 @@ static float FaceRaycast(Vector3 mousePosition,
437441
438442 selection . Clear ( ) ;
439443
440- float distance = Mathf . Infinity ;
441-
442- for ( int i = 0 , next = 0 , pickedCount = s_OverlappingGameObjects . Count ; i < pickedCount ; i ++ )
444+ // First, find all ProBuilder meshes and their hit faces under the mouse
445+ for ( int i = 0 , pickedCount = s_OverlappingGameObjects . Count ; i < pickedCount ; i ++ )
443446 {
444447 var go = s_OverlappingGameObjects [ i ] ;
445448 var mesh = go . GetComponent < ProBuilderMesh > ( ) ;
446- Face face = null ;
447449
448450 if ( mesh != null && ( allowUnselected || MeshSelection . topInternal . Contains ( mesh ) ) )
449451 {
@@ -456,59 +458,91 @@ static float FaceRaycast(Vector3 mousePosition,
456458 Mathf . Infinity ,
457459 pickerOptions . cullMode ) )
458460 {
459- face = mesh . facesInternal [ hit . face ] ;
460- distance = Vector2 . SqrMagnitude ( ( ( Vector2 ) mousePosition ) - HandleUtility . WorldToGUIPoint ( mesh . transform . TransformPoint ( hit . point ) ) ) ;
461+ Face face = mesh . facesInternal [ hit . face ] ;
462+ float dist = Vector2 . SqrMagnitude ( ( ( Vector2 ) mousePosition ) - HandleUtility . WorldToGUIPoint ( mesh . transform . TransformPoint ( hit . point ) ) ) ;
463+ s_PbHits . Add ( ( mesh , face , dist , face . GetHashCode ( ) ) ) ;
461464 }
462465 }
466+ }
463467
464- // pb_Face doesn't define GetHashCode, meaning it falls to object.GetHashCode (reference comparison)
465- int hash = face == null ? go . GetHashCode ( ) : face . GetHashCode ( ) ;
468+ if ( s_PbHits . Count > 0 )
469+ {
470+ // Sort ProBuilder hits by distance (closest first)
471+ s_PbHits . Sort ( ( a , b ) => a . dist . CompareTo ( b . dist ) ) ;
466472
467- if ( s_DeepSelectionPrevious == hash )
468- next = ( i + ( 1 + deepClickOffset ) ) % pickedCount ;
473+ int chosenIndex = 0 ;
469474
470- if ( next == i )
475+ // Apply deep click cycling logic only if it's an actual click and a previous selection exists
476+ if ( ! isPreview && s_DeepSelectionPrevious != 0 )
471477 {
472- pickedGo = go ;
473- pickedPb = mesh ;
474- pickedFace = face ;
478+ int currentSelectionIndex = - 1 ;
479+ // Find the index of the previously selected item (if it's still in the list)
480+ for ( int i = 0 ; i < s_PbHits . Count ; i ++ )
481+ {
482+ if ( s_PbHits [ i ] . hash == s_DeepSelectionPrevious )
483+ {
484+ currentSelectionIndex = i ;
485+ break ;
486+ }
487+ }
488+
489+ if ( currentSelectionIndex != - 1 )
490+ {
491+ // Calculate the next index for cycling
492+ chosenIndex = ( currentSelectionIndex + ( 1 + deepClickOffset ) ) % s_PbHits . Count ;
493+ // Handle negative result from modulo for deepClickOffset = -1 if currentSelectionIndex is 0
494+ if ( chosenIndex < 0 ) chosenIndex += s_PbHits . Count ;
495+ }
496+ // If s_DeepSelectionPrevious was set but no matching PB hit is found in current list,
497+ // fall back to the closest one (chosenIndex remains 0)
498+ }
499+ // else for first click or if s_DeepSelectionPrevious is 0, chosenIndex remains 0 (selects closest)
475500
476- newHash = hash ;
501+ var selectedHit = s_PbHits [ chosenIndex ] ;
502+ candidateGo = selectedHit . mesh . gameObject ;
503+ candidatePb = selectedHit . mesh ;
504+ candidateFace = selectedHit . face ;
505+ candidateDistance = selectedHit . dist ;
477506
478- // a prior hash was matched, this is the next. if
479- // it's just the first iteration don't break (but do
480- // set the default).
481- if ( next != 0 )
482- break ;
507+ // Update s_DeepSelectionPrevious only for actual clicks after cycling/filtering (not hovers)
508+ if ( ! isPreview )
509+ {
510+ s_DeepSelectionPrevious = selectedHit . hash ;
511+ }
512+ }
513+ else // No ProBuilder meshes were hit, fallback to standard GameObject picking
514+ {
515+ // This means the mouse is over a non-ProBuilder GameObject, or nothing at all.
516+ // We should still allow picking of that topmost non-ProBuilder GameObject.
517+ GameObject topmostGo = HandleUtility . PickGameObject ( mousePosition , false ) ;
518+ if ( topmostGo != null )
519+ {
520+ candidateGo = topmostGo ;
521+ candidateDistance = 0f ; // Indicate a direct hit (distance not relevant for non-PB pick)
522+ s_DeepSelectionPrevious = 0 ; // Reset deep selection if a non-PB object is picked
483523 }
484524 }
485525
486- if ( ! isPreview )
487- s_DeepSelectionPrevious = newHash ;
488-
489- if ( pickedGo != null )
526+ // Final selection update
527+ if ( candidateGo != null )
490528 {
491- Event . current . Use ( ) ;
529+ Event . current ? . Use ( ) ;
530+
531+ selection . gameObject = candidateGo ;
532+ resultDistance = Mathf . Sqrt ( candidateDistance ) ;
492533
493- if ( pickedPb != null )
534+ if ( candidatePb != null )
494535 {
495- if ( pickedPb . selectable )
536+ if ( candidatePb . selectable )
496537 {
497- selection . gameObject = pickedGo ;
498- selection . mesh = pickedPb ;
499- selection . SetSingleFace ( pickedFace ) ;
500-
501- return Mathf . Sqrt ( distance ) ;
538+ selection . mesh = candidatePb ;
539+ selection . SetSingleFace ( candidateFace ) ;
502540 }
503541 }
504-
505- // If clicked off a pb_Object but onto another gameobject, set the selection
506- // and dip out.
507- selection . gameObject = pickedGo ;
508- return Mathf . Sqrt ( distance ) ;
509542 }
510543
511- return distance ;
544+ s_PbHits . Clear ( ) ;
545+ return resultDistance ;
512546 }
513547
514548 static float VertexRaycast ( Vector3 mousePosition , ScenePickerPreferences pickerOptions , bool allowUnselected , SceneSelection selection )
0 commit comments