| import {ThreeViewer} from 'threepipe' | import {ThreeViewer} from 'threepipe' | ||||
| import {ThreeSVGRendererPlugin} from '@threepipe/plugin-svg-renderer' | import {ThreeSVGRendererPlugin} from '@threepipe/plugin-svg-renderer' | ||||
| const viewer = new ThreeViewer({...}) | |||||
| const viewer = new ThreeViewer({ | |||||
| ..., | |||||
| rgbm: false, // this is required | |||||
| }) | |||||
| const svgRender = viewer.addPluginSync(ThreeSVGRendererPlugin) | const svgRender = viewer.addPluginSync(ThreeSVGRendererPlugin) | ||||
| svgRender.autoRender = true // automatically render when camera or any object changes. | svgRender.autoRender = true // automatically render when camera or any object changes. | ||||
| svgRender.autoMakeSvgObjects = true // automatically create SVG objects for all meshes in the scene. | svgRender.autoMakeSvgObjects = true // automatically create SVG objects for all meshes in the scene. | ||||
| const model = await viewer.load<IOBject3D>('path/to/file.glb') | const model = await viewer.load<IOBject3D>('path/to/file.glb') | ||||
| // clear the background of the viewer | // clear the background of the viewer | ||||
| // this is only required if rgbm = false in the viewer | |||||
| viewer.scene.backgroundColor = null | viewer.scene.backgroundColor = null | ||||
| // this is only required if rgbm = true in the viewer | |||||
| viewer.renderManager.screenPass.clipBackground = true | |||||
| viewer.scene.background = null | |||||
| // disable damping to get better experience. | // disable damping to get better experience. | ||||
| viewer.scene.mainCamera.controls!.enableDamping = false | viewer.scene.mainCamera.controls!.enableDamping = false |
| }), | }), | ||||
| ]) | ]) | ||||
| viewer.scene.overrideMaterial = new PhysicalMaterial({ | |||||
| viewer.renderManager.renderPass.overrideMaterial = new PhysicalMaterial({ | |||||
| color: 'white', | color: 'white', | ||||
| roughness: 1, | roughness: 1, | ||||
| metalness: 0, | metalness: 0, | ||||
| expanded: true, | expanded: true, | ||||
| }) | }) | ||||
| ui.setupPlugins(ProgressivePlugin) | ui.setupPlugins(ProgressivePlugin) | ||||
| ui.appendChild(viewer.renderManager.uiConfig) | |||||
| await viewer.getPlugin(ProgressivePlugin)?.convergedPromise | await viewer.getPlugin(ProgressivePlugin)?.convergedPromise | ||||
| 'https://threejs.org/examples/models/obj/male02/male02.obj', | 'https://threejs.org/examples/models/obj/male02/male02.obj', | ||||
| 'https://threejs.org/examples/models/gltf/kira.glb', // slow | 'https://threejs.org/examples/models/gltf/kira.glb', // slow | ||||
| // not working | |||||
| // not working/very slow | |||||
| 'https://threejs.org/examples/models/gltf/Soldier.glb', | 'https://threejs.org/examples/models/gltf/Soldier.glb', | ||||
| 'https://threejs.org/examples/models/gltf/LittlestTokyo.glb', | 'https://threejs.org/examples/models/gltf/LittlestTokyo.glb', | ||||
| 'https://threejs.org/examples/models/gltf/ferrari.glb', | 'https://threejs.org/examples/models/gltf/ferrari.glb', | ||||
| viewer.scene.backgroundColor = null | viewer.scene.backgroundColor = null | ||||
| viewer.scene.background = null | viewer.scene.background = null | ||||
| // viewer.renderManager.screenPass.clipBackground = true // required when rgbm: true | |||||
| viewer.scene.mainCamera.controls!.enableDamping = false | viewer.scene.mainCamera.controls!.enableDamping = false | ||||
| #ifdef HAS_GBUFFER | #ifdef HAS_GBUFFER | ||||
| #ifdef CLIP_BACKGROUND | |||||
| #if (defined(CLIP_BACKGROUND) && CLIP_BACKGROUND > 0) || defined(CLIP_BACKGROUND_FORCE) | |||||
| if(isBackground) diffuseColor.a = 0.0; | if(isBackground) diffuseColor.a = 0.0; | ||||
| if(depth>0.99 && transparentColor.a >= 0.001) diffuseColor.a = transparentColor.a; | if(depth>0.99 && transparentColor.a >= 0.001) diffuseColor.a = transparentColor.a; | ||||
| #endif | #endif |
| this._needsReRender = false | this._needsReRender = false | ||||
| this.reRender(renderManager.renderer) | this.reRender(renderManager.renderer) | ||||
| if (this.clipBackground && !(renderManager as ViewerRenderManager).gbufferTarget) { | if (this.clipBackground && !(renderManager as ViewerRenderManager).gbufferTarget) { | ||||
| // todo warn only when rgbm | |||||
| console.warn('ScreenPass: clipBackground set to true but no gbufferTarget set. Try adding GBufferPlugin.') | console.warn('ScreenPass: clipBackground set to true but no gbufferTarget set. Try adding GBufferPlugin.') | ||||
| } | } | ||||
| } | } | ||||
| super.dispose() | super.dispose() | ||||
| } | } | ||||
| /** | |||||
| * Force clip background. If this is `true` {@link clipBackground} is overridden. | |||||
| * This happens when scene.background and scene.backgroundColor are both null. | |||||
| * This is set in {@link ViewerRenderManager.render}. | |||||
| */ | |||||
| @matDefineBool('CLIP_BACKGROUND_FORCE', undefined, undefined, ScreenPass.prototype.setDirty, true) | |||||
| clipBackgroundForce = false | |||||
| // todo: this is not serialized anymore? | // todo: this is not serialized anymore? | ||||
| @matDefineBool('CLIP_BACKGROUND', undefined, undefined, ScreenPass.prototype.setDirty, true) | |||||
| @matDefineBool('CLIP_BACKGROUND', undefined, undefined, ScreenPass.prototype.setDirty) | |||||
| @uiToggle() clipBackground = false | @uiToggle() clipBackground = false | ||||
| beforeRender(_: IScene, _1: ICamera, renderManager: ViewerRenderManager) { | beforeRender(_: IScene, _1: ICamera, renderManager: ViewerRenderManager) { |
| * @param customDefines - object for setting define value (like ShaderMaterial.defines), otherwise this.material.defines is taken | * @param customDefines - object for setting define value (like ShaderMaterial.defines), otherwise this.material.defines is taken | ||||
| * @param thisMat - access this.defines instead of this.material.defines | * @param thisMat - access this.defines instead of this.material.defines | ||||
| * @param onChange - function to call when the value changes. If a string, it is used as a property name in `this` and called. If a function, it is called. The function is called with the following parameters: key, newVal | * @param onChange - function to call when the value changes. If a string, it is used as a property name in `this` and called. If a function, it is called. The function is called with the following parameters: key, newVal | ||||
| * @param deleteOnFalse - sets to undefined instead of '0' when false | |||||
| * @param deleteOnFalse - sets to undefined instead of '0' when false. Note deleteOnFalse doesn't work with tweakpane ui because the value will be undefined. | |||||
| */ | */ | ||||
| export function matDefineBool(key?: string|symbol, customDefines?: any, thisMat = false, onChange?: (...args: any[]) => any, deleteOnFalse = false): PropertyDecorator { | export function matDefineBool(key?: string|symbol, customDefines?: any, thisMat = false, onChange?: (...args: any[]) => any, deleteOnFalse = false): PropertyDecorator { | ||||
| return matDefine(key, customDefines, thisMat, onChange, (v: any)=>v ? '1' : deleteOnFalse ? undefined : '0', (v: any)=>v && v !== '0') | return matDefine(key, customDefines, thisMat, onChange, (v: any)=>v ? '1' : deleteOnFalse ? undefined : '0', (v: any)=>v && v !== '0') |
| * Same as pixelRatio in three.js | * Same as pixelRatio in three.js | ||||
| * Can be set to `window.devicePixelRatio` to render at device resolution in browsers. | * Can be set to `window.devicePixelRatio` to render at device resolution in browsers. | ||||
| * An optimal value is `Math.min(2, window.devicePixelRatio)` to prevent issues on mobile. This is set when 'auto' is passed. | * An optimal value is `Math.min(2, window.devicePixelRatio)` to prevent issues on mobile. This is set when 'auto' is passed. | ||||
| * Default is 1. | |||||
| */ | */ | ||||
| renderScale?: number | 'auto' | renderScale?: number | 'auto' | ||||
| import {IRenderTarget, RenderManager} from '../rendering' | import {IRenderTarget, RenderManager} from '../rendering' | ||||
| import {HalfFloatType, LinearMipMapLinearFilter, NoColorSpace, RGBM16ColorSpace, UnsignedByteType} from 'three' | import {HalfFloatType, LinearMipMapLinearFilter, NoColorSpace, RGBM16ColorSpace, UnsignedByteType} from 'three' | ||||
| import {IRenderManagerEvent, IRenderManagerOptions} from '../core' | |||||
| import {IRenderManagerEvent, IRenderManagerOptions, IScene} from '../core' | |||||
| import {ExtendedRenderPass, ScreenPass, TViewerScreenShader} from '../postprocessing' | import {ExtendedRenderPass, ScreenPass, TViewerScreenShader} from '../postprocessing' | ||||
| import {uiFolderContainer} from 'uiconfig.js' | |||||
| import {uiFolderContainer, UiObjectConfig} from 'uiconfig.js' | |||||
| import {MaterialExtension} from '../materials' | import {MaterialExtension} from '../materials' | ||||
| import {onChange3} from 'ts-browser-helpers' | import {onChange3} from 'ts-browser-helpers' | ||||
| readonly zPrepass: boolean | readonly zPrepass: boolean | ||||
| readonly renderPass: ExtendedRenderPass | readonly renderPass: ExtendedRenderPass | ||||
| readonly screenPass: ScreenPass | readonly screenPass: ScreenPass | ||||
| declare uiConfig: UiObjectConfig | |||||
| constructor({rgbm = true, msaa = false, depthBuffer = false, ...options}: ViewerRenderManagerOptions) { | constructor({rgbm = true, msaa = false, depthBuffer = false, ...options}: ViewerRenderManagerOptions) { | ||||
| super({ | super({ | ||||
| this.dispatchEvent({type: 'gbufferUnpackExtensionChanged', ...params}) | this.dispatchEvent({type: 'gbufferUnpackExtensionChanged', ...params}) | ||||
| } | } | ||||
| render(scene: IScene, renderToScreen?: boolean): void { | |||||
| const cbf = this.screenPass.clipBackgroundForce | |||||
| if (this.rgbm) { | |||||
| const val = !scene.background && !scene.backgroundColor | |||||
| if (val !== cbf) this.screenPass.clipBackgroundForce = val | |||||
| } | |||||
| super.render(scene, renderToScreen) | |||||
| } | |||||
| } | } |