threepipe
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

GeometryUVPreviewPlugin.ts 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import {AViewerPluginSync, ThreeViewer} from '../../viewer'
  2. import {createDiv, createStyles, getOrCall, onChange, ValOrFunc} from 'ts-browser-helpers'
  3. import styles from './GeometryUVPreviewPlugin.css'
  4. import {CustomContextMenu} from '../../utils'
  5. import {uiFolderContainer, uiToggle} from 'uiconfig.js'
  6. import {IGeometry} from '../../core'
  7. import {UVsDebug} from 'three/examples/jsm/utils/UVsDebug.js'
  8. export interface TargetBlock {
  9. target: ValOrFunc<IGeometry|undefined>
  10. name: string
  11. visible: boolean
  12. div: HTMLDivElement
  13. uvCanvas?: HTMLCanvasElement
  14. }
  15. @uiFolderContainer('Render Target Preview Plugin')
  16. export class GeometryUVPreviewPlugin<TEvent extends string> extends AViewerPluginSync<TEvent> {
  17. static readonly PluginType = 'GeometryUVPreviewPlugin'
  18. @uiToggle('Enabled')
  19. @onChange(GeometryUVPreviewPlugin.prototype.refreshUi) enabled = true
  20. toJSON: any = null
  21. mainDiv: HTMLDivElement = createDiv({id: 'GeometryUVPreviewPluginContainer', addToBody: false})
  22. stylesheet?: HTMLStyleElement
  23. constructor(enabled = true) {
  24. super()
  25. this.enabled = enabled
  26. }
  27. targetBlocks: TargetBlock[] = []
  28. onAdded(viewer: ThreeViewer): void {
  29. super.onAdded(viewer)
  30. viewer.addEventListener('postRender', this._postRender)
  31. this.stylesheet = createStyles(styles, viewer.container)
  32. this.refreshUi()
  33. }
  34. onRemove(viewer: ThreeViewer): void {
  35. viewer.removeEventListener('postRender', this._postRender)
  36. this.stylesheet?.remove()
  37. this.stylesheet = undefined
  38. this.refreshUi()
  39. super.onRemove(viewer)
  40. }
  41. private _postRender = () => {
  42. if (!this._viewer) return
  43. for (const target of this.targetBlocks) {
  44. if (!target.visible) continue
  45. const geo = getOrCall(target.target)
  46. if (!geo?.attributes?.uv) {
  47. // todo draw white or pink
  48. continue
  49. }
  50. if (!target.uvCanvas) {
  51. target.uvCanvas = UVsDebug(geo, 1024)
  52. target.uvCanvas.style.width = '100%'
  53. target.uvCanvas.style.height = '100%'
  54. }
  55. if (target.uvCanvas && target.uvCanvas.parentElement !== target.div) target.div.appendChild(target.uvCanvas)
  56. // const rect = target.div.getBoundingClientRect()
  57. // const canvasRect = this._viewer.canvas.getBoundingClientRect()
  58. // rect.x = rect.x - canvasRect.x
  59. // rect.y = canvasRect.height + canvasRect.y - rect.y - rect.height
  60. // if (Array.isArray(tex)) {
  61. // // todo support multi target
  62. // this._viewer.console.warn('Multi target preview not supported yet')
  63. // continue
  64. // }
  65. // const outputColorSpace = this._viewer.renderManager.webglRenderer.outputColorSpace
  66. // if (!target.originalColorSpace) this._viewer.renderManager.webglRenderer.outputColorSpace = SRGBColorSpace
  67. // this._viewer.renderManager.blit(null, {
  68. // source: tex,
  69. // clear: !target.transparent,
  70. // respectColorSpace: !target.originalColorSpace,
  71. // viewport: new Vector4(rect.x, rect.y, rect.width, rect.height),
  72. // })
  73. // this._viewer.renderManager.webglRenderer.outputColorSpace = outputColorSpace
  74. }
  75. }
  76. addGeometry(target: ValOrFunc<IGeometry|undefined>, name: string, visible = true): this {
  77. if (!target) return this
  78. const div = document.createElement('div')
  79. const targetDef: TargetBlock = {target, name, div, visible}
  80. div.classList.add('GeometryUVPreviewPluginTarget')
  81. if (!targetDef.visible) div.classList.add('GeometryUVPreviewPluginCollapsed')
  82. const header = document.createElement('div')
  83. header.classList.add('GeometryUVPreviewPluginTargetHeader')
  84. header.innerText = name
  85. header.onclick = () => {
  86. targetDef.visible = !targetDef.visible
  87. if (!targetDef.visible) div.classList.add('GeometryUVPreviewPluginCollapsed')
  88. else div.classList.remove('GeometryUVPreviewPluginCollapsed')
  89. this._viewer?.setDirty()
  90. }
  91. header.oncontextmenu = (e) => {
  92. e.preventDefault()
  93. e.stopPropagation()
  94. CustomContextMenu.Create({
  95. 'Download': () => this.downloadGeometryUV(targetDef),
  96. 'Remove': () => this.removeGeometry(target),
  97. }, e.clientX, e.clientY)
  98. }
  99. div.appendChild(header)
  100. this.mainDiv.appendChild(div)
  101. this.targetBlocks.push(targetDef)
  102. this.refreshUi()
  103. return this
  104. }
  105. removeGeometry(target: ValOrFunc<IGeometry|undefined>): this {
  106. const index = this.targetBlocks.findIndex(t => t.target === target)
  107. if (index >= 0) {
  108. const t = this.targetBlocks[index]
  109. this.targetBlocks.splice(index, 1)
  110. t.div.remove()
  111. }
  112. this.refreshUi()
  113. return this
  114. }
  115. downloadGeometryUV(targetDef: TargetBlock): this {
  116. if (!this._viewer) return this
  117. if (!targetDef.uvCanvas) return this
  118. const canvas = targetDef.uvCanvas
  119. const url = canvas.toDataURL('image/png')
  120. const link = document.createElement('a')
  121. document.body.appendChild(link)
  122. link.style.display = 'none'
  123. link.href = url
  124. link.download = 'renderTarget.' + 'png'
  125. link.click()
  126. document.body.removeChild(link)
  127. URL.revokeObjectURL(url)
  128. return this
  129. }
  130. refreshUi(): void {
  131. if (!this.mainDiv) return
  132. if (!this._viewer) {
  133. if (this.mainDiv.parentElement) this.mainDiv.remove()
  134. this.mainDiv.style.display = 'none'
  135. this.mainDiv.style.zIndex = '1000'
  136. return
  137. }
  138. if (!this.mainDiv.parentElement) this._viewer.container?.appendChild(this.mainDiv)
  139. this.mainDiv.style.display = this.enabled ? 'flex' : 'none'
  140. this.mainDiv.style.zIndex = parseInt(this._viewer.canvas.style.zIndex || '0') + 1 + ''
  141. this._viewer?.setDirty()
  142. }
  143. dispose() {
  144. for (const target of this.targetBlocks) {
  145. this.removeGeometry(target.target)
  146. }
  147. super.dispose()
  148. }
  149. }