| res = await loader.loadAsync(path + (options.queryString ? (path.includes('?') ? '&' : '?') + options.queryString : ''), (e)=>{ | res = await loader.loadAsync(path + (options.queryString ? (path.includes('?') ? '&' : '?') + options.queryString : ''), (e)=>{ | ||||
| if (onDownloadProgress) onDownloadProgress(e) | if (onDownloadProgress) onDownloadProgress(e) | ||||
| this.dispatchEvent({type: 'importFile', path, state:'downloading', progress: e.loaded / e.total}) | |||||
| this.dispatchEvent({type: 'importFile', path, state:'downloading', progress: e.total > 0 ? e.loaded / e.total : 1}) | |||||
| }) | }) | ||||
| if (loader.transform) res = await loader.transform(res, options) | if (loader.transform) res = await loader.transform(res, options) | ||||
| IMaterialParameters, | IMaterialParameters, | ||||
| IMaterialTemplate, | IMaterialTemplate, | ||||
| ITexture, | ITexture, | ||||
| ITextureEvent, | |||||
| PhysicalMaterial, | PhysicalMaterial, | ||||
| UnlitMaterial, | UnlitMaterial, | ||||
| } from '../core' | } from '../core' | ||||
| this._refreshTextureRefs(mat) | this._refreshTextureRefs(mat) | ||||
| } | } | ||||
| protected _textureUpdate = function(this: IMaterial, e: ITextureEvent<'update'>) { | |||||
| if (!this || this.assetType !== 'material') return | |||||
| this.dispatchEvent({texture: e.target, bubbleToParent: true, bubbleToObject: true, ...e, type: 'textureUpdate'}) | |||||
| } | |||||
| private _refreshTextureRefs(mat: any) { | private _refreshTextureRefs(mat: any) { | ||||
| if (!mat.__textureUpdate) mat.__textureUpdate = this._textureUpdate.bind(mat) | |||||
| const newMaps = this._getMapsForMaterial(mat) | const newMaps = this._getMapsForMaterial(mat) | ||||
| const oldMaps = this._materialMaps.get(mat.uuid) || new Set<ITexture>() | const oldMaps = this._materialMaps.get(mat.uuid) || new Set<ITexture>() | ||||
| newMaps.forEach(map => { | |||||
| if (!oldMaps.has(map)) { | |||||
| if (!map.userData.__appliedMaterials) map.userData.__appliedMaterials = new Set<IMaterial>() | |||||
| map.userData.__appliedMaterials.add(mat) | |||||
| } | |||||
| }) | |||||
| oldMaps.forEach(map => { | |||||
| if (!newMaps.has(map)) { | |||||
| if (!map.userData.__appliedMaterials) return | |||||
| const mats = map.userData.__appliedMaterials | |||||
| mats?.delete(mat) | |||||
| if (!mats || map.userData.disposeOnIdle === false) return | |||||
| if (mats.size === 0) map.dispose() | |||||
| } | |||||
| }) | |||||
| for (const map of newMaps) { | |||||
| if (oldMaps.has(map)) continue | |||||
| if (!map.userData.__appliedMaterials) map.userData.__appliedMaterials = new Set<IMaterial>() | |||||
| map.userData.__appliedMaterials.add(mat) | |||||
| map.addEventListener('update', mat.__textureUpdate) | |||||
| } | |||||
| for (const map of oldMaps) { | |||||
| if (newMaps.has(map)) continue | |||||
| map.removeEventListener('update', mat.__textureUpdate) | |||||
| if (!map.userData.__appliedMaterials) continue | |||||
| const mats = map.userData.__appliedMaterials | |||||
| mats?.delete(mat) | |||||
| if (!mats || map.userData.disposeOnIdle === false) continue | |||||
| if (mats.size === 0) map.dispose() | |||||
| } | |||||
| this._materialMaps.set(mat.uuid, newMaps) | this._materialMaps.set(mat.uuid, newMaps) | ||||
| } | } | ||||
| for (const c of currentMats) { | for (const c of currentMats) { | ||||
| // console.log(c) | // console.log(c) | ||||
| if (!c) continue | if (!c) continue | ||||
| if (c === material) continue | |||||
| if (c.userData.__isVariation) continue | if (c.userData.__isVariation) continue | ||||
| const cType = Object.getPrototypeOf(c).constructor.TYPE | const cType = Object.getPrototypeOf(c).constructor.TYPE | ||||
| // console.log(cType, mType) | // console.log(cType, mType) |
| import type {IImportResultUserData} from '../assetmanager' | import type {IImportResultUserData} from '../assetmanager' | ||||
| export type IMaterialParameters = MaterialParameters & {customMaterialExtensions?: MaterialExtension[]} | export type IMaterialParameters = MaterialParameters & {customMaterialExtensions?: MaterialExtension[]} | ||||
| export type IMaterialEventTypes = 'dispose' | 'materialUpdate' | 'beforeRender' | 'beforeCompile' | 'afterRender' | 'textureChanged' | 'beforeDeserialize' | |||||
| export type IMaterialEventTypes = 'dispose' | 'materialUpdate' | 'beforeRender' | 'beforeCompile' | 'afterRender' | 'textureUpdate' | 'beforeDeserialize' | |||||
| export type IMaterialEvent<T extends string = IMaterialEventTypes> = Event & { | export type IMaterialEvent<T extends string = IMaterialEventTypes> = Event & { | ||||
| type: T | type: T | ||||
| bubbleToObject?: boolean | bubbleToObject?: boolean |
| import {IImportResultUserData} from '../assetmanager' | import {IImportResultUserData} from '../assetmanager' | ||||
| import {GLTF} from 'three/examples/jsm/loaders/GLTFLoader.js' | import {GLTF} from 'three/examples/jsm/loaders/GLTFLoader.js' | ||||
| export type IObject3DEventTypes = 'dispose' | 'materialUpdate' | 'objectUpdate' | 'geometryChanged' | | |||||
| export type IObject3DEventTypes = 'dispose' | 'materialUpdate' | 'objectUpdate' | 'textureUpdate' | 'geometryChanged' | | |||||
| 'materialChanged' | 'geometryUpdate' | 'added' | 'removed' | 'select' | 'beforeDeserialize' | | 'materialChanged' | 'geometryUpdate' | 'added' | 'removed' | 'select' | 'beforeDeserialize' | | ||||
| 'setView' | 'activateMain' | 'cameraUpdate' // from camera | 'setView' | 'activateMain' | 'cameraUpdate' // from camera | ||||
| // | string | // | string |
| import {IMaterial} from './IMaterial' | import {IMaterial} from './IMaterial' | ||||
| import {Texture} from 'three' | |||||
| import {Event, Texture} from 'three' | |||||
| import {ChangeEvent} from 'uiconfig.js' | |||||
| export interface ITextureUserData{ | export interface ITextureUserData{ | ||||
| mimeType?: string | mimeType?: string | ||||
| disposeOnIdle?: boolean // automatically dispose when added to a material and then not used in any material | disposeOnIdle?: boolean // automatically dispose when added to a material and then not used in any material | ||||
| __appliedMaterials?: Set<IMaterial> | __appliedMaterials?: Set<IMaterial> | ||||
| } | } | ||||
| export type ITextureEventTypes = 'dispose' | 'update' | |||||
| export type ITextureEvent<T extends string = ITextureEventTypes> = Event & { | |||||
| type: T | |||||
| texture?: ITexture | |||||
| uiChangeEvent?: ChangeEvent | |||||
| } | |||||
| export interface ITexture extends Texture { | export interface ITexture extends Texture { | ||||
| assetType?: 'texture' | assetType?: 'texture' | ||||
| userData: ITextureUserData | userData: ITextureUserData |
| export type {IObject3D, IObject3DEvent, IObjectSetDirtyOptions, IObjectProcessor, IObject3DEventTypes, IObject3DUserData} from './IObject' | export type {IObject3D, IObject3DEvent, IObjectSetDirtyOptions, IObjectProcessor, IObject3DEventTypes, IObject3DUserData} from './IObject' | ||||
| export type {IRenderManager, IRenderManagerOptions, IWebGLRenderer, IRenderManagerEventTypes, IAnimationLoopEvent, TThreeRendererMode, TThreeRendererModeUserData, IRenderManagerUpdateEvent, IRenderManagerEvent} from './IRenderer' | export type {IRenderManager, IRenderManagerOptions, IWebGLRenderer, IRenderManagerEventTypes, IAnimationLoopEvent, TThreeRendererMode, TThreeRendererModeUserData, IRenderManagerUpdateEvent, IRenderManagerEvent} from './IRenderer' | ||||
| export type {IScene, ISceneEvent, ISceneEventTypes, ISceneSetDirtyOptions, AddObjectOptions, ISceneUserData, IWidget} from './IScene' | export type {IScene, ISceneEvent, ISceneEventTypes, ISceneSetDirtyOptions, AddObjectOptions, ISceneUserData, IWidget} from './IScene' | ||||
| export type {ITexture, ITextureUserData} from './ITexture' | |||||
| export type {ITexture, ITextureUserData, ITextureEvent, ITextureEventTypes} from './ITexture' |
| superDispatchEvent.call(this, event) | superDispatchEvent.call(this, event) | ||||
| const type = event.type | const type = event.type | ||||
| if (event.bubbleToObject && ( | if (event.bubbleToObject && ( | ||||
| type === 'beforeDeserialize' || type === 'materialUpdate' // todo - add more events | |||||
| type === 'beforeDeserialize' || type === 'materialUpdate' || type === 'textureUpdate' // todo - add more events | |||||
| )) { | )) { | ||||
| this.appliedMeshes.forEach(m => m.dispatchEvent({...event, material: this, type})) | this.appliedMeshes.forEach(m => m.dispatchEvent({...event, material: this, type})) | ||||
| } | } |
| this._composer.setPixelRatio(this._renderScale, false) | this._composer.setPixelRatio(this._renderScale, false) | ||||
| this._composer.setSize(this._renderSize.width, this._renderSize.height) | this._composer.setSize(this._renderSize.width, this._renderSize.height) | ||||
| this._resizeTracedTargets() | |||||
| this.resizeTrackedTargets() | |||||
| // console.log('setSize', {...this._renderSize}, this._trackedTargets.length) | // console.log('setSize', {...this._renderSize}, this._trackedTargets.length) | ||||
| import {createRenderTargetKey, CreateRenderTargetOptions, IRenderTarget} from './RenderTarget' | import {createRenderTargetKey, CreateRenderTargetOptions, IRenderTarget} from './RenderTarget' | ||||
| import { | import { | ||||
| BaseEvent, | BaseEvent, | ||||
| ClampToEdgeWrapping, | |||||
| DepthTexture, | DepthTexture, | ||||
| EventDispatcher, | EventDispatcher, | ||||
| LinearFilter, | LinearFilter, | ||||
| this._trackedTempTargets = [] | this._trackedTempTargets = [] | ||||
| } | } | ||||
| protected _resizeTracedTargets() { | |||||
| this._trackedTargets.forEach(v=>{ | |||||
| const target = v as any as WebGLRenderTarget | |||||
| const multiplier = (target as any).sizeMultiplier | |||||
| if (multiplier) { | |||||
| const s = this.renderSize.clone().multiplyScalar(this.renderScale * multiplier) | |||||
| target.setSize(Math.floor(s.width), Math.floor(s.height)) | |||||
| } | |||||
| }) | |||||
| /** | |||||
| * Resizes all tracked targets with a sizeMultiplier based on the current renderSize and renderScale. | |||||
| * This must be automatically called by the renderer on resize, and manually when sizeMultiplier of a target changes. | |||||
| */ | |||||
| resizeTrackedTargets() { | |||||
| for (const v of this._trackedTargets) this.resizeTrackedTarget(v) | |||||
| } | |||||
| resizeTrackedTarget(target: IRenderTarget): void { | |||||
| const multiplier = target.sizeMultiplier | |||||
| if (multiplier) { | |||||
| const s = this.renderSize.clone().multiplyScalar(this.renderScale * multiplier) | |||||
| target.setSize(Math.floor(s.width), Math.floor(s.height)) | |||||
| } | |||||
| } | } | ||||
| private _processNewTempTarget(target: IRenderTarget, key: string): IRenderTarget { | private _processNewTempTarget(target: IRenderTarget, key: string): IRenderTarget { | ||||
| private _setTargetTextureOptions(texture: Texture, op: CreateRenderTargetOptions) { | private _setTargetTextureOptions(texture: Texture, op: CreateRenderTargetOptions) { | ||||
| texture.minFilter = op.minFilter ?? LinearFilter | texture.minFilter = op.minFilter ?? LinearFilter | ||||
| texture.magFilter = op.magFilter ?? LinearFilter | texture.magFilter = op.magFilter ?? LinearFilter | ||||
| texture.wrapS = op.wrapS ?? ClampToEdgeWrapping | |||||
| texture.wrapT = op.wrapT ?? ClampToEdgeWrapping | |||||
| texture.generateMipmaps = op.generateMipmaps ?? false | texture.generateMipmaps = op.generateMipmaps ?? false | ||||
| if (texture.generateMipmaps && texture.minFilter === LinearFilter) // todo: check if this is needed for magFilter | |||||
| if (texture.generateMipmaps && texture.minFilter === LinearFilter) | |||||
| texture.minFilter = LinearMipMapLinearFilter | texture.minFilter = LinearMipMapLinearFilter | ||||
| if (!texture.generateMipmaps && texture.minFilter === LinearMipMapLinearFilter) | if (!texture.generateMipmaps && texture.minFilter === LinearMipMapLinearFilter) | ||||
| texture.minFilter = LinearFilter | texture.minFilter = LinearFilter |
| if (!obj?.isWebGLRenderTarget || !obj.uuid) throw new Error('Expected a IRenderTarget') | if (!obj?.isWebGLRenderTarget || !obj.uuid) throw new Error('Expected a IRenderTarget') | ||||
| if (meta?.extras[obj.uuid]) return {uuid: obj.uuid, resource: 'extras'} | if (meta?.extras[obj.uuid]) return {uuid: obj.uuid, resource: 'extras'} | ||||
| // This is for the class implementing IRenderTarget, check {@link RenderTargetManager} for class implementation | |||||
| const tex = Array.isArray(obj.texture) ? obj.texture[0] : obj.texture | const tex = Array.isArray(obj.texture) ? obj.texture[0] : obj.texture | ||||
| let res: any = { | let res: any = { | ||||
| metadata: {type: 'RenderTarget'}, | metadata: {type: 'RenderTarget'}, | ||||
| if (!meta) return {} | if (!meta) return {} | ||||
| const res: Partial<SerializationResourcesType> = {...meta} | const res: Partial<SerializationResourcesType> = {...meta} | ||||
| if (res._context) delete res._context | if (res._context) delete res._context | ||||
| return meta | |||||
| return res | |||||
| } | } | ||||
| export function metaFromResources(resources?: Partial<SerializationResourcesType>, viewer?: ThreeViewer): SerializationMetaType { | export function metaFromResources(resources?: Partial<SerializationResourcesType>, viewer?: ThreeViewer): SerializationMetaType { | ||||
| return { | return { |
| this._scene.addEventListener('materialUpdate', (e) => this.setDirty(this._scene, e)) | this._scene.addEventListener('materialUpdate', (e) => this.setDirty(this._scene, e)) | ||||
| this._scene.addEventListener('materialChanged', (e) => this.setDirty(this._scene, e)) | this._scene.addEventListener('materialChanged', (e) => this.setDirty(this._scene, e)) | ||||
| this._scene.addEventListener('objectUpdate', (e) => this.setDirty(this._scene, e)) | this._scene.addEventListener('objectUpdate', (e) => this.setDirty(this._scene, e)) | ||||
| this._scene.addEventListener('textureUpdate', (e) => this.setDirty(this._scene, e)) | |||||
| this._scene.addEventListener('sceneUpdate', (e) => { | this._scene.addEventListener('sceneUpdate', (e) => { | ||||
| this.setDirty(this._scene, e) | this.setDirty(this._scene, e) | ||||
| if (e.geometryChanged === false) return | if (e.geometryChanged === false) return |