webXRProfiledMotionController.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import { WebXRAbstractMotionController } from "./webXRAbstractMotionController.js";
  2. import { SceneLoader } from "../../Loading/sceneLoader.js";
  3. import { Mesh } from "../../Meshes/mesh.js";
  4. import { Axis, Space } from "../../Maths/math.axis.js";
  5. import { Color3 } from "../../Maths/math.color.js";
  6. import { WebXRControllerComponent } from "./webXRControllerComponent.js";
  7. import { CreateSphere } from "../../Meshes/Builders/sphereBuilder.js";
  8. import { StandardMaterial } from "../../Materials/standardMaterial.js";
  9. import { Logger } from "../../Misc/logger.js";
  10. /**
  11. * A profiled motion controller has its profile loaded from an online repository.
  12. * The class is responsible of loading the model, mapping the keys and enabling model-animations
  13. */
  14. export class WebXRProfiledMotionController extends WebXRAbstractMotionController {
  15. constructor(scene, xrInput, _profile, _repositoryUrl,
  16. // eslint-disable-next-line @typescript-eslint/naming-convention
  17. controllerCache) {
  18. super(scene, _profile.layouts[xrInput.handedness || "none"], xrInput.gamepad, xrInput.handedness, undefined, controllerCache);
  19. this._repositoryUrl = _repositoryUrl;
  20. this.controllerCache = controllerCache;
  21. this._buttonMeshMapping = {};
  22. this._touchDots = {};
  23. this.profileId = _profile.profileId;
  24. }
  25. dispose() {
  26. super.dispose();
  27. if (!this.controllerCache) {
  28. Object.keys(this._touchDots).forEach((visResKey) => {
  29. this._touchDots[visResKey].dispose();
  30. });
  31. }
  32. }
  33. _getFilenameAndPath() {
  34. return {
  35. filename: this.layout.assetPath,
  36. path: `${this._repositoryUrl}/profiles/${this.profileId}/`,
  37. };
  38. }
  39. _getModelLoadingConstraints() {
  40. const glbLoaded = SceneLoader.IsPluginForExtensionAvailable(".glb");
  41. if (!glbLoaded) {
  42. Logger.Warn("glTF / glb loader was not registered, using generic controller instead");
  43. }
  44. return glbLoaded;
  45. }
  46. _processLoadedModel(_meshes) {
  47. this.getComponentIds().forEach((type) => {
  48. const componentInLayout = this.layout.components[type];
  49. this._buttonMeshMapping[type] = {
  50. mainMesh: this._getChildByName(this.rootMesh, componentInLayout.rootNodeName),
  51. states: {},
  52. };
  53. Object.keys(componentInLayout.visualResponses).forEach((visualResponseKey) => {
  54. const visResponse = componentInLayout.visualResponses[visualResponseKey];
  55. if (visResponse.valueNodeProperty === "transform") {
  56. this._buttonMeshMapping[type].states[visualResponseKey] = {
  57. valueMesh: this._getChildByName(this.rootMesh, visResponse.valueNodeName),
  58. minMesh: this._getChildByName(this.rootMesh, visResponse.minNodeName),
  59. maxMesh: this._getChildByName(this.rootMesh, visResponse.maxNodeName),
  60. };
  61. }
  62. else {
  63. // visibility, usually for touchpads
  64. const nameOfMesh = componentInLayout.type === WebXRControllerComponent.TOUCHPAD_TYPE && componentInLayout.touchPointNodeName
  65. ? componentInLayout.touchPointNodeName
  66. : visResponse.valueNodeName;
  67. this._buttonMeshMapping[type].states[visualResponseKey] = {
  68. valueMesh: this._getChildByName(this.rootMesh, nameOfMesh),
  69. };
  70. if (componentInLayout.type === WebXRControllerComponent.TOUCHPAD_TYPE && !this._touchDots[visualResponseKey]) {
  71. const dot = CreateSphere(visualResponseKey + "dot", {
  72. diameter: 0.0015,
  73. segments: 8,
  74. }, this.scene);
  75. dot.material = new StandardMaterial(visualResponseKey + "mat", this.scene);
  76. dot.material.diffuseColor = Color3.Red();
  77. dot.parent = this._buttonMeshMapping[type].states[visualResponseKey].valueMesh || null;
  78. dot.isVisible = false;
  79. this._touchDots[visualResponseKey] = dot;
  80. }
  81. }
  82. });
  83. });
  84. }
  85. _setRootMesh(meshes) {
  86. this.rootMesh = new Mesh(this.profileId + "-" + this.handedness, this.scene);
  87. this.rootMesh.isPickable = false;
  88. let rootMesh;
  89. // Find the root node in the loaded glTF scene, and attach it as a child of 'parentMesh'
  90. for (let i = 0; i < meshes.length; i++) {
  91. const mesh = meshes[i];
  92. mesh.isPickable = false;
  93. if (!mesh.parent) {
  94. // Handle root node, attach to the new parentMesh
  95. rootMesh = mesh;
  96. }
  97. }
  98. if (rootMesh) {
  99. rootMesh.setParent(this.rootMesh);
  100. }
  101. if (!this.scene.useRightHandedSystem) {
  102. this.rootMesh.rotate(Axis.Y, Math.PI, Space.WORLD);
  103. }
  104. }
  105. _updateModel(_xrFrame) {
  106. if (this.disableAnimation) {
  107. return;
  108. }
  109. this.getComponentIds().forEach((id) => {
  110. const component = this.getComponent(id);
  111. if (!component.hasChanges) {
  112. return;
  113. }
  114. const meshes = this._buttonMeshMapping[id];
  115. const componentInLayout = this.layout.components[id];
  116. Object.keys(componentInLayout.visualResponses).forEach((visualResponseKey) => {
  117. const visResponse = componentInLayout.visualResponses[visualResponseKey];
  118. let value = component.value;
  119. if (visResponse.componentProperty === "xAxis") {
  120. value = component.axes.x;
  121. }
  122. else if (visResponse.componentProperty === "yAxis") {
  123. value = component.axes.y;
  124. }
  125. if (visResponse.valueNodeProperty === "transform") {
  126. this._lerpTransform(meshes.states[visualResponseKey], value, visResponse.componentProperty !== "button");
  127. }
  128. else {
  129. // visibility
  130. const valueMesh = meshes.states[visualResponseKey].valueMesh;
  131. if (valueMesh) {
  132. valueMesh.isVisible = component.touched || component.pressed;
  133. }
  134. if (this._touchDots[visualResponseKey]) {
  135. this._touchDots[visualResponseKey].isVisible = component.touched || component.pressed;
  136. }
  137. }
  138. });
  139. });
  140. }
  141. }
  142. //# sourceMappingURL=webXRProfiledMotionController.js.map