Skip to content

Commit

Permalink
hide gizmo handles depending on view angle
Browse files Browse the repository at this point in the history
  • Loading branch information
fabmax committed Feb 26, 2024
1 parent 9ef5053 commit 3dc74a8
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package de.fabmax.kool.modules.gizmo

import de.fabmax.kool.input.Pointer
import de.fabmax.kool.math.RayD
import de.fabmax.kool.math.Vec3f
import de.fabmax.kool.math.deg
import de.fabmax.kool.math.*
import de.fabmax.kool.modules.ksl.KslUnlitShader
import de.fabmax.kool.pipeline.Attribute
import de.fabmax.kool.pipeline.DepthCompareOp
Expand All @@ -12,6 +10,8 @@ import de.fabmax.kool.scene.MeshRayTest
import de.fabmax.kool.scene.Node
import de.fabmax.kool.scene.TrsTransformD
import de.fabmax.kool.util.Color
import kotlin.math.abs
import kotlin.math.cos

class AxisHandle(
val color: Color,
Expand All @@ -33,6 +33,9 @@ class AxisHandle(
private val mesh: Mesh = Mesh(Attribute.POSITIONS, Attribute.NORMALS, name = "${name}-mesh")
private val coveredMesh: Mesh = Mesh(Attribute.POSITIONS, Attribute.NORMALS, name = "${name}-coveredMesh")

private var isHovered = false
private var alphaFactor = 1f

init {
transform = TrsTransformD().apply {
rotation.set(axis.orientation)
Expand All @@ -48,7 +51,6 @@ class AxisHandle(
coveredMesh.setupGeometry(handleShape, innerDistance, length, 0.015f, 0.07f)
coveredMesh.setupShader(DepthCompareOp.ALWAYS)
hitMesh.setupGeometry(HandleType.SPHERE, innerDistance, length, 0.07f, 0.15f)
setColors(colorIdle, coveredColorIdle)

// hasChanged flag is usually cleared after mesh is drawn the first time, but hitMesh is never drawn
// -> clear flag manually to avoid hitTest kd-tree being regenerated every frame
Expand All @@ -57,19 +59,49 @@ class AxisHandle(
addNode(coveredMesh)
addNode(mesh)
addNode(hitMesh)

val camDelta = MutableVec3d()
val alphaThreshHigh = cos(5f.deg.rad)
val alphaThreshLow = cos(15f.deg.rad)

onUpdate {
modelMatD.transform(camDelta.set(Vec3d.ZERO), 1.0)
camDelta.subtract(it.camera.dataD.globalPos)
val cosAngle = abs(camDelta.norm() dot axis.axis).toFloat()
alphaFactor = if (cosAngle > alphaThreshLow) {
1f - smoothStep(alphaThreshLow, alphaThreshHigh, cosAngle)
} else {
1f
}

isVisible = alphaFactor > 0.01f
hitMesh.isPickable = isVisible
updateColors()
}
}

private fun setColors(mainColor: Color, coveredColor: Color) {
private fun updateColors() {
var mainColor: Color = if (isHovered) color else colorIdle
var coveredColor: Color = if (isHovered) coveredColor else coveredColorIdle

if (alphaFactor > 0f && isHovered) {
alphaFactor = 1f
}
if (alphaFactor != 1f) {
mainColor = mainColor.withAlpha(mainColor.a * alphaFactor)
coveredColor = coveredColor.withAlpha(coveredColor.a * alphaFactor)
}

(mesh.shader as KslUnlitShader).color = mainColor
(coveredMesh.shader as KslUnlitShader).color = coveredColor
}

override fun onHover(pointer: Pointer, globalRay: RayD, gizmo: GizmoNode) {
setColors(color, coveredColor)
isHovered = true
}

override fun onHoverExit(gizmo: GizmoNode) {
setColors(colorIdle, coveredColorIdle)
isHovered = false
}

private fun Mesh.setupGeometry(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package de.fabmax.kool.modules.gizmo

import de.fabmax.kool.input.Pointer
import de.fabmax.kool.math.RayD
import de.fabmax.kool.math.Vec3f
import de.fabmax.kool.math.deg
import de.fabmax.kool.math.*
import de.fabmax.kool.modules.ksl.KslUnlitShader
import de.fabmax.kool.pipeline.Attribute
import de.fabmax.kool.pipeline.DepthCompareOp
Expand All @@ -12,6 +10,8 @@ import de.fabmax.kool.scene.MeshRayTest
import de.fabmax.kool.scene.Node
import de.fabmax.kool.scene.TrsTransformD
import de.fabmax.kool.util.Color
import kotlin.math.abs
import kotlin.math.cos

class AxisRotationHandle(
val color: Color,
Expand All @@ -31,6 +31,9 @@ class AxisRotationHandle(
private val mesh: Mesh = Mesh(Attribute.POSITIONS, Attribute.NORMALS, name = "${name}-mesh")
private val coveredMesh: Mesh = Mesh(Attribute.POSITIONS, Attribute.NORMALS, name = "${name}-coveredMesh")

private var isHovered = false
private var alphaFactor = 1f

init {
transform = TrsTransformD().apply {
rotation.set(axis.orientation)
Expand All @@ -46,7 +49,6 @@ class AxisRotationHandle(
coveredMesh.setupGeometry(radius, 0.01f)
coveredMesh.setupShader(DepthCompareOp.ALWAYS)
hitMesh.setupGeometry(radius, 0.05f)
setColors(colorIdle, coveredColorIdle)

// hasChanged flag is usually cleared after mesh is drawn the first time, but hitMesh is never drawn
// -> clear flag manually to avoid hitTest kd-tree being regenerated every frame
Expand All @@ -55,13 +57,51 @@ class AxisRotationHandle(
addNode(coveredMesh)
addNode(mesh)
addNode(hitMesh)

val camDelta = MutableVec3d()
val alphaThreshHigh = cos(75f.deg.rad)
val alphaThreshLow = cos(85f.deg.rad)

onUpdate {
modelMatD.transform(camDelta.set(Vec3d.ZERO), 1.0)
camDelta.subtract(it.camera.dataD.globalPos)
val cosAngle = abs(camDelta.norm() dot axis.axis).toFloat()
alphaFactor = if (cosAngle < alphaThreshHigh) {
smoothStep(alphaThreshLow, alphaThreshHigh, cosAngle)
} else {
1f
}

isVisible = alphaFactor > 0.01f
hitMesh.isPickable = isVisible
updateColors()
}
}

private fun setColors(mainColor: Color, coveredColor: Color) {
private fun updateColors() {
var mainColor: Color = if (isHovered) color else colorIdle
var coveredColor: Color = if (isHovered) coveredColor else coveredColorIdle

if (alphaFactor > 0f && isHovered) {
alphaFactor = 1f
}
if (alphaFactor != 1f) {
mainColor = mainColor.withAlpha(mainColor.a * alphaFactor)
coveredColor = coveredColor.withAlpha(coveredColor.a * alphaFactor)
}

(mesh.shader as KslUnlitShader).color = mainColor
(coveredMesh.shader as KslUnlitShader).color = coveredColor
}

override fun onHover(pointer: Pointer, globalRay: RayD, gizmo: GizmoNode) {
isHovered = true
}

override fun onHoverExit(gizmo: GizmoNode) {
isHovered = false
}

private fun Mesh.setupGeometry(
orbitRadius: Float,
geomRadius: Float,
Expand Down Expand Up @@ -95,12 +135,4 @@ class AxisRotationHandle(
color { uniformColor() }
}
}

override fun onHover(pointer: Pointer, globalRay: RayD, gizmo: GizmoNode) {
setColors(color, coveredColor)
}

override fun onHoverExit(gizmo: GizmoNode) {
setColors(colorIdle, coveredColorIdle)
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package de.fabmax.kool.modules.gizmo

import de.fabmax.kool.input.Pointer
import de.fabmax.kool.math.RayD
import de.fabmax.kool.math.Vec3f
import de.fabmax.kool.math.deg
import de.fabmax.kool.math.*
import de.fabmax.kool.modules.ksl.KslUnlitShader
import de.fabmax.kool.pipeline.Attribute
import de.fabmax.kool.pipeline.CullMethod
Expand All @@ -17,6 +15,7 @@ import de.fabmax.kool.scene.geometry.PrimitiveType
import de.fabmax.kool.util.Color
import kotlin.math.abs
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.max

class PlaneHandle(
Expand All @@ -38,6 +37,9 @@ class PlaneHandle(
private val coveredMesh: Mesh
private val lineMesh: Mesh

private var isHovered = false
private var alphaFactor = 1f

init {
transform = TrsTransformD().apply {
rotation.set(axis.orientation)
Expand All @@ -55,21 +57,56 @@ class PlaneHandle(
lineMesh = Mesh(IndexedVertexList(Attribute.POSITIONS, primitiveType = PrimitiveType.LINES), name = "${name}-lineMesh")
lineMesh.makeOutline(size, innerDistance, mesh)


setColors(colorIdle, coveredColorIdle)

addNode(coveredMesh)
addNode(mesh)
addNode(lineMesh)

val camDelta = MutableVec3d()
val alphaThreshHigh = cos(75f.deg.rad)
val alphaThreshLow = cos(85f.deg.rad)

onUpdate {
modelMatD.transform(camDelta.set(Vec3d.ZERO), 1.0)
camDelta.subtract(it.camera.dataD.globalPos)
val cosAngle = abs(camDelta.norm() dot axis.axis).toFloat()
alphaFactor = if (cosAngle < alphaThreshHigh) {
smoothStep(alphaThreshLow, alphaThreshHigh, cosAngle)
} else {
1f
}

isVisible = alphaFactor > 0.01f
mesh.isPickable = isVisible
updateColors()
}
}

private fun setColors(mainColor: Color, coveredColor: Color) {
private fun updateColors() {
var mainColor: Color = if (isHovered) color else colorIdle
var coveredColor: Color = if (isHovered) coveredColor else coveredColorIdle

if (alphaFactor > 0f && isHovered) {
alphaFactor = 1f
}
if (alphaFactor != 1f) {
mainColor = mainColor.withAlpha(mainColor.a * alphaFactor)
coveredColor = coveredColor.withAlpha(coveredColor.a * alphaFactor)
}

val planeAlpha = mainColor.a * 0.3f
(mesh.shader as KslUnlitShader).color = mainColor.withAlpha(planeAlpha)
(coveredMesh.shader as KslUnlitShader).color = coveredColor
(lineMesh.shader as KslUnlitShader).color = mainColor
}

override fun onHover(pointer: Pointer, globalRay: RayD, gizmo: GizmoNode) {
isHovered = true
}

override fun onHoverExit(gizmo: GizmoNode) {
isHovered = false
}

private fun Mesh.setup(
planeSize: Float,
innerDistance: Float,
Expand Down Expand Up @@ -119,12 +156,4 @@ class PlaneHandle(
color { uniformColor(color) }
}
}

override fun onHover(pointer: Pointer, globalRay: RayD, gizmo: GizmoNode) {
setColors(color, coveredColor)
}

override fun onHoverExit(gizmo: GizmoNode) {
setColors(colorIdle, coveredColorIdle)
}
}

0 comments on commit 3dc74a8

Please sign in to comment.