threepipe
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

TransfrSharePlugin.ts 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import {AssetExporterPlugin, AViewerPluginSync, IObject3D, uiButton, uiFolderContainer, uiInput} from 'threepipe'
  2. /**
  3. * Transfr Share Plugin
  4. * A sample plugin that provides helpers to export and upload scene to a server and get a shareable link.
  5. * It uses the options from the {@link AssetExporterPlugin} to export the scene or object, and can be configured using it's ui.
  6. *
  7. * Uses the free service [transfr.one](https://transfr.one/) by default which deletes the files after a certain time,
  8. * but the url can be changed to a custom backend or a self-hosted version of transfr.
  9. *
  10. * Note: since the uploaded files are publicly accessible by anyone by default, it is recommended to encrypt the file using the exporter options or use a secure backend.
  11. */
  12. @uiFolderContainer('Share Link')
  13. export class TransfrSharePlugin extends AViewerPluginSync<''> {
  14. public static readonly PluginType = 'TransfrSharePlugin'
  15. toJSON: any = null
  16. enabled = true
  17. dependencies = [AssetExporterPlugin]
  18. @uiInput('Server URL')
  19. serverUrl = 'https://bee.transfr.one/scene.glb'
  20. @uiInput()
  21. queryParam = 'm'
  22. @uiInput()
  23. pageUrl = window.location.href
  24. baseUrls: Record<string, string> = {
  25. 'editor': '',
  26. 'viewer': '',
  27. }
  28. async exportObject(model?: IObject3D) {
  29. const exporter = this._viewer?.getPlugin(AssetExporterPlugin)
  30. if (!this._viewer) throw new Error('TransfrSharePlugin: AssetExporter not found')
  31. return model ?
  32. this._viewer.export(model, exporter?.exportOptions ?? {format: 'glb'}) :
  33. this._viewer.exportScene(exporter?.exportOptions ?? {})
  34. }
  35. /**
  36. * Export and get the link of the 3d model or scene
  37. * @param model
  38. */
  39. async getLink(model?: IObject3D) {
  40. const obj = await this.exportObject(model)
  41. if (!obj) {
  42. throw new Error('Failed to export object or scene')
  43. }
  44. const path = 'transfr.one/scene.glb'
  45. this._viewer!.assetManager.setProcessState(path, {
  46. state: 'Uploading',
  47. // progress: data.progress ? data.progress * 100 : undefined,
  48. })
  49. const res = await fetch(this.serverUrl, {
  50. method: 'PUT',
  51. body: obj,
  52. })
  53. if (res.status !== 200) {
  54. throw new Error('Failed to upload file')
  55. }
  56. const data = (await res.text())?.trim()
  57. this._viewer!.assetManager.setProcessState(path, undefined)
  58. // console.log(data)
  59. try {
  60. new URL(data)
  61. } catch (e) {
  62. throw new Error('Invalid URL ' + data)
  63. }
  64. return data
  65. }
  66. private _exporting = false
  67. /**
  68. * Upload the scene and copy the link to clipboard along with the base url and query param if provided
  69. * @param base
  70. * @param param
  71. */
  72. async shareLink(base?: string|URL, param?: string) {
  73. if (this._exporting) return null
  74. this._exporting = true
  75. let link = await this.getLink().catch(e=>{
  76. this._viewer?.console.error(e)
  77. this._viewer?.dialog.alert('Error: Failed to share scene: \n' + e.message)
  78. return null
  79. })
  80. if (link) {
  81. if (base) {
  82. const url = typeof base === 'string' ?
  83. new URL(this.baseUrls[base] ?? base) : base
  84. url.searchParams.set(param || this.queryParam || 'm', link)
  85. link = url.href
  86. }
  87. let copied = false
  88. try {
  89. if (window && window.navigator && navigator.clipboard) {
  90. await navigator.clipboard.writeText(link)
  91. copied = true
  92. }
  93. } catch (e) {
  94. console.error('Failed to copy link', e)
  95. }
  96. this._viewer?.dialog.alert('Link' + (copied ? ' Copied' : '') + ': ' + link + '\n\nNote: File will be deleted in 1 days')
  97. }
  98. this._exporting = false
  99. return link
  100. }
  101. @uiButton('Share editor link', (t: TransfrSharePlugin)=>({hidden: ()=>!t.baseUrls.editor}))
  102. async shareEditorLink() {
  103. return this.shareLink(this.baseUrls.editor, this.queryParam)
  104. }
  105. @uiButton('Share viewer link', (t: TransfrSharePlugin)=>({hidden: ()=>!t.baseUrls.viewer}))
  106. async shareViewerLink() {
  107. return this.shareLink(this.baseUrls.viewer, this.queryParam)
  108. }
  109. @uiButton('Share page link', (t: TransfrSharePlugin)=>({hidden: ()=>!t.pageUrl}))
  110. async sharePageLink() {
  111. return this.shareLink(this.pageUrl, this.queryParam)
  112. }
  113. @uiButton('Share glb link')
  114. async shareGlb() {
  115. return this.shareLink()
  116. }
  117. }