babylon.multiPointerScaleBehavior.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. module BABYLON {
  2. /**
  3. * A behavior that when attached to a mesh will allow the mesh to be scaled
  4. */
  5. export class MultiPointerScaleBehavior implements Behavior<Mesh> {
  6. private _dragBehaviorA:PointerDragBehavior;
  7. private _dragBehaviorB:PointerDragBehavior;
  8. private _startDistance = 0;
  9. private _initialScale = new Vector3(0,0,0);
  10. private _targetScale = new Vector3(0,0,0);
  11. private _ownerNode:Mesh;
  12. private _sceneRenderObserver:Nullable<Observer<Scene>> = null;
  13. constructor(){
  14. this._dragBehaviorA = new BABYLON.PointerDragBehavior({});
  15. this._dragBehaviorA.moveAttached = false;
  16. this._dragBehaviorB = new BABYLON.PointerDragBehavior({});
  17. this._dragBehaviorB.moveAttached = false;
  18. }
  19. /**
  20. * The name of the behavior
  21. */
  22. public get name(): string {
  23. return "MultiPointerScale";
  24. }
  25. /**
  26. * Initializes the behavior
  27. */
  28. public init() {}
  29. private _getCurrentDistance(){
  30. return this._dragBehaviorA.lastDragPosition.subtract(this._dragBehaviorB.lastDragPosition).length();
  31. }
  32. /**
  33. * Attaches the scale behavior the passed in mesh
  34. * @param ownerNode The mesh that will be scaled around once attached
  35. */
  36. public attach(ownerNode: Mesh): void {
  37. this._ownerNode = ownerNode;
  38. // Create 2 drag behaviors such that each will only be triggered by a separate pointer
  39. this._dragBehaviorA.onDragStartObservable.add((e)=>{
  40. if(this._dragBehaviorA.dragging && this._dragBehaviorB.dragging){
  41. if(this._dragBehaviorA.currentDraggingPointerID == this._dragBehaviorB.currentDraggingPointerID){
  42. this._dragBehaviorA.releaseDrag();
  43. }else{
  44. this._initialScale.copyFrom(ownerNode.scaling)
  45. this._startDistance = this._getCurrentDistance();
  46. }
  47. }
  48. });
  49. this._dragBehaviorB.onDragStartObservable.add((e)=>{
  50. if(this._dragBehaviorA.dragging && this._dragBehaviorB.dragging){
  51. if(this._dragBehaviorA.currentDraggingPointerID == this._dragBehaviorB.currentDraggingPointerID){
  52. this._dragBehaviorB.releaseDrag();
  53. }else{
  54. this._initialScale.copyFrom(ownerNode.scaling)
  55. this._startDistance = this._getCurrentDistance();
  56. }
  57. }
  58. });
  59. // Once both drag behaviors are active scale based on the distance between the two pointers
  60. [this._dragBehaviorA, this._dragBehaviorB].forEach((behavior)=>{
  61. behavior.onDragObservable.add(()=>{
  62. if(this._dragBehaviorA.dragging && this._dragBehaviorB.dragging){
  63. var ratio = this._getCurrentDistance()/this._startDistance;
  64. this._initialScale.scaleToRef(ratio, this._targetScale);
  65. }
  66. });
  67. })
  68. ownerNode.addBehavior(this._dragBehaviorA);
  69. ownerNode.addBehavior(this._dragBehaviorB);
  70. // On every frame move towards target scaling to avoid jitter caused by vr controllers
  71. this._sceneRenderObserver = ownerNode.getScene().onBeforeRenderObservable.add(()=>{
  72. if(this._dragBehaviorA.dragging && this._dragBehaviorB.dragging){
  73. var change = this._targetScale.subtract(ownerNode.scaling).scaleInPlace(0.1);
  74. if(change.length()>0.01){
  75. ownerNode.scaling.addInPlace(change);
  76. }
  77. }
  78. });
  79. }
  80. /**
  81. * Detaches the behavior from the mesh
  82. */
  83. public detach(): void {
  84. this._ownerNode.getScene().onBeforeRenderObservable.remove(this._sceneRenderObserver);
  85. [this._dragBehaviorA, this._dragBehaviorB].forEach((behavior)=>{
  86. behavior.onDragStartObservable.clear();
  87. behavior.onDragObservable.clear();
  88. this._ownerNode.removeBehavior(behavior);
  89. });
  90. }
  91. }
  92. }