Published
Edited
Apr 12, 2021
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
BasisFile = {
const BASIS = await require('three@0.124.0/examples/js/libs/basis/basis_transcoder.js');
return new Promise(resolve => BASIS().then(transcoder => {
transcoder.initializeBasis();
resolve(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
COMPRESSED.RGB_S3TC_DXT1_EXT
Insert cell
gl.getExtension('WEBGL_compressed_texture_s3tc').COMPRESSED_RGB_S3TC_DXT1_EXT
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();
console.log(images, levels);

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');
}
const dstSize2 = basisFile.getImageTranscodedSizeInBytes(1, 0, format);
const dst2 = new Uint8Array(dstSize2);

if (!basisFile.transcodeImage(dst2, 1, 0, format, 0, 0)) {
cleanup();
throw new Error('transcodeImage failed');
}
console.log({dstSize, dstSize2});
const dst0 = new Uint8Array(dstSize*2);
dst0.set(dst);
dst0.set(dst, dst.size);
cleanup();

const alignedWidth = (width + 3) & ~3;
const alignedHeight = (height + 3) & ~3;
console.log({width, alignedWidth, height, alignedHeight, format, formats: BASIS_FORMAT});
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);
tex = createCompressedTexture3D([dst, dst2], 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.
var pix = 0;
for (var y = 0; y < height; y++)
for (var x = 0; x < width; x++, pix++)
dstTex[pix] = dst[2 * pix + 0] | (dst[2 * pix + 1] << 8);

tex = createRgb565Texture(dstTex, width, height);
}
console.log({tex});
return tex;
}
Insert cell
Insert cell
createCompressedTexture3D = (data, width, height, format) => {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D_ARRAY, tex);
gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, format, width, height, 2);
for (let i=0; i<data.length; i++) {
// void gl.compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, ArrayBufferView srcData, optional srcOffset, optional srcLengthOverride);

const res = gl.compressedTexSubImage3D(
gl.TEXTURE_2D_ARRAY,
0,
0,
0,
0,
width,
height,
i,
format,
data[0]);
console.log({res, i, d: data[i]});
}
// gl.compressedTexImage3D(
// gl.TEXTURE_2D_ARRAY,
// 0,
// format,
// width,
// height,
// 2,
// 0,
// data);
// gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
// gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
// gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
// gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
return tex;
};
Insert cell
//
Insert cell
//
Insert cell
gl.compress
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_ARRAY, texture);
gl.uniform1i(gl.getUniformLocation(model.program, "tex"), 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
webgl.unbind();
}
Insert cell
data = ({
tiles: await FileAttachment("tiles@1.basis").arrayBuffer(),
tiles1: await FileAttachment("tiles1.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: `#version 300 es

precision highp int;
precision highp float;

in vec2 position;
in vec2 uv;

out vec2 v_uv;

void main() {
v_uv = uv;
gl_Position = vec4(position, 0.0, 1.0);
}`,
fs: `#version 300 es

precision highp int;
precision highp float;
precision highp sampler2DArray;
precision highp sampler2D;

uniform sampler2DArray tex;
//uniform sampler2D tex;
in vec2 v_uv;
out vec4 fragColor;

void main() {
vec2 uv = vec2(v_uv.x, 1.0 - v_uv.y);
vec4 color = texture(tex, vec3(uv, 1));
fragColor = color;
//fragColor = texture2D(tex, v_uv);
//fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}`
})
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more