Skip to content
Snippets Groups Projects
Commit d58e90d9 authored by Alexander Rose's avatar Alexander Rose
Browse files

add cylinders geometry and shader

parent cd872b47
No related branches found
No related tags found
No related merge requests found
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ChunkedArray } from '../../../mol-data/util';
import { Cylinders } from './cylinders';
import { Vec3 } from '../../../mol-math/linear-algebra';
export interface CylindersBuilder {
add(startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number): void
addFixedCountDashes(start: Vec3, end: Vec3, segmentCount: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number): void
addFixedLengthDashes(start: Vec3, end: Vec3, segmentLength: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number): void
getCylinders(): Cylinders
}
const tmpVecA = Vec3();
const tmpVecB = Vec3();
const tmpDir = Vec3();
// avoiding namespace lookup improved performance in Chrome (Aug 2020)
const caAdd = ChunkedArray.add;
const caAdd3 = ChunkedArray.add3;
export namespace CylindersBuilder {
export function create(initialCount = 2048, chunkSize = 1024, cylinders?: Cylinders): CylindersBuilder {
const groups = ChunkedArray.create(Float32Array, 1, chunkSize, cylinders ? cylinders.groupBuffer.ref.value : initialCount);
const starts = ChunkedArray.create(Float32Array, 3, chunkSize, cylinders ? cylinders.startBuffer.ref.value : initialCount);
const ends = ChunkedArray.create(Float32Array, 3, chunkSize, cylinders ? cylinders.endBuffer.ref.value : initialCount);
const scales = ChunkedArray.create(Float32Array, 1, chunkSize, cylinders ? cylinders.scaleBuffer.ref.value : initialCount);
const caps = ChunkedArray.create(Float32Array, 1, chunkSize, cylinders ? cylinders.capBuffer.ref.value : initialCount);
const add = (startX: number, startY: number, startZ: number, endX: number, endY: number, endZ: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number) => {
for (let i = 0; i < 6; ++i) {
caAdd3(starts, startX, startY, startZ);
caAdd3(ends, endX, endY, endZ);
caAdd(groups, group);
caAdd(scales, radiusScale);
caAdd(caps, (topCap ? 1 : 0) + (bottomCap ? 2 : 0));
}
};
const addFixedCountDashes = (start: Vec3, end: Vec3, segmentCount: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number) => {
const d = Vec3.distance(start, end);
const s = Math.floor(segmentCount / 2);
const step = 1 / segmentCount;
Vec3.sub(tmpDir, end, start);
for (let j = 0; j < s; ++j) {
const f = step * (j * 2 + 1);
Vec3.setMagnitude(tmpDir, tmpDir, d * f);
Vec3.add(tmpVecA, start, tmpDir);
Vec3.setMagnitude(tmpDir, tmpDir, d * step * ((j + 1) * 2));
Vec3.add(tmpVecB, start, tmpDir);
add(tmpVecA[0], tmpVecA[1], tmpVecA[2], tmpVecB[0], tmpVecB[1], tmpVecB[2], radiusScale, topCap, bottomCap, group);
}
};
return {
add,
addFixedCountDashes,
addFixedLengthDashes: (start: Vec3, end: Vec3, segmentLength: number, radiusScale: number, topCap: boolean, bottomCap: boolean, group: number) => {
const d = Vec3.distance(start, end);
addFixedCountDashes(start, end, d / segmentLength, radiusScale, topCap, bottomCap, group);
},
getCylinders: () => {
const cylinderCount = groups.elementCount / 6;
const gb = ChunkedArray.compact(groups, true) as Float32Array;
const sb = ChunkedArray.compact(starts, true) as Float32Array;
const eb = ChunkedArray.compact(ends, true) as Float32Array;
const ab = ChunkedArray.compact(scales, true) as Float32Array;
const cb = ChunkedArray.compact(caps, true) as Float32Array;
const mb = cylinders && cylinderCount <= cylinders.cylinderCount ? cylinders.mappingBuffer.ref.value : new Float32Array(cylinderCount * 18);
const ib = cylinders && cylinderCount <= cylinders.cylinderCount ? cylinders.indexBuffer.ref.value : new Uint32Array(cylinderCount * 12);
if (!cylinders || cylinderCount > cylinders.cylinderCount) fillMappingAndIndices(cylinderCount, mb, ib);
return Cylinders.create(mb, ib, gb, sb, eb, ab, cb, cylinderCount, cylinders);
}
};
}
}
function fillMappingAndIndices(n: number, mb: Float32Array, ib: Uint32Array) {
for (let i = 0; i < n; ++i) {
const mo = i * 18;
mb[mo] = -1; mb[mo + 1] = 1; mb[mo + 2] = -1;
mb[mo + 3] = -1; mb[mo + 4] = -1; mb[mo + 5] = -1;
mb[mo + 6] = 1; mb[mo + 7] = 1; mb[mo + 8] = -1;
mb[mo + 9] = 1; mb[mo + 10] = 1; mb[mo + 11] = 1;
mb[mo + 12] = 1; mb[mo + 13] = -1; mb[mo + 14] = -1;
mb[mo + 15] = 1; mb[mo + 16] = -1; mb[mo + 17] = 1;
}
for (let i = 0; i < n; ++i) {
const o = i * 6;
const io = i * 12;
ib[io] = o; ib[io + 1] = o + 1; ib[io + 2] = o + 2;
ib[io + 3] = o + 1; ib[io + 4] = o + 4; ib[io + 5] = o + 2;
ib[io + 6] = o + 2; ib[io + 7] = o + 4; ib[io + 8] = o + 3;
ib[io + 9] = o + 4; ib[io + 10] = o + 5; ib[io + 11] = o + 3;
}
}
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { ValueCell } from '../../../mol-util';
import { Mat4, Vec3, Vec4 } from '../../../mol-math/linear-algebra';
import { transformPositionArray, GroupMapping, createGroupMapping} from '../../util';
import { GeometryUtils } from '../geometry';
import { createColors } from '../color-data';
import { createMarkers } from '../marker-data';
import { createSizes, getMaxSize } from '../size-data';
import { TransformData } from '../transform-data';
import { LocationIterator, PositionLocation } from '../../util/location-iterator';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { calculateInvariantBoundingSphere, calculateTransformBoundingSphere } from '../../../mol-gl/renderable/util';
import { Sphere3D } from '../../../mol-math/geometry';
import { Theme } from '../../../mol-theme/theme';
import { Color } from '../../../mol-util/color';
import { BaseGeometry } from '../base';
import { createEmptyOverpaint } from '../overpaint-data';
import { createEmptyTransparency } from '../transparency-data';
import { hashFnv32a } from '../../../mol-data/util';
import { createEmptyClipping } from '../clipping-data';
import { CylindersValues } from '../../../mol-gl/renderable/cylinders';
import { RenderableState } from '../../../mol-gl/renderable';
export interface Cylinders {
readonly kind: 'cylinders',
/** Number of cylinders */
cylinderCount: number,
/** Mapping buffer as array of uvw values wrapped in a value cell */
readonly mappingBuffer: ValueCell<Float32Array>,
/** Index buffer as array of vertex index triplets wrapped in a value cell */
readonly indexBuffer: ValueCell<Uint32Array>,
/** Group buffer as array of group ids for each vertex wrapped in a value cell */
readonly groupBuffer: ValueCell<Float32Array>,
/** Cylinder start buffer as array of xyz values wrapped in a value cell */
readonly startBuffer: ValueCell<Float32Array>,
/** Cylinder end buffer as array of xyz values wrapped in a value cell */
readonly endBuffer: ValueCell<Float32Array>,
/** Cylinder scale buffer as array of scaling factors wrapped in a value cell */
readonly scaleBuffer: ValueCell<Float32Array>,
/** Cylinder cap buffer as array of cap flags wrapped in a value cell */
readonly capBuffer: ValueCell<Float32Array>,
/** Bounding sphere of the cylinders */
readonly boundingSphere: Sphere3D
/** Maps group ids to cylinder indices */
readonly groupMapping: GroupMapping
setBoundingSphere(boundingSphere: Sphere3D): void
}
export namespace Cylinders {
export function create(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, scales: Float32Array, caps: Float32Array, cylinderCount: number, cylinders?: Cylinders): Cylinders {
return cylinders ?
update(mappings, indices, groups, starts, ends, scales, caps, cylinderCount, cylinders) :
fromArrays(mappings, indices, groups, starts, ends, scales, caps, cylinderCount);
}
export function createEmpty(cylinders?: Cylinders): Cylinders {
const mb = cylinders ? cylinders.mappingBuffer.ref.value : new Float32Array(0);
const ib = cylinders ? cylinders.indexBuffer.ref.value : new Uint32Array(0);
const gb = cylinders ? cylinders.groupBuffer.ref.value : new Float32Array(0);
const sb = cylinders ? cylinders.startBuffer.ref.value : new Float32Array(0);
const eb = cylinders ? cylinders.endBuffer.ref.value : new Float32Array(0);
const ab = cylinders ? cylinders.scaleBuffer.ref.value : new Float32Array(0);
const cb = cylinders ? cylinders.capBuffer.ref.value : new Float32Array(0);
return create(mb, ib, gb, sb, eb, ab, cb, 0, cylinders);
}
function hashCode(cylinders: Cylinders) {
return hashFnv32a([
cylinders.cylinderCount, cylinders.mappingBuffer.ref.version, cylinders.indexBuffer.ref.version,
cylinders.groupBuffer.ref.version, cylinders.startBuffer.ref.version, cylinders.endBuffer.ref.version, cylinders.scaleBuffer.ref.version, cylinders.capBuffer.ref.version
]);
}
function fromArrays(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, scales: Float32Array, caps: Float32Array, cylinderCount: number): Cylinders {
const boundingSphere = Sphere3D();
let groupMapping: GroupMapping;
let currentHash = -1;
let currentGroup = -1;
const cylinders = {
kind: 'cylinders' as const,
cylinderCount,
mappingBuffer: ValueCell.create(mappings),
indexBuffer: ValueCell.create(indices),
groupBuffer: ValueCell.create(groups),
startBuffer: ValueCell.create(starts),
endBuffer: ValueCell.create(ends),
scaleBuffer: ValueCell.create(scales),
capBuffer: ValueCell.create(caps),
get boundingSphere() {
const newHash = hashCode(cylinders);
if (newHash !== currentHash) {
const s = calculateInvariantBoundingSphere(cylinders.startBuffer.ref.value, cylinders.cylinderCount * 6, 6);
const e = calculateInvariantBoundingSphere(cylinders.endBuffer.ref.value, cylinders.cylinderCount * 6, 6);
Sphere3D.expandBySphere(boundingSphere, s, e);
currentHash = newHash;
}
return boundingSphere;
},
get groupMapping() {
if (cylinders.groupBuffer.ref.version !== currentGroup) {
groupMapping = createGroupMapping(cylinders.groupBuffer.ref.value, cylinders.cylinderCount, 6);
currentGroup = cylinders.groupBuffer.ref.version;
}
return groupMapping;
},
setBoundingSphere(sphere: Sphere3D) {
Sphere3D.copy(boundingSphere, sphere);
currentHash = hashCode(cylinders);
}
};
return cylinders;
}
function update(mappings: Float32Array, indices: Uint32Array, groups: Float32Array, starts: Float32Array, ends: Float32Array, scales: Float32Array, caps: Float32Array, cylinderCount: number, cylinders: Cylinders) {
if (cylinderCount > cylinders.cylinderCount) {
ValueCell.update(cylinders.mappingBuffer, mappings);
ValueCell.update(cylinders.indexBuffer, indices);
}
cylinders.cylinderCount = cylinderCount;
ValueCell.update(cylinders.groupBuffer, groups);
ValueCell.update(cylinders.startBuffer, starts);
ValueCell.update(cylinders.endBuffer, ends);
ValueCell.update(cylinders.scaleBuffer, scales);
ValueCell.update(cylinders.capBuffer, caps);
return cylinders;
}
export function transform(cylinders: Cylinders, t: Mat4) {
const start = cylinders.startBuffer.ref.value;
transformPositionArray(t, start, 0, cylinders.cylinderCount * 6);
ValueCell.update(cylinders.startBuffer, start);
const end = cylinders.endBuffer.ref.value;
transformPositionArray(t, end, 0, cylinders.cylinderCount * 6);
ValueCell.update(cylinders.endBuffer, end);
}
//
export const Params = {
...BaseGeometry.Params,
sizeFactor: PD.Numeric(1, { min: 0, max: 10, step: 0.1 }),
sizeAspectRatio: PD.Numeric(1, { min: 0, max: 3, step: 0.01 }),
doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
xrayShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
};
export type Params = typeof Params
export const Utils: GeometryUtils<Cylinders, Params> = {
Params,
createEmpty,
createValues,
createValuesSimple,
updateValues,
updateBoundingSphere,
createRenderableState,
updateRenderableState,
createPositionIterator
};
function createPositionIterator(cylinders: Cylinders, transform: TransformData): LocationIterator {
const groupCount = cylinders.cylinderCount * 6;
const instanceCount = transform.instanceCount.ref.value;
const location = PositionLocation();
const p = location.position;
const s = cylinders.startBuffer.ref.value;
const e = cylinders.endBuffer.ref.value;
const m = transform.aTransform.ref.value;
const getLocation = (groupIndex: number, instanceIndex: number) => {
const v = groupIndex % 6 === 0 ? s : e;
if (instanceIndex < 0) {
Vec3.fromArray(p, v, groupIndex * 3);
} else {
Vec3.transformMat4Offset(p, v, m, 0, groupIndex * 3, instanceIndex * 16);
}
return location;
};
return LocationIterator(groupCount, instanceCount, 2, getLocation);
}
function createValues(cylinders: Cylinders, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): CylindersValues {
const { instanceCount, groupCount } = locationIt;
const positionIt = createPositionIterator(cylinders, transform);
const color = createColors(locationIt, positionIt, theme.color);
const size = createSizes(locationIt, theme.size);
const marker = createMarkers(instanceCount * groupCount);
const overpaint = createEmptyOverpaint();
const transparency = createEmptyTransparency();
const clipping = createEmptyClipping();
const counts = { drawCount: cylinders.cylinderCount * 4 * 3, vertexCount: cylinders.cylinderCount * 6, groupCount, instanceCount };
const padding = getMaxSize(size) * props.sizeFactor;
const invariantBoundingSphere = Sphere3D.clone(cylinders.boundingSphere);
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
return {
aMapping: cylinders.mappingBuffer,
aGroup: cylinders.groupBuffer,
aStart: cylinders.startBuffer,
aEnd: cylinders.endBuffer,
aScale: cylinders.scaleBuffer,
aCap: cylinders.capBuffer,
elements: cylinders.indexBuffer,
boundingSphere: ValueCell.create(boundingSphere),
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
...color,
...size,
...marker,
...overpaint,
...transparency,
...clipping,
...transform,
padding: ValueCell.create(padding),
...BaseGeometry.createValues(props, counts),
uSizeFactor: ValueCell.create(props.sizeFactor * props.sizeAspectRatio),
dDoubleSided: ValueCell.create(props.doubleSided),
dIgnoreLight: ValueCell.create(props.ignoreLight),
dXrayShaded: ValueCell.create(props.xrayShaded),
};
}
function createValuesSimple(cylinders: Cylinders, props: Partial<PD.Values<Params>>, colorValue: Color, sizeValue: number, transform?: TransformData) {
const s = BaseGeometry.createSimple(colorValue, sizeValue, transform);
const p = { ...PD.getDefaultValues(Params), ...props };
return createValues(cylinders, s.transform, s.locationIterator, s.theme, p);
}
function updateValues(values: CylindersValues, props: PD.Values<Params>) {
BaseGeometry.updateValues(values, props);
ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor * props.sizeAspectRatio);
ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
}
function updateBoundingSphere(values: CylindersValues, cylinders: Cylinders) {
const invariantBoundingSphere = Sphere3D.clone(cylinders.boundingSphere);
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, values.aTransform.ref.value, values.instanceCount.ref.value);
if (!Sphere3D.equals(boundingSphere, values.boundingSphere.ref.value)) {
ValueCell.update(values.boundingSphere, boundingSphere);
}
if (!Sphere3D.equals(invariantBoundingSphere, values.invariantBoundingSphere.ref.value)) {
ValueCell.update(values.invariantBoundingSphere, invariantBoundingSphere);
ValueCell.update(values.uInvariantBoundingSphere, Vec4.fromSphere(values.uInvariantBoundingSphere.ref.value, invariantBoundingSphere));
}
}
function createRenderableState(props: PD.Values<Params>): RenderableState {
const state = BaseGeometry.createRenderableState(props);
updateRenderableState(state, props);
return state;
}
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
BaseGeometry.updateRenderableState(state, props);
state.opaque = state.opaque && !props.xrayShaded;
state.writeDepth = state.opaque;
}
}
\ No newline at end of file
...@@ -22,13 +22,15 @@ import { Theme } from '../../mol-theme/theme'; ...@@ -22,13 +22,15 @@ import { Theme } from '../../mol-theme/theme';
import { RenderObjectValues } from '../../mol-gl/render-object'; import { RenderObjectValues } from '../../mol-gl/render-object';
import { TextureMesh } from './texture-mesh/texture-mesh'; import { TextureMesh } from './texture-mesh/texture-mesh';
import { Image } from './image/image'; import { Image } from './image/image';
import { Cylinders } from './cylinders/cylinders';
export type GeometryKind = 'mesh' | 'points' | 'spheres' | 'text' | 'lines' | 'direct-volume' | 'image' | 'texture-mesh' export type GeometryKind = 'mesh' | 'points' | 'spheres' | 'cylinders' | 'text' | 'lines' | 'direct-volume' | 'image' | 'texture-mesh'
export type Geometry<T extends GeometryKind = GeometryKind> = export type Geometry<T extends GeometryKind = GeometryKind> =
T extends 'mesh' ? Mesh : T extends 'mesh' ? Mesh :
T extends 'points' ? Points : T extends 'points' ? Points :
T extends 'spheres' ? Spheres : T extends 'spheres' ? Spheres :
T extends 'cylinders' ? Cylinders :
T extends 'text' ? Text : T extends 'text' ? Text :
T extends 'lines' ? Lines : T extends 'lines' ? Lines :
T extends 'direct-volume' ? DirectVolume : T extends 'direct-volume' ? DirectVolume :
...@@ -39,6 +41,7 @@ type GeometryParams<T extends GeometryKind> = ...@@ -39,6 +41,7 @@ type GeometryParams<T extends GeometryKind> =
T extends 'mesh' ? Mesh.Params : T extends 'mesh' ? Mesh.Params :
T extends 'points' ? Points.Params : T extends 'points' ? Points.Params :
T extends 'spheres' ? Spheres.Params : T extends 'spheres' ? Spheres.Params :
T extends 'cylinders' ? Cylinders.Params :
T extends 'text' ? Text.Params : T extends 'text' ? Text.Params :
T extends 'lines' ? Lines.Params : T extends 'lines' ? Lines.Params :
T extends 'direct-volume' ? DirectVolume.Params : T extends 'direct-volume' ? DirectVolume.Params :
...@@ -65,6 +68,7 @@ export namespace Geometry { ...@@ -65,6 +68,7 @@ export namespace Geometry {
case 'mesh': return geometry.triangleCount * 3; case 'mesh': return geometry.triangleCount * 3;
case 'points': return geometry.pointCount; case 'points': return geometry.pointCount;
case 'spheres': return geometry.sphereCount * 2 * 3; case 'spheres': return geometry.sphereCount * 2 * 3;
case 'cylinders': return geometry.cylinderCount * 4 * 3;
case 'text': return geometry.charCount * 2 * 3; case 'text': return geometry.charCount * 2 * 3;
case 'lines': return geometry.lineCount * 2 * 3; case 'lines': return geometry.lineCount * 2 * 3;
case 'direct-volume': return 12 * 3; case 'direct-volume': return 12 * 3;
...@@ -78,6 +82,7 @@ export namespace Geometry { ...@@ -78,6 +82,7 @@ export namespace Geometry {
case 'mesh': return geometry.vertexCount; case 'mesh': return geometry.vertexCount;
case 'points': return geometry.pointCount; case 'points': return geometry.pointCount;
case 'spheres': return geometry.sphereCount * 4; case 'spheres': return geometry.sphereCount * 4;
case 'cylinders': return geometry.cylinderCount * 6;
case 'text': return geometry.charCount * 4; case 'text': return geometry.charCount * 4;
case 'lines': return geometry.lineCount * 4; case 'lines': return geometry.lineCount * 4;
case 'direct-volume': case 'direct-volume':
...@@ -93,6 +98,7 @@ export namespace Geometry { ...@@ -93,6 +98,7 @@ export namespace Geometry {
case 'mesh': case 'mesh':
case 'points': case 'points':
case 'spheres': case 'spheres':
case 'cylinders':
case 'text': case 'text':
case 'lines': case 'lines':
return getDrawCount(geometry) === 0 ? 0 : (arrayMax(geometry.groupBuffer.ref.value) + 1); return getDrawCount(geometry) === 0 ? 0 : (arrayMax(geometry.groupBuffer.ref.value) + 1);
...@@ -111,6 +117,7 @@ export namespace Geometry { ...@@ -111,6 +117,7 @@ export namespace Geometry {
case 'mesh': return Mesh.Utils as any; case 'mesh': return Mesh.Utils as any;
case 'points': return Points.Utils as any; case 'points': return Points.Utils as any;
case 'spheres': return Spheres.Utils as any; case 'spheres': return Spheres.Utils as any;
case 'cylinders': return Cylinders.Utils as any;
case 'text': return Text.Utils as any; case 'text': return Text.Utils as any;
case 'lines': return Lines.Utils as any; case 'lines': return Lines.Utils as any;
case 'direct-volume': return DirectVolume.Utils as any; case 'direct-volume': return DirectVolume.Utils as any;
......
...@@ -15,6 +15,7 @@ import { SpheresValues, SpheresRenderable } from './renderable/spheres'; ...@@ -15,6 +15,7 @@ import { SpheresValues, SpheresRenderable } from './renderable/spheres';
import { TextValues, TextRenderable } from './renderable/text'; import { TextValues, TextRenderable } from './renderable/text';
import { TextureMeshValues, TextureMeshRenderable } from './renderable/texture-mesh'; import { TextureMeshValues, TextureMeshRenderable } from './renderable/texture-mesh';
import { ImageValues, ImageRenderable } from './renderable/image'; import { ImageValues, ImageRenderable } from './renderable/image';
import { CylindersRenderable, CylindersValues } from './renderable/cylinders';
const getNextId = idFactory(0, 0x7FFFFFFF); const getNextId = idFactory(0, 0x7FFFFFFF);
...@@ -28,12 +29,13 @@ export interface GraphicsRenderObject<T extends RenderObjectType = RenderObjectT ...@@ -28,12 +29,13 @@ export interface GraphicsRenderObject<T extends RenderObjectType = RenderObjectT
readonly materialId: number readonly materialId: number
} }
export type RenderObjectType = 'mesh' | 'points' | 'spheres' | 'text' | 'lines' | 'direct-volume' | 'image' | 'texture-mesh' export type RenderObjectType = 'mesh' | 'points' | 'spheres' | 'cylinders' | 'text' | 'lines' | 'direct-volume' | 'image' | 'texture-mesh'
export type RenderObjectValues<T extends RenderObjectType> = export type RenderObjectValues<T extends RenderObjectType> =
T extends 'mesh' ? MeshValues : T extends 'mesh' ? MeshValues :
T extends 'points' ? PointsValues : T extends 'points' ? PointsValues :
T extends 'spheres' ? SpheresValues : T extends 'spheres' ? SpheresValues :
T extends 'cylinders' ? CylindersValues :
T extends 'text' ? TextValues : T extends 'text' ? TextValues :
T extends 'lines' ? LinesValues : T extends 'lines' ? LinesValues :
T extends 'direct-volume' ? DirectVolumeValues : T extends 'direct-volume' ? DirectVolumeValues :
...@@ -51,6 +53,7 @@ export function createRenderable<T extends RenderObjectType>(ctx: WebGLContext, ...@@ -51,6 +53,7 @@ export function createRenderable<T extends RenderObjectType>(ctx: WebGLContext,
case 'mesh': return MeshRenderable(ctx, o.id, o.values as MeshValues, o.state, o.materialId); case 'mesh': return MeshRenderable(ctx, o.id, o.values as MeshValues, o.state, o.materialId);
case 'points': return PointsRenderable(ctx, o.id, o.values as PointsValues, o.state, o.materialId); case 'points': return PointsRenderable(ctx, o.id, o.values as PointsValues, o.state, o.materialId);
case 'spheres': return SpheresRenderable(ctx, o.id, o.values as SpheresValues, o.state, o.materialId); case 'spheres': return SpheresRenderable(ctx, o.id, o.values as SpheresValues, o.state, o.materialId);
case 'cylinders': return CylindersRenderable(ctx, o.id, o.values as CylindersValues, o.state, o.materialId);
case 'text': return TextRenderable(ctx, o.id, o.values as TextValues, o.state, o.materialId); case 'text': return TextRenderable(ctx, o.id, o.values as TextValues, o.state, o.materialId);
case 'lines': return LinesRenderable(ctx, o.id, o.values as LinesValues, o.state, o.materialId); case 'lines': return LinesRenderable(ctx, o.id, o.values as LinesValues, o.state, o.materialId);
case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values as DirectVolumeValues, o.state, o.materialId); case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values as DirectVolumeValues, o.state, o.materialId);
......
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { Renderable, RenderableState, createRenderable } from '../renderable';
import { WebGLContext } from '../webgl/context';
import { createGraphicsRenderItem } from '../webgl/render-item';
import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema } from './schema';
import { CylindersShaderCode } from '../shader-code';
import { ValueCell } from '../../mol-util';
export const CylindersSchema = {
...BaseSchema,
...SizeSchema,
aStart: AttributeSpec('float32', 3, 0),
aEnd: AttributeSpec('float32', 3, 0),
aMapping: AttributeSpec('float32', 3, 0),
aScale: AttributeSpec('float32', 1, 0),
aCap: AttributeSpec('float32', 1, 0),
elements: ElementsSpec('uint32'),
padding: ValueSpec('number'),
dDoubleSided: DefineSpec('boolean'),
dIgnoreLight: DefineSpec('boolean'),
dXrayShaded: DefineSpec('boolean'),
};
export type CylindersSchema = typeof CylindersSchema
export type CylindersValues = Values<CylindersSchema>
export function CylindersRenderable(ctx: WebGLContext, id: number, values: CylindersValues, state: RenderableState, materialId: number): Renderable<CylindersValues> {
const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...CylindersSchema };
const internalValues: InternalValues = {
uObjectId: ValueCell.create(id),
};
const shaderCode = CylindersShaderCode;
const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
return createRenderable(renderItem, values, state);
}
\ No newline at end of file
...@@ -117,6 +117,8 @@ export function ShaderCode(name: string, vert: string, frag: string, extensions: ...@@ -117,6 +117,8 @@ export function ShaderCode(name: string, vert: string, frag: string, extensions:
return { id: shaderCodeId(), name, vert: addIncludes(vert), frag: addIncludes(frag), extensions }; return { id: shaderCodeId(), name, vert: addIncludes(vert), frag: addIncludes(frag), extensions };
} }
// Note: `drawBuffers` need to be 'optional' for wboit
import points_vert from './shader/points.vert'; import points_vert from './shader/points.vert';
import points_frag from './shader/points.frag'; import points_frag from './shader/points.frag';
export const PointsShaderCode = ShaderCode('points', points_vert, points_frag, { drawBuffers: 'optional' }); export const PointsShaderCode = ShaderCode('points', points_vert, points_frag, { drawBuffers: 'optional' });
...@@ -125,6 +127,10 @@ import spheres_vert from './shader/spheres.vert'; ...@@ -125,6 +127,10 @@ import spheres_vert from './shader/spheres.vert';
import spheres_frag from './shader/spheres.frag'; import spheres_frag from './shader/spheres.frag';
export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: 'required', drawBuffers: 'optional' }); export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: 'required', drawBuffers: 'optional' });
import cylinders_vert from './shader/cylinders.vert';
import cylinders_frag from './shader/cylinders.frag';
export const CylindersShaderCode = ShaderCode('cylinders', cylinders_vert, cylinders_frag, { fragDepth: 'required', drawBuffers: 'optional' });
import text_vert from './shader/text.vert'; import text_vert from './shader/text.vert';
import text_frag from './shader/text.frag'; import text_frag from './shader/text.frag';
export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { standardDerivatives: 'required', drawBuffers: 'optional' }); export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { standardDerivatives: 'required', drawBuffers: 'optional' });
......
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
export default `
precision highp float;
precision highp int;
uniform mat4 uView;
varying mat4 vTransform;
varying vec3 vStart;
varying vec3 vEnd;
varying float vSize;
varying float vCap;
uniform vec3 uCameraDir;
uniform float uIsOrtho;
uniform vec3 uCameraPosition;
#include common
#include common_frag_params
#include color_frag_params
#include light_frag_params
#include common_clip
#include wboit_params
// adapted from https://www.shadertoy.com/view/4lcSRn
// The MIT License, Copyright 2016 Inigo Quilez
bool CylinderImpostor(
in vec3 rayOrigin, in vec3 rayDir,
in vec3 start, in vec3 end, in float radius,
out vec4 intersection, out bool interior
){
vec3 ba = end - start;
vec3 oc = rayOrigin - start;
float baba = dot(ba, ba);
float bard = dot(ba, rayDir);
float baoc = dot(ba, oc);
float k2 = baba - bard*bard;
float k1 = baba * dot(oc, rayDir) - baoc * bard;
float k0 = baba * dot(oc, oc) - baoc * baoc - radius * radius * baba;
float h = k1 * k1 - k2 * k0;
if (h < 0.0) return false;
bool topCap = (vCap > 0.9 && vCap < 1.1) || vCap >= 2.9;
bool bottomCap = (vCap > 1.9 && vCap < 2.1) || vCap >= 2.9;
// body outside
h = sqrt(h);
float t = (-k1 - h) / k2;
float y = baoc + t * bard;
if (y > 0.0 && y < baba) {
interior = false;
intersection = vec4(t, (oc + t * rayDir - ba * y / baba) / radius);
return true;
}
if (topCap && y < 0.0) {
// top cap
t = -baoc / bard;
if (abs(k1 + k2 * t) < h) {
interior = false;
intersection = vec4(t, ba * sign(y) / baba);
return true;
}
} else if(bottomCap && y >= 0.0) {
// bottom cap
t = (baba - baoc) / bard;
if (abs(k1 + k2 * t) < h) {
interior = false;
intersection = vec4(t, ba * sign(y) / baba);
return true;
}
}
#ifdef dDoubleSided
// body inside
h = -h;
t = (-k1 - h) / k2;
y = baoc + t * bard;
if (y > 0.0 && y < baba) {
interior = true;
intersection = vec4(t, (oc + t * rayDir - ba * y / baba) / radius);
return true;
}
// TODO: handle inside caps???
#endif
return false;
}
void main() {
#include clip_pixel
vec3 rayDir = mix(normalize(vModelPosition - uCameraPosition), uCameraDir, uIsOrtho);
vec4 intersection;
bool interior;
bool hit = CylinderImpostor(vModelPosition, rayDir, vStart, vEnd, vSize, intersection, interior);
if (!hit) discard;
vec3 vViewPosition = vModelPosition + intersection.x * rayDir;
vViewPosition = (uView * vec4(vViewPosition, 1.0)).xyz;
gl_FragDepthEXT = calcDepth(vViewPosition);
// bugfix (mac only?)
if (gl_FragDepthEXT < 0.0) discard;
if (gl_FragDepthEXT > 1.0) discard;
#include assign_material_color
#if defined(dRenderVariant_pick)
#include check_picking_alpha
gl_FragColor = material;
#elif defined(dRenderVariant_depth)
gl_FragColor = material;
#elif defined(dRenderVariant_color)
#ifdef dIgnoreLight
gl_FragColor = material;
#else
mat3 normalMatrix = transpose3(inverse3(mat3(uView)));
vec3 normal = normalize(normalMatrix * -normalize(intersection.yzw));
#include apply_light_color
#endif
#include apply_interior_color
#include apply_marker_color
#include apply_fog
float fragmentDepth = gl_FragDepthEXT;
#include wboit_write
#endif
}
`;
\ No newline at end of file
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
export default `
precision highp float;
precision highp int;
#include common
#include read_from_texture
#include common_vert_params
#include color_vert_params
#include size_vert_params
#include common_clip
uniform mat4 uModelView;
attribute mat4 aTransform;
attribute float aInstance;
attribute float aGroup;
attribute vec3 aMapping;
attribute vec3 aStart;
attribute vec3 aEnd;
attribute float aScale;
attribute float aCap;
varying mat4 vTransform;
varying vec3 vStart;
varying vec3 vEnd;
varying float vSize;
varying float vCap;
uniform float uIsOrtho;
uniform vec3 uCameraDir;
void main() {
#include assign_group
#include assign_color_varying
#include assign_marker_varying
#include assign_clipping_varying
#include assign_size
mat4 modelTransform = uModel * aTransform;
vTransform = aTransform;
vStart = (modelTransform * vec4(aStart, 1.0)).xyz;
vEnd = (modelTransform * vec4(aEnd, 1.0)).xyz;
vSize = size * aScale;
vCap = aCap;
vModelPosition = (vStart + vEnd) * 0.5;
vec3 camDir = -mix(normalize(vModelPosition - uCameraPosition), uCameraDir, uIsOrtho);
vec3 dir = vEnd - vStart;
// ensure cylinder 'dir' is pointing towards the camera
if(dot(camDir, dir) < 0.0) dir = -dir;
vec3 left = cross(camDir, dir);
vec3 up = cross(left, dir);
left = vSize * normalize(left);
up = vSize * normalize(up);
// move vertex in object-space from center to corner
vModelPosition += aMapping.x * dir + aMapping.y * left + aMapping.z * up;
vec4 mvPosition = uView * vec4(vModelPosition, 1.0);
vViewPosition = mvPosition.xyz;
gl_Position = uProjection * mvPosition;
#include clip_instance
}
`;
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment