| @@ -0,0 +1,34 @@ | |||
| <!DOCTYPE html> | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="UTF-8"> | |||
| <title>Directional Light</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" | |||
| } | |||
| } | |||
| </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,66 @@ | |||
| import { | |||
| _testFinish, | |||
| Box3B, | |||
| DirectionalLight, | |||
| IObject3D, | |||
| Mesh, | |||
| PCFSoftShadowMap, | |||
| PhysicalMaterial, | |||
| PlaneGeometry, | |||
| RenderTargetPreviewPlugin, | |||
| ThreeViewer, | |||
| Vector3, | |||
| } from 'threepipe' | |||
| async function init() { | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| msaa: true, | |||
| dropzone: { | |||
| allowedExtensions: ['gltf', 'glb', 'hdr', 'obj', 'fbx', 'bin', 'png', 'jpeg', 'webp', 'jpg', 'exr'], | |||
| addOptions: { | |||
| disposeSceneObjects: true, | |||
| autoSetEnvironment: true, // when hdr/exr is dropped | |||
| }, | |||
| }, | |||
| }) | |||
| // viewer.scene.addObject(new HemisphereLight(0xffffff, 0x444444, 10)) | |||
| const result = await viewer.load<IObject3D>('https://threejs.org/examples/models/fbx/Samba Dancing.fbx', { | |||
| autoCenter: true, | |||
| autoScale: true, | |||
| }) | |||
| const ground = new Mesh( | |||
| new PlaneGeometry(100, 100) | |||
| .rotateX(-Math.PI / 2) | |||
| .translate(0, new Box3B().expandByObject(result!).getSize(new Vector3()).y / -2, 0), | |||
| new PhysicalMaterial({ | |||
| color: '#ffffff', | |||
| }) | |||
| ) | |||
| ground.castShadow = false | |||
| ground.receiveShadow = true | |||
| viewer.scene.addObject(ground) | |||
| const directionalLight = viewer.scene.addObject(new DirectionalLight(0xffffff, 4)) | |||
| directionalLight.position.set(2, 2, 2) | |||
| directionalLight.lookAt(0, 0, 0) | |||
| directionalLight.castShadow = true | |||
| directionalLight.shadow.mapSize.setScalar(1024) | |||
| directionalLight.shadow.camera.near = 0.1 | |||
| directionalLight.shadow.camera.far = 10 | |||
| directionalLight.shadow.camera.top = 2 | |||
| directionalLight.shadow.camera.bottom = -2 | |||
| directionalLight.shadow.camera.left = -2 | |||
| directionalLight.shadow.camera.right = 2 | |||
| viewer.renderManager.renderer.shadowMap.type = PCFSoftShadowMap | |||
| const rt = viewer.addPluginSync(RenderTargetPreviewPlugin) | |||
| rt.addTarget(()=>directionalLight.shadow.map || undefined, 'shadow', true, true, true) | |||
| } | |||
| init().then(_testFinish) | |||
| @@ -6,7 +6,7 @@ async function init() { | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| dropzone: { // this can also be set to true and configured by getting a reference to the DropzonePlugin | |||
| allowedExtensions: ['gltf', 'glb', 'hdr', 'png', 'jpg', 'json', 'fbx', 'obj'], // only allow these file types. If undefined, all files are allowed. | |||
| allowedExtensions: ['gltf', 'glb', 'hdr', 'bin', 'png', 'jpeg', 'webp', 'jpg', 'exr', 'fbx', 'obj'], // only allow these file types. If undefined, all files are allowed. | |||
| addOptions: { | |||
| disposeSceneObjects: true, // auto dispose of old scene objects | |||
| autoSetEnvironment: true, // when hdr is dropped | |||
| @@ -6,7 +6,7 @@ async function init() { | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| msaa: true, | |||
| dropzone: { | |||
| allowedExtensions: ['gltf', 'glb', 'hdr'], | |||
| allowedExtensions: ['gltf', 'glb', 'hdr', 'bin', 'png', 'jpeg', 'webp', 'jpg', 'exr'], | |||
| addOptions: { | |||
| disposeSceneObjects: true, | |||
| autoSetEnvironment: true, // when hdr is dropped | |||
| @@ -6,14 +6,6 @@ async function init() { | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| rgbm: false, | |||
| dropzone: { | |||
| allowedExtensions: ['gltf', 'glb', 'hdr'], | |||
| addOptions: { | |||
| disposeSceneObjects: true, | |||
| autoSetEnvironment: true, // when hdr is dropped | |||
| autoSetBackground: true, | |||
| }, | |||
| }, | |||
| }) | |||
| viewer.scene.backgroundColor = new Color().set('black') | |||
| viewer.getPlugin(TonemapPlugin)!.exposure = 0.04 | |||
| @@ -6,14 +6,6 @@ async function init() { | |||
| const viewer = new ThreeViewer({ | |||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||
| rgbm: true, | |||
| dropzone: { | |||
| allowedExtensions: ['gltf', 'glb', 'hdr'], | |||
| addOptions: { | |||
| disposeSceneObjects: true, | |||
| autoSetEnvironment: true, // when hdr is dropped | |||
| autoSetBackground: true, | |||
| }, | |||
| }, | |||
| }) | |||
| viewer.scene.backgroundColor = new Color().set('black') | |||
| viewer.getPlugin(TonemapPlugin)!.exposure = 0.04 | |||
| @@ -1,5 +1,15 @@ | |||
| import {IDisposable, PartialRecord} from 'ts-browser-helpers' | |||
| import {Clock, Event, ShaderMaterial, Texture, Vector2, Vector4, WebGLRenderer, WebGLRenderTarget} from 'three' | |||
| import { | |||
| Blending, | |||
| Clock, | |||
| Event, | |||
| ShaderMaterial, | |||
| Texture, | |||
| Vector2, | |||
| Vector4, | |||
| WebGLRenderer, | |||
| WebGLRenderTarget, | |||
| } from 'three' | |||
| import {CreateRenderTargetOptions, IRenderTarget, RenderTargetManager} from '../rendering' | |||
| import {IShaderPropertiesUpdater} from '../materials' | |||
| import {EffectComposer2, IPassID, IPipelinePass} from '../postprocessing' | |||
| @@ -25,6 +35,7 @@ export type IRenderManagerEvent = Partial<IAnimationLoopEvent>&Partial<IRenderMa | |||
| [key: string]: any | |||
| } | |||
| export type IRenderManagerEventTypes = 'animationLoop'|'update'|'resize'|'contextLost'|'contextRestored' | |||
| export interface RendererBlitOptions {source?: Texture, viewport?: Vector4, material?: ShaderMaterial, clear?: boolean, respectColorSpace?: boolean, blending?: Blending, transparent?: boolean} | |||
| export interface IRenderManager<E extends IRenderManagerEvent = IRenderManagerEvent, ET extends string = IRenderManagerEventTypes> extends RenderTargetManager<E, ET>, IDisposable, IShaderPropertiesUpdater{ | |||
| readonly renderer: IWebGLRenderer | |||
| readonly needsRender: boolean | |||
| @@ -52,7 +63,7 @@ export interface IRenderManager<E extends IRenderManagerEvent = IRenderManagerEv | |||
| webglRenderer: WebGLRenderer | |||
| clock: Clock | |||
| blit(destination: IRenderTarget|undefined|null, options?: {source?: Texture, viewport?: Vector4, material?: ShaderMaterial, clear?: boolean}): void | |||
| blit(destination: IRenderTarget|undefined|null, options?: RendererBlitOptions): void | |||
| clearColor({r, g, b, a, target, depth = true, stencil = true, viewport}: | |||
| {r?: number, g?: number, b?: number, a?: number, target?: IRenderTarget, depth?: boolean, stencil?: boolean, viewport?: Vector4}): void | |||
| @@ -32,7 +32,6 @@ export abstract class PipelinePassPlugin<T extends IPipelinePass, TPassId extend | |||
| protected _beforeRender(): boolean {return this._pass?.enabled && this.enabled || false} | |||
| private _enabledTemp = true // to save enabled state when pass is not yet created | |||
| constructor() { | |||
| super() | |||
| this._beforeRender = this._beforeRender.bind(this) | |||
| @@ -7,6 +7,7 @@ import { | |||
| DoubleSide, | |||
| FrontSide, | |||
| MeshDepthMaterial, | |||
| MeshDepthMaterialParameters, | |||
| NoBlending, | |||
| Object3D, | |||
| Scene, | |||
| @@ -49,6 +50,7 @@ export class DepthBufferPlugin | |||
| @uiImage('Depth Buffer' /* {readOnly: true}*/) texture?: Texture | |||
| // @uiConfig() // not supported in this material yet | |||
| readonly material: MeshDepthMaterial = new MeshDepthMaterialOverride({ | |||
| depthPacking: BasicDepthPacking, | |||
| blending: NoBlending, | |||
| @@ -113,6 +115,8 @@ export class DepthBufferPlugin | |||
| this.texture = this.target.texture | |||
| this.texture.name = 'depthBuffer' | |||
| if (this._pass) this._pass.target = this.target | |||
| if (this.isPrimaryGBuffer) { | |||
| this._viewer.renderManager.gbufferTarget = this.target | |||
| this._viewer.renderManager.screenPass.material.registerMaterialExtensions([this.unpackExtension]) | |||
| @@ -168,6 +172,12 @@ export class DepthBufferPlugin | |||
| } | |||
| class MeshDepthMaterialOverride extends MeshDepthMaterial { | |||
| constructor(parameters: MeshDepthMaterialParameters) { | |||
| super(parameters) | |||
| this.reset() | |||
| } | |||
| onBeforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) { | |||
| super.onBeforeRender(renderer, scene, camera, geometry, object) | |||
| @@ -178,7 +188,7 @@ class MeshDepthMaterialOverride extends MeshDepthMaterial { | |||
| if (!material) return | |||
| if (material.map !== undefined) this.map = material.map // in case there is alpha in the map. | |||
| if (material.side !== undefined) this.side = DoubleSide | |||
| if (material.side !== undefined) this.side = material.side ?? FrontSide | |||
| if (material.alphaMap !== undefined) this.alphaMap = material.alphaMap | |||
| if (material.alphaTest !== undefined) this.alphaTest = material.alphaTest < 1e-4 ? 1e-4 : material.alphaTest | |||
| @@ -196,8 +206,12 @@ class MeshDepthMaterialOverride extends MeshDepthMaterial { | |||
| onAfterRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) { | |||
| super.onAfterRender(renderer, scene, camera, geometry, object) | |||
| this.reset() | |||
| } | |||
| reset() { | |||
| this.map = null | |||
| this.side = FrontSide | |||
| this.side = DoubleSide | |||
| this.alphaMap = null | |||
| this.alphaTest = 0.001 | |||
| @@ -2,10 +2,11 @@ import { | |||
| BufferGeometry, | |||
| Camera, | |||
| Color, | |||
| FrontSide, | |||
| DoubleSide, | |||
| HalfFloatType, | |||
| LinearSRGBColorSpace, | |||
| MeshNormalMaterial, | |||
| MeshNormalMaterialParameters, | |||
| NearestFilter, | |||
| NoBlending, | |||
| Object3D, | |||
| @@ -66,6 +67,8 @@ export class NormalBufferPlugin | |||
| }) | |||
| this.texture = this.target.texture | |||
| this.texture.name = 'normalBuffer' | |||
| if (this._pass) this._pass.target = this.target | |||
| } | |||
| protected _disposeTarget() { | |||
| if (!this._viewer) return | |||
| @@ -106,6 +109,12 @@ export class NormalBufferPlugin | |||
| } | |||
| class MeshNormalMaterialOverride extends MeshNormalMaterial { | |||
| constructor(parameters: MeshNormalMaterialParameters) { | |||
| super(parameters) | |||
| this.reset() | |||
| } | |||
| onBeforeRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) { | |||
| super.onBeforeRender(renderer, scene, camera, geometry, object) | |||
| @@ -136,7 +145,10 @@ class MeshNormalMaterialOverride extends MeshNormalMaterial { | |||
| onAfterRender(renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, object: Object3D) { | |||
| super.onAfterRender(renderer, scene, camera, geometry, object) | |||
| this.reset() | |||
| } | |||
| reset() { | |||
| this.bumpMap = null | |||
| this.bumpScale = 1 | |||
| // this.alphaMap = null | |||
| @@ -151,7 +163,7 @@ class MeshNormalMaterialOverride extends MeshNormalMaterial { | |||
| this.flatShading = false | |||
| this.side = FrontSide | |||
| this.side = DoubleSide | |||
| this.wireframe = false | |||
| this.wireframeLinewidth = 1 | |||
| @@ -28,20 +28,5 @@ export class AddBlendTexturePass extends ExtendedShaderPass implements IPass { | |||
| this.clear = false | |||
| this.needsSwap = true | |||
| } | |||
| set weights2(value: Vector4) { | |||
| (this.uniforms.weight2.value as Vector4).copy(value) | |||
| } | |||
| get weights2(): Vector4 { | |||
| return this.uniforms.weight2.value as Vector4 | |||
| } | |||
| set weights1(value: Vector4) { | |||
| (this.uniforms.weight.value as Vector4).copy(value) | |||
| } | |||
| get weights1(): Vector4 { | |||
| return this.uniforms.weight.value as Vector4 | |||
| } | |||
| set blendTexture(value: Texture) { | |||
| this.uniforms.tDiffuse2.value = value | |||
| } | |||
| } | |||
| @@ -12,8 +12,8 @@ export class GenericBlendTexturePass extends ExtendedShaderPass implements IPass | |||
| varying vec2 vUv; | |||
| ${extraFrag} | |||
| void main() { | |||
| vec4 a = tDiffuseTexelToLinear ( texture2D( tDiffuse, vUv ) ) | |||
| vec4 b = tDiffuse2TexelToLinear ( texture2D( tDiffuse2, vUv ) ) | |||
| vec4 a = tDiffuseTexelToLinear ( texture2D( tDiffuse, vUv ) ); | |||
| vec4 b = tDiffuse2TexelToLinear ( texture2D( tDiffuse2, vUv ) ); | |||
| vec4 c = vec4(0); | |||
| ${blendFunc} | |||
| c = clamp(c, vec4(0), vec4(8)); | |||
| @@ -1,4 +1,4 @@ | |||
| import {IDisposable} from 'ts-browser-helpers' | |||
| import {IDisposable, ValOrFunc} from 'ts-browser-helpers' | |||
| import {IUniform} from 'three' | |||
| import {Pass} from 'three/examples/jsm/postprocessing/Pass.js' | |||
| import {IShaderPropertiesUpdater, MaterialExtension} from '../materials' | |||
| @@ -12,7 +12,7 @@ export interface IPass<Tid extends IPassID = IPassID> extends Pass, IDisposable | |||
| updateShaderProperties?: (updater?: (IShaderPropertiesUpdater|undefined) | (IShaderPropertiesUpdater|undefined)[])=>void | |||
| materialExtension?: MaterialExtension | |||
| dirty?: boolean // isDirty (optional) | |||
| dirty?: ValOrFunc<boolean> // isDirty (optional) | |||
| setDirty?(): void | |||
| onDirty?: (()=>void)[]; | |||
| @@ -1,5 +1,4 @@ | |||
| import { | |||
| Blending, | |||
| Color, | |||
| FloatType, | |||
| HalfFloatType, | |||
| @@ -9,7 +8,6 @@ import { | |||
| NormalBlending, | |||
| NoToneMapping, | |||
| PCFShadowMap, | |||
| ShaderMaterial, | |||
| Texture, | |||
| Vector2, | |||
| Vector4, | |||
| @@ -31,10 +29,20 @@ import { | |||
| IWebGLRenderer, | |||
| upgradeWebGLRenderer, | |||
| } from '../core' | |||
| import {base64ToArrayBuffer, canvasFlipY, Class, onChange2, serializable, serialize, ValOrArr} from 'ts-browser-helpers' | |||
| import { | |||
| base64ToArrayBuffer, | |||
| canvasFlipY, | |||
| Class, | |||
| getOrCall, | |||
| onChange2, | |||
| serializable, | |||
| serialize, | |||
| ValOrArr, | |||
| } from 'ts-browser-helpers' | |||
| import {uiButton, uiConfig, uiFolderContainer, uiMonitor, uiSlider, uiToggle} from 'uiconfig.js' | |||
| import {generateUUID, textureDataToImageData} from '../three' | |||
| import {BlobExt, EXRExporter2} from '../assetmanager' | |||
| import {RendererBlitOptions} from '../core/IRenderer' | |||
| @serializable('RenderManager') | |||
| @uiFolderContainer('Render Manager') | |||
| @@ -204,7 +212,7 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen | |||
| } | |||
| get needsRender(): boolean { | |||
| this._dirty = this._dirty || this._passes.findIndex(value => value.dirty) >= 0 // todo: check for enabled passes only. | |||
| this._dirty = this._dirty || this._passes.findIndex(value => getOrCall(value.dirty)) >= 0 // todo: check for enabled passes only. | |||
| return this._dirty | |||
| } | |||
| @@ -371,7 +379,7 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen | |||
| * @param blending - Note - Set to NormalBlending if transparent is set to false | |||
| * @param transparent | |||
| */ | |||
| blit(destination: IRenderTarget|undefined|null, {source, viewport, material, clear = true, respectColorSpace = false, blending = NoBlending, transparent = true}: {source?: Texture, viewport?: Vector4, material?: ShaderMaterial, clear?: boolean, respectColorSpace?: boolean, blending?: Blending, transparent?: boolean} = {}): void { | |||
| blit(destination: IRenderTarget|undefined|null, {source, viewport, material, clear = true, respectColorSpace = false, blending = NoBlending, transparent = true}: RendererBlitOptions = {}): void { | |||
| const copyPass = !respectColorSpace ? this._composer.copyPass : this._composer.copyPass2 | |||
| const {renderToScreen, material: oldMaterial, uniforms: oldUniforms, clear: oldClear} = copyPass | |||
| if (material) { | |||