Skip to content

Commit

Permalink
Textures of all types can now only be loaded by providing an Asset ty…
Browse files Browse the repository at this point in the history
…pe. Asset now has a mandatory string id. Assets no longer caches the IBL PMREM texture, but the HDRI textures themselves because the PMREMs were only stored in video memory and when read from cache with a different viewer instance they would fail resulting in null IBL (#2192)
  • Loading branch information
AlexandruPopovici authored Apr 4, 2024
1 parent fcc2cea commit 79cd985
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 49 deletions.
4 changes: 3 additions & 1 deletion packages/viewer/src/IViewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Loader } from './modules/loaders/Loader'

export interface ViewerParams {
showStats: boolean
environmentSrc: Asset | string
environmentSrc: Asset
verbose: boolean
}
export enum AssetType {
Expand All @@ -24,6 +24,7 @@ export enum AssetType {
}

export interface Asset {
id: string
src: string
type: AssetType
}
Expand All @@ -43,6 +44,7 @@ export const DefaultViewerParams: ViewerParams = {
showStats: false,
verbose: false,
environmentSrc: {
id: 'defaultHDRI',
src: sampleHdri,
type: AssetType.TEXTURE_EXR
}
Expand Down
79 changes: 33 additions & 46 deletions packages/viewer/src/modules/Assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,92 +31,79 @@ export class Assets {
}
}

private static hdriToPMREM(renderer: WebGLRenderer, hdriTex: Texture): Texture {
const generator = new PMREMGenerator(renderer)
generator.compileEquirectangularShader()
const pmremRT = generator.fromEquirectangular(hdriTex)
generator.dispose()
return pmremRT.texture
}

public static getEnvironment(
asset: Asset | string,
asset: Asset,
renderer: WebGLRenderer
): Promise<Texture> {
let srcUrl: string = null
let assetType: AssetType = undefined
if ((<Asset>asset).src) {
srcUrl = (asset as Asset).src
assetType = (asset as Asset).type
} else {
srcUrl = asset as string
}
if (this._cache[srcUrl]) {
return Promise.resolve(this._cache[srcUrl] as Texture)
if (this._cache[asset.id]) {
return Promise.resolve(
Assets.hdriToPMREM(renderer, this._cache[asset.id] as Texture)
)
}

return new Promise<Texture>((resolve, reject) => {
const loader = Assets.getLoader(srcUrl, assetType)
const loader = Assets.getLoader(asset.src, asset.type)
if (loader) {
loader.load(
srcUrl,
asset.src,
(texture) => {
const generator = new PMREMGenerator(renderer)
generator.compileEquirectangularShader()
const pmremRT = generator.fromEquirectangular(texture)
this._cache[srcUrl] = pmremRT.texture
texture.dispose()
generator.dispose()
resolve(this._cache[srcUrl] as Texture)
this._cache[asset.id] = texture
resolve(Assets.hdriToPMREM(renderer, texture))
},
undefined,
(error: ErrorEvent) => {
reject(`Loading asset ${srcUrl} failed ${error.message}`)
reject(`Loading asset ${asset.id} failed ${error.message}`)
}
)
} else {
reject(`Loading asset ${srcUrl} failed`)
reject(`Loading asset ${asset.id} failed`)
}
})
}

/** Will unify with environment fetching soon */
public static getTexture(asset: Asset | string): Promise<Texture> {
let srcUrl: string = null
let assetType: AssetType = undefined
if ((<Asset>asset).src) {
srcUrl = (asset as Asset).src
assetType = (asset as Asset).type
} else {
srcUrl = asset as string
}

if (this._cache[srcUrl]) {
return Promise.resolve(this._cache[srcUrl] as Texture)
public static getTexture(asset: Asset): Promise<Texture> {
if (this._cache[asset.id]) {
return Promise.resolve(this._cache[asset.id] as Texture)
}
return new Promise<Texture>((resolve, reject) => {
// Hack to load 'data:image's - for some reason, the frontend receives the default
// gradient map as a data image url, rather than a file (?).
if (srcUrl.includes('data:image')) {
if (asset.src.includes('data:image')) {
const image = new Image()
image.src = srcUrl
image.src = asset.src
image.onload = () => {
const texture = new Texture(image)
texture.needsUpdate = true
this._cache[srcUrl] = texture
this._cache[asset.id] = texture
resolve(texture)
}
image.onerror = (ev) => {
reject(`Loading asset ${srcUrl} failed with ${ev.toString()}`)
reject(`Loading asset ${asset.id} failed with ${ev.toString()}`)
}
} else {
const loader = Assets.getLoader(srcUrl, assetType)
const loader = Assets.getLoader(asset.src, asset.type)
if (loader) {
loader.load(
srcUrl,
asset.src,
(texture) => {
this._cache[srcUrl] = texture
resolve(this._cache[srcUrl] as Texture)
this._cache[asset.id] = texture
resolve(this._cache[asset.id] as Texture)
},
undefined,
(error: ErrorEvent) => {
reject(`Loading asset ${srcUrl} failed ${error.message}`)
reject(`Loading asset ${asset.id} failed ${error.message}`)
}
)
} else {
reject(`Loading asset ${srcUrl} failed`)
reject(`Loading asset ${asset.id} failed`)
}
}
})
Expand Down Expand Up @@ -149,7 +136,7 @@ export class Assets {
}

/** To be used wisely */
public static async getTextureData(asset: Asset | string): Promise<ImageData> {
public static async getTextureData(asset: Asset): Promise<ImageData> {
const texture = await Assets.getTexture(asset)
const canvas = document.createElement('canvas')
canvas.width = texture.image.width
Expand Down
10 changes: 8 additions & 2 deletions packages/viewer/src/modules/materials/Materials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ import SpeckleLineMaterial from './SpeckleLineMaterial'
import SpeckleStandardMaterial from './SpeckleStandardMaterial'
import SpecklePointMaterial from './SpecklePointMaterial'
import SpeckleStandardColoredMaterial from './SpeckleStandardColoredMaterial'
import defaultGradient from '../../assets/gradient.png'
import defaultGradientTexture from '../../assets/gradient.png'
import { Assets } from '../Assets'
import { getConversionFactor } from '../converter/Units'
import SpeckleGhostMaterial from './SpeckleGhostMaterial'
import SpeckleTextMaterial from './SpeckleTextMaterial'
import { SpeckleMaterial } from './SpeckleMaterial'
import SpecklePointColouredMaterial from './SpecklePointColouredMaterial'
import { MaterialOptions } from '../../IViewer'
import { Asset, AssetType, MaterialOptions } from '../../IViewer'

const defaultGradient: Asset = {
id: 'defaultGradient',
src: defaultGradientTexture,
type: AssetType.TEXTURE_8BPP
}

export interface RenderMaterial {
id: string
Expand Down

0 comments on commit 79cd985

Please sign in to comment.