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

marker-data improvements

- add uniform marker type
- check if a loci is superset of a visual
- special case to improve reversing the previous mark
parent e1ad67a0
Branches
No related tags found
No related merge requests found
Showing
with 204 additions and 48 deletions
......@@ -9,8 +9,10 @@ import { Vec2 } from '../../mol-math/linear-algebra';
import { TextureImage, createTextureImage } from '../../mol-gl/renderable/util';
export type MarkerData = {
uMarker: ValueCell<number>,
tMarker: ValueCell<TextureImage<Uint8Array>>
uMarkerTexDim: ValueCell<Vec2>
dMarkerType: ValueCell<string>,
markerAverage: ValueCell<number>
markerStatus: ValueCell<number>
}
......@@ -19,7 +21,7 @@ export function getMarkersAverage(array: Uint8Array, count: number): number {
if (count === 0) return 0;
let sum = 0;
for (let i = 0; i < count; ++i) {
if (array[i]) sum += 1;
sum += +!!array[i];
}
return sum / count;
}
......@@ -29,17 +31,21 @@ export function createMarkers(count: number, markerData?: MarkerData): MarkerDat
const average = getMarkersAverage(markers.array, count);
const status = average === 0 ? 0 : -1;
if (markerData) {
ValueCell.updateIfChanged(markerData.uMarker, 0);
ValueCell.update(markerData.tMarker, markers);
ValueCell.update(markerData.uMarkerTexDim, Vec2.create(markers.width, markers.height));
ValueCell.updateIfChanged(markerData.dMarkerType, status === -1 ? 'groupInstance' : 'uniform');
ValueCell.updateIfChanged(markerData.markerAverage, average);
ValueCell.updateIfChanged(markerData.markerStatus, status);
return markerData;
} else {
return {
uMarker: ValueCell.create(0),
tMarker: ValueCell.create(markers),
uMarkerTexDim: ValueCell.create(Vec2.create(markers.width, markers.height)),
markerAverage: ValueCell.create(average),
markerStatus: ValueCell.create(status),
dMarkerType: ValueCell.create('uniform'),
};
}
}
......@@ -47,17 +53,21 @@ export function createMarkers(count: number, markerData?: MarkerData): MarkerDat
const emptyMarkerTexture = { array: new Uint8Array(1), width: 1, height: 1 };
export function createEmptyMarkers(markerData?: MarkerData): MarkerData {
if (markerData) {
ValueCell.updateIfChanged(markerData.uMarker, 0);
ValueCell.update(markerData.tMarker, emptyMarkerTexture);
ValueCell.update(markerData.uMarkerTexDim, Vec2.create(1, 1));
ValueCell.updateIfChanged(markerData.dMarkerType, 'uniform');
ValueCell.updateIfChanged(markerData.markerAverage, 0);
ValueCell.updateIfChanged(markerData.markerStatus, 0);
return markerData;
} else {
return {
uMarker: ValueCell.create(0),
tMarker: ValueCell.create(emptyMarkerTexture),
uMarkerTexDim: ValueCell.create(Vec2.create(1, 1)),
markerAverage: ValueCell.create(0),
markerStatus: ValueCell.create(0),
dMarkerType: ValueCell.create('uniform'),
};
}
}
\ No newline at end of file
......@@ -210,8 +210,10 @@ export type SizeSchema = typeof SizeSchema
export type SizeValues = Values<SizeSchema>
export const MarkerSchema = {
uMarker: UniformSpec('f', 'material'),
uMarkerTexDim: UniformSpec('v2'),
tMarker: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
dMarkerType: DefineSpec('string', ['uniform', 'groupInstance']),
markerAverage: ValueSpec('number'),
markerStatus: ValueSpec('number'),
} as const;
......
export const apply_marker_color = `
float marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on some cards on win
if (marker > 0.1) {
if (intMod(marker, 2.0) > 0.1) {
gl_FragColor.rgb = mix(gl_FragColor.rgb, uHighlightColor, uHighlightStrength);
......
export const assign_marker_varying = `
#if defined(dMarkerType_groupInstance)
vMarker = readFromTexture(tMarker, aInstance * float(uGroupCount) + group, uMarkerTexDim).a;
#endif
`;
\ No newline at end of file
export const assign_material_color = `
#if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
#if defined(dMarkerType_uniform)
float marker = uMarker;
#elif defined(dMarkerType_groupInstance)
float marker = vMarker;
#endif
marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
#endif
#if defined(dRenderVariant_color)
#if defined(dUsePalette)
vec4 material = vec4(texture2D(tPalette, vec2(vPaletteV, 0.5)).rgb, uAlpha);
......@@ -21,7 +30,7 @@ export const assign_material_color = `
vec4 material = packDepthToRGBA(gl_FragCoord.z);
#endif
#elif defined(dRenderVariant_markingDepth)
if (vMarker > 0.0)
if (marker > 0.0)
discard;
#ifdef enabledFragDepth
vec4 material = packDepthToRGBA(gl_FragDepthEXT);
......@@ -29,13 +38,13 @@ export const assign_material_color = `
vec4 material = packDepthToRGBA(gl_FragCoord.z);
#endif
#elif defined(dRenderVariant_markingMask)
if (vMarker == 0.0)
if (marker == 0.0)
discard;
float depthTest = 1.0;
if (uMarkingDepthTest) {
depthTest = (fragmentDepth >= getDepth(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0;
}
bool isHighlight = intMod(floor(vMarker * 255.0 + 0.5), 2.0) > 0.1;
bool isHighlight = intMod(marker, 2.0) > 0.1;
vec4 material = vec4(0.0, depthTest, isHighlight ? 1.0 : 0.0, 1.0);
#endif
......
......@@ -23,11 +23,16 @@ uniform vec3 uHighlightColor;
uniform vec3 uSelectColor;
uniform float uHighlightStrength;
uniform float uSelectStrength;
#if defined(dMarkerType_uniform)
uniform float uMarker;
#elif defined(dMarkerType_groupInstance)
#if __VERSION__ == 100
varying float vMarker;
#else
flat in float vMarker;
#endif
#endif
varying vec3 vModelPosition;
varying vec3 vViewPosition;
......
......@@ -26,6 +26,9 @@ uniform vec4 uInvariantBoundingSphere;
#endif
#endif
#if defined(dMarkerType_uniform)
uniform float uMarker;
#elif defined(dMarkerType_groupInstance)
uniform vec2 uMarkerTexDim;
uniform sampler2D tMarker;
#if __VERSION__ == 100
......@@ -33,6 +36,7 @@ uniform sampler2D tMarker;
#else
flat out float vMarker;
#endif
#endif
varying vec3 vModelPosition;
varying vec3 vViewPosition;
......
......@@ -55,8 +55,13 @@ uniform vec3 uHighlightColor;
uniform vec3 uSelectColor;
uniform float uHighlightStrength;
uniform float uSelectStrength;
#if defined(dMarkerType_uniform)
uniform float uMarker;
#elif defined(dMarkerType_groupInstance)
uniform vec2 uMarkerTexDim;
uniform sampler2D tMarker;
#endif
uniform float uFogNear;
uniform float uFogFar;
......@@ -329,7 +334,12 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
#include apply_light_color
#endif
float vMarker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
#if defined(dMarkerType_uniform)
float marker = uMarker;
#elif defined(dMarkerType_groupInstance)
float marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
#endif
#include apply_interior_color
#include apply_marker_color
......@@ -392,14 +402,18 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
gl_FragColor.a = material.a * uAlpha * uTransferScale;
#if defined(dMarkerType_uniform)
float marker = uMarker;
#elif defined(dMarkerType_groupInstance)
#ifdef dPackedGroup
float group = decodeFloatRGB(textureGroup(floor(unitPos * uGridDim + 0.5) / uGridDim).rgb);
#else
vec3 g = floor(unitPos * uGridDim + 0.5);
float group = g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y;
#endif
float vMarker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
float marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
#endif
#include apply_marker_color
preFogAlphaBlended = (1.0 - preFogAlphaBlended) * gl_FragColor.a + preFogAlphaBlended;
......
......@@ -116,20 +116,25 @@ void main() {
discard;
gl_FragColor = packDepthToRGBA(gl_FragCoord.z);
#elif defined(dRenderVariant_marking)
#if defined(dMarkerType_uniform)
float marker = uMarker;
#elif defined(dMarkerType_groupInstance)
float group = decodeFloatRGB(texture2D(tGroupTex, vUv).rgb);
float vMarker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
float marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
#endif
#if defined(dRenderVariant_markingDepth)
if (vMarker > 0.0 || imageData.a < 0.05)
if (marker > 0.0 || imageData.a < 0.05)
discard;
gl_FragColor = packDepthToRGBA(gl_FragCoord.z);
#elif defined(dRenderVariant_markingMask)
if (vMarker == 0.0 || imageData.a < 0.05)
if (marker == 0.0 || imageData.a < 0.05)
discard;
float depthTest = 1.0;
if (uMarkingDepthTest) {
depthTest = (fragmentDepth >= getDepth(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0;
}
bool isHighlight = intMod(floor(vMarker * 255.0 + 0.5), 2.0) > 0.1;
bool isHighlight = intMod(marker, 2.0) > 0.1;
gl_FragColor = vec4(0.0, depthTest, isHighlight ? 1.0 : 0.0, 1.0);
#endif
#elif defined(dRenderVariant_color)
......@@ -138,8 +143,13 @@ void main() {
gl_FragColor = imageData;
gl_FragColor.a *= uAlpha;
#if defined(dMarkerType_uniform)
float marker = uMarker;
#elif defined(dMarkerType_groupInstance)
float group = decodeFloatRGB(texture2D(tGroupTex, vUv).rgb);
float vMarker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
float marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
#endif
#include apply_marker_color
#include apply_fog
......
......@@ -67,6 +67,7 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge
const { defaultProps, createGeometry, createLocationIterator, getLoci, eachLocation, setUpdateState, mustRecreate, processValues, dispose } = builder;
const { updateValues, updateBoundingSphere, updateRenderableState, createPositionIterator } = builder.geometryUtils;
const updateState = VisualUpdateState.create();
const previousMark: Visual.PreviousMark = { loci: EmptyLoci, action: MarkerAction.None, status: -1 };
let renderObject: GraphicsRenderObject<G['kind']> | undefined;
......@@ -235,7 +236,7 @@ export function ComplexVisual<G extends Geometry, P extends StructureParams & Ge
return renderObject ? getLoci(pickingId, currentStructure, renderObject.id) : EmptyLoci;
},
mark(loci: Loci, action: MarkerAction) {
return Visual.mark(renderObject, loci, action, lociApply);
return Visual.mark(renderObject, loci, action, lociApply, previousMark);
},
setVisibility(visible: boolean) {
Visual.setVisibility(renderObject, visible);
......
......@@ -71,6 +71,7 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
const { defaultProps, createGeometry, createLocationIterator, getLoci, eachLocation, setUpdateState, mustRecreate, processValues, dispose } = builder;
const { createEmpty: createEmptyGeometry, updateValues, updateBoundingSphere, updateRenderableState, createPositionIterator } = builder.geometryUtils;
const updateState = VisualUpdateState.create();
const previousMark: Visual.PreviousMark = { loci: EmptyLoci, action: MarkerAction.None, status: -1 };
let renderObject: GraphicsRenderObject<G['kind']> | undefined;
......@@ -289,7 +290,7 @@ export function UnitsVisual<G extends Geometry, P extends StructureParams & Geom
return renderObject ? getLoci(pickingId, currentStructureGroup, renderObject.id) : EmptyLoci;
},
mark(loci: Loci, action: MarkerAction) {
return Visual.mark(renderObject, loci, action, lociApply);
return Visual.mark(renderObject, loci, action, lociApply, previousMark);
},
setVisibility(visible: boolean) {
Visual.setVisibility(renderObject, visible);
......
......@@ -7,8 +7,8 @@
import { RuntimeContext } from '../mol-task';
import { GraphicsRenderObject } from '../mol-gl/render-object';
import { PickingId } from '../mol-geo/geometry/picking';
import { Loci, isEmptyLoci, isEveryLoci } from '../mol-model/loci';
import { MarkerAction, applyMarkerAction, getMarkerInfo } from '../mol-util/marker-action';
import { Loci, isEmptyLoci, isEveryLoci, EveryLoci } from '../mol-model/loci';
import { MarkerAction, applyMarkerAction, getMarkerInfo, setMarkerValue, getPartialMarkerAverage, MarkerActions, MarkerInfo } from '../mol-util/marker-action';
import { ParamDefinition as PD } from '../mol-util/param-definition';
import { WebGLContext } from '../mol-gl/webgl/context';
import { Theme } from '../mol-theme/theme';
......@@ -68,32 +68,63 @@ namespace Visual {
if (renderObject) renderObject.state.colorOnly = colorOnly;
}
export function mark(renderObject: GraphicsRenderObject | undefined, loci: Loci, action: MarkerAction, lociApply: LociApply) {
if (!renderObject) return false;
export type PreviousMark = { loci: Loci, action: MarkerAction, status: MarkerInfo['status'] }
const { tMarker, markerAverage, markerStatus, uGroupCount, instanceCount } = renderObject.values;
export function mark(renderObject: GraphicsRenderObject | undefined, loci: Loci, action: MarkerAction, lociApply: LociApply, previous?: PreviousMark) {
if (!renderObject || isEmptyLoci(loci)) return false;
const { tMarker, dMarkerType, uMarker, markerAverage, markerStatus, uGroupCount, instanceCount } = renderObject.values;
const count = uGroupCount.ref.value * instanceCount.ref.value;
const { array } = tMarker.ref.value;
if (!isEveryLoci(loci)) {
let intervalSize = 0;
lociApply(loci, interval => {
intervalSize += Interval.size(interval);
return true;
}, true);
if (intervalSize === count) loci = EveryLoci;
}
let changed = false;
let average = -1;
let status = -1;
let status: MarkerInfo['status'] = -1;
if (isEveryLoci(loci)) {
changed = applyMarkerAction(array, Interval.ofLength(count), action);
if (changed) {
const info = getMarkerInfo(action, markerStatus.ref.value);
if (info.status !== -1) {
changed = markerStatus.ref.value !== info.status;
if (changed) setMarkerValue(array, info.status, count);
} else {
changed = applyMarkerAction(array, Interval.ofLength(count), action);
}
average = info.average;
status = info.status;
}
} else if (!isEmptyLoci(loci)) {
} else {
changed = lociApply(loci, interval => applyMarkerAction(array, interval, action), true);
if (changed) {
average = getPartialMarkerAverage(action, markerStatus.ref.value);
if (previous && previous.status !== -1 && average === -1 &&
MarkerActions.isReverse(previous.action, action) &&
Loci.areEqual(loci, previous.loci)
) {
status = previous.status;
average = status === 0 ? 0 : 1;
}
}
}
if (changed) {
if (average === -1) {
average = getMarkersAverage(array, count);
if (average === 0) status = 0;
}
ValueCell.update(tMarker, tMarker.ref.value);
if (previous) {
previous.action = action;
previous.loci = loci;
previous.status = markerStatus.ref.value as MarkerInfo['status'];
}
ValueCell.updateIfChanged(uMarker, status);
if (status === -1) ValueCell.update(tMarker, tMarker.ref.value);
ValueCell.updateIfChanged(dMarkerType, status === -1 ? 'groupInstance' : 'uniform');
ValueCell.updateIfChanged(markerAverage, average);
ValueCell.updateIfChanged(markerStatus, status);
}
......
......@@ -35,6 +35,20 @@ export namespace MarkerActions {
MarkerAction.Select | MarkerAction.Deselect | MarkerAction.Toggle |
MarkerAction.Clear
) as MarkerActions;
export function isReverse(a: MarkerAction, b: MarkerAction) {
return (
(a === MarkerAction.Highlight && b === MarkerAction.RemoveHighlight) ||
(a === MarkerAction.RemoveHighlight && b === MarkerAction.Highlight) ||
(a === MarkerAction.Select && b === MarkerAction.Deselect) ||
(a === MarkerAction.Deselect && b === MarkerAction.Select) ||
(a === MarkerAction.Toggle && b === MarkerAction.Toggle)
);
}
}
export function setMarkerValue(array: Uint8Array, status: 0 | 1 | 2 | 3, count: number) {
array.fill(status, 0, count);
}
export function applyMarkerActionAtPosition(array: Uint8Array, i: number, action: MarkerAction) {
......@@ -205,3 +219,57 @@ export function getMarkerInfo(action: MarkerAction, currentStatus: number): Mark
}
return { average, status };
}
/**
* Assumes the action is applied to a partial set that is
* neither the empty set nor the full set.
*/
export function getPartialMarkerAverage(action: MarkerAction, currentStatus: number): MarkerInfo['average'] {
switch (action) {
case MarkerAction.Highlight:
return 1;
case MarkerAction.RemoveHighlight:
if (currentStatus === 0) {
return 0;
} else if (currentStatus === 1) {
return -1;
} else if (currentStatus === 2 || currentStatus === 3) {
return 1;
}
return -1;
case MarkerAction.Select:
return 1;
case MarkerAction.Deselect:
if (currentStatus === 1 || currentStatus === 3) {
return 1;
} else if (currentStatus === 0) {
return 0;
} else if (currentStatus === 2) {
return -1;
}
return -1;
case MarkerAction.Toggle:
if (currentStatus === 1) {
return 1;
} else if (currentStatus === 2) {
return 1;
} else if (currentStatus === 3) {
return 1;
} else if (currentStatus === 0) {
return 1;
}
return -1;
case MarkerAction.Clear:
if (currentStatus === 1) {
return 1;
} else if (currentStatus === 2) {
return 1;
} else if (currentStatus === 3) {
return 1;
} else if (currentStatus === 0) {
return 0;
}
return -1;
}
return -1;
}
\ 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