textureDome.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. import { TransformNode } from "../Meshes/transformNode.js";
  2. import { Mesh } from "../Meshes/mesh.js";
  3. import { Texture } from "../Materials/Textures/texture.js";
  4. import { BackgroundMaterial } from "../Materials/Background/backgroundMaterial.js";
  5. import { CreateSphere } from "../Meshes/Builders/sphereBuilder.js";
  6. import { Observable } from "../Misc/observable.js";
  7. import { Vector3 } from "../Maths/math.vector.js";
  8. import { Axis } from "../Maths/math.js";
  9. /**
  10. * Display a 360/180 degree texture on an approximately spherical surface, useful for VR applications or skyboxes.
  11. * As a subclass of TransformNode, this allow parenting to the camera or multiple textures with different locations in the scene.
  12. * This class achieves its effect with a Texture and a correctly configured BackgroundMaterial on an inverted sphere.
  13. * Potential additions to this helper include zoom and and non-infinite distance rendering effects.
  14. */
  15. export class TextureDome extends TransformNode {
  16. /**
  17. * Gets the texture being displayed on the sphere
  18. */
  19. get texture() {
  20. return this._texture;
  21. }
  22. /**
  23. * Sets the texture being displayed on the sphere
  24. */
  25. set texture(newTexture) {
  26. if (this._texture === newTexture) {
  27. return;
  28. }
  29. this._texture = newTexture;
  30. if (this._useDirectMapping) {
  31. this._texture.wrapU = Texture.CLAMP_ADDRESSMODE;
  32. this._texture.wrapV = Texture.CLAMP_ADDRESSMODE;
  33. this._material.diffuseTexture = this._texture;
  34. }
  35. else {
  36. this._texture.coordinatesMode = Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE; // matches orientation
  37. this._texture.wrapV = Texture.CLAMP_ADDRESSMODE;
  38. this._material.reflectionTexture = this._texture;
  39. }
  40. this._changeTextureMode(this._textureMode);
  41. }
  42. /**
  43. * Gets the mesh used for the dome.
  44. */
  45. get mesh() {
  46. return this._mesh;
  47. }
  48. /**
  49. * The current fov(field of view) multiplier, 0.0 - 2.0. Defaults to 1.0. Lower values "zoom in" and higher values "zoom out".
  50. * Also see the options.resolution property.
  51. */
  52. get fovMultiplier() {
  53. return this._material.fovMultiplier;
  54. }
  55. set fovMultiplier(value) {
  56. this._material.fovMultiplier = value;
  57. }
  58. /**
  59. * Gets or set the current texture mode for the texture. It can be:
  60. * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
  61. * * TextureDome.MODE_TOPBOTTOM : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
  62. * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
  63. */
  64. get textureMode() {
  65. return this._textureMode;
  66. }
  67. /**
  68. * Sets the current texture mode for the texture. It can be:
  69. * * TextureDome.MODE_MONOSCOPIC : Define the texture source as a Monoscopic panoramic 360.
  70. * * TextureDome.MODE_TOPBOTTOM : Define the texture source as a Stereoscopic TopBottom/OverUnder panoramic 360.
  71. * * TextureDome.MODE_SIDEBYSIDE : Define the texture source as a Stereoscopic Side by Side panoramic 360.
  72. */
  73. set textureMode(value) {
  74. if (this._textureMode === value) {
  75. return;
  76. }
  77. this._changeTextureMode(value);
  78. }
  79. /**
  80. * Is it a 180 degrees dome (half dome) or 360 texture (full dome)
  81. */
  82. get halfDome() {
  83. return this._halfDome;
  84. }
  85. /**
  86. * Set the halfDome mode. If set, only the front (180 degrees) will be displayed and the back will be blacked out.
  87. */
  88. set halfDome(enabled) {
  89. this._halfDome = enabled;
  90. this._halfDomeMask.setEnabled(enabled);
  91. this._changeTextureMode(this._textureMode);
  92. }
  93. /**
  94. * Set the cross-eye mode. If set, images that can be seen when crossing eyes will render correctly
  95. */
  96. set crossEye(enabled) {
  97. this._crossEye = enabled;
  98. this._changeTextureMode(this._textureMode);
  99. }
  100. /**
  101. * Is it a cross-eye texture?
  102. */
  103. get crossEye() {
  104. return this._crossEye;
  105. }
  106. /**
  107. * The background material of this dome.
  108. */
  109. get material() {
  110. return this._material;
  111. }
  112. /**
  113. * Create an instance of this class and pass through the parameters to the relevant classes- Texture, StandardMaterial, and Mesh.
  114. * @param name Element's name, child elements will append suffixes for their own names.
  115. * @param textureUrlOrElement defines the url(s) or the (video) HTML element to use
  116. * @param options An object containing optional or exposed sub element properties
  117. * @param options.resolution
  118. * @param options.clickToPlay
  119. * @param options.autoPlay
  120. * @param options.loop
  121. * @param options.size
  122. * @param options.poster
  123. * @param options.faceForward
  124. * @param options.useDirectMapping
  125. * @param options.halfDomeMode
  126. * @param options.crossEyeMode
  127. * @param options.generateMipMaps
  128. * @param options.mesh
  129. * @param scene
  130. * @param onError
  131. */
  132. constructor(name, textureUrlOrElement, options, scene,
  133. // eslint-disable-next-line @typescript-eslint/naming-convention
  134. onError = null) {
  135. super(name, scene);
  136. this.onError = onError;
  137. this._halfDome = false;
  138. this._crossEye = false;
  139. this._useDirectMapping = false;
  140. this._textureMode = TextureDome.MODE_MONOSCOPIC;
  141. /**
  142. * Oberserver used in Stereoscopic VR Mode.
  143. */
  144. this._onBeforeCameraRenderObserver = null;
  145. /**
  146. * Observable raised when an error occurred while loading the texture
  147. */
  148. this.onLoadErrorObservable = new Observable();
  149. /**
  150. * Observable raised when the texture finished loading
  151. */
  152. this.onLoadObservable = new Observable();
  153. scene = this.getScene();
  154. // set defaults and manage values
  155. name = name || "textureDome";
  156. options.resolution = Math.abs(options.resolution) | 0 || 32;
  157. options.clickToPlay = Boolean(options.clickToPlay);
  158. options.autoPlay = options.autoPlay === undefined ? true : Boolean(options.autoPlay);
  159. options.loop = options.loop === undefined ? true : Boolean(options.loop);
  160. options.size = Math.abs(options.size) || (scene.activeCamera ? scene.activeCamera.maxZ * 0.48 : 1000);
  161. if (options.useDirectMapping === undefined) {
  162. this._useDirectMapping = true;
  163. }
  164. else {
  165. this._useDirectMapping = options.useDirectMapping;
  166. }
  167. if (options.faceForward === undefined) {
  168. options.faceForward = true;
  169. }
  170. this._setReady(false);
  171. if (!options.mesh) {
  172. this._mesh = CreateSphere(name + "_mesh", { segments: options.resolution, diameter: options.size, updatable: false, sideOrientation: Mesh.BACKSIDE }, scene);
  173. }
  174. else {
  175. this._mesh = options.mesh;
  176. }
  177. // configure material
  178. const material = (this._material = new BackgroundMaterial(name + "_material", scene));
  179. material.useEquirectangularFOV = true;
  180. material.fovMultiplier = 1.0;
  181. material.opacityFresnel = false;
  182. const texture = this._initTexture(textureUrlOrElement, scene, options);
  183. this.texture = texture;
  184. // configure mesh
  185. this._mesh.material = material;
  186. this._mesh.parent = this;
  187. // create a (disabled until needed) mask to cover unneeded segments of 180 texture.
  188. this._halfDomeMask = CreateSphere("", { slice: 0.5, diameter: options.size * 0.98, segments: options.resolution * 2, sideOrientation: Mesh.BACKSIDE }, scene);
  189. this._halfDomeMask.rotate(Axis.X, -Math.PI / 2);
  190. // set the parent, so it will always be positioned correctly AND will be disposed when the main sphere is disposed
  191. this._halfDomeMask.parent = this._mesh;
  192. this._halfDome = !!options.halfDomeMode;
  193. // enable or disable according to the settings
  194. this._halfDomeMask.setEnabled(this._halfDome);
  195. this._crossEye = !!options.crossEyeMode;
  196. // create
  197. this._texture.anisotropicFilteringLevel = 1;
  198. this._texture.onLoadObservable.addOnce(() => {
  199. this._setReady(true);
  200. });
  201. // Initial rotation
  202. if (options.faceForward && scene.activeCamera) {
  203. const camera = scene.activeCamera;
  204. const forward = Vector3.Forward();
  205. const direction = Vector3.TransformNormal(forward, camera.getViewMatrix());
  206. direction.normalize();
  207. this.rotation.y = Math.acos(Vector3.Dot(forward, direction));
  208. }
  209. this._changeTextureMode(this._textureMode);
  210. }
  211. _changeTextureMode(value) {
  212. this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
  213. this._textureMode = value;
  214. // Default Setup and Reset.
  215. this._texture.uScale = 1;
  216. this._texture.vScale = 1;
  217. this._texture.uOffset = 0;
  218. this._texture.vOffset = 0;
  219. this._texture.vAng = 0;
  220. switch (value) {
  221. case TextureDome.MODE_MONOSCOPIC:
  222. if (this._halfDome) {
  223. this._texture.uScale = 2;
  224. this._texture.uOffset = -1;
  225. }
  226. break;
  227. case TextureDome.MODE_SIDEBYSIDE: {
  228. // in half-dome mode the uScale should be double of 360 texture
  229. // Use 0.99999 to boost perf by not switching program
  230. this._texture.uScale = this._halfDome ? 0.99999 : 0.5;
  231. const rightOffset = this._halfDome ? 0.0 : 0.5;
  232. const leftOffset = this._halfDome ? -0.5 : 0.0;
  233. this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add((camera) => {
  234. let isRightCamera = camera.isRightCamera;
  235. if (this._crossEye) {
  236. isRightCamera = !isRightCamera;
  237. }
  238. if (isRightCamera) {
  239. this._texture.uOffset = rightOffset;
  240. }
  241. else {
  242. this._texture.uOffset = leftOffset;
  243. }
  244. });
  245. break;
  246. }
  247. case TextureDome.MODE_TOPBOTTOM:
  248. // in half-dome mode the vScale should be double of 360 texture
  249. // Use 0.99999 to boost perf by not switching program
  250. this._texture.vScale = this._halfDome ? 0.99999 : 0.5;
  251. this._onBeforeCameraRenderObserver = this._scene.onBeforeCameraRenderObservable.add((camera) => {
  252. let isRightCamera = camera.isRightCamera;
  253. // allow "cross-eye" if left and right were switched in this mode
  254. if (this._crossEye) {
  255. isRightCamera = !isRightCamera;
  256. }
  257. this._texture.vOffset = isRightCamera ? 0.5 : 0.0;
  258. });
  259. break;
  260. }
  261. }
  262. /**
  263. * Releases resources associated with this node.
  264. * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
  265. * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
  266. */
  267. dispose(doNotRecurse, disposeMaterialAndTextures = false) {
  268. this._texture.dispose();
  269. this._mesh.dispose();
  270. this._material.dispose();
  271. this._scene.onBeforeCameraRenderObservable.remove(this._onBeforeCameraRenderObserver);
  272. this.onLoadErrorObservable.clear();
  273. this.onLoadObservable.clear();
  274. super.dispose(doNotRecurse, disposeMaterialAndTextures);
  275. }
  276. }
  277. /**
  278. * Define the source as a Monoscopic panoramic 360/180.
  279. */
  280. TextureDome.MODE_MONOSCOPIC = 0;
  281. /**
  282. * Define the source as a Stereoscopic TopBottom/OverUnder panoramic 360/180.
  283. */
  284. TextureDome.MODE_TOPBOTTOM = 1;
  285. /**
  286. * Define the source as a Stereoscopic Side by Side panoramic 360/180.
  287. */
  288. TextureDome.MODE_SIDEBYSIDE = 2;
  289. //# sourceMappingURL=textureDome.js.map