threepipe
Version:
A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.
119 lines (102 loc) • 4.8 kB
text/typescript
import {DataTexture, EquirectangularReflectionMapping, ShaderLib, Vector3} from 'three'
import {onChange, serialize} from 'ts-browser-helpers'
import hdriGroundProj from './HDRiGroundPlugin.glsl'
import {AViewerPluginSync, ThreeViewer} from '../../viewer'
import {shaderReplaceString} from '../../utils'
import {uiPanelContainer, uiSlider, uiToggle, uiVector} from 'uiconfig.js'
export class HDRiGroundPlugin extends AViewerPluginSync {
static readonly PluginType = 'HDRiGroundPlugin'
enabled = false
worldRadius = 100
tripodHeight = 10
originPosition = new Vector3(0, 0, 0)
promptOnBackgroundMismatch = true
// todo
// /**
// * Automatically set the origin position based on the ground position in GroundPlugin
// */
// @serialize()
// @onChange(HDRiGroundPlugin.prototype.setDirty)
// @uiToggle('Auto Ground Position')
// autoGroundPosition = false
constructor(enabled = false, promptOnBackgroundMismatch = true) {
super()
this.setDirty = this.setDirty.bind(this)
this.enabled = enabled
this.promptOnBackgroundMismatch = promptOnBackgroundMismatch
this.addEventListener('deserialize', this.setDirty)
}
setDirty() {
if (!this._viewer) return
const bg = this._viewer.scene.background
if (this.enabled && bg !== this._viewer.scene.environment && bg !== 'environment') {
if (bg && (bg as any).isDataTexture) (bg as DataTexture).mapping = EquirectangularReflectionMapping
else {
const change = this.promptOnBackgroundMismatch ? this._viewer.dialog.confirmSync('Background must be same as environment, do you want to change it?') : true
if (change) {
// const bgui = this._viewer.getPlugin<SimpleBackgroundEnvUiPlugin>('SimpleBackgroundEnvUiPlugin1')
// if (bgui) {
// bgui.envmapBg = true
// bgui.uiConfig.uiRefresh?.(true, 'postFrame')
// } else
this._viewer.scene.background = 'environment'
} else this.enabled = false
}
}
const cubeMat = this._viewer.renderManager.renderer.background.getBoxMesh2()?.material
const unif = cubeMat?.uniforms ?? ShaderLib.backgroundCube.uniforms
if (!unif.tripodHeight) unif.tripodHeight = {value: 1.0}
if (!unif.worldRadius) unif.worldRadius = {value: 1.0}
if (!unif.originPosition) unif.originPosition = {value: new Vector3()}
unif.tripodHeight.value = this.tripodHeight
unif.worldRadius.value = this.worldRadius
unif.originPosition.value.copy(this.originPosition)
if (cubeMat) {
if (this.isDisabled() && cubeMat.defines.HDRi_GROUND_PROJ)
delete cubeMat.defines.HDRi_GROUND_PROJ
else if (!this.isDisabled())
cubeMat.defines.HDRi_GROUND_PROJ = '1'
cubeMat.needsUpdate = true
}
this._viewer.setDirty()
// const m = this._viewer?.scene.modelRoot.children ?? []
// for (const m1 of m) {
// m1.position.y = -this.tripodHeight + new Box3B().expandByObject(m1, true, true).getSize(new Vector3()).y / 2
// }
}
onAdded(viewer: ThreeViewer): void {
super.onAdded(viewer)
if (this._viewer?.renderManager.webglRenderer?.background.getBoxMesh())
viewer.console.error('HDRi Ground Plugin must be added before setting any cube or env map')
if (!ShaderLib.backgroundCube.fragmentShader.includes('#ifdef HDRi_GROUND_PROJ')) {
ShaderLib.backgroundCube.fragmentShader = shaderReplaceString(ShaderLib.backgroundCube.fragmentShader, 'void main() {', hdriGroundProj, {prepend: true})
ShaderLib.backgroundCube.fragmentShader = shaderReplaceString(ShaderLib.backgroundCube.fragmentShader, 'vec3 vReflect = vWorldDirection;', `
vec3 vReflect =
#ifdef HDRi_GROUND_PROJ
hdriProject()
#else
vWorldDirection
#endif
;
`)
}
viewer.scene.addEventListener('environmentChanged', this.setDirty)
}
}