@3dsource/angular-unreal-module 0.0.132 → 0.0.133

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4493,6 +4493,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
4493
4493
  }], ctorParameters: () => [] });
4494
4494
 
4495
4495
  const INITIAL_FINGER_IDS = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
4496
+ const UNSIGNED_RANGE = 65536;
4497
+ const UNSIGNED_OUT_OF_RANGE = 65535;
4498
+ const SIGNED_RANGE = 32767;
4499
+ const WHEEL_DELTA_SCALE = -120;
4500
+ const RESIZE_DEBOUNCE_MS = 300;
4501
+ const FUNCTION_KEY_MIN = 112;
4502
+ const FUNCTION_KEY_MAX = 123;
4503
+ const TAB_KEY_CODE = 9;
4496
4504
  class InputService extends SubService {
4497
4505
  constructor() {
4498
4506
  super();
@@ -4522,19 +4530,14 @@ class InputService extends SubService {
4522
4530
  this.deactivatedVideoTrigger$ = NEVER;
4523
4531
  this.normalizeAndQuantizeUnsigned = (_x, _y) => ({
4524
4532
  inRange: false,
4525
- x: 65535,
4526
- y: 65535,
4533
+ x: UNSIGNED_OUT_OF_RANGE,
4534
+ y: UNSIGNED_OUT_OF_RANGE,
4527
4535
  });
4528
4536
  this.normalizeAndQuantizeSigned = (_x, _y) => ({ x: 0, y: 0 });
4529
4537
  this.videoBoundingRect = new DOMRect(0, 0, 1, 1);
4530
4538
  this.fingerIds = new Map();
4531
4539
  this.maxByteValue = 255;
4532
4540
  this.fingers = [...INITIAL_FINGER_IDS];
4533
- // Captured straight from normalizeCoordinates inside the emit* methods —
4534
- // i.e., the exact quantized values that just went out over the data channel.
4535
- // Resending these on resize avoids any re-transformation through stale
4536
- // offsetParams or container dimensions.
4537
- this.lastNormalizedCoordinates = null;
4538
4541
  this.offsetParams = {
4539
4542
  scaleX: 1,
4540
4543
  scaleY: 1,
@@ -4573,26 +4576,11 @@ class InputService extends SubService {
4573
4576
  this.fingerIds.clear();
4574
4577
  this.fingers = [...INITIAL_FINGER_IDS];
4575
4578
  merge(fromResizeObserver(video), this.actions$.pipe(ofType(videoAdaptedToContainer)))
4576
- .pipe(debounceTime$1(300), takeUntil$1(this.deactivatedVideoTrigger$))
4577
- .subscribe(() => {
4578
- this.setupNormalizeAndQuantize(video);
4579
- this.resendLastMousePosition();
4580
- });
4579
+ .pipe(debounceTime$1(RESIZE_DEBOUNCE_MS), takeUntil$1(this.deactivatedVideoTrigger$))
4580
+ .subscribe(() => this.setupNormalizeAndQuantize(video));
4581
4581
  this.setupNormalizeAndQuantize(video);
4582
4582
  this.registerInputs(video);
4583
4583
  }
4584
- resendLastMousePosition() {
4585
- const last = this.lastNormalizedCoordinates;
4586
- if (!last)
4587
- return;
4588
- const data = new DataView(new ArrayBuffer(9));
4589
- data.setUint8(0, MessageType.MouseMove);
4590
- data.setUint16(1, last.x, true);
4591
- data.setUint16(3, last.y, true);
4592
- data.setInt16(5, 0, true);
4593
- data.setInt16(7, 0, true);
4594
- this.sendInputData(data.buffer);
4595
- }
4596
4584
  /**
4597
4585
  * We use `object-fit: cover` on the video element. Since the video can have
4598
4586
  * a different aspect ratio from its container (and potentially be larger),
@@ -4610,18 +4598,13 @@ class InputService extends SubService {
4610
4598
  videoWidth,
4611
4599
  videoHeight,
4612
4600
  });
4613
- // Compute aspect ratios
4614
4601
  const containerRatio = width / height;
4615
4602
  const videoRatio = videoWidth / videoHeight;
4616
- // Determine how much to scale the video to fit the container (cover mode)
4617
4603
  const scale = videoRatio > containerRatio ? height / videoHeight : width / videoWidth;
4618
- // Calculate the displayed (scaled) dimensions of the video
4619
4604
  const scaledWidth = videoWidth * scale;
4620
4605
  const scaledHeight = videoHeight * scale;
4621
- // Offsets in x/y directions (like "padding") once the video is scaled
4622
4606
  const offsetByX = (width - scaledWidth) / 2;
4623
4607
  const offsetByY = (height - scaledHeight) / 2;
4624
- // Scale factors for coordinate transformation from container space to video space
4625
4608
  const scaleX = width / scaledWidth;
4626
4609
  const scaleY = height / scaledHeight;
4627
4610
  return {
@@ -4637,14 +4620,13 @@ class InputService extends SubService {
4637
4620
  this.videoBoundingRect = videoElement.getBoundingClientRect();
4638
4621
  this.offsetParams = this.getObjectFitOffset(videoElement);
4639
4622
  // Unsigned XY positions are the ratio (0.0..1.0) along a viewport axis,
4640
- // quantized into an uint16 (0..65536).
4623
+ // quantized into a uint16 (0..65536).
4641
4624
  // Signed XY deltas are the ratio (-1.0..1.0) along a viewport axis,
4642
4625
  // quantized into an int16 (-32767..32767).
4643
- // This allows the browser viewport and client viewport to have a different
4644
- // size.
4645
- // Hack: Currently, we set an out-of-range position to an extreme (65535)
4646
- // as we can't yet accurately detect mouse enter and leave events
4647
- // precisely inside a video with an aspect ratio which causes mattes.
4626
+ // This allows the browser viewport and client viewport to have different sizes.
4627
+ // Hack: an out-of-range position becomes UNSIGNED_OUT_OF_RANGE because we
4628
+ // can't yet accurately detect mouse enter/leave precisely inside a video
4629
+ // with an aspect ratio that causes mattes.
4648
4630
  this.normalizeAndQuantizeUnsigned = (x, y) => {
4649
4631
  const normalizedX = x / videoElement.clientWidth;
4650
4632
  const normalizedY = y / videoElement.clientHeight;
@@ -4654,25 +4636,22 @@ class InputService extends SubService {
4654
4636
  normalizedY > 1.0) {
4655
4637
  return {
4656
4638
  inRange: false,
4657
- x: 65535,
4658
- y: 65535,
4659
- };
4660
- }
4661
- else {
4662
- return {
4663
- inRange: true,
4664
- x: normalizedX * 65536,
4665
- y: normalizedY * 65536,
4639
+ x: UNSIGNED_OUT_OF_RANGE,
4640
+ y: UNSIGNED_OUT_OF_RANGE,
4666
4641
  };
4667
4642
  }
4643
+ return {
4644
+ inRange: true,
4645
+ x: normalizedX * UNSIGNED_RANGE,
4646
+ y: normalizedY * UNSIGNED_RANGE,
4647
+ };
4668
4648
  };
4669
- // Signed.
4670
4649
  this.normalizeAndQuantizeSigned = (x, y) => {
4671
4650
  const normalizedX = x / (0.5 * videoElement.clientWidth);
4672
4651
  const normalizedY = y / (0.5 * videoElement.clientHeight);
4673
4652
  return {
4674
- x: normalizedX * 32767,
4675
- y: normalizedY * 32767,
4653
+ x: normalizedX * SIGNED_RANGE,
4654
+ y: normalizedY * SIGNED_RANGE,
4676
4655
  };
4677
4656
  };
4678
4657
  }
@@ -4695,7 +4674,7 @@ class InputService extends SubService {
4695
4674
  y: (clientY - offsetByY - this.videoBoundingRect.top) * scaleY,
4696
4675
  };
4697
4676
  }
4698
- getMousePositionWithOffset({ offsetX, offsetY, }) {
4677
+ getMousePositionWithOffset({ offsetX, offsetY }) {
4699
4678
  const { offsetByX, offsetByY, scaleX, scaleY } = this.offsetParams;
4700
4679
  return {
4701
4680
  x: (offsetX - offsetByX) * scaleX,
@@ -4703,8 +4682,6 @@ class InputService extends SubService {
4703
4682
  };
4704
4683
  }
4705
4684
  registerTouchEvents(htmlVideoElement) {
4706
- // We need to assign a unique identifier to each finger.
4707
- // We do this by mapping each Touch object to the identifier.
4708
4685
  const rememberTouch = (touch) => {
4709
4686
  const finger = this.fingers.pop();
4710
4687
  if (finger === undefined) {
@@ -4715,39 +4692,41 @@ class InputService extends SubService {
4715
4692
  };
4716
4693
  const forgetTouch = (touch) => {
4717
4694
  const finger = this.fingerIds.get(touch.identifier);
4718
- if (finger) {
4695
+ if (finger !== undefined) {
4719
4696
  this.fingers.push(finger);
4697
+ // Re-sort descending so the lowest available id is popped next.
4698
+ this.fingers.sort((a, b) => b - a);
4720
4699
  }
4721
- // Sort array back into descending order. This means if finger '1' were to lift after finger '0', we would ensure that 0 will be the first index to pop
4722
- this.fingers.sort((a, b) => b - a);
4723
4700
  this.fingerIds.delete(touch.identifier);
4724
4701
  };
4725
4702
  const emitTouchData = (type, touches) => {
4726
4703
  // eslint-disable-next-line @typescript-eslint/prefer-for-of
4727
4704
  for (let t = 0; t < touches.length; t++) {
4728
4705
  let byte = 2;
4729
- const numTouches = 1; // the number of touches to be sent this message
4706
+ const numTouches = 1;
4730
4707
  const touch = touches[t];
4708
+ const finger = this.fingerIds.get(touch.identifier);
4709
+ // Skip unknown identifiers (e.g., stale touch on a fast retap) — sending
4710
+ // 0 here would alias finger '0' and corrupt UE's touch state.
4711
+ if (finger === undefined) {
4712
+ continue;
4713
+ }
4731
4714
  const { x, y } = this.getTouchPositionWithOffset(touch);
4732
- Logger.info(`F${this.fingerIds.get(touch.identifier)}=(${x}, ${y})`);
4733
- const normalizeCoordinates = this.normalizeAndQuantizeUnsigned(x, y);
4715
+ Logger.info(`F${finger}=(${x}, ${y})`);
4716
+ const coord = this.normalizeAndQuantizeUnsigned(x, y);
4734
4717
  const data = new DataView(new ArrayBuffer(9));
4735
4718
  data.setUint8(0, type);
4736
4719
  data.setUint8(1, numTouches);
4737
- data.setUint16(byte, normalizeCoordinates.x, true);
4720
+ data.setUint16(byte, coord.x, true);
4738
4721
  byte += 2;
4739
- data.setUint16(byte, normalizeCoordinates.y, true);
4722
+ data.setUint16(byte, coord.y, true);
4740
4723
  byte += 2;
4741
- const finger = this.fingerIds.get(touch.identifier);
4742
- if (finger) {
4743
- data.setUint8(byte, finger);
4744
- }
4724
+ data.setUint8(byte, finger);
4745
4725
  byte += 1;
4746
- // force is between 0.0 and 1.0, so quantize into byte.
4747
- // iOs bug: Forced to be always 0.5 on all devices as we do not use forced touch any way;/
4726
+ // iOS bug: force is locked to 0.5 since forced touch is unused.
4748
4727
  data.setUint8(byte, this.maxByteValue * 0.5);
4749
4728
  byte += 1;
4750
- data.setUint8(byte, normalizeCoordinates.inRange ? 1 : 0);
4729
+ data.setUint8(byte, coord.inRange ? 1 : 0);
4751
4730
  byte += 1;
4752
4731
  this.sendInputData(data.buffer);
4753
4732
  }
@@ -4757,7 +4736,6 @@ class InputService extends SubService {
4757
4736
  .subscribe((e) => {
4758
4737
  this.focusWindow();
4759
4738
  const touches = e.changedTouches;
4760
- // Assign a unique identifier to each touch.
4761
4739
  // eslint-disable-next-line @typescript-eslint/prefer-for-of
4762
4740
  for (let t = 0; t < touches.length; t++) {
4763
4741
  rememberTouch(touches[t]);
@@ -4772,7 +4750,6 @@ class InputService extends SubService {
4772
4750
  const touches = e.changedTouches;
4773
4751
  Logger.info('touch end');
4774
4752
  emitTouchData(MessageType.TouchEnd, touches);
4775
- // Re-cycle unique identifiers previously assigned to each touch.
4776
4753
  // eslint-disable-next-line @typescript-eslint/prefer-for-of
4777
4754
  for (let t = 0; t < touches.length; t++) {
4778
4755
  forgetTouch(touches[t]);
@@ -4790,45 +4767,41 @@ class InputService extends SubService {
4790
4767
  window.focus();
4791
4768
  }
4792
4769
  emitMouseMove(x, y, deltaX, deltaY) {
4793
- const normalizeCoordinates = this.normalizeAndQuantizeUnsigned(x, y);
4794
- this.lastNormalizedCoordinates = normalizeCoordinates;
4770
+ const coord = this.normalizeAndQuantizeUnsigned(x, y);
4795
4771
  const delta = this.normalizeAndQuantizeSigned(deltaX, deltaY);
4796
4772
  const data = new DataView(new ArrayBuffer(9));
4797
4773
  data.setUint8(0, MessageType.MouseMove);
4798
- data.setUint16(1, normalizeCoordinates.x, true);
4799
- data.setUint16(3, normalizeCoordinates.y, true);
4774
+ data.setUint16(1, coord.x, true);
4775
+ data.setUint16(3, coord.y, true);
4800
4776
  data.setInt16(5, delta.x, true);
4801
4777
  data.setInt16(7, delta.y, true);
4802
4778
  this.sendInputData(data.buffer);
4803
4779
  }
4804
4780
  emitMouseDown(button, x, y) {
4805
- const normalizeCoordinates = this.normalizeAndQuantizeUnsigned(x, y);
4806
- this.lastNormalizedCoordinates = normalizeCoordinates;
4781
+ const coord = this.normalizeAndQuantizeUnsigned(x, y);
4807
4782
  const data = new DataView(new ArrayBuffer(6));
4808
4783
  data.setUint8(0, MessageType.MouseDown);
4809
4784
  data.setUint8(1, button);
4810
- data.setUint16(2, normalizeCoordinates.x, true);
4811
- data.setUint16(4, normalizeCoordinates.y, true);
4785
+ data.setUint16(2, coord.x, true);
4786
+ data.setUint16(4, coord.y, true);
4812
4787
  this.sendInputData(data.buffer);
4813
4788
  }
4814
4789
  emitMouseUp(button, x, y) {
4815
- const normalizeCoordinates = this.normalizeAndQuantizeUnsigned(x, y);
4816
- this.lastNormalizedCoordinates = normalizeCoordinates;
4790
+ const coord = this.normalizeAndQuantizeUnsigned(x, y);
4817
4791
  const data = new DataView(new ArrayBuffer(6));
4818
4792
  data.setUint8(0, MessageType.MouseUp);
4819
4793
  data.setUint8(1, button);
4820
- data.setUint16(2, normalizeCoordinates.x, true);
4821
- data.setUint16(4, normalizeCoordinates.y, true);
4794
+ data.setUint16(2, coord.x, true);
4795
+ data.setUint16(4, coord.y, true);
4822
4796
  this.sendInputData(data.buffer);
4823
4797
  }
4824
4798
  emitMouseWheel(delta, x, y) {
4825
- const normalizeCoordinates = this.normalizeAndQuantizeUnsigned(x, y);
4826
- this.lastNormalizedCoordinates = normalizeCoordinates;
4799
+ const coord = this.normalizeAndQuantizeUnsigned(x, y);
4827
4800
  const data = new DataView(new ArrayBuffer(7));
4828
4801
  data.setUint8(0, MessageType.MouseWheel);
4829
4802
  data.setInt16(1, delta, true);
4830
- data.setUint16(3, normalizeCoordinates.x, true);
4831
- data.setUint16(5, normalizeCoordinates.y, true);
4803
+ data.setUint16(3, coord.x, true);
4804
+ data.setUint16(5, coord.y, true);
4832
4805
  this.sendInputData(data.buffer);
4833
4806
  }
4834
4807
  releaseMouseButtons(buttons, x, y) {
@@ -4848,7 +4821,6 @@ class InputService extends SubService {
4848
4821
  this.emitMouseUp(MouseButton.FifthButton, x, y);
4849
4822
  }
4850
4823
  }
4851
- // If the user has any mouse buttons pressed, then press them again.
4852
4824
  pressMouseButtons(buttons, x, y) {
4853
4825
  if (buttons & MouseButtonsMask.PrimaryButton) {
4854
4826
  this.emitMouseDown(MouseButton.MainButton, x, y);
@@ -4892,7 +4864,7 @@ class InputService extends SubService {
4892
4864
  fromEvent(htmlVideoElement, 'wheel')
4893
4865
  .pipe(takeUntil$1(this.deactivatedVideoTrigger$))
4894
4866
  .subscribe((e) => {
4895
- this.emitMouseWheel(e.deltaY * -120, e.offsetX, e.offsetY);
4867
+ this.emitMouseWheel(e.deltaY * WHEEL_DELTA_SCALE, e.offsetX, e.offsetY);
4896
4868
  e.preventDefault();
4897
4869
  });
4898
4870
  }
@@ -4909,7 +4881,6 @@ class InputService extends SubService {
4909
4881
  fromEvent(htmlVideoElement, 'mouseleave')
4910
4882
  .pipe(takeUntil$1(this.deactivatedVideoTrigger$))
4911
4883
  .subscribe((e) => {
4912
- this.lastNormalizedCoordinates = null;
4913
4884
  const data = new DataView(new ArrayBuffer(1));
4914
4885
  data.setUint8(0, MessageType.MouseLeave);
4915
4886
  this.sendInputData(data.buffer);
@@ -4918,8 +4889,8 @@ class InputService extends SubService {
4918
4889
  }
4919
4890
  // Browser keys do not have a charCode so we only need to test keyCode.
4920
4891
  isKeyCodeBrowserKey(keyCode) {
4921
- // Function keys or tab key.
4922
- return (keyCode >= 112 && keyCode <= 123) || keyCode === 9;
4892
+ return ((keyCode >= FUNCTION_KEY_MIN && keyCode <= FUNCTION_KEY_MAX) ||
4893
+ keyCode === TAB_KEY_CODE);
4923
4894
  }
4924
4895
  getKeyCode(e) {
4925
4896
  if (e.keyCode === SpecialKeyCodes.Shift && e.code === 'ShiftRight')
@@ -4932,9 +4903,7 @@ class InputService extends SubService {
4932
4903
  return e.keyCode;
4933
4904
  }
4934
4905
  registerKeyboardEvents(availableKeys) {
4935
- const availableKeyNames = Object.entries(availableKeys).map(([, value]) => {
4936
- return InvertedKeyMap[value];
4937
- });
4906
+ const availableKeyNames = availableKeys.map((value) => InvertedKeyMap[value]);
4938
4907
  const keyBlock = (event) => {
4939
4908
  const code = event.code;
4940
4909
  const isInputFieldFocused = event.target?.localName === 'input';
@@ -4949,7 +4918,6 @@ class InputService extends SubService {
4949
4918
  }
4950
4919
  startListenKeys(_filter) {
4951
4920
  const filter = _filter ?? (() => false);
4952
- // Use RxJS fromEvent with takeUntil to properly clean up subscriptions
4953
4921
  fromEvent(document, 'keydown')
4954
4922
  .pipe(takeUntil$1(this.deactivatedVideoTrigger$))
4955
4923
  .subscribe((e) => {
@@ -4967,8 +4935,8 @@ class InputService extends SubService {
4967
4935
  this.getKeyCode(e),
4968
4936
  Number(e.repeat),
4969
4937
  ]).buffer);
4970
- // Backspace is not considered a keypress in JavaScript, but we need it
4971
- // to be so characters may be deleted in a UE4 text entry field.
4938
+ // Backspace isn't a keypress in JS, but UE4 text-entry fields need it
4939
+ // to delete characters.
4972
4940
  if (e.keyCode === SpecialKeyCodes.BackSpace) {
4973
4941
  const data = new DataView(new ArrayBuffer(3));
4974
4942
  data.setUint8(0, MessageType.KeyPress);