Published
Edited
Nov 30, 2021
1 fork
5 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
BasisFile = {
const BASIS = await require('three@0.135.0/examples/js/libs/basis/basis_transcoder.js');
return BASIS().then(transcoder => (transcoder.initializeBasis(), transcoder.BasisFile));
}
Insert cell
BASIS_FORMAT = ({
ETC1: 0,
ETC2: 1,
BC1: 2,
BC3: 3,
BC4: 4,
BC5: 5,
BC7: 6,
PVRTC1_4_RGB: 8,
PVRTC1_4_RGBA: 9,
ASTC_4x4: 10,
ATC_RGB: 11,
ATC_RGBA_INTERPOLATED_ALPHA: 12,
RGBA32: 13,
RGB565: 14,
BGR565: 15,
RGBA4444: 16,
})
Insert cell
COMPRESSED = ({
RGBA_ASTC_4x4_KHR: 0x93B0,

// DXT formats, from:
// http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/
RGB_S3TC_DXT1_EXT: 0x83F0,
RGBA_S3TC_DXT1_EXT: 0x83F1,
RGBA_S3TC_DXT3_EXT: 0x83F2,
RGBA_S3TC_DXT5_EXT: 0x83F3,

// BC7 format, from:
// https://www.khronos.org/registry/webgl/extensions/EXT_texture_compression_bptc/
RGBA_BPTC_UNORM: 0x8E8C,

// ETC format, from:
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc1/
RGB_ETC1_WEBGL: 0x8D64,
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc/
RGBA8_ETC2_EAC: 0x9278,

// PVRTC format, from:
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/
RGB_PVRTC_4BPPV1_IMG: 0x8C00,
RGBA_PVRTC_4BPPV1_IMG: 0x8C02,
})
Insert cell
SUPPORTS = ({
astc: !!gl.getExtension('WEBGL_compressed_texture_astc'),
etc: !!gl.getExtension('WEBGL_compressed_texture_etc1'),
dxt: !!gl.getExtension('WEBGL_compressed_texture_s3tc'),
pvrtc: !!(gl.getExtension('WEBGL_compressed_texture_pvrtc')) || !!(gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc')),
bc7: !!gl.getExtension('EXT_texture_compression_bptc')
})
Insert cell
createBasisTexture = data => {
const basisFile = new BasisFile(new Uint8Array(data));
function cleanup() {
basisFile.close();
basisFile.delete();
}

const width = basisFile.getImageWidth(0, 0);
const height = basisFile.getImageHeight(0, 0);
const images = basisFile.getNumImages();
const levels = basisFile.getNumLevels(0);
const has_alpha = basisFile.getHasAlpha();

if (!width || !height || !images || !levels) {
cleanup();
throw new Error('Invalid .basis file');
}
let format;
if (SUPPORTS.astc) {
format = BASIS_FORMAT.ASTC_4x4;
} else if (SUPPORTS.bc7) {
format = BASIS_FORMAT.BC7;
} else if (SUPPORTS.dxt) {
format = has_alpha ? BASIS_FORMAT.BC3 : BASIS_FORMAT.BC1;
} else if (SUPPORTS.pvrtc) {
format = has_alpha ? BASIS_FORMAT.PVRTC1_4_RGBA : BASIS_FORMAT.PVRTC1_4_RGB;
if (((width & (width - 1)) != 0) || ((height & (height - 1)) != 0))
console.warn('ERROR: PVRTC1 requires square power of 2 textures');
if (width != height)
console.warn('ERROR: PVRTC1 requires square power of 2 textures');
} else if (SUPPORTS.etc) {
format = BASIS_FORMAT.ETC1;
} else {
format = BASIS_FORMAT.RGB565;
}
if (!basisFile.startTranscoding()) {
cleanup();
throw new Error('startTranscoding failed');
}
const dstSize = basisFile.getImageTranscodedSizeInBytes(0, 0, format);
const dst = new Uint8Array(dstSize);

if (!basisFile.transcodeImage(dst, 0, 0, format, 0, 0)) {
cleanup();
throw new Error('transcodeImage failed');
}
cleanup();

const alignedWidth = (width + 3) & ~3;
const alignedHeight = (height + 3) & ~3;
let tex;
if (format === BASIS_FORMAT.ASTC_4x4) {
tex = createCompressedTexture(dst, alignedWidth, alignedHeight, COMPRESSED.RGBA_ASTC_4x4_KHR);
} else if (format == BASIS_FORMAT.BC3) {
tex = createCompressedTexture(dst, alignedWidth, alignedHeight, COMPRESSED.RGBA_S3TC_DXT5_EXT);
} else if (format == BASIS_FORMAT.BC1) {
tex = createCompressedTexture(dst, alignedWidth, alignedHeight, COMPRESSED.RGB_S3TC_DXT1_EXT);
} else if (format == BASIS_FORMAT.BC7) {
tex = createCompressedTexture(dst, alignedWidth, alignedHeight, COMPRESSED.RGBA_BPTC_UNORM);
} else if (format === BASIS_FORMAT.ETC1) {
tex = createCompressedTexture(dst, alignedWidth, alignedHeight, COMPRESSED.RGB_ETC1_WEBGL);
} else if (format === BASIS_FORMAT.PVRTC1_4_RGB) {
tex = createCompressedTexture(dst, alignedWidth, alignedHeight, COMPRESSED.RGB_PVRTC_4BPPV1_IMG);
} else if (format === BASIS_FORMAT.PVRTC1_4_RGBA) {
tex = createCompressedTexture(dst, alignedWidth, alignedHeight, COMPRESSED.RGBA_PVRTC_4BPPV1_IMG);
} else {
// Create 565 texture.
const dstTex = new Uint16Array(width * height);
// Convert the array of bytes to an array of uint16's.
let pix = 0;
for (let y = 0; y < height; y++)
for (let x = 0; x < width; x++, pix++)
dstTex[pix] = dst[2 * pix + 0] | (dst[2 * pix + 1] << 8);

tex = createRgb565Texture(dstTex, width, height);
}
return tex;
}
Insert cell
Insert cell
Insert cell
Insert cell
render = {
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);

webgl.bind(model);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(gl.getUniformLocation(model.program, "tex"), 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
webgl.unbind();
}
Insert cell
data = ({
kodim26_uastc_1024: await FileAttachment("kodim26_uastc_1024.basis").arrayBuffer(),
kodim20: await FileAttachment("kodim20.basis").arrayBuffer(),
kodim03_uastc: await FileAttachment("kodim03_uastc.basis").arrayBuffer(),
alpha3: await FileAttachment("alpha3.basis").arrayBuffer(),
})
Insert cell
texture = createBasisTexture(data[name])
Insert cell
model = webgl.createObject({
attribs: {
position: { data: Float32Array.of(-1, 1, -1, -1, 1, -1, -1, 1, 1, -1, 1, 1), size: 2 },
uv: { data: Float32Array.of(0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1), size: 2 },
},
vs: `
precision highp int;
precision highp float;

attribute vec2 position;
attribute vec2 uv;

varying vec2 v_uv;

void main() {
v_uv = uv;
gl_Position = vec4(position, 0.0, 1.0);
}`,
fs: `
precision highp int;
precision highp float;

uniform sampler2D tex;

varying vec2 v_uv;

void main() {
vec2 uv = vec2(v_uv.x, 1.0 - v_uv.y);
vec4 color = texture2D(tex, uv);
gl_FragColor = color;
}`
})
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more