Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 29 additions & 20 deletions src/main/kotlin/graphics/scenery/controls/CursorTool.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import kotlin.getValue
/**
* A spherical cursor that can be attached to VR controllers via [attachCursor].
* You can access the cursor's world position via [getPosition].
* The cursor can be scaled up or down with [scaleByFactor] or set directly with [setRadius].
* The cursor can be scaled up or down with [scaleByFactor] or set directly with [radius].
* [visualScale] is decoupled from the actual radius and only affects the sphere scale, not the reported radius.
* The radius is constrained by [minRadius] and [maxRadius].
* Its color is defined by [defaultColor], but can be adjusted with [setColor] and reset with [resetColor].
* @author Samuel Pantze
Expand All @@ -22,35 +23,50 @@ class CursorTool(
val initPos: Vector3f = Vector3f(-0.01f, -0.05f, -0.03f),
val defaultColor: Vector3f = Vector3f(0.15f, 0.2f, 1f),
minRadius: Float = 0.001f,
maxRadius: Float = 0.15f
maxRadius: Float = 0.15f,
visualScale: Float = 1f
) {
private val logger by lazyLogger()
private val initRadius = radius
val initRadius = radius
var visualScale = visualScale
set(value) {
field = value
updateTransforms()
}
var radius = radius
private set
set(value) {
field = value
updateTransforms()
}
var minRadius = minRadius
private set
var maxRadius = maxRadius
private set

val cursor = Sphere(radius)
val cursorSphere = Sphere(radius)

private fun updateTransforms() {
cursorSphere.spatial().scale = Vector3f(radius / initRadius * visualScale)
cursorSphere.spatial().position = Vector3f(initPos) +
Vector3f(initPos).normalize().times(radius - initRadius)
}

/** Get the current world space position of this cursor. */
fun getPosition() = cursor.spatial().worldPosition()
fun getPosition() = cursorSphere.spatial().worldPosition()

