Public
Edited
Aug 26, 2023
12 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
data = await FileAttachment("nymph@3.drc").arrayBuffer()
Insert cell
mesh = decodeDracoGeometry(data, {
position: Float32Array,
normal: Float32Array
})
Insert cell
draco = {
const PREFIX = "https://www.gstatic.com/draco/versioned/decoders/1.5.6/";
const wasmBinary = await fetch(`${PREFIX}/draco_decoder.wasm`).then(resp => {
if (!resp.ok)
throw new Error("HTTP error, status = " + resp.status);
return resp.arrayBuffer();
});

const wrapper = await require(`${PREFIX}/draco_wasm_wrapper.js`);
return new Promise(resolve => wrapper({ wasmBinary }).then(draco => {
delete draco.then; // tell Observable not wait for this;
resolve(draco);
}));
}
Insert cell
decodeDracoGeometry = {
function decodeBuffer(decoder, data) {
const buffer = new draco.DecoderBuffer();
buffer.Init(new Int8Array(data), data.byteLength);
const geometryType = decoder.GetEncodedGeometryType(buffer);
let geometry, status;
if (geometryType === draco.TRIANGULAR_MESH) {
geometry = new draco.Mesh();
status = decoder.DecodeBufferToMesh(buffer, geometry);
} else if (geometryType === draco.POINT_CLOUD) {
geometry = new draco.PointCloud();
status = decoder.DecodeBufferToPointCloud(buffer, geometry);
} else {
draco.destroy(decoder);
draco.destroy(geometry);
draco.destroy(buffer);
throw new Error('Unknown geometry type.');
}

if (!status.ok() || geometry.ptr === 0) {
draco.destroy(decoder);
draco.destroy(geometry);
draco.destroy(buffer);
throw new Error('Decoding failed: ' + status.error_msg());
}
draco.destroy(buffer);
return geometry;
}

const ATTRIB_ID = {
position: draco.POSITION,
normal: draco.NORMAL,
color: draco.COLOR,
uv: draco.TEX_COORD
};
const DATA_TYPE = {
[Float32Array]: draco.DT_FLOAT32,
[Int8Array]: draco.DT_INT8,
[Int16Array]: draco.DT_INT16,
[Int32Array]: draco.DT_INT32,
[Uint8Array]: draco.DT_UINT8,
[Uint16Array]: draco.DT_UINT16,
[Uint32Array]: draco.DT_UINT32
};

function decodeAttribute(decoder, geometry, key, type) {
const attId = decoder.GetAttributeId(geometry, ATTRIB_ID[key]);
if (attId == -1)
return null;

const attribute = decoder.GetAttribute(geometry, attId);
const numComponents = attribute.num_components();
const numPoints = geometry.num_points();
const numValues = numPoints * numComponents;
const byteLength = numValues * type.BYTES_PER_ELEMENT;
const dataType = DATA_TYPE[type];

const ptr = draco._malloc( byteLength );
decoder.GetAttributeDataArrayForAllPoints(geometry, attribute, dataType, byteLength, ptr);
const array = new type(draco.HEAPF32.buffer, ptr, numValues).slice();
draco._free( ptr );

return array;
}
function decodeIndices(decoder, geometry) {
const numFaces = geometry.num_faces();
const numIndices = numFaces * 3;
const byteLength = numIndices * 4;

const ptr = draco._malloc(byteLength);
decoder.GetTrianglesUInt32Array(geometry, byteLength, ptr);
const index = new Uint32Array(draco.HEAPF32.buffer, ptr, numIndices).slice();
draco._free( ptr );
return index;
}
return (data, attribs) => {
const decoder = new draco.Decoder();
const geometry = decodeBuffer(decoder, data);
const mesh = {};
for (const key in attribs) {
mesh[key] = decodeAttribute(decoder, geometry, key, attribs[key]);
}

mesh["indices"] = decodeIndices(decoder, geometry);
draco.destroy(geometry);
draco.destroy(decoder);
return mesh;
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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