molstar
Version:
A comprehensive macromolecular library.
160 lines (159 loc) • 11 kB
JavaScript
/**
* Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractIsosurface = exports.createIsosurfaceBuffers = void 0;
var tslib_1 = require("tslib");
var renderable_1 = require("../../renderable");
var render_item_1 = require("../../webgl/render-item");
var schema_1 = require("../../renderable/schema");
var shader_code_1 = require("../../../mol-gl/shader-code");
var mol_util_1 = require("../../../mol-util");
var util_1 = require("../util");
var reduction_1 = require("../histogram-pyramid/reduction");
var tables_1 = require("./tables");
var quad_vert_1 = require("../../../mol-gl/shader/quad.vert");
var isosurface_frag_1 = require("../../../mol-gl/shader/marching-cubes/isosurface.frag");
var active_voxels_1 = require("./active-voxels");
var compat_1 = require("../../webgl/compat");
var debug_1 = require("../../../mol-util/debug");
var IsosurfaceSchema = tslib_1.__assign(tslib_1.__assign({}, util_1.QuadSchema), { tTriIndices: (0, schema_1.TextureSpec)('image-uint8', 'alpha', 'ubyte', 'nearest'), tActiveVoxelsPyramid: (0, schema_1.TextureSpec)('texture', 'rgba', 'float', 'nearest'), tActiveVoxelsBase: (0, schema_1.TextureSpec)('texture', 'rgba', 'float', 'nearest'), tVolumeData: (0, schema_1.TextureSpec)('texture', 'rgba', 'ubyte', 'nearest'), uIsoValue: (0, schema_1.UniformSpec)('f'), uSize: (0, schema_1.UniformSpec)('f'), uLevels: (0, schema_1.UniformSpec)('f'), uCount: (0, schema_1.UniformSpec)('f'), uInvert: (0, schema_1.UniformSpec)('b'), uGridDim: (0, schema_1.UniformSpec)('v3'), uGridTexDim: (0, schema_1.UniformSpec)('v3'), uGridTransform: (0, schema_1.UniformSpec)('m4'), uScale: (0, schema_1.UniformSpec)('v2'), dPackedGroup: (0, schema_1.DefineSpec)('boolean'), dAxisOrder: (0, schema_1.DefineSpec)('string', ['012', '021', '102', '120', '201', '210']), dConstantGroup: (0, schema_1.DefineSpec)('boolean') });
var IsosurfaceName = 'isosurface';
function getIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup) {
if (ctx.namedComputeRenderables[IsosurfaceName]) {
var v = ctx.namedComputeRenderables[IsosurfaceName].values;
mol_util_1.ValueCell.update(v.tActiveVoxelsPyramid, activeVoxelsPyramid);
mol_util_1.ValueCell.update(v.tActiveVoxelsBase, activeVoxelsBase);
mol_util_1.ValueCell.update(v.tVolumeData, volumeData);
mol_util_1.ValueCell.updateIfChanged(v.uIsoValue, isoValue);
mol_util_1.ValueCell.updateIfChanged(v.uSize, Math.pow(2, levels));
mol_util_1.ValueCell.updateIfChanged(v.uLevels, levels);
mol_util_1.ValueCell.updateIfChanged(v.uCount, count);
mol_util_1.ValueCell.updateIfChanged(v.uInvert, invert);
mol_util_1.ValueCell.update(v.uGridDim, gridDim);
mol_util_1.ValueCell.update(v.uGridTexDim, gridTexDim);
mol_util_1.ValueCell.update(v.uGridTransform, transform);
mol_util_1.ValueCell.update(v.uScale, scale);
mol_util_1.ValueCell.updateIfChanged(v.dPackedGroup, packedGroup);
mol_util_1.ValueCell.updateIfChanged(v.dAxisOrder, axisOrder.join(''));
mol_util_1.ValueCell.updateIfChanged(v.dConstantGroup, constantGroup);
ctx.namedComputeRenderables[IsosurfaceName].update();
}
else {
ctx.namedComputeRenderables[IsosurfaceName] = createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
}
return ctx.namedComputeRenderables[IsosurfaceName];
}
function createIsosurfaceRenderable(ctx, activeVoxelsPyramid, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup) {
// console.log('uSize', Math.pow(2, levels))
var values = tslib_1.__assign(tslib_1.__assign({}, util_1.QuadValues), { tTriIndices: mol_util_1.ValueCell.create((0, tables_1.getTriIndices)()), tActiveVoxelsPyramid: mol_util_1.ValueCell.create(activeVoxelsPyramid), tActiveVoxelsBase: mol_util_1.ValueCell.create(activeVoxelsBase), tVolumeData: mol_util_1.ValueCell.create(volumeData), uIsoValue: mol_util_1.ValueCell.create(isoValue), uSize: mol_util_1.ValueCell.create(Math.pow(2, levels)), uLevels: mol_util_1.ValueCell.create(levels), uCount: mol_util_1.ValueCell.create(count), uInvert: mol_util_1.ValueCell.create(invert), uGridDim: mol_util_1.ValueCell.create(gridDim), uGridTexDim: mol_util_1.ValueCell.create(gridTexDim), uGridTransform: mol_util_1.ValueCell.create(transform), uScale: mol_util_1.ValueCell.create(scale), dPackedGroup: mol_util_1.ValueCell.create(packedGroup), dAxisOrder: mol_util_1.ValueCell.create(axisOrder.join('')), dConstantGroup: mol_util_1.ValueCell.create(constantGroup) });
var schema = tslib_1.__assign({}, IsosurfaceSchema);
var shaderCode = (0, shader_code_1.ShaderCode)('isosurface', quad_vert_1.quad_vert, isosurface_frag_1.isosurface_frag, { drawBuffers: 'required' });
var renderItem = (0, render_item_1.createComputeRenderItem)(ctx, 'triangles', shaderCode, schema, values);
return (0, renderable_1.createComputeRenderable)(renderItem, values);
}
function setRenderingDefaults(ctx) {
var gl = ctx.gl, state = ctx.state;
state.disable(gl.CULL_FACE);
state.disable(gl.BLEND);
state.disable(gl.DEPTH_TEST);
state.disable(gl.SCISSOR_TEST);
state.depthMask(false);
state.colorMask(true, true, true, true);
state.clearColor(0, 0, 0, 0);
}
function createIsosurfaceBuffers(ctx, activeVoxelsBase, volumeData, histogramPyramid, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, axisOrder, constantGroup, vertexTexture, groupTexture, normalTexture) {
var drawBuffers = ctx.extensions.drawBuffers;
if (!drawBuffers)
throw new Error('need WebGL draw buffers');
if (debug_1.isTimingMode)
ctx.timer.mark('createIsosurfaceBuffers');
var gl = ctx.gl, state = ctx.state, resources = ctx.resources, extensions = ctx.extensions;
var pyramidTex = histogramPyramid.pyramidTex, height = histogramPyramid.height, levels = histogramPyramid.levels, scale = histogramPyramid.scale, count = histogramPyramid.count;
var width = pyramidTex.getWidth();
// console.log('width', width, 'height', height);
// console.log('iso', 'gridDim', gridDim, 'scale', scale, 'gridTexDim', gridTexDim);
// console.log('iso volumeData', volumeData);
if (!ctx.namedFramebuffers[IsosurfaceName]) {
ctx.namedFramebuffers[IsosurfaceName] = resources.framebuffer();
}
var framebuffer = ctx.namedFramebuffers[IsosurfaceName];
if ((0, compat_1.isWebGL2)(gl)) {
if (!vertexTexture) {
vertexTexture = extensions.colorBufferHalfFloat && extensions.textureHalfFloat
? resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
: resources.texture('image-float32', 'rgba', 'float', 'nearest');
}
if (!groupTexture) {
groupTexture = resources.texture('image-uint8', 'rgba', 'ubyte', 'nearest');
}
if (!normalTexture) {
normalTexture = extensions.colorBufferHalfFloat && extensions.textureHalfFloat
? resources.texture('image-float16', 'rgba', 'fp16', 'nearest')
: resources.texture('image-float32', 'rgba', 'float', 'nearest');
}
}
else {
// in webgl1 drawbuffers must be in the same format for some reason
// this is quite wasteful but good enough for medium size meshes
if (!vertexTexture) {
vertexTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest');
}
if (!groupTexture) {
groupTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest');
}
if (!normalTexture) {
normalTexture = resources.texture('image-float32', 'rgba', 'float', 'nearest');
}
}
vertexTexture.define(width, height);
groupTexture.define(width, height);
normalTexture.define(width, height);
vertexTexture.attachFramebuffer(framebuffer, 0);
groupTexture.attachFramebuffer(framebuffer, 1);
normalTexture.attachFramebuffer(framebuffer, 2);
var renderable = getIsosurfaceRenderable(ctx, pyramidTex, activeVoxelsBase, volumeData, gridDim, gridTexDim, transform, isoValue, levels, scale, count, invert, packedGroup, axisOrder, constantGroup);
ctx.state.currentRenderItemId = -1;
framebuffer.bind();
drawBuffers.drawBuffers([
drawBuffers.COLOR_ATTACHMENT0,
drawBuffers.COLOR_ATTACHMENT1,
drawBuffers.COLOR_ATTACHMENT2,
]);
setRenderingDefaults(ctx);
state.viewport(0, 0, width, height);
gl.clear(gl.COLOR_BUFFER_BIT);
renderable.render();
gl.finish();
if (debug_1.isTimingMode)
ctx.timer.markEnd('createIsosurfaceBuffers');
// printTextureImage(readTexture(ctx, vertexTexture, new Float32Array(width * height * 4)), { scale: 0.75 });
// printTextureImage(readTexture(ctx, groupTexture, new Uint8Array(width * height * 4)), { scale: 0.75 });
// printTextureImage(readTexture(ctx, normalTexture, new Float32Array(width * height * 4)), { scale: 0.75 });
return { vertexTexture: vertexTexture, groupTexture: groupTexture, normalTexture: normalTexture, vertexCount: count };
}
exports.createIsosurfaceBuffers = createIsosurfaceBuffers;
//
/**
* GPU isosurface extraction
*
* Algorithm from "High‐speed Marching Cubes using HistoPyramids"
* by C Dyken, G Ziegler, C Theobalt, HP Seidel
* https://doi.org/10.1111/j.1467-8659.2008.01182.x
*
* Implementation based on http://www.miaumiau.cat/2016/10/stream-compaction-in-webgl/
*/
function extractIsosurface(ctx, volumeData, gridDim, gridTexDim, gridTexScale, transform, isoValue, invert, packedGroup, axisOrder, constantGroup, vertexTexture, groupTexture, normalTexture) {
if (debug_1.isTimingMode)
ctx.timer.mark('extractIsosurface');
var activeVoxelsTex = (0, active_voxels_1.calcActiveVoxels)(ctx, volumeData, gridDim, gridTexDim, isoValue, gridTexScale);
var compacted = (0, reduction_1.createHistogramPyramid)(ctx, activeVoxelsTex, gridTexScale, gridTexDim);
var gv = createIsosurfaceBuffers(ctx, activeVoxelsTex, volumeData, compacted, gridDim, gridTexDim, transform, isoValue, invert, packedGroup, axisOrder, constantGroup, vertexTexture, groupTexture, normalTexture);
if (debug_1.isTimingMode)
ctx.timer.markEnd('extractIsosurface');
return gv;
}
exports.extractIsosurface = extractIsosurface;
;