XTelevision.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import XStaticMesh from "./XStaticMesh.js"
  2. import EMeshType from "./enum/EMeshType.js"
  3. import Logger from "./Logger.js"
  4. import Stream from "./Stream.js";
  5. const logger = new Logger('Television')
  6. export default class XTelevision {
  7. constructor(scene, meshUrl, scenemanager, option) {
  8. E(this, "videoElement");
  9. E(this, "meshPath");
  10. E(this, "scene");
  11. E(this, "tvMeshs", []);
  12. E(this, "vAng");
  13. E(this, "videoMat");
  14. E(this, "videoTexture");
  15. E(this, "widthHeightScale");
  16. E(this, "fitMode");
  17. E(this, "_scenemanager");
  18. this.scene = scene
  19. this.meshPath = meshUrl
  20. this._scenemanager = scenemanager
  21. if (option != null) {
  22. const {vAng=0, widthHeightScale=-1, fitMode="fill"} = option;
  23. this.vAng = vAng,
  24. this.widthHeightScale = widthHeightScale,
  25. this.fitMode = fitMode
  26. }
  27. }
  28. set tvWidthHeightscale(e) {
  29. this.widthHeightScale = e
  30. }
  31. get tvWidthHeightscale() {
  32. return this.widthHeightScale
  33. }
  34. get tvFitMode() {
  35. return this.fitMode
  36. }
  37. set tvFitMode(e) {
  38. this.fitMode = e
  39. }
  40. setPlaySpeed(e) {
  41. this.videoElement != null && (this.videoElement.playbackRate = e)
  42. }
  43. getMesh() {
  44. return this.tvMeshs
  45. }
  46. createElement(e, t=!1) {
  47. const n = new Stream().el;
  48. return n.loop = t,
  49. n.autoplay = !0,
  50. n.src = e,
  51. n
  52. }
  53. async setUrl(e) {
  54. const {url, isLive: r=!1, poster: n=null, bLoop: o=!1, bMuted: a=!0} = e || {};
  55. if (typeof url != "string")
  56. return logger.error("[Engine] Tv setUrl Error, url must be string: ", url),
  57. Promise.reject(new XTvMediaUrlError("[Engine] url must be string"));
  58. if (this.videoElement) {
  59. this.videoElement.src = url,
  60. n != null && n.length > 0 && (this.videoElement.poster = n);
  61. const l = this.play();
  62. return "bMuted"in e && l !== void 0 && l.then(()=>{
  63. this.videoElement.muted = a
  64. }),
  65. this.videoElement.addEventListener("loadedmetadata", u=>{
  66. this.videoElement.videoWidth > 0
  67. ? this.videoMat.setFloat("mvWidthHeightScale", this.videoElement.videoWidth / this.videoElement.videoHeight)
  68. : this.videoMat.setFloat("mvWidthHeightScale", 16 / 9)
  69. }),
  70. Promise.resolve(this)
  71. }
  72. const s = this.createElement(url, o);
  73. n != null && n.length > 0 && (s.poster = n)
  74. return this.setVideo(s, r).then(()=>{
  75. const l = this.videoElement && this.videoElement.play();
  76. "bMuted"in e && l !== void 0 && l.then(()=>{
  77. this.videoElement.muted = a
  78. })
  79. }).catch(l=>{
  80. logger.error("[Engine] setUrl error! " + l),
  81. new XTvMediaUrlError("[Engine] setUrl error! " + l)
  82. })
  83. }
  84. setCurrentTime(e) {
  85. if (!this.videoElement) {
  86. logger.warn("[Engine] The television is not been initialize succesfully");
  87. return
  88. }
  89. const {currentTime: t} = e;
  90. if (typeof t != "number") {
  91. logger.warn("[Engine] video currentTime must be number");
  92. return
  93. }
  94. this.videoElement.currentTime = t / 1e3
  95. }
  96. getCurrentTime() {
  97. return this.videoElement ? this.videoElement.currentTime * 1e3 : -1
  98. }
  99. play() {
  100. return logger.info("[Engine] Play television"),
  101. this.toggle(!0),
  102. this.videoElement ? this.videoElement.play() : Promise.resolve()
  103. }
  104. pause() {
  105. var e;
  106. return logger.info("[Engine] Pause television"),
  107. (e = this.videoElement) == null ? void 0 : e.pause()
  108. }
  109. stop() {
  110. logger.info("[Engine] Stop television"),
  111. this.pause(),
  112. setTimeout(()=>{
  113. this.setCurrentTime({
  114. currentTime: 0
  115. })
  116. }
  117. ),
  118. this.toggle(!1)
  119. }
  120. toggle(e) {
  121. logger.info(`[Engine] Set Tv visibility = ${e}`);
  122. for (let t = 0; t < this.tvMeshs.length; ++t)
  123. e == !0 ? this.tvMeshs[t].show() : this.tvMeshs[t].hide()
  124. }
  125. getVideoMat() {
  126. return this.videoMat
  127. }
  128. changeTvFitMode() {
  129. this.fitMode == "contain" ? (this.widthHeightScale < 0 && (this.widthHeightScale = 2.4),
  130. this.videoMat.setFloat("tvWidthHeightScale", this.widthHeightScale),
  131. this.videoMat.setFloat("bforceforceKeepContent", 1)) : this.fitMode == "cover" ? (this.widthHeightScale < 0 && (this.widthHeightScale = this.calWidthHeightScale()),
  132. this.videoMat.setFloat("tvWidthHeightScale", this.widthHeightScale),
  133. this.videoMat.setFloat("bforceforceKeepContent", -1)) : this.videoMat.setFloat("tvWidthHeightScale", -1)
  134. }
  135. async setVideo(e, t=!1, r=!0)
  136. {
  137. return this.tvMeshs.length != 0 ? (
  138. logger.warn(`[Engine] Set Video. length!=0, mesh: ${this.meshPath}, src: ${e.src}`),
  139. new Promise((n,o)=>
  140. {
  141. if (!(e instanceof HTMLVideoElement))
  142. return logger.error("[Engine] Error, param of setVideo must be a HTMLVideoElement"),
  143. o(new XTvVideoElementError("[Engine] param of setVideo must be a HTMLVideoElement"));
  144. this.videoElement = e,
  145. r == !1
  146. && (t == !1 || checkOS().isIOS)
  147. && e.crossOrigin !== "anonymous"
  148. && (e.crossOrigin = "anonymous", e.load()),
  149. this.videoElement.addEventListener("loadedmetadata", a=>{
  150. this.videoElement.videoWidth > 0 ? this.videoMat.setFloat("mvWidthHeightScale", this.videoElement.videoWidth / this.videoElement.videoHeight) : this.videoMat.setFloat("mvWidthHeightScale", 16 / 9)
  151. }),
  152. this.videoTexture.updateURL(this.videoElement.src),
  153. n(this)
  154. })
  155. ) : (
  156. logger.warn(`[Engine] Set Video. length==0, mesh: ${this.meshPath}, src: ${e.src}`),
  157. this.meshPath == ""
  158. ? (
  159. logger.error("[Engine] Error, television meshPath is empty."),
  160. Promise.reject(new XTvVideoElementError("[Engine] Error, television meshPath is empty."))
  161. )
  162. : this._scenemanager.urlTransformer(this.meshPath).then(n => new Promise((o,a) =>
  163. e instanceof HTMLVideoElement
  164. ? (
  165. this.videoElement = e,
  166. r == !1
  167. && (t == !1 || checkOS().isIOS)
  168. && e.crossOrigin !== "anonymous"
  169. && (e.crossOrigin = "anonymous", e.load()),
  170. BABYLON.SceneLoader.LoadAssetContainerAsync("", n, this.scene, null, ".glb").then(s=>{
  171. for (let u = s.materials.length - 1; u >= 0; --u) s.materials[u].dispose();
  172. this.videoTexture = new BABYLON.VideoTexture("videoTex_" + Date.now(),e,this.scene,!1,!0,void 0,{
  173. autoPlay: !0,
  174. autoUpdateTexture: !0,
  175. muted: !0
  176. }),
  177. this.videoTexture.vAng = this.vAng,
  178. this.videoMat = new BABYLON.ShaderMaterial("videoMat_" + Date.now(),this.scene,{
  179. vertexSource: tvVertex,
  180. fragmentSource: tvFragment
  181. },{
  182. attributes: ["uv", "position"],
  183. uniforms: ["view", "projection", "worldViewProjection", "world"]
  184. }),
  185. this.videoMat.setTexture("texture_video", this.videoTexture),
  186. this.videoMat.setFloat("tvWidthHeightScale", -1),
  187. this.videoMat.setFloat("mvWidthHeightScale", 16 / 9),
  188. this.videoMat.setFloat("bforceforceKeepContent", -1),
  189. this.videoMat.backFaceCulling = !1,
  190. this.videoMat.sideOrientation = BABYLON.Mesh.FRONTSIDE,
  191. this.videoElement.addEventListener("loadedmetadata", u=>{
  192. this.videoElement.videoWidth > 0
  193. ? this.videoMat.setFloat("mvWidthHeightScale", this.videoElement.videoWidth / this.videoElement.videoHeight)
  194. : this.videoMat.setFloat("mvWidthHeightScale", 16 / 9)
  195. });
  196. const tvMeshs = [];
  197. for (let u = 0; u < s.meshes.length; ++u)
  198. s.meshes[u].visibility = 1,
  199. s.meshes[u].isPickable = !0,
  200. s.meshes[u].checkCollisions = !1,
  201. s.meshes[u].material = this.videoMat,
  202. "hasVertexAlpha"in s.meshes[u] && (s.meshes[u].hasVertexAlpha = !1),
  203. this.scene.addMesh(s.meshes[u]),
  204. tvMeshs.push(new XStaticMesh({
  205. id: s.meshes[u].id,
  206. mesh: s.meshes[u],
  207. xtype: EMeshType.Tv
  208. }));
  209. this.changeTvFitMode(),
  210. this.tvMeshs = tvMeshs,
  211. this.toggle(!0),
  212. o(this)
  213. }).catch(s=>{
  214. logger.error("[Engine] setVideo: create Tv by input mesh error! " + s),
  215. a(new XTvModelError("[Engine] setVideo: create Tv by input mesh error! " + s))
  216. })
  217. )
  218. : a(new XTvVideoElementError("[Engine] param of setVideo must be a HTMLVideoElement"))
  219. ))
  220. )
  221. }
  222. async setSameVideo(e, t="") {
  223. return e == null || e == null ? (logger.error("[Engine] setSameVideo: input material is null or undefined "),
  224. Promise.reject(new XTvModelError("[Engine] setSameVideo input material is null or undefined !"))) : this.tvMeshs.length != 0 && t == "" ? (logger.warn(`[Engine] Set mirror video. length!=0, mesh: ${this.meshPath}`),
  225. new Promise((r,n)=>{
  226. try {
  227. this.videoMat = e,
  228. this.tvMeshs.forEach(o=>{
  229. o.setMaterial(e)
  230. }
  231. ),
  232. this.changeTvFitMode(),
  233. r(this)
  234. } catch (o) {
  235. logger.error("[Engine] setSameVideo: create Tv by input mesh error! " + o),
  236. n(new XTvModelError("[Engine] create Tv by input mesh error! " + o))
  237. }
  238. }
  239. )) : (t != "" && (this.meshPath = t,
  240. this.widthHeightScale = -1),
  241. this.meshPath == "" ? (logger.error("[Engine] Error, setSameVideo television meshPath is empty."),
  242. Promise.reject(new XTvVideoElementError("[Engine] Error, setSameVideo television meshPath is empty."))) : (logger.warn(`[Engine] Set mirror video. length==0, mesh: ${this.meshPath}`),
  243. this._scenemanager.urlTransformer(this.meshPath).then(r=>new Promise((n,o)=>(this.videoMat = e,
  244. e != null && e.getActiveTextures()[0] && (this.videoElement = e == null ? void 0 : e.getActiveTextures()[0].video),
  245. BABYLON.SceneLoader.LoadAssetContainerAsync("", r, this.scene, null, ".glb").then(a=>{
  246. for (let l = a.materials.length - 1; l >= 0; --l)
  247. a.materials[l].dispose();
  248. const s = [];
  249. for (let l = 0; l < a.meshes.length; ++l)
  250. a.meshes[l].visibility = 0,
  251. a.meshes[l].isPickable = !0,
  252. a.meshes[l].checkCollisions = !1,
  253. a.meshes[l].material = this.videoMat,
  254. "hasVertexAlpha"in a.meshes[l] && (a.meshes[l].hasVertexAlpha = !1),
  255. this.scene.addMesh(a.meshes[l]),
  256. s.push(new XStaticMesh({
  257. id: a.meshes[l].id,
  258. mesh: a.meshes[l],
  259. xtype: EMeshType.Tv
  260. }));
  261. t != "" && this.cleanTv(!1, !1),
  262. this.tvMeshs = s,
  263. this.changeTvFitMode(),
  264. n(this)
  265. }
  266. ).catch(a=>{
  267. logger.error("[Engine] setSameVideo: create Tv by input mesh error! " + a),
  268. o(new XTvModelError("[Engine] create Tv by input mesh error! " + a))
  269. }
  270. ))))))
  271. }
  272. async changeTvModel(e="") {
  273. return e != "" && (this.meshPath = e,
  274. this.widthHeightScale = -1),
  275. this.meshPath == "" ? (logger.error("[Engine] Error,changeTvModel television meshPath is empty."),
  276. Promise.reject(new XTvVideoElementError("[Engine] Error, changeTvModel television meshPath is empty."))) : this.videoMat == null || this.videoMat == null ? (logger.error("[Engine] changeTvModel: videoMat is null or undefined! "),
  277. Promise.reject(new XTvModelError("[Engine] changeTvModel: videoMat is null or undefined!"))) : this._scenemanager.urlTransformer(this.meshPath).then(t=>new Promise((r,n)=>BABYLON.SceneLoader.LoadAssetContainerAsync("", t, this.scene, null, ".glb").then(o=>{
  278. for (let s = o.materials.length - 1; s >= 0; --s)
  279. o.materials[s].dispose();
  280. const a = [];
  281. for (let s = 0; s < o.meshes.length; ++s)
  282. o.meshes[s].visibility = 0,
  283. o.meshes[s].isPickable = !0,
  284. o.meshes[s].checkCollisions = !1,
  285. o.meshes[s].material = this.videoMat,
  286. "hasVertexAlpha"in o.meshes[s] && (o.meshes[s].hasVertexAlpha = !1),
  287. this.scene.addMesh(o.meshes[s]),
  288. a.push(new XStaticMesh({
  289. id: o.meshes[s].id,
  290. mesh: o.meshes[s],
  291. xtype: EMeshType.Tv
  292. }));
  293. e != "" && this.cleanTv(!1, !1),
  294. this.tvMeshs = a,
  295. this.changeTvFitMode(),
  296. r(this)
  297. }
  298. ).catch(o=>{
  299. logger.error("[Engine] changeTvModel: create Tv by input mesh error! " + o),
  300. n(new XTvModelError("[Engine] changeTvModel: create Tv by input mesh error! " + o))
  301. }
  302. )))
  303. }
  304. calWidthHeightScale() {
  305. const e = [1e5, 1e5, 1e5]
  306. , t = [-1e5, -1e5, -1e5];
  307. for (let a = 0; a < this.tvMeshs.length; ++a)
  308. if (this.tvMeshs[a].mesh.name != "__root__") {
  309. const s = this.tvMeshs[a].mesh.getBoundingInfo().boundingBox.vectorsWorld;
  310. for (let l = 0; l < s.length; ++l)
  311. e[0] > s[l].x && (e[0] = s[l].x),
  312. e[1] > s[l].y && (e[1] = s[l].y),
  313. e[2] > s[l].z && (e[2] = s[l].z),
  314. t[0] < s[l].x && (t[0] = s[l].x),
  315. t[1] < s[l].y && (t[1] = s[l].y),
  316. t[2] < s[l].z && (t[2] = s[l].z);
  317. break
  318. }
  319. const r = t[0] - e[0]
  320. , n = t[1] - e[1]
  321. , o = t[2] - e[2];
  322. return Math.sqrt(r * r + o * o) / Math.abs(n)
  323. }
  324. cleanTv(e=!1, t=!0) {
  325. logger.warn("[Engine] cleanTV");
  326. for (let r = 0; r < this.tvMeshs.length; ++r)
  327. this.tvMeshs[r].dispose(e, t);
  328. this.tvMeshs = [],
  329. this.meshPath = ""
  330. }
  331. }