From 58359b9baba460592b660bb7b71521d97f883cba Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Wed, 21 Dec 2022 22:09:47 +0100 Subject: [PATCH] [orx-camera] Add ChangeEvents interface --- orx-camera/src/commonMain/kotlin/Camera2D.kt | 19 +++++++- .../src/commonMain/kotlin/ChangeEvents.kt | 8 ++++ orx-camera/src/commonMain/kotlin/Orbital.kt | 11 ++++- .../src/commonMain/kotlin/OrbitalCamera.kt | 43 ++++++++++++++----- .../commonMain/kotlin/ParametricOrbital.kt | 29 ++++++++++++- 5 files changed, 97 insertions(+), 13 deletions(-) create mode 100644 orx-camera/src/commonMain/kotlin/ChangeEvents.kt diff --git a/orx-camera/src/commonMain/kotlin/Camera2D.kt b/orx-camera/src/commonMain/kotlin/Camera2D.kt index c0c65e81..1f5ce954 100644 --- a/orx-camera/src/commonMain/kotlin/Camera2D.kt +++ b/orx-camera/src/commonMain/kotlin/Camera2D.kt @@ -5,6 +5,7 @@ import org.openrndr.MouseEvents import org.openrndr.Program import org.openrndr.draw.Drawer import org.openrndr.draw.RenderTarget +import org.openrndr.events.Event import org.openrndr.math.Matrix44 import org.openrndr.math.transforms.buildTransform @@ -15,13 +16,27 @@ import org.openrndr.math.transforms.buildTransform * * Usage: `extend(Camera2D())` */ -class Camera2D : Extension { +class Camera2D : Extension, ChangeEvents { override var enabled = true + var view = Matrix44.IDENTITY + override val changed = Event() + + private var dirty = true + set(value) { + if (value && !field) { + changed.trigger(Unit) + } + field = value + } + override val hasChanged: Boolean + get() = dirty + fun setupMouseEvents(mouse: MouseEvents) { mouse.dragged.listen { view = buildTransform { translate(it.dragDisplacement) } * view + dirty = true } mouse.scrolled.listen { val scaleFactor = 1.0 - it.rotation.y * 0.03 @@ -30,6 +45,7 @@ class Camera2D : Extension { scale(scaleFactor) translate(-it.position) } * view + dirty = true } } override fun setup(program: Program) { @@ -43,6 +59,7 @@ class Camera2D : Extension { } override fun afterDraw(drawer: Drawer, program: Program) { + dirty = false drawer.popTransforms() } } diff --git a/orx-camera/src/commonMain/kotlin/ChangeEvents.kt b/orx-camera/src/commonMain/kotlin/ChangeEvents.kt new file mode 100644 index 00000000..2f5962bf --- /dev/null +++ b/orx-camera/src/commonMain/kotlin/ChangeEvents.kt @@ -0,0 +1,8 @@ +package org.openrndr.extra.camera + +import org.openrndr.events.Event + +interface ChangeEvents { + val changed : Event + val hasChanged: Boolean +} \ No newline at end of file diff --git a/orx-camera/src/commonMain/kotlin/Orbital.kt b/orx-camera/src/commonMain/kotlin/Orbital.kt index fb891bf3..1c25b6c0 100644 --- a/orx-camera/src/commonMain/kotlin/Orbital.kt +++ b/orx-camera/src/commonMain/kotlin/Orbital.kt @@ -3,14 +3,22 @@ package org.openrndr.extra.camera import org.openrndr.Extension import org.openrndr.Program import org.openrndr.draw.Drawer +import org.openrndr.events.Event import org.openrndr.math.Vector3 /** * Extension that provides orbital camera view and controls. */ -class Orbital : Extension { +class Orbital : Extension, ChangeEvents { override var enabled: Boolean = true + override val changed = Event() + override val hasChanged: Boolean + get() { + return camera.hasChanged + } + + var eye = Vector3.UNIT_Z * 10.0 var lookAt = Vector3.ZERO var near = 0.1 @@ -28,6 +36,7 @@ class Orbital : Extension { val camera by lazy { OrbitalCamera(eye, lookAt, fov, near, far, projectionType).apply { dampingFactor = this@Orbital.dampingFactor + this.changed.listen(this@Orbital.changed) } } val controls by lazy { OrbitalControls(camera, userInteraction, keySpeed) } diff --git a/orx-camera/src/commonMain/kotlin/OrbitalCamera.kt b/orx-camera/src/commonMain/kotlin/OrbitalCamera.kt index 5f814844..c014b9f9 100644 --- a/orx-camera/src/commonMain/kotlin/OrbitalCamera.kt +++ b/orx-camera/src/commonMain/kotlin/OrbitalCamera.kt @@ -4,6 +4,7 @@ import org.openrndr.Extension import org.openrndr.Program import org.openrndr.draw.DepthTestPass import org.openrndr.draw.Drawer +import org.openrndr.events.Event import org.openrndr.math.Matrix44 import org.openrndr.math.Spherical import org.openrndr.math.Vector3 @@ -17,7 +18,21 @@ enum class ProjectionType { } -class OrbitalCamera(eye: Vector3 = Vector3.ZERO, lookAt: Vector3 = Vector3.UNIT_Z, var fov: Double = 90.0, var near: Double = 0.1, var far: Double = 1000.0, var projectionType: ProjectionType = ProjectionType.PERSPECTIVE) : Extension { +class OrbitalCamera( + eye: Vector3 = Vector3.ZERO, + lookAt: Vector3 = Vector3.UNIT_Z, + var fov: Double = 90.0, + var near: Double = 0.1, + var far: Double = 1000.0, + var projectionType: ProjectionType = ProjectionType.PERSPECTIVE +) : Extension, ChangeEvents { + + override val changed = Event() + + override val hasChanged: Boolean + get() = dirty + + // current position in spherical coordinates var spherical = Spherical.fromVector(eye) private set @@ -32,6 +47,12 @@ class OrbitalCamera(eye: Vector3 = Vector3.ZERO, lookAt: Vector3 = Vector3.UNIT_ private var sphericalEnd = Spherical.fromVector(eye) private var lookAtEnd = lookAt private var dirty: Boolean = true + set(value) { + if (value && !field) { + changed.trigger(Unit) + } + field = value + } private var lastSeconds: Double = -1.0 var fovEnd = fov @@ -127,19 +148,21 @@ class OrbitalCamera(eye: Vector3 = Vector3.ZERO, lookAt: Vector3 = Vector3.UNIT_ if (!dirty) return dirty = false - val dampingFactor = if (dampingFactor > 0.0) { dampingFactor * timeDelta / 0.0060 } else 1.0 + val dampingFactor = if (dampingFactor > 0.0) { + dampingFactor * timeDelta / 0.0060 + } else 1.0 val sphericalDelta = sphericalEnd - spherical val lookAtDelta = lookAtEnd - lookAt val fovDelta = fovEnd - fov val magnitudeDelta = magnitudeEnd - magnitude if ( - abs(sphericalDelta.radius) > EPSILON || - abs(sphericalDelta.theta) > EPSILON || - abs(sphericalDelta.phi) > EPSILON || - abs(lookAtDelta.x) > EPSILON || - abs(lookAtDelta.y) > EPSILON || - abs(lookAtDelta.z) > EPSILON || - abs(fovDelta) > EPSILON + abs(sphericalDelta.radius) > EPSILON || + abs(sphericalDelta.theta) > EPSILON || + abs(sphericalDelta.phi) > EPSILON || + abs(lookAtDelta.x) > EPSILON || + abs(lookAtDelta.y) > EPSILON || + abs(lookAtDelta.z) > EPSILON || + abs(fovDelta) > EPSILON ) { fov += (fovDelta * dampingFactor) spherical += (sphericalDelta * dampingFactor) @@ -222,4 +245,4 @@ fun OrbitalCamera.applyTo(drawer: Drawer) { } } -private fun pow(a:Double, x:Double): Double = a.pow(x) \ No newline at end of file +private fun pow(a: Double, x: Double): Double = a.pow(x) \ No newline at end of file diff --git a/orx-camera/src/commonMain/kotlin/ParametricOrbital.kt b/orx-camera/src/commonMain/kotlin/ParametricOrbital.kt index 8e23d1dc..513ffe64 100644 --- a/orx-camera/src/commonMain/kotlin/ParametricOrbital.kt +++ b/orx-camera/src/commonMain/kotlin/ParametricOrbital.kt @@ -3,6 +3,7 @@ package org.openrndr.extra.camera import org.openrndr.Extension import org.openrndr.Program import org.openrndr.draw.Drawer +import org.openrndr.events.Event import org.openrndr.extra.parameters.Description import org.openrndr.extra.parameters.DoubleParameter import org.openrndr.extra.parameters.Vector3Parameter @@ -13,12 +14,25 @@ import org.openrndr.math.Vector3 * Extension that provides orbital camera through annotated parameters */ @Description("Orbital camera") -class ParametricOrbital : Extension { +class ParametricOrbital : Extension, ChangeEvents { override var enabled: Boolean = true + override val changed = Event() + override val hasChanged: Boolean + get() = dirty + private var dirty = true + set(value) { + if (value && !field) { + changed.trigger(Unit) + } + field = value + } @DoubleParameter("fov", 1.0, 90.0, order = 0) var fov = 45.0 set(value) { + if (field != value) { + dirty = true + } field = value camera.zoomTo(fov) } @@ -32,6 +46,9 @@ class ParametricOrbital : Extension { @DoubleParameter("phi", 0.0, 180.0, order = 2) var phi = 0.0 set(value) { + if (field != value) { + dirty = true + } field = value camera.rotateTo(theta, phi.coerceAtLeast(1E-3)) } @@ -39,6 +56,9 @@ class ParametricOrbital : Extension { @DoubleParameter("theta", -180.0, 180.0, order = 1) var theta = 0.0 set(value) { + if (field != value) { + dirty = true + } field = value camera.rotateTo(theta, phi.coerceAtLeast(1E-3)) } @@ -47,6 +67,9 @@ class ParametricOrbital : Extension { @DoubleParameter("orbit radius", 0.1, 100.0, order = 3) var radius = 10.0 set(value) { + if (field != value) { + dirty = true + } field = value camera.dollyTo(radius) } @@ -55,6 +78,9 @@ class ParametricOrbital : Extension { @Vector3Parameter("center", order = 4) var center = Vector3.ZERO set(value) { + if (field != value) { + dirty = true + } field = value camera.panTo(value) } @@ -72,6 +98,7 @@ class ParametricOrbital : Extension { } override fun afterDraw(drawer: Drawer, program: Program) { + dirty = false camera.afterDraw(drawer, program) } } \ No newline at end of file