소스 검색

Add GeometryUVPreviewPlugin and its example.

master
Palash Bansal 2 년 전
부모
커밋
39ddebb1c3
No account linked to committer's email address

+ 35
- 0
examples/geometry-uv-preview/index.html 파일 보기

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Render Target Preview</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"threepipe": "./../../dist/index.mjs"
}
}

</script>
<style id="example-style">
html, body, #canvas-container, #mcanvas {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
}
</style>
<script type="module" src="../examples-utils/simple-code-preview.mjs"></script>
<script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script>
</head>
<body>
<div id="canvas-container">
<canvas id="mcanvas"></canvas>
</div>

</body>

+ 35
- 0
examples/geometry-uv-preview/script.ts 파일 보기

@@ -0,0 +1,35 @@
import {_testFinish, GeometryUVPreviewPlugin, IObject3D, ThreeViewer} from 'threepipe'

const viewer = new ThreeViewer({
canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
rgbm: true,
})

async function init() {

const uvPreview = viewer.addPluginSync(GeometryUVPreviewPlugin)

await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr')
const result = await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/IridescentDishWithOlives.glb', {
autoCenter: true,
autoScale: true,
})

const added = false

result?.traverse((obj) => {
if (obj.geometry && !added) {
// added = true
uvPreview.addGeometry(obj.geometry, obj.name)
}
})


// uvPreview.add(()=>depth.target, 'depth', false, true)
// uvPreview.add(()=>normal.target, 'normal', false, false)
// uvPreview.add(()=>viewer.renderManager.composerTarget, 'composer-1', false, false)
// uvPreview.add(()=>viewer.renderManager.composerTarget2, 'composer-2', false, false)

}

init().then(_testFinish)

+ 2
- 1
examples/index.html 파일 보기

@@ -267,13 +267,14 @@
<h2 class="category">Animation</h2>
<ul>
<li><a href="./gltf-animation-plugin/">glTF Animation Plugin </a></li>
<li><a href="./gltf-animation-plugin/">Popmotion Plugin </a></li>
<li><a href="./popmotion-plugin/">Popmotion Plugin </a></li>
<li><a href="./gltf-camera-animation/">glTF Camera Animation </a></li>
<li><a href="./gltf-animation-page-scroll/">glTF Animation Page Scroll </a></li>
</ul>
<h2 class="category">Utils</h2>
<ul>
<li><a href="./render-target-preview/">Render Target Preview Plugin </a></li>
<li><a href="./geometry-uv-preview/">Geometry UV Preview Plugin </a></li>
<li><a href="./parallel-asset-import/">Parallel Asset Import </a></li>
<li><a href="./obj-to-glb/">Convert OBJ to GLB </a></li>
<li><a href="./3dm-to-glb/">Convert 3DM to GLB </a></li>

+ 1
- 0
src/plugins/index.ts 파일 보기

@@ -12,6 +12,7 @@ export type {NormalBufferPluginEventTypes, NormalBufferPluginPass, NormalBufferP

// ui
export {RenderTargetPreviewPlugin} from './ui/RenderTargetPreviewPlugin'
export {GeometryUVPreviewPlugin} from './ui/GeometryUVPreviewPlugin'
export {ViewerUiConfigPlugin} from './ui/ViewerUiConfigPlugin'
export {SceneUiConfigPlugin} from './ui/SceneUiConfigPlugin'


+ 49
- 0
src/plugins/ui/GeometryUVPreviewPlugin.css 파일 보기

@@ -0,0 +1,49 @@
#GeometryUVPreviewPluginContainer{
position: absolute;
left: 0;
bottom: 0;
width: 100%;
z-index: 1000;
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 5px;
padding: 5px;
pointer-events: none;
height: auto;
}
.GeometryUVPreviewPluginTarget{
position: relative;
width: 200px;
height: 200px;
}
.GeometryUVPreviewPluginCollapsed{
height: 25px;
}
.GeometryUVPreviewPluginTargetHeader{
position: absolute;
top: 0;
left: 0;
right: 0;
background: rgba(0,0,0,0.5);
color: white;
padding: 2px;
font-size: 16px;
height: 20px;
font-family: monospace;
text-align: center;
pointer-events: auto;
cursor: pointer;
}
.GeometryUVPreviewPluginTargetHeader::after{
content: '-';
position: absolute;
right: 2px;
width: 20px;
height: 20px;
line-height: 16px;
}
.GeometryUVPreviewPluginCollapsed .GeometryUVPreviewPluginTargetHeader::after{
content: '+';
line-height: 20px;
}

+ 166
- 0
src/plugins/ui/GeometryUVPreviewPlugin.ts 파일 보기

@@ -0,0 +1,166 @@
import {AViewerPluginSync, ThreeViewer} from '../../viewer'
import {createDiv, createStyles, getOrCall, onChange, ValOrFunc} from 'ts-browser-helpers'
import styles from './GeometryUVPreviewPlugin.css'
import {CustomContextMenu} from '../../utils'
import {uiFolderContainer, uiToggle} from 'uiconfig.js'
import {IGeometry} from '../../core'
import {UVsDebug} from 'three/examples/jsm/utils/UVsDebug.js'

export interface TargetBlock {
target: ValOrFunc<IGeometry|undefined>
name: string
visible: boolean
div: HTMLDivElement
uvCanvas?: HTMLCanvasElement
}

