Skip to content

Commit

Permalink
feat(texture): move texture loading into worker (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
fallenoak authored Jan 6, 2024
1 parent 10dabe1 commit 28af560
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 21 deletions.
40 changes: 19 additions & 21 deletions src/lib/texture/TextureManager.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as THREE from 'three';
import { Blp, BLP_IMAGE_FORMAT } from '@wowserhq/format';
import { BLP_IMAGE_FORMAT } from '@wowserhq/format';
import ManagedCompressedTexture from './ManagedCompressedTexture.js';
import ManagedDataTexture from './ManagedDataTexture.js';
import FormatManager from '../FormatManager.js';
import TextureLoader from './loader/TextureLoader.js';
import { AssetHost, normalizePath } from '../asset.js';
import { TextureSpec } from './loader/types.js';

const THREE_TEXTURE_FORMAT: Record<number, THREE.PixelFormat | THREE.CompressedPixelFormat> = {
[BLP_IMAGE_FORMAT.IMAGE_DXT1]: THREE.RGBA_S3TC_DXT1_Format,
Expand All @@ -14,20 +15,19 @@ const THREE_TEXTURE_FORMAT: Record<number, THREE.PixelFormat | THREE.CompressedP

type TextureManagerOptions = {
host: AssetHost;
formatManager?: FormatManager;
};

class TextureManager {
#host: AssetHost;
#formatManager: FormatManager;
#loader: TextureLoader;

#loaded = new Map<string, THREE.Texture>();
#loading = new Map<string, Promise<THREE.Texture>>();
#refs = new Map<string, number>();

constructor(options: TextureManagerOptions) {
this.#host = options.host;
this.#formatManager = options.formatManager ?? new FormatManager({ host: options.host });
this.#loader = new TextureLoader({ host: options.host });
}

get(
Expand Down Expand Up @@ -102,50 +102,48 @@ class TextureManager {
minFilter: THREE.MinificationTextureFilter,
magFilter: THREE.MagnificationTextureFilter,
) {
let blp: Blp;
let spec: TextureSpec;
try {
blp = await this.#formatManager.get(path, Blp);
spec = await this.#loader.loadSpec(path);
} catch (error) {
this.#loading.delete(refId);
throw error;
}

const images = blp.getImages();
const firstImage = images[0];
const imageFormat = firstImage.format;
const specFormat = spec.format;

const threeFormat = THREE_TEXTURE_FORMAT[imageFormat];
const threeFormat = THREE_TEXTURE_FORMAT[specFormat];
if (threeFormat === undefined) {
this.#loading.delete(refId);
throw new Error(`Unsupported texture format: ${imageFormat}`);
throw new Error(`Unsupported texture format: ${specFormat}`);
}

let texture: ManagedCompressedTexture | ManagedDataTexture;
if (
imageFormat === BLP_IMAGE_FORMAT.IMAGE_DXT1 ||
imageFormat === BLP_IMAGE_FORMAT.IMAGE_DXT3 ||
imageFormat === BLP_IMAGE_FORMAT.IMAGE_DXT5
specFormat === BLP_IMAGE_FORMAT.IMAGE_DXT1 ||
specFormat === BLP_IMAGE_FORMAT.IMAGE_DXT3 ||
specFormat === BLP_IMAGE_FORMAT.IMAGE_DXT5
) {
texture = new ManagedCompressedTexture(
this,
refId,
null,
blp.width,
blp.height,
spec.width,
spec.height,
threeFormat as THREE.CompressedPixelFormat,
);
} else if (imageFormat === BLP_IMAGE_FORMAT.IMAGE_ABGR8888) {
} else if (specFormat === BLP_IMAGE_FORMAT.IMAGE_ABGR8888) {
texture = new ManagedDataTexture(
this,
refId,
null,
blp.width,
blp.height,
spec.width,
spec.height,
threeFormat as THREE.PixelFormat,
);
}

texture.mipmaps = images;
texture.mipmaps = spec.mipmaps;
texture.wrapS = wrapS;
texture.wrapT = wrapT;
texture.minFilter = minFilter;
Expand Down
25 changes: 25 additions & 0 deletions src/lib/texture/loader/TextureLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import SceneWorkerController from '../../worker/SceneWorkerController.js';
import { TextureSpec } from './types.js';
import { AssetHost } from '../../asset.js';

const createWorker = () =>
new Worker(new URL('./worker.js', import.meta.url), {
name: 'texture-loader',
type: 'module',
});

type TextureLoaderOptions = {
host: AssetHost;
};

class TextureLoader extends SceneWorkerController {
constructor(options: TextureLoaderOptions) {
super(createWorker, { host: options.host });
}

loadSpec(path: string): Promise<TextureSpec> {
return this.request('loadSpec', path);
}
}

export default TextureLoader;
52 changes: 52 additions & 0 deletions src/lib/texture/loader/TextureLoaderWorker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Blp } from '@wowserhq/format';
import { TextureSpec } from './types.js';
import SceneWorker from '../../worker/SceneWorker.js';
import { AssetHost, loadAsset } from '../../asset.js';

type TextureLoaderWorkerOptions = {
host: AssetHost;
};

class TextureLoaderWorker extends SceneWorker {
#host: AssetHost;

initialize(options: TextureLoaderWorkerOptions) {
this.#host = options.host;
}

async loadSpec(path: string) {
const blpData = await loadAsset(this.#host, path);
const blp = new Blp().load(blpData);

const images = blp.getImages();
const format = images[0].format;

const mipmaps = new Array(images.length);
const buffers = new Set<ArrayBuffer>();

for (let i = 0; i < images.length; i++) {
const image = images[i];

mipmaps[i] = {
width: image.width,
height: image.height,
data: image.data,
};

buffers.add(image.data.buffer);
}

const spec: TextureSpec = {
width: blp.width,
height: blp.height,
format,
mipmaps,
};

const transfer = [...buffers];

return [spec, transfer];
}
}

export default TextureLoaderWorker;
16 changes: 16 additions & 0 deletions src/lib/texture/loader/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { BLP_IMAGE_FORMAT } from '@wowserhq/format';

type TextureMipmapSpec = {
width: number;
height: number;
data: Uint8Array;
};

type TextureSpec = {
width: number;
height: number;
format: BLP_IMAGE_FORMAT;
mipmaps: TextureMipmapSpec[];
};

export { TextureSpec };
3 changes: 3 additions & 0 deletions src/lib/texture/loader/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import TextureLoaderWorker from './TextureLoaderWorker.js';

const worker = new TextureLoaderWorker();

0 comments on commit 28af560

Please sign in to comment.