| @@ -30,6 +30,7 @@ | |||
| color: #333; | |||
| font-size: 2em; | |||
| font-family: sans-serif; | |||
| pointer-events: none; | |||
| } | |||
| </style> | |||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||
| @@ -0,0 +1,35 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>GLTF Transmission Test</title> | |||
| <!-- 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", | |||
| "uiconfig-tweakpane": "https://unpkg.com/uiconfig-tweakpane@latest/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> | |||
| @@ -0,0 +1,51 @@ | |||
| import {_testFinish, IObject3D, RenderTargetPreviewPlugin, ThreeViewer, TweakpaneUiPlugin} from 'threepipe' | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| msaa: false, | |||
| rgbm: true, | |||
| zPrepass: false, | |||
| }) | |||
| async function init() { | |||
| const targetPreview = viewer.addPluginSync(RenderTargetPreviewPlugin) | |||
| targetPreview.addTarget(()=>viewer.renderManager.composerTarget, 'composer-1', false, false) | |||
| viewer.renderManager.renderPass.preserveTransparentTarget = true | |||
| targetPreview.addTarget(()=>viewer.renderManager.renderPass.transparentTarget, 'transparent', true, true) | |||
| targetPreview.addTarget(()=>viewer.renderManager.composerTarget2, 'composer-2', false, false) | |||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||
| const [model, model2] = await Promise.all([ | |||
| viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/IridescenceLamp.glb', { | |||
| autoCenter: true, | |||
| autoScale: true, | |||
| }), | |||
| viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/IridescentDishWithOlives.glb', { | |||
| autoCenter: true, | |||
| autoScale: true, | |||
| }), | |||
| ]) | |||
| if (!model || !model2) { | |||
| console.error('model not loaded') | |||
| return | |||
| } | |||
| model.position.x = -1 | |||
| model2.position.x = 1 | |||
| model2.position.y = -1.2 | |||
| const ui = viewer.addPluginSync(new TweakpaneUiPlugin(false)) | |||
| const m1 = model?.getObjectByName('lamp_transmission') | |||
| const m2 = model2?.getObjectByName('glassCover') | |||
| const materials = [...m1?.materials || [], ...m2?.materials || []] | |||
| for (const material of materials) { | |||
| const config = material.uiConfig | |||
| if (!config) continue | |||
| ui.appendChild(config) | |||
| } | |||
| } | |||
| init().then(_testFinish) | |||
| @@ -243,15 +243,17 @@ | |||
| </ul> | |||
| <h2 class="category">Utils</h2> | |||
| <ul> | |||
| <li><a href="./parallel-asset-import/">Parallel Asset Import </a></li> | |||
| <li><a href="./obj-to-glb/">Convert OBJ to GLB </a></li> | |||
| </ul> | |||
| <h2 class="category">Tests</h2> | |||
| <ul> | |||
| <li><a href="./import-test/">Import Test</a></li> | |||
| <li><a href="./sphere-rgbm-test/">Sphere RGBM Test </a></li> | |||
| <li><a href="./sphere-half-float-test/">Sphere Half Float Test </a></li> | |||
| <li><a href="./sphere-msaa-test/">Sphere MSAA Test </a></li> | |||
| <li><a href="./gltf-transmission-test/">GLTF Transmission Test </a></li> | |||
| <li><a href="./sphere-rgbm-test/">RGBM Test </a></li> | |||
| <li><a href="./sphere-half-float-test/">Half Float Test </a></li> | |||
| <li><a href="./sphere-msaa-test/">MSAA Test </a></li> | |||
| <li><a href="./z-prepass/">Z-Prepass Test </a></li> | |||
| <li><a href="./import-test/">Import Test</a></li> | |||
| </ul> | |||
| </div> | |||
| <div class="iframe-container"> | |||
| @@ -0,0 +1,35 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Parallel Asset Import/Download</title> | |||
| <!-- 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", | |||
| "uiconfig-tweakpane": "https://unpkg.com/uiconfig-tweakpane@latest/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> | |||
| @@ -0,0 +1,35 @@ | |||
| import {_testFinish, IObject3D, ThreeViewer} from 'threepipe' | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| msaa: false, | |||
| rgbm: true, | |||
| zPrepass: false, | |||
| }) | |||
| async function init() { | |||
| const [env, model, model2] = await Promise.all([ | |||
| viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr'), | |||
| viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/IridescenceLamp.glb', { | |||
| autoCenter: true, | |||
| autoScale: true, | |||
| }), | |||
| viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/IridescentDishWithOlives.glb', { | |||
| autoCenter: true, | |||
| autoScale: true, | |||
| }), | |||
| ]) | |||
| console.log(env, model, model2) | |||
| if (!model || !model2) { | |||
| console.error('model not loaded') | |||
| return | |||
| } | |||
| model.position.x = -1 | |||
| model2.position.x = 1 | |||
| model2.position.y = -1.2 | |||
| } | |||
| init().then(_testFinish) | |||
| @@ -28,8 +28,9 @@ export class MaterialManager<T = ''> extends EventDispatcher<BaseEvent, T> { | |||
| /** | |||
| * @param info: uuid or template name or material type | |||
| * @param params | |||
| */ | |||
| public findOrCreate(info: string, params?: IMaterialParameters): IMaterial | undefined { | |||
| public findOrCreate(info: string, params?: IMaterialParameters|Material): IMaterial | undefined { | |||
| let mat = this.findMaterial(info) | |||
| if (!mat) mat = this.create(info, params) | |||
| return mat | |||
| @@ -41,7 +42,7 @@ export class MaterialManager<T = ''> extends EventDispatcher<BaseEvent, T> { | |||
| * @param register | |||
| * @param params | |||
| */ | |||
| public create<TM extends IMaterial>(nameOrType: string, {register = true, ...params}: IMaterialParameters&{register?: boolean} = {}): TM | undefined { | |||
| public create<TM extends IMaterial>(nameOrType: string, params: IMaterialParameters = {}, register = true): TM | undefined { | |||
| let template: IMaterialTemplate<any> = {materialType: nameOrType, name: nameOrType} | |||
| while (!template.generator) { // looping so that we can inherit templates, not fully implemented yet | |||
| const t2 = this.findTemplate(template.materialType) // todo add a baseTemplate property to the template? | |||
| @@ -52,7 +53,7 @@ export class MaterialManager<T = ''> extends EventDispatcher<BaseEvent, T> { | |||
| template = {...template, ...t2} | |||
| } | |||
| const material = this._create<TM>(template, params) | |||
| if (material && register !== false) this.registerMaterial(material) | |||
| if (material && register) this.registerMaterial(material) | |||
| return material | |||
| } | |||
| @@ -357,10 +357,15 @@ export const iMaterialUI = { | |||
| type: 'folder', | |||
| label: 'Refraction', | |||
| children: [ | |||
| // { | |||
| // type: 'slider', | |||
| // bounds: [0, 1], | |||
| // property: [material, 'reflectivity'], | |||
| // }, | |||
| { | |||
| type: 'slider', | |||
| bounds: [0, 1], | |||
| property: [material, 'reflectivity'], | |||
| bounds: [0, 4], | |||
| property: [material, 'ior'], | |||
| }, | |||
| { | |||
| type: 'slider', | |||
| @@ -384,6 +389,14 @@ export const iMaterialUI = { | |||
| property: [material, 'thicknessMap'], | |||
| }, | |||
| makeSamplerUi(material, 'thicknessMap'), | |||
| { | |||
| type: 'number', | |||
| property: [material, 'attenuationDistance'], | |||
| }, | |||
| { | |||
| type: 'color', | |||
| property: [material, 'attenuationColor'], | |||
| }, | |||
| ], | |||
| } | |||
| ), | |||
| @@ -431,6 +444,50 @@ export const iMaterialUI = { | |||
| ], | |||
| } | |||
| ), | |||
| iridescence: (material: PhysicalMaterial): UiObjectConfig => ( | |||
| { | |||
| type: 'folder', | |||
| label: 'Iridescence', | |||
| children: [ | |||
| { | |||
| type: 'slider', | |||
| bounds: [0, 3], | |||
| label: 'Intensity', | |||
| property: [material, 'iridescence'], | |||
| }, | |||
| { | |||
| type: 'slider', | |||
| bounds: [0, 3], | |||
| label: 'IOR', | |||
| property: [material, 'iridescenceIOR'], | |||
| }, | |||
| { | |||
| type: 'slider', | |||
| bounds: [0, 500], | |||
| label: 'Thickness0', | |||
| property: [material.iridescenceThicknessRange, '0'], | |||
| onChange: (ev)=>material.setDirty({uiChangeEvent: ev}), | |||
| }, | |||
| { | |||
| type: 'slider', | |||
| bounds: [0, 500], | |||
| label: 'Thickness1', | |||
| property: [material.iridescenceThicknessRange, '1'], | |||
| onChange: (ev)=>material.setDirty({uiChangeEvent: ev}), | |||
| }, | |||
| { | |||
| type: 'image', | |||
| property: [material, 'iridescenceMap'], | |||
| }, | |||
| makeSamplerUi(material, 'iridescenceMap'), | |||
| { | |||
| type: 'image', | |||
| property: [material, 'iridescenceThicknessMap'], | |||
| }, | |||
| makeSamplerUi(material, 'iridescenceThicknessMap'), | |||
| ], | |||
| } | |||
| ), | |||
| sheen: (material: PhysicalMaterial): UiObjectConfig => ( | |||
| { | |||
| type: 'folder', | |||
| @@ -61,6 +61,7 @@ export class PhysicalMaterial extends MeshPhysicalMaterial<IMaterialEvent, Physi | |||
| constructor({customMaterialExtensions, ...parameters}: MeshPhysicalMaterialParameters & IMaterialParameters = {}) { | |||
| super(parameters) | |||
| this.fog = false | |||
| this.attenuationDistance = 0 // infinite distance (for Ui) | |||
| this.setDirty = this.setDirty.bind(this) | |||
| if (customMaterialExtensions) this.registerMaterialExtensions(customMaterialExtensions) | |||
| iMaterialCommons.upgradeMaterial.call(this) | |||
| @@ -138,6 +139,7 @@ export class PhysicalMaterial extends MeshPhysicalMaterial<IMaterialEvent, Physi | |||
| iMaterialUI.emission(this), | |||
| iMaterialUI.transmission(this), | |||
| iMaterialUI.clearcoat(this), | |||
| iMaterialUI.iridescence(this), | |||
| iMaterialUI.sheen(this), | |||
| ...iMaterialUI.misc(this), | |||
| ], | |||
| @@ -170,6 +172,9 @@ export class PhysicalMaterial extends MeshPhysicalMaterial<IMaterialEvent, Physi | |||
| if (clearCurrentUserData === undefined) clearCurrentUserData = (<Material>parameters).isMaterial | |||
| if (clearCurrentUserData) this.userData = {} | |||
| iMaterialCommons.setValues(super.setValues).call(this, parameters) | |||
| if (!isFinite(this.attenuationDistance)) this.attenuationDistance = 0 // hack for ui | |||
| this.userData.uuid = this.uuid // just in case | |||
| return this | |||
| } | |||
| @@ -273,6 +278,7 @@ export class PhysicalMaterial extends MeshPhysicalMaterial<IMaterialEvent, Physi | |||
| reflectivity: 0.5, // because this is used in Material.js->toJSON and fromJSON instead of ior | |||
| iridescence: 0, | |||
| iridescenceMap: null, | |||
| iridescenceIOR: 1.3, | |||
| iridescenceThicknessRange: [100, 400], | |||
| @@ -1,7 +1,6 @@ | |||
| import {IPipelinePass} from './Pass' | |||
| import {RenderPass} from 'three/examples/jsm/postprocessing/RenderPass.js' | |||
| import { | |||
| CanvasTexture, | |||
| Color, | |||
| HalfFloatType, | |||
| LinearFilter, | |||
| @@ -53,12 +52,10 @@ export class ExtendedRenderPass extends RenderPass implements IPipelinePass<'ren | |||
| this._transparentTarget = undefined | |||
| } | |||
| canvasTexture: CanvasTexture | |||
| constructor(renderManager: ViewerRenderManager, overrideMaterial?: Material, clearColor = new Color(0, 0, 0), clearAlpha = 0) { | |||
| super(undefined, undefined, overrideMaterial, clearColor, clearAlpha) | |||
| this.renderManager = renderManager | |||
| // this.canvasTexture = new CanvasTexture(renderManager.renderer.domElement) | |||
| this._blendPass = new GenericBlendTexturePass({}, 'c = vec4(a.rgb * (1. - b.a) + b.rgb * b.a, 1.);') | |||
| this.setDirty = this.setDirty.bind(this) | |||
| } | |||
| @@ -250,6 +247,7 @@ export class ExtendedRenderPass extends RenderPass implements IPipelinePass<'ren | |||
| this.clear = curClear | |||
| } | |||
| // console.log(renderer.info.render.calls) | |||
| if (renderer.info.render.calls > 0) { | |||
| // console.log('missive blit', renderer.info.render.frame) | |||
| @@ -370,9 +370,10 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| * @param setBackground - Set the background image of the scene from the same map. | |||
| * @param options - Options for importing the asset. See {@link ImportAssetOptions} | |||
| */ | |||
| async setEnvironmentMap(map: string | IAsset | null | ITexture, {setBackground = false, ...options}: ImportAssetOptions&{setBackground?: boolean} = {}): Promise<void> { | |||
| async setEnvironmentMap(map: string | IAsset | null | ITexture, {setBackground = false, ...options}: ImportAssetOptions&{setBackground?: boolean} = {}): Promise<ITexture | null> { | |||
| this._scene.environment = map && !(<ITexture>map).isTexture ? await this.assetManager.importer.importSingle<ITexture>(map as string|IAsset, options) || null : <ITexture>map || null | |||
| if (setBackground) return this.setBackgroundMap(this._scene.environment) | |||
| return this._scene.environment | |||
| } | |||
| /** | |||
| @@ -381,9 +382,10 @@ export class ThreeViewer extends EventDispatcher<IViewerEvent, IViewerEventTypes | |||
| * @param setEnvironment - Set the environment map of the scene from the same map. | |||
| * @param options - Options for importing the asset. See {@link ImportAssetOptions} | |||
| */ | |||
| async setBackgroundMap(map: string | IAsset | null | ITexture, {setEnvironment = false, ...options}: ImportAssetOptions&{setBackground?: boolean} = {}): Promise<void> { | |||
| async setBackgroundMap(map: string | IAsset | null | ITexture, {setEnvironment = false, ...options}: ImportAssetOptions&{setBackground?: boolean} = {}): Promise<ITexture | null> { | |||
| this._scene.background = map && !(<ITexture>map).isTexture ? await this.assetManager.importer.importSingle<ITexture>(map as string|IAsset, options) || null : <ITexture>map || null | |||
| if (setEnvironment) return this.setEnvironmentMap(this._scene.background) | |||
| return this._scene.background | |||
| } | |||
| /** | |||