/** Attach the cursor to another object, typically a VR controller.
* Enabling [debug] will also attach the cursor's bounding grid to the [parent]. */
fun attachCursor(parent: Node, debug: Boolean = false) {
cursor.name = "VR Cursor"
cursor.material {
cursorSphere.name = "VR Cursor"
cursorSphere.material {
diffuse = Vector3f(0.15f, 0.2f, 1f)
}
cursor.spatial().position = initPos
parent.addChild(cursor)
cursorSphere.spatial().position = initPos
parent.addChild(cursorSphere)

if (debug) {
val bb = BoundingGrid()
bb.node = cursor
bb.node = cursorSphere
bb.name = "Cursor BB"
bb.lineWidth = 2f
bb.gridColor = Vector3f(1f, 0.3f, 0.25f)
Expand All @@ -67,14 +83,7 @@ class CursorTool(
clampedFac = factor
}
radius *= clampedFac
cursor.spatial().scale = Vector3f(radius / initRadius)
cursor.spatial().position = Vector3f(initPos) +
Vector3f(initPos).normalize().times(radius - initRadius)
}

/** Set the radius directly. */
fun setRadius(r: Float) {
radius = r
}

/** Reset the radius to its initial value. */
Expand All @@ -84,12 +93,12 @@ class CursorTool(

/** Set the cursor to a specified [color]. */
fun setColor(color: Vector3f) {
cursor.material().diffuse = color
cursorSphere.material().diffuse = color
}

/** Reset the cursor color to [defaultColor]. */
fun resetColor() {
cursor.material().diffuse = defaultColor
cursorSphere.material().diffuse = defaultColor
}

/** Set the minimum radius the cursor can be scaled down to. */
Expand Down
14 changes: 12 additions & 2 deletions src/main/kotlin/graphics/scenery/volumes/Volume.kt
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ open class Volume(
/** Plane equations for slicing planes mapped to origin */
var slicingPlaneEquations = mapOf<Int, Vector4f>()

/** Standard deviation of the gauss function used for lensing */
var lensingRadius = 1f
/** Center of the lensing gauss function */
var lensingPosition = Vector3f(0f)

/** Modes how assigned slicing planes interact with the volume */
var slicingMode = SlicingMode.None

Expand All @@ -205,7 +210,10 @@ open class Volume(
Slicing(2),
// The slice around the slicing planes is rendered with no transparency
// while also the cropping rule applies for the rest of the volume.
Both(3)
Both(3),
// Only render the volume within a defined radius either as a 3D gaussian or a hard falloff
LensingSmooth(4),
LensingHard(5)
}

@Transient
Expand All @@ -218,7 +226,7 @@ open class Volume(
/** Current timepoint. */
var currentTimepoint: Int = 0
get() {
// despite IDEAs warning this might be not be false if kryo uses its de/serialization magic
// despite IDEAs warning this might not be false if kryo uses its de/serialization magic
return if (dataSource is VolumeDataSource.NullSource) {
0
} else {
Expand Down Expand Up @@ -427,6 +435,8 @@ open class Volume(
this.colormap = fresh.colormap
this.transferFunction = fresh.transferFunction
this.slicingMode = fresh.slicingMode
this.lensingRadius = fresh.lensingRadius
this.lensingPosition = fresh.lensingPosition
this.multiResolutionLevelLimits = fresh.multiResolutionLevelLimits
this.origin = fresh.origin

Expand Down
6 changes: 5 additions & 1 deletion src/main/kotlin/graphics/scenery/volumes/VolumeManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -335,13 +335,15 @@ class VolumeManager(
"slicingMode",
"usedSlicingPlanes",
"sceneGraphVisibility",
"lensingRadius",
"lensingPosition"
)
segments[SegmentType.SampleVolume] = SegmentTemplate(
"SampleSimpleVolume.frag",
"im", "sourcemax", "intersectBoundingBox",
"volume", "transferFunction", "colorMap", "sampleVolume", "convert", "slicingPlanes",
"slicingMode", "usedSlicingPlanes",
"sceneGraphVisibility", "volTextureSize"
"sceneGraphVisibility", "volTextureSize", "lensingRadius", "lensingPosition"
)
segments[SegmentType.Convert] = SegmentTemplate(
"Converter.frag",
Expand Down Expand Up @@ -564,6 +566,8 @@ class VolumeManager(
currentProg.setUniform(i, "usedSlicingPlanes",
min(state.node.slicingPlaneEquations.size, Volume.MAX_SUPPORTED_SLICING_PLANES))
currentProg.setUniform(i, "sceneGraphVisibility", if (state.node.visible) 1 else 0)
currentProg.setUniform(i, "lensingRadius", state.node.lensingRadius)
currentProg.setUniform(i, "lensingPosition", state.node.lensingPosition)

context.bindTexture(state.transferFunction)
context.bindTexture(state.colorMap)
Expand Down
20 changes: 18 additions & 2 deletions src/main/resources/graphics/scenery/volumes/SampleBlockVolume.frag
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ uniform vec3 sourcemax;
uniform vec4 slicingPlanes[16];
uniform int slicingMode;
uniform int usedSlicingPlanes;
uniform float lensingRadius;
uniform vec3 lensingPosition;

void intersectBoundingBox( vec4 wfront, vec4 wback, out float tnear, out float tfar )
{
Expand All @@ -25,6 +27,7 @@ vec4 sampleVolume( vec4 wpos, sampler3D volumeCache, vec3 cacheSize, vec3 blockS
{
bool cropping = slicingMode == 1 || slicingMode == 3;
bool slicing = slicingMode == 2 || slicingMode == 3;
bool lensing = slicingMode == 4 || slicingMode == 5;

bool isCropped = false;
bool isInSlice = false;
Expand All @@ -41,6 +44,17 @@ vec4 sampleVolume( vec4 wpos, sampler3D volumeCache, vec3 cacheSize, vec3 blockS
isInSlice = isInSlice || dist < 0.02f;
}

float lensingAlpha = 1.0;
if (lensing) {
vec3 dp = wpos.xyz - lensingPosition;
float distSq = dot(dp, dp);
float sigma2 = lensingRadius * lensingRadius;
if (distSq > 9.0 * sigma2) return vec4(0);
if (slicingMode == 4) {
lensingAlpha = exp(-distSq / (2.0 * sigma2));
}
}

if ( (!cropping && slicing && !isInSlice)
|| ( cropping && !slicing && isCropped)
|| ( cropping && slicing && !(!isCropped || isInSlice ))){
Expand All @@ -62,6 +76,8 @@ vec4 sampleVolume( vec4 wpos, sampler3D volumeCache, vec3 cacheSize, vec3 blockS
float tf = texture(transferFunction, vec2(rawsample + 0.001f, 0.5f)).r;
vec3 cmapplied = tf * texture(colorMap, vec2(rawsample + 0.001f, 0.5f)).rgb;

int intransparent = int( slicing && isInSlice) ;
return vec4(cmapplied*tf,1) * intransparent + vec4(cmapplied, tf) * (1-intransparent);
int isOpaque = int(slicing && isInSlice);
vec4 opaque = vec4(cmapplied * tf, 1.0);
vec4 transparent = vec4(cmapplied, tf * lensingAlpha); // transparency modulated by lensingAlpha
return mix(transparent, opaque, float(isOpaque));
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ uniform vec3 sourcemax;
uniform vec4 slicingPlanes[16];
uniform int slicingMode;
uniform int usedSlicingPlanes;
uniform float lensingRadius;
uniform vec3 lensingPosition;
uniform ivec3 volTextureSize;

void intersectBoundingBox( vec4 wfront, vec4 wback, out float tnear, out float tfar )
Expand All @@ -20,6 +22,7 @@ vec4 sampleVolume( vec4 wpos )
{
bool cropping = slicingMode == 1 || slicingMode == 3;
bool slicing = slicingMode == 2 || slicingMode == 3;
bool lensing = slicingMode == 4;

bool isCropped = false;
bool isInSlice = false;
Expand All @@ -36,6 +39,15 @@ vec4 sampleVolume( vec4 wpos )
isInSlice = isInSlice || dist < 0.02f;
}

float lensingAlpha = 1.0;
if (lensing) {
vec3 dp = wpos.xyz - lensingPosition;
float distSq = dot(dp, dp);
float sigma2 = lensingRadius * lensingRadius;
if (distSq > 7.0 * sigma2) return vec4(0);
lensingAlpha = exp(-distSq / (2.0 * sigma2));
}

if ( (!cropping && slicing && !isInSlice)
|| ( cropping && !slicing && isCropped)
|| ( cropping && slicing && !(!isCropped || isInSlice ))){
Expand All @@ -49,6 +61,9 @@ vec4 sampleVolume( vec4 wpos )
float tf = texture(transferFunction, vec2(rawsample + 0.001f, 0.5f)).r;
vec3 cmapplied = texture(colorMap, vec2(rawsample + 0.001f, 0.5f)).rgb;

int intransparent = int( slicing && isInSlice) ;
return vec4(cmapplied*tf,1) * intransparent + vec4(cmapplied, tf) * (1-intransparent);
int isOpaque = int(slicing && isInSlice);
vec4 opaque = vec4(cmapplied * tf, 1.0);
vec4 transparent = vec4(cmapplied, tf * lensingAlpha); // transparency modulated by lensingAlpha
return mix(opaque, transparent, float(isOpaque));

}
Loading
Loading