|
@@ -26,16 +26,17 @@ import { TransformData } from '../transform-data';
|
|
import { createEmptyTransparency } from '../transparency-data';
|
|
import { createEmptyTransparency } from '../transparency-data';
|
|
import { createTransferFunctionTexture, getControlPointsFromVec2Array } from './transfer-function';
|
|
import { createTransferFunctionTexture, getControlPointsFromVec2Array } from './transfer-function';
|
|
import { createEmptyClipping } from '../clipping-data';
|
|
import { createEmptyClipping } from '../clipping-data';
|
|
|
|
+import { Grid, Volume } from '../../../mol-model/volume';
|
|
|
|
|
|
const VolumeBox = Box();
|
|
const VolumeBox = Box();
|
|
-const RenderModeOptions = [['isosurface', 'Isosurface'], ['volume', 'Volume']] as [string, string][];
|
|
|
|
|
|
|
|
export interface DirectVolume {
|
|
export interface DirectVolume {
|
|
readonly kind: 'direct-volume',
|
|
readonly kind: 'direct-volume',
|
|
|
|
|
|
- readonly gridTexture: ValueCell<Texture>,
|
|
|
|
- readonly gridTextureDim: ValueCell<Vec3>,
|
|
|
|
- readonly gridDimension: ValueCell<Vec3>,
|
|
|
|
|
|
+ readonly gridTexture: ValueCell<Texture>
|
|
|
|
+ readonly gridTextureDim: ValueCell<Vec3>
|
|
|
|
+ readonly gridDimension: ValueCell<Vec3>
|
|
|
|
+ readonly gridStats: ValueCell<Vec4> // [min, max, mean, sigma]
|
|
readonly bboxSize: ValueCell<Vec3>
|
|
readonly bboxSize: ValueCell<Vec3>
|
|
readonly bboxMin: ValueCell<Vec3>
|
|
readonly bboxMin: ValueCell<Vec3>
|
|
readonly bboxMax: ValueCell<Vec3>
|
|
readonly bboxMax: ValueCell<Vec3>
|
|
@@ -46,20 +47,21 @@ export interface DirectVolume {
|
|
}
|
|
}
|
|
|
|
|
|
export namespace DirectVolume {
|
|
export namespace DirectVolume {
|
|
- export function create(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, directVolume?: DirectVolume): DirectVolume {
|
|
|
|
|
|
+ export function create(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, stats: Grid['stats'], directVolume?: DirectVolume): DirectVolume {
|
|
return directVolume ?
|
|
return directVolume ?
|
|
- update(bbox, gridDimension, transform, texture, directVolume) :
|
|
|
|
- fromData(bbox, gridDimension, transform, texture);
|
|
|
|
|
|
+ update(bbox, gridDimension, transform, texture, stats, directVolume) :
|
|
|
|
+ fromData(bbox, gridDimension, transform, texture, stats);
|
|
}
|
|
}
|
|
|
|
|
|
function hashCode(directVolume: DirectVolume) {
|
|
function hashCode(directVolume: DirectVolume) {
|
|
return hashFnv32a([
|
|
return hashFnv32a([
|
|
directVolume.bboxSize.ref.version, directVolume.gridDimension.ref.version,
|
|
directVolume.bboxSize.ref.version, directVolume.gridDimension.ref.version,
|
|
directVolume.gridTexture.ref.version, directVolume.transform.ref.version,
|
|
directVolume.gridTexture.ref.version, directVolume.transform.ref.version,
|
|
|
|
+ directVolume.gridStats.ref.version
|
|
]);
|
|
]);
|
|
}
|
|
}
|
|
|
|
|
|
- function fromData(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture): DirectVolume {
|
|
|
|
|
|
+ function fromData(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, stats: Grid['stats']): DirectVolume {
|
|
const boundingSphere = Sphere3D();
|
|
const boundingSphere = Sphere3D();
|
|
let currentHash = -1;
|
|
let currentHash = -1;
|
|
|
|
|
|
@@ -72,6 +74,7 @@ export namespace DirectVolume {
|
|
gridDimension: ValueCell.create(gridDimension),
|
|
gridDimension: ValueCell.create(gridDimension),
|
|
gridTexture: ValueCell.create(texture),
|
|
gridTexture: ValueCell.create(texture),
|
|
gridTextureDim: ValueCell.create(Vec3.create(width, height, depth)),
|
|
gridTextureDim: ValueCell.create(Vec3.create(width, height, depth)),
|
|
|
|
+ gridStats: ValueCell.create(Vec4.create(stats.min, stats.max, stats.mean, stats.sigma)),
|
|
bboxMin: ValueCell.create(bbox.min),
|
|
bboxMin: ValueCell.create(bbox.min),
|
|
bboxMax: ValueCell.create(bbox.max),
|
|
bboxMax: ValueCell.create(bbox.max),
|
|
bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)),
|
|
bboxSize: ValueCell.create(Vec3.sub(Vec3.zero(), bbox.max, bbox.min)),
|
|
@@ -89,7 +92,7 @@ export namespace DirectVolume {
|
|
return directVolume;
|
|
return directVolume;
|
|
}
|
|
}
|
|
|
|
|
|
- function update(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, directVolume: DirectVolume): DirectVolume {
|
|
|
|
|
|
+ function update(bbox: Box3D, gridDimension: Vec3, transform: Mat4, texture: Texture, stats: Grid['stats'], directVolume: DirectVolume): DirectVolume {
|
|
const width = texture.getWidth();
|
|
const width = texture.getWidth();
|
|
const height = texture.getHeight();
|
|
const height = texture.getHeight();
|
|
const depth = texture.getDepth();
|
|
const depth = texture.getDepth();
|
|
@@ -97,6 +100,7 @@ export namespace DirectVolume {
|
|
ValueCell.update(directVolume.gridDimension, gridDimension);
|
|
ValueCell.update(directVolume.gridDimension, gridDimension);
|
|
ValueCell.update(directVolume.gridTexture, texture);
|
|
ValueCell.update(directVolume.gridTexture, texture);
|
|
ValueCell.update(directVolume.gridTextureDim, Vec3.set(directVolume.gridTextureDim.ref.value, width, height, depth));
|
|
ValueCell.update(directVolume.gridTextureDim, Vec3.set(directVolume.gridTextureDim.ref.value, width, height, depth));
|
|
|
|
+ ValueCell.update(directVolume.gridStats, Vec4.set(directVolume.gridStats.ref.value, stats.min, stats.max, stats.mean, stats.sigma));
|
|
ValueCell.update(directVolume.bboxMin, bbox.min);
|
|
ValueCell.update(directVolume.bboxMin, bbox.min);
|
|
ValueCell.update(directVolume.bboxMax, bbox.max);
|
|
ValueCell.update(directVolume.bboxMax, bbox.max);
|
|
ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min));
|
|
ValueCell.update(directVolume.bboxSize, Vec3.sub(directVolume.bboxSize.ref.value, bbox.max, bbox.min));
|
|
@@ -108,15 +112,32 @@ export namespace DirectVolume {
|
|
return {} as DirectVolume; // TODO
|
|
return {} as DirectVolume; // TODO
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ export function createRenderModeParam(volume?: Volume) {
|
|
|
|
+ const isoValueParam = volume
|
|
|
|
+ ? Volume.createIsoValueParam(Volume.IsoValue.relative(2), volume.grid.stats)
|
|
|
|
+ : Volume.IsoValueParam;
|
|
|
|
+
|
|
|
|
+ return PD.MappedStatic('volume', {
|
|
|
|
+ isosurface: PD.Group({
|
|
|
|
+ isoValue: isoValueParam,
|
|
|
|
+ }, { isFlat: true }),
|
|
|
|
+ volume: PD.Group({
|
|
|
|
+ controlPoints: PD.LineGraph([
|
|
|
|
+ Vec2.create(0.19, 0.0), Vec2.create(0.2, 0.05), Vec2.create(0.25, 0.05), Vec2.create(0.26, 0.0),
|
|
|
|
+ Vec2.create(0.79, 0.0), Vec2.create(0.8, 0.05), Vec2.create(0.85, 0.05), Vec2.create(0.86, 0.0),
|
|
|
|
+ ]),
|
|
|
|
+ list: PD.ColorList('red-yellow-blue'),
|
|
|
|
+ }, { isFlat: true })
|
|
|
|
+ }, { isEssential: true });
|
|
|
|
+ }
|
|
|
|
+
|
|
export const Params = {
|
|
export const Params = {
|
|
...BaseGeometry.Params,
|
|
...BaseGeometry.Params,
|
|
- isoValueNorm: PD.Numeric(0.22, { min: 0, max: 1, step: 0.01 }, { description: 'Normalized Isolevel Value' }),
|
|
|
|
- renderMode: PD.Select('volume', RenderModeOptions),
|
|
|
|
- controlPoints: PD.LineGraph([
|
|
|
|
- Vec2.create(0.19, 0.0), Vec2.create(0.2, 0.05), Vec2.create(0.25, 0.05), Vec2.create(0.26, 0.0),
|
|
|
|
- Vec2.create(0.79, 0.0), Vec2.create(0.8, 0.05), Vec2.create(0.85, 0.05), Vec2.create(0.86, 0.0),
|
|
|
|
- ]),
|
|
|
|
- list: PD.ColorList('red-yellow-blue'),
|
|
|
|
|
|
+ // doubleSided: PD.Boolean(false, BaseGeometry.CustomQualityParamInfo),
|
|
|
|
+ // flipSided: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
|
|
|
+ flatShaded: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
|
|
|
+ ignoreLight: PD.Boolean(false, BaseGeometry.ShadingCategory),
|
|
|
|
+ renderMode: createRenderModeParam(),
|
|
};
|
|
};
|
|
export type Params = typeof Params
|
|
export type Params = typeof Params
|
|
|
|
|
|
@@ -131,8 +152,15 @@ export namespace DirectVolume {
|
|
updateRenderableState
|
|
updateRenderableState
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ function getNormalizedIsoValue(out: Vec2, isoValue: Volume.IsoValue, stats: Vec4) {
|
|
|
|
+ const [min, max, mean, sigma] = stats;
|
|
|
|
+ const value = Volume.IsoValue.toAbsolute(isoValue, { min, max, mean, sigma }).absoluteValue;
|
|
|
|
+ Vec2.set(out, (value - min) / (max - min), (0 - min) / (max - min));
|
|
|
|
+ return out;
|
|
|
|
+ }
|
|
|
|
+
|
|
function createValues(directVolume: DirectVolume, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): DirectVolumeValues {
|
|
function createValues(directVolume: DirectVolume, transform: TransformData, locationIt: LocationIterator, theme: Theme, props: PD.Values<Params>): DirectVolumeValues {
|
|
- const { gridTexture, gridTextureDim } = directVolume;
|
|
|
|
|
|
+ const { gridTexture, gridTextureDim, gridStats } = directVolume;
|
|
const { bboxSize, bboxMin, bboxMax, gridDimension, transform: gridTransform } = directVolume;
|
|
const { bboxSize, bboxMin, bboxMax, gridDimension, transform: gridTransform } = directVolume;
|
|
|
|
|
|
const { instanceCount, groupCount } = locationIt;
|
|
const { instanceCount, groupCount } = locationIt;
|
|
@@ -147,10 +175,14 @@ export namespace DirectVolume {
|
|
const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere);
|
|
const invariantBoundingSphere = Sphere3D.clone(directVolume.boundingSphere);
|
|
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
|
|
const boundingSphere = calculateTransformBoundingSphere(invariantBoundingSphere, transform.aTransform.ref.value, instanceCount);
|
|
|
|
|
|
- const controlPoints = getControlPointsFromVec2Array(props.controlPoints);
|
|
|
|
- const transferTex = createTransferFunctionTexture(controlPoints, props.list.colors);
|
|
|
|
|
|
+ const controlPoints = props.renderMode.name === 'volume' ? getControlPointsFromVec2Array(props.renderMode.params.controlPoints) : [];
|
|
|
|
+ const transferTex = createTransferFunctionTexture(controlPoints, props.renderMode.name === 'volume' ? props.renderMode.params.list.colors : []);
|
|
|
|
|
|
- const maxSteps = Math.ceil(Vec3.magnitude(gridDimension.ref.value)) * 2 * 5;
|
|
|
|
|
|
+ const isoValue = props.renderMode.name === 'isosurface'
|
|
|
|
+ ? props.renderMode.params.isoValue
|
|
|
|
+ : Volume.IsoValue.relative(2);
|
|
|
|
+
|
|
|
|
+ const maxSteps = Math.ceil(Vec3.magnitude(gridDimension.ref.value) * 5);
|
|
|
|
|
|
return {
|
|
return {
|
|
...color,
|
|
...color,
|
|
@@ -167,19 +199,25 @@ export namespace DirectVolume {
|
|
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
|
|
invariantBoundingSphere: ValueCell.create(invariantBoundingSphere),
|
|
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
|
|
uInvariantBoundingSphere: ValueCell.create(Vec4.ofSphere(invariantBoundingSphere)),
|
|
|
|
|
|
- uIsoValue: ValueCell.create(props.isoValueNorm),
|
|
|
|
|
|
+ uIsoValue: ValueCell.create(getNormalizedIsoValue(Vec2(), isoValue, directVolume.gridStats.ref.value)),
|
|
uBboxMin: bboxMin,
|
|
uBboxMin: bboxMin,
|
|
uBboxMax: bboxMax,
|
|
uBboxMax: bboxMax,
|
|
uBboxSize: bboxSize,
|
|
uBboxSize: bboxSize,
|
|
- dMaxSteps: ValueCell.create(maxSteps),
|
|
|
|
|
|
+ uMaxSteps: ValueCell.create(maxSteps),
|
|
uTransform: gridTransform,
|
|
uTransform: gridTransform,
|
|
uGridDim: gridDimension,
|
|
uGridDim: gridDimension,
|
|
- dRenderMode: ValueCell.create(props.renderMode),
|
|
|
|
|
|
+ dRenderMode: ValueCell.create(props.renderMode.name),
|
|
tTransferTex: transferTex,
|
|
tTransferTex: transferTex,
|
|
|
|
|
|
dGridTexType: ValueCell.create(gridTexture.ref.value.getDepth() > 0 ? '3d' : '2d'),
|
|
dGridTexType: ValueCell.create(gridTexture.ref.value.getDepth() > 0 ? '3d' : '2d'),
|
|
uGridTexDim: gridTextureDim,
|
|
uGridTexDim: gridTextureDim,
|
|
tGridTex: gridTexture,
|
|
tGridTex: gridTexture,
|
|
|
|
+ uGridStats: gridStats,
|
|
|
|
+
|
|
|
|
+ dDoubleSided: ValueCell.create(false),
|
|
|
|
+ dFlatShaded: ValueCell.create(props.flatShaded),
|
|
|
|
+ dFlipSided: ValueCell.create(true),
|
|
|
|
+ dIgnoreLight: ValueCell.create(props.ignoreLight),
|
|
};
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
@@ -190,12 +228,20 @@ export namespace DirectVolume {
|
|
}
|
|
}
|
|
|
|
|
|
function updateValues(values: DirectVolumeValues, props: PD.Values<Params>) {
|
|
function updateValues(values: DirectVolumeValues, props: PD.Values<Params>) {
|
|
- ValueCell.updateIfChanged(values.uIsoValue, props.isoValueNorm);
|
|
|
|
|
|
+ ValueCell.updateIfChanged(values.alpha, props.alpha);
|
|
ValueCell.updateIfChanged(values.uAlpha, props.alpha);
|
|
ValueCell.updateIfChanged(values.uAlpha, props.alpha);
|
|
- ValueCell.updateIfChanged(values.dRenderMode, props.renderMode);
|
|
|
|
-
|
|
|
|
- const controlPoints = getControlPointsFromVec2Array(props.controlPoints);
|
|
|
|
- createTransferFunctionTexture(controlPoints, props.list.colors, values.tTransferTex);
|
|
|
|
|
|
+ // ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
|
|
|
|
+ ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded);
|
|
|
|
+ // ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
|
|
|
|
+ ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
|
|
|
|
+ ValueCell.updateIfChanged(values.dRenderMode, props.renderMode.name);
|
|
|
|
+
|
|
|
|
+ if (props.renderMode.name === 'isosurface') {
|
|
|
|
+ ValueCell.updateIfChanged(values.uIsoValue, getNormalizedIsoValue(values.uIsoValue.ref.value, props.renderMode.params.isoValue, values.uGridStats.ref.value));
|
|
|
|
+ } else if (props.renderMode.name === 'volume') {
|
|
|
|
+ const controlPoints = getControlPointsFromVec2Array(props.renderMode.params.controlPoints);
|
|
|
|
+ createTransferFunctionTexture(controlPoints, props.renderMode.params.list.colors, values.tTransferTex);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
function updateBoundingSphere(values: DirectVolumeValues, directVolume: DirectVolume) {
|
|
function updateBoundingSphere(values: DirectVolumeValues, directVolume: DirectVolume) {
|
|
@@ -220,6 +266,7 @@ export namespace DirectVolume {
|
|
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
|
|
function updateRenderableState(state: RenderableState, props: PD.Values<Params>) {
|
|
BaseGeometry.updateRenderableState(state, props);
|
|
BaseGeometry.updateRenderableState(state, props);
|
|
state.opaque = false;
|
|
state.opaque = false;
|
|
|
|
+ state.writeDepth = props.renderMode.name === 'isosurface';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|