@uiFolderContainer('Render Target Preview Plugin')
export class GeometryUVPreviewPlugin<TEvent extends string> extends AViewerPluginSync<TEvent> {
static readonly PluginType = 'GeometryUVPreviewPlugin'

@uiToggle('Enabled')
@onChange(GeometryUVPreviewPlugin.prototype.refreshUi) enabled = true
toJSON: any = null

mainDiv: HTMLDivElement = createDiv({id: 'GeometryUVPreviewPluginContainer', addToBody: false})
stylesheet?: HTMLStyleElement

constructor(enabled = true) {
super()
this.enabled = enabled
}

targetBlocks: TargetBlock[] = []

onAdded(viewer: ThreeViewer): void {
super.onAdded(viewer)

viewer.addEventListener('postRender', this._postRender)
this.stylesheet = createStyles(styles, viewer.container)
this.refreshUi()
}

onRemove(viewer: ThreeViewer): void {
viewer.removeEventListener('postRender', this._postRender)
this.stylesheet?.remove()
this.stylesheet = undefined
this.refreshUi()
super.onRemove(viewer)
}

private _postRender = () => {
if (!this._viewer) return

for (const target of this.targetBlocks) {
if (!target.visible) continue
const geo = getOrCall(target.target)
if (!geo?.attributes?.uv) {
// todo draw white or pink
continue
}
if (!target.uvCanvas) {
target.uvCanvas = UVsDebug(geo, 1024)
target.uvCanvas.style.width = '100%'
target.uvCanvas.style.height = '100%'
}
if (target.uvCanvas && target.uvCanvas.parentElement !== target.div) target.div.appendChild(target.uvCanvas)
// const rect = target.div.getBoundingClientRect()
// const canvasRect = this._viewer.canvas.getBoundingClientRect()
// rect.x = rect.x - canvasRect.x
// rect.y = canvasRect.height + canvasRect.y - rect.y - rect.height
// if (Array.isArray(tex)) {
// // todo support multi target
// this._viewer.console.warn('Multi target preview not supported yet')
// continue
// }
// const outputColorSpace = this._viewer.renderManager.webglRenderer.outputColorSpace
// if (!target.originalColorSpace) this._viewer.renderManager.webglRenderer.outputColorSpace = SRGBColorSpace
// this._viewer.renderManager.blit(null, {
// source: tex,
// clear: !target.transparent,
// respectColorSpace: !target.originalColorSpace,
// viewport: new Vector4(rect.x, rect.y, rect.width, rect.height),
// })
// this._viewer.renderManager.webglRenderer.outputColorSpace = outputColorSpace
}
}

addGeometry(target: ValOrFunc<IGeometry|undefined>, name: string, visible = true): this {
if (!target) return this
const div = document.createElement('div')
const targetDef: TargetBlock = {target, name, div, visible}
div.classList.add('GeometryUVPreviewPluginTarget')
if (!targetDef.visible) div.classList.add('GeometryUVPreviewPluginCollapsed')
const header = document.createElement('div')
header.classList.add('GeometryUVPreviewPluginTargetHeader')
header.innerText = name
header.onclick = () => {
targetDef.visible = !targetDef.visible
if (!targetDef.visible) div.classList.add('GeometryUVPreviewPluginCollapsed')
else div.classList.remove('GeometryUVPreviewPluginCollapsed')
this._viewer?.setDirty()
}
header.oncontextmenu = (e) => {
e.preventDefault()
e.stopPropagation()
CustomContextMenu.Create({
'Download': () => this.downloadGeometryUV(targetDef),
'Remove': () => this.removeGeometry(target),
}, e.clientX, e.clientY)
}
div.appendChild(header)
this.mainDiv.appendChild(div)
this.targetBlocks.push(targetDef)
this.refreshUi()
return this
}

removeGeometry(target: ValOrFunc<IGeometry|undefined>): this {
const index = this.targetBlocks.findIndex(t => t.target === target)
if (index >= 0) {
const t = this.targetBlocks[index]
this.targetBlocks.splice(index, 1)
t.div.remove()
}
this.refreshUi()
return this
}

downloadGeometryUV(targetDef: TargetBlock): this {
if (!this._viewer) return this
if (!targetDef.uvCanvas) return this
const canvas = targetDef.uvCanvas
const url = canvas.toDataURL('image/png')
const link = document.createElement('a')
document.body.appendChild(link)
link.style.display = 'none'
link.href = url
link.download = 'renderTarget.' + 'png'
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
return this
}

refreshUi(): void {
if (!this.mainDiv) return
if (!this._viewer) {
if (this.mainDiv.parentElement) this.mainDiv.remove()
this.mainDiv.style.display = 'none'
this.mainDiv.style.zIndex = '1000'
return
}
if (!this.mainDiv.parentElement) this._viewer.container?.appendChild(this.mainDiv)
this.mainDiv.style.display = this.enabled ? 'flex' : 'none'
this.mainDiv.style.zIndex = parseInt(this._viewer.canvas.style.zIndex || '0') + 1 + ''
this._viewer?.setDirty()
}

dispose() {
for (const target of this.targetBlocks) {
this.removeGeometry(target.target)
}
super.dispose()
}

}

Loading…
취소
저장