Skip to content

Commit

Permalink
USDZ / GLTF exporter supports exposing of compressed textures on WebG…
Browse files Browse the repository at this point in the history
…PU (#6394)

* Support for Picker on WebGPU

* Gizmos example on webgpu

* USDZ / GLTF exporter supports exposing of compressed textures on WebGPU

* tiny comment update

---------

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
  • Loading branch information
mvaligursky and Martin Valigursky committed May 22, 2024
1 parent 44008b2 commit be95064
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 23 deletions.
11 changes: 9 additions & 2 deletions examples/src/examples/loaders/gltf-export.example.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ await new Promise((resolve) => {
pc.WasmModule.getInstance('DracoDecoderModule', () => resolve());
});

// initialize basis to allow to load compressed textures
pc.basisInitialize({
glueUrl: rootPath + '/static/lib/basis/basis.wasm.js',
wasmUrl: rootPath + '/static/lib/basis/basis.wasm.wasm',
fallbackUrl: rootPath + '/static/lib/basis/basis.js'
});

const assets = {
helipad: new pc.Asset(
'helipad-env-atlas',
Expand All @@ -42,7 +49,7 @@ const assets = {
model: new pc.Asset('model', 'container', { url: rootPath + '/static/assets/models/bitmoji.glb' }),
board: new pc.Asset('statue', 'container', { url: rootPath + '/static/assets/models/chess-board.glb' }),
boombox: new pc.Asset('statue', 'container', { url: rootPath + '/static/assets/models/boom-box.glb' }),
color: new pc.Asset('color', 'texture', { url: rootPath + '/static/assets/textures/seaside-rocks01-color.jpg' })
color: new pc.Asset('color', 'texture', { url: rootPath + '/static/assets/textures/seaside-rocks01-color.basis' })
};

const gfxOptions = {
Expand Down Expand Up @@ -115,7 +122,7 @@ assetListLoader.load(() => {

// mesh with a basic material
const basicMaterial = new pc.BasicMaterial();
basicMaterial.color.set(0.5, 1.0, 0.7);
basicMaterial.color.set(1.6, 2.7, 1.9);
basicMaterial.colorMap = assets.color.resource;

const capsule = new pc.Entity('capsule');
Expand Down
48 changes: 27 additions & 21 deletions src/extras/exporters/core-exporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Texture } from '../../platform/graphics/texture.js';
import { BlendState } from '../../platform/graphics/blend-state.js';
import { drawQuadWithShader } from '../../scene/graphics/quad-render-utils.js';
import { RenderTarget } from '../../platform/graphics/render-target.js';
import { FILTER_LINEAR, ADDRESS_CLAMP_TO_EDGE } from '../../platform/graphics/constants.js';
import { FILTER_LINEAR, ADDRESS_CLAMP_TO_EDGE, isCompressedPixelFormat, PIXELFORMAT_RGBA8 } from '../../platform/graphics/constants.js';

const textureBlitVertexShader = /* glsl */`
attribute vec2 vertex_position;
Expand Down Expand Up @@ -88,12 +88,13 @@ class CoreExporter {
// for other image sources, for example compressed textures, we extract the data by rendering the texture to a render target
const device = texture.device;
const { width, height } = this.calcTextureSize(texture.width, texture.height, options.maxTextureSize);
const format = isCompressedPixelFormat(texture.format) ? PIXELFORMAT_RGBA8 : texture.format;

const dstTexture = new Texture(device, {
name: 'ExtractedTexture',
width,
height,
format: texture.format,
format: format,
cubemap: false,
mipmaps: false,
minFilter: FILTER_LINEAR,
Expand All @@ -113,26 +114,31 @@ class CoreExporter {
device.setBlendState(BlendState.NOBLEND);
drawQuadWithShader(device, renderTarget, shader);

// read back the pixels
// TODO: use async API when ready
const pixels = new Uint8ClampedArray(width * height * 4);
device.readPixels(0, 0, width, height, pixels);

dstTexture.destroy();
renderTarget.destroy();

// copy pixels to a canvas
const newImage = new ImageData(pixels, width, height);
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const newContext = canvas.getContext('2d');
if (!newContext) {
return Promise.resolve(undefined);
}
newContext.putImageData(newImage, 0, 0);
// async read back the pixels of the texture
return dstTexture.read(0, 0, width, height, {
renderTarget: renderTarget,
immediate: true
}).then((textureData) => {

dstTexture.destroy();
renderTarget.destroy();

const pixels = new Uint8ClampedArray(width * height * 4);
pixels.set(textureData);

return Promise.resolve(canvas);
// copy pixels to a canvas
const newImage = new ImageData(pixels, width, height);
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const newContext = canvas.getContext('2d');
if (!newContext) {
return Promise.resolve(undefined);
}
newContext.putImageData(newImage, 0, 0);

return Promise.resolve(canvas);
});
}

calcTextureSize(width, height, maxTextureSize) {
Expand Down

0 comments on commit be95064

Please sign in to comment.