Skip to content
Draft
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
126 changes: 125 additions & 1 deletion src/main/kotlin/graphics/scenery/Camera.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import graphics.scenery.attribute.renderable.HasRenderable
import graphics.scenery.attribute.spatial.DefaultSpatial
import graphics.scenery.attribute.spatial.HasCustomSpatial
import graphics.scenery.net.Networkable
import graphics.scenery.primitives.Cylinder
import graphics.scenery.utils.extensions.minus
import graphics.scenery.utils.extensions.plus
import graphics.scenery.utils.extensions.times
Expand Down Expand Up @@ -222,7 +223,7 @@ open class Camera : DefaultNode("Camera"), HasRenderable, HasMaterial, HasCustom
}

/**
* Returns the starting position and direction of a ray starting at the the screen space position
* Returns the starting position and direction of a ray starting at the screen space position
* indicated by [x] and [y] targeting away from the camera.
*
* Returns (worldPos, worldDir)
Expand Down Expand Up @@ -492,6 +493,129 @@ open class Camera : DefaultNode("Camera"), HasRenderable, HasMaterial, HasCustom

}

/**
* This class provides possible position states for the orientation overlay.
*/
enum class OverlayAlignment {
CENTER, TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT
}

private var orientationOverlayVisible = false

/**
* Class to create an orientation overlay, consisting of three axes pointing towards positive X/Y/Z.
* Can be placed in the center or in any of the screen corners.
* @param camera the camera to attach the overlay to
* @param radius radius for the axes
* @param length length of the axes
* @param align four corners or the center of type [OverlayAlignment]
* @param margin how far from the corners the overlay should be
*/
private inner class OrientationOverlay(
camera: Camera = this,
radius: Float,
length: Float,
align: OverlayAlignment,
margin: Float
) : DefaultNode("Orientation Overlay") {

val axesRadius: Float = radius / 10
val axesLength: Float = length / 10
val axesParent: Node = Group()

init {
axesParent.name = "Orientation Axes"
axesParent.ifSpatial {
position = getRelativePosition(margin, align)
}
var c = Cylinder(axesRadius / 2.0f, axesLength, 12)

c.name = "X axis"
c.material {
diffuse = Vector3f(1f, 0f, 0f)
emissive = Vector4f(1f, 0f, 0f, 3f)
}
val halfPI = Math.PI.toFloat() / 2.0f
c.spatial().rotation = Quaternionf().rotateLocalZ(-halfPI)
axesParent.addChild(c)

c = Cylinder(axesRadius / 2.0f, axesLength, 12)
c.name = "Y axis"
c.material {
diffuse = Vector3f(0f, 1f, 0f)
emissive = Vector4f(0f, 1f, 0f, 3f)
}
axesParent.addChild(c)

c = Cylinder(axesRadius / 2.0f, axesLength, 12)
c.name = "Z axis"
c.material {
diffuse = Vector3f(0f, 0f, 1f)
emissive = Vector4f(0f, 0f, 1f, 3f)
}
c.spatial().rotation = Quaternionf().rotateLocalX(halfPI)
axesParent.addChild(c)
camera.addChild(axesParent)

// workaround: continuously update the position.
// TODO Should be done with camera callback instead
thread {
while (true) {
axesParent.ifSpatial {
position = getRelativePosition(margin, align)
}
Thread.sleep(1000)
}
}
}

fun getRelativePosition(margin: Float, align: OverlayAlignment): Vector3f {
// position normalized in range -1 to 1
var pos = when (align) {
OverlayAlignment.CENTER -> Vector2f(0f, 0f)
OverlayAlignment.TOP_LEFT -> Vector2f(margin, margin)
OverlayAlignment.TOP_RIGHT -> Vector2f(1f - margin, 1f - margin)
OverlayAlignment.BOTTOM_RIGHT -> Vector2f(1f - margin, 1f - margin)
OverlayAlignment.BOTTOM_LEFT -> Vector2f(margin, 1f - margin)
}
return spatial().viewportToView(pos)
}

fun updateRotation() {
axesParent.ifSpatial {
// NB: camera rotation is already inverse, no need to conjugate
rotation = this@Camera.spatial().rotation
}
}
}

/**
* Call this method to attach an orientation overlay to the camera,
* consisting of three axes pointing towards positive X/Y/Z.
* @param align where to spawn the overlay, defaults to [OverlayAlignment.TOP_RIGHT].
* @param margin how far from the screen corners to spawn the overlay. Defaults to a factor of 0.1.
* @param radius radius for the axes
* @param length length of the axes
*/
fun toggleOrientationOverlay(
align: OverlayAlignment = OverlayAlignment.TOP_RIGHT,
margin: Float = 0.1f,
radius: Float = 0.05f,
length: Float = 0.3f
) {
orientationOverlayVisible = !orientationOverlayVisible
if (orientationOverlayVisible) {
val overlay = OrientationOverlay(this, radius, length, align, margin)
postUpdate.add {
overlay.updateRotation()
}
} else {
this.removeChild("Orientation Overlay")
}
}



@Deprecated(message = "", replaceWith = ReplaceWith("spatial().viewportToWorld(vector)"))
fun viewportToWorld(vector: Vector2f): Vector3f {
return spatial().viewportToWorld(vector)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class AtmosphereExample : SceneryBase("Atmosphere Example",
renderer = hub.add(
SceneryElement.Renderer,
Renderer.createRenderer(hub, applicationName, scene, windowWidth, windowHeight))

renderer?.pushMode = true
if (useVR) {
hmd = OpenVRHMD(useCompositor = true)
hub.add(SceneryElement.HMDInput, hmd)
Expand Down Expand Up @@ -80,7 +80,8 @@ class AtmosphereExample : SceneryBase("Atmosphere Example",
spatial {
position = Vector3f(0.0f, 0.0f, 5.0f)
}
perspectiveCamera(70.0f, 512, 768)
perspectiveCamera(70.0f, 768, 512)
if (!useVR) toggleOrientationOverlay()
scene.addChild(this)
}

Expand Down