package phenax.extensions import kotlin.math.absoluteValue import org.openrndr.* import org.openrndr.color.* import org.openrndr.draw.* import org.openrndr.math.Quaternion import org.openrndr.math.Vector2 import org.openrndr.math.Vector3 import phenax.utils.recordingEnabled enum class ActionType { PAN, ROTATE } class CameraManager(public var cameraPosition: Vector3, public var cameraRotation: Vector3) : Extension { override var enabled: Boolean = true public var cameraQuaternion: Quaternion = Quaternion.IDENTITY public var lightPosition: Vector3 = Vector3(1.0, 2.0, 1.0) var font: FontImageMap? = null var actionType: ActionType? = null var lastPos: Vector3? = null constructor() : this(Vector3(0.0, 1.0, 0.0), Vector3.ZERO) {} constructor( cameraPosition: Vector3, cameraRotation: Vector3, lightPos: Vector3 ) : this(cameraPosition, cameraRotation) { lightPosition = lightPos } override fun setup(program: Program) { if (recordingEnabled) return cameraQuaternion = Quaternion.fromAngles(cameraRotation.x, cameraRotation.y, cameraRotation.z) font = program.loadFont("data/fonts/default.otf", 16.0) program.keyboard.keyDown.listen { when (it.name) { "escape" -> program.application.exit() "x" -> cameraQuaternion = Quaternion.IDENTITY "y" -> cameraQuaternion = Quaternion.fromAngles(0.0, -90.0, 0.0) "z" -> cameraQuaternion = Quaternion.fromAngles(90.0, 0.0, 0.0) } } program.mouse.moved.listen { val dimens = Vector3(program.width.toDouble(), program.height.toDouble(), 1.0) var pos = (Vector3(it.position.x, -it.position.y, 0.0) - dimens / 2.0).div(dimens) pos = cameraQuaternion.inversed.matrix * pos val increment = lastPos?.let { pos - it } if (actionType == ActionType.PAN) { increment?.let { cameraPosition -= it } } else if (actionType == ActionType.ROTATE) { increment?.let { val mag = 10.0 val rot = if (it.x.absoluteValue >= it.y.absoluteValue) Vector2(it.x * mag, 0.0) else Vector2(0.0, it.y * mag) cameraQuaternion *= Quaternion.fromAngles(rot.x, rot.y, 0.0) } // lastPos?.let { lastPos -> lightPosition += (pos - lastPos) } } actionType?.let { lastPos = pos } } program.mouse.buttonDown.listen { val isCtrl = KeyModifier.CTRL in it.modifiers actionType = if (isCtrl) ActionType.ROTATE else ActionType.PAN } program.mouse.scrolled.listen { val isCtrl = KeyModifier.CTRL in it.modifiers if (isCtrl) { cameraQuaternion *= Quaternion.fromAngles(it.rotation.y, 0.0, 0.0) } else { cameraPosition += cameraQuaternion.inversed.matrix * Vector3(0.0, 0.0, it.rotation.y * 0.1) } } program.mouse.buttonUp.listen { actionType = null lastPos = null } } fun setUniforms(cs: ComputeShader) { cs.uniform("camera_position", cameraPosition) cs.uniform("camera_transformation", cameraQuaternion.matrix) cs.uniform("key_light_position", lightPosition) } override fun beforeDraw(drawer: Drawer, program: Program) {} override fun afterDraw(drawer: Drawer, program: Program) { if (recordingEnabled) return drawer.fontMap = font drawer.fill = ColorRGBa.WHITE drawer.text( "C: %.2f %.2f %.2f".format(cameraPosition.x, cameraPosition.y, cameraPosition.z), 8.0, 16.0 ) drawer.text( "L: %.2f %.2f %.2f".format(lightPosition.x, lightPosition.y, lightPosition.z), 8.0, 32.0 ) drawer.text( "L: %.2f %.2f %.2f".format(lightPosition.x, lightPosition.y, lightPosition.z), 8.0, 32.0 ) } }