forked from LeenkxTeam/LNXSDK
340 lines
8.6 KiB
Haxe
340 lines
8.6 KiB
Haxe
package leenkx.trait;
|
|
|
|
import iron.Trait;
|
|
import iron.Scene;
|
|
import iron.object.CameraObject;
|
|
import iron.system.Input;
|
|
import kha.graphics2.Graphics;
|
|
import zui.Zui;
|
|
import zui.Id;
|
|
import iron.data.Data;
|
|
import leenkx.ui.Canvas;
|
|
import leenkx.renderpath.Postprocess;
|
|
|
|
/**
|
|
* Camera Controls Trait for parameter controls via ZUI
|
|
*/
|
|
|
|
class CameraControls extends Trait {
|
|
|
|
@prop var enableUI: Bool = true;
|
|
@prop var toggleKey: String = "c";
|
|
@prop var defaultFStop: Float = 5.6;
|
|
@prop var defaultShutterSpeed: Float = 60.0;
|
|
@prop var defaultISO: Float = 400.0;
|
|
@prop var defaultFOV: Float = 50.0;
|
|
@prop var defaultAutoExposure: Bool = false;
|
|
@prop var defaultAutoFocus: Bool = false;
|
|
@prop var defaultFocusDistance: Float = 2.5;
|
|
@prop var exposureCompensation: Float = 8.0;
|
|
|
|
var ui: Zui;
|
|
var controlsVisible: Bool = false;
|
|
var camera: CameraObject;
|
|
|
|
var fStop: Float = 5.6;
|
|
var shutterSpeed: Float = 60.0;
|
|
var iso: Float = 400.0;
|
|
var fov: Float = 50.0;
|
|
var autoExposure: Bool = false;
|
|
var autoFocus: Bool = false;
|
|
var focusDistance: Float = 2.5;
|
|
var expComp: Float = 8.0;
|
|
|
|
var sensorSize: Float = 0.3;
|
|
var cocSize: Float = 0.03;
|
|
|
|
public function new() {
|
|
super();
|
|
notifyOnInit(init);
|
|
notifyOnUpdate(update);
|
|
notifyOnRender2D(render);
|
|
}
|
|
|
|
function init() {
|
|
camera = Scene.active.camera;
|
|
if (camera == null) {
|
|
trace("ERROR: No active camera available for camera control");
|
|
return;
|
|
}
|
|
fStop = defaultFStop;
|
|
shutterSpeed = defaultShutterSpeed;
|
|
iso = defaultISO;
|
|
fov = defaultFOV;
|
|
autoExposure = defaultAutoExposure;
|
|
autoFocus = defaultAutoFocus;
|
|
focusDistance = defaultFocusDistance;
|
|
expComp = exposureCompensation;
|
|
applyToRenderPipeline();
|
|
}
|
|
|
|
function update() {
|
|
if (!enableUI) return;
|
|
if (Input.getKeyboard().started(toggleKey)) {
|
|
controlsVisible = !controlsVisible;
|
|
if (!controlsVisible) Input.occupied = false;
|
|
|
|
if (controlsVisible && ui == null) {
|
|
Data.getFont(Canvas.defaultFontName, function(font: kha.Font) {
|
|
ui = new Zui({font: font});
|
|
var cam = iron.Scene.active.camera;
|
|
});
|
|
}
|
|
}
|
|
if (controlsVisible) Input.occupied = true;
|
|
}
|
|
|
|
function render(g: Graphics) {
|
|
if (!controlsVisible || ui == null) return;
|
|
|
|
g.end();
|
|
ui.begin(g);
|
|
|
|
drawControlPanel();
|
|
|
|
ui.end();
|
|
g.begin(false);
|
|
}
|
|
|
|
function drawControlPanel() {
|
|
var h = Id.handle();
|
|
h.nest(8000);
|
|
h.redraws = 1;
|
|
|
|
var panelWidth = 400;
|
|
var panelHeight = 600;
|
|
var panelX = 20;
|
|
var panelY = Std.int((iron.App.h() - panelHeight) / 2);
|
|
|
|
if (ui.window(h, panelX, panelY, panelWidth, panelHeight, true)) {
|
|
ui.text("CAMERA CONTROLS");
|
|
ui.separator();
|
|
|
|
if (camera == null) {
|
|
ui.text("ERROR: No active camera");
|
|
return;
|
|
}
|
|
|
|
ui.text("Camera: " + camera.name);
|
|
ui.separator();
|
|
|
|
ui.text("Requires Postprocess enabled");
|
|
ui.text("RP Blender settings");
|
|
ui.separator();
|
|
|
|
ui.text("F-Stop (Aperture)");
|
|
var hFStop = Id.handle();
|
|
hFStop.nest(8001);
|
|
hFStop.value = fStop;
|
|
fStop = ui.slider(hFStop, "f/" + formatValue(fStop, 1), 1.4, 16.0, true, 100, true, Left, true);
|
|
|
|
if (hFStop.changed) {
|
|
applyToRenderPipeline();
|
|
}
|
|
|
|
ui.separator();
|
|
|
|
ui.text("Shutter Speed (Exposure)");
|
|
var hShutter = Id.handle();
|
|
hShutter.nest(8002);
|
|
hShutter.value = shutterSpeed;
|
|
shutterSpeed = ui.slider(hShutter, "1/" + Std.int(shutterSpeed) + "s", 15.0, 2000.0, true, 100, true, Left, true);
|
|
|
|
if (hShutter.changed) {
|
|
applyToRenderPipeline();
|
|
}
|
|
|
|
ui.separator();
|
|
|
|
ui.text("ISO (Film Sensitivity)");
|
|
var hISO = Id.handle();
|
|
hISO.nest(8003);
|
|
hISO.value = iso;
|
|
iso = ui.slider(hISO, "ISO " + Std.int(iso), 100.0, 3200.0, true, 100, true, Left, true);
|
|
|
|
if (hISO.changed) {
|
|
applyToRenderPipeline();
|
|
}
|
|
|
|
ui.separator();
|
|
|
|
ui.text("Field of View");
|
|
var hFOV = Id.handle();
|
|
hFOV.nest(8004);
|
|
hFOV.value = fov;
|
|
var focalLengthEquiv = fovToFocalLength(fov);
|
|
fov = ui.slider(hFOV, Std.int(focalLengthEquiv) + "mm", 10.0, 120.0, true, 100, true, Left, true);
|
|
|
|
if (hFOV.changed) {
|
|
applyFOVToCamera();
|
|
}
|
|
|
|
ui.separator();
|
|
|
|
var hAutoFocus = Id.handle();
|
|
hAutoFocus.nest(8012);
|
|
hAutoFocus.selected = autoFocus;
|
|
|
|
ui.row([1/5, 4/5]);
|
|
autoFocus = ui.check(hAutoFocus, "");
|
|
ui.text("Auto-Focus (GPU)");
|
|
|
|
if (hAutoFocus.changed) {
|
|
applyToRenderPipeline();
|
|
}
|
|
|
|
ui.separator();
|
|
|
|
if (!autoFocus) {
|
|
ui.text("Focus Distance" + (Input.getKeyboard().down("shift") ? " (PRECISE)" : ""));
|
|
} else {
|
|
ui.text("Focus Distance (AUTO - disabled)");
|
|
}
|
|
var hFocus = Id.handle();
|
|
hFocus.nest(8005);
|
|
hFocus.value = focusDistance;
|
|
|
|
var basePrecision = (focusDistance < 2.0) ? 3000 : 1000;
|
|
var shiftMultiplier = Input.getKeyboard().down("shift") ? 4.0 : 1.0;
|
|
var focusPrecision = Std.int(basePrecision * shiftMultiplier);
|
|
|
|
if (!autoFocus) {
|
|
focusDistance = ui.slider(hFocus, formatValue(focusDistance, 4) + "m", 0.001, 5.000, true, focusPrecision, true, Left, true);
|
|
} else {
|
|
ui.slider(hFocus, formatValue(focusDistance, 4) + "m (AUTO)", 0.001, 5.000, true, focusPrecision, true, Left, false);
|
|
}
|
|
|
|
if (hFocus.changed && !autoFocus) {
|
|
applyToRenderPipeline();
|
|
}
|
|
|
|
ui.separator();
|
|
|
|
ui.text("Sensor Size (DoF)");
|
|
var hSensor = Id.handle();
|
|
hSensor.nest(8010);
|
|
hSensor.value = sensorSize;
|
|
sensorSize = ui.slider(hSensor, formatValue(sensorSize, 1) + "mm", 10.0, 50.0, true, 100, true, Left, true);
|
|
|
|
if (hSensor.changed) {
|
|
applyToRenderPipeline();
|
|
}
|
|
|
|
ui.separator();
|
|
|
|
ui.text("CoC (DoF Sharpness)");
|
|
var hCoC = Id.handle();
|
|
hCoC.nest(8011);
|
|
hCoC.value = cocSize;
|
|
cocSize = ui.slider(hCoC, formatValue(cocSize, 3) + "mm", 0.010, 0.100, true, 100, true, Left, true);
|
|
|
|
if (hCoC.changed) {
|
|
applyToRenderPipeline();
|
|
}
|
|
|
|
ui.separator();
|
|
|
|
ui.text("Exposure Compensation");
|
|
var hExpComp = Id.handle();
|
|
hExpComp.nest(8006);
|
|
hExpComp.value = expComp;
|
|
expComp = ui.slider(hExpComp, formatValue(expComp, 1) + " EV", 0.0, 16.0, true, 100, true, Left, true);
|
|
|
|
if (hExpComp.changed) {
|
|
applyToRenderPipeline();
|
|
}
|
|
|
|
ui.separator();
|
|
|
|
var hAutoExp = Id.handle();
|
|
hAutoExp.nest(8009);
|
|
hAutoExp.selected = autoExposure;
|
|
|
|
ui.row([1/5, 4/5]);
|
|
autoExposure = ui.check(hAutoExp, "");
|
|
ui.text("Auto Exposure");
|
|
|
|
if (hAutoExp.changed) {
|
|
applyToRenderPipeline();
|
|
}
|
|
ui.separator();
|
|
|
|
var ev = calculateExposureValue();
|
|
ui.text("Exposure Value: EV " + formatValue(ev, 2));
|
|
ui.separator();
|
|
var hReset = Id.handle();
|
|
hReset.nest(8007);
|
|
if (ui.button("Reset to Defaults")) {
|
|
resetToDefaults();
|
|
}
|
|
ui.separator();
|
|
var hClose = Id.handle();
|
|
hClose.nest(8008);
|
|
if (ui.button("Close Panel")) {
|
|
controlsVisible = false;
|
|
Input.occupied = false;
|
|
}
|
|
}
|
|
}
|
|
function applyToRenderPipeline() {
|
|
if (camera == null) return;
|
|
Postprocess.camera_uniforms[0] = fStop;
|
|
Postprocess.camera_uniforms[1] = shutterSpeed;
|
|
Postprocess.camera_uniforms[2] = iso;
|
|
Postprocess.camera_uniforms[3] = expComp;
|
|
Postprocess.camera_uniforms[5] = autoFocus ? 1 : 0;
|
|
Postprocess.camera_uniforms[6] = focusDistance;
|
|
Postprocess.camera_uniforms[7] = fovToFocalLength(fov);
|
|
Postprocess.camera_uniforms[8] = fStop;
|
|
Postprocess.auto_exposure_uniforms[0] = autoExposure ? 1 : 0;
|
|
var focalLengthMM = fovToFocalLength(fov);
|
|
var apertureDiameter = focalLengthMM / fStop;
|
|
}
|
|
|
|
function applyFOVToCamera() {
|
|
if (camera == null) return;
|
|
|
|
var fovRadians = fov * (Math.PI / 180.0);
|
|
camera.data.raw.fov = fovRadians;
|
|
camera.buildProjection();
|
|
|
|
var focalLengthEquiv = fovToFocalLength(fov);
|
|
}
|
|
|
|
|
|
function calculateExposureValue(): Float {
|
|
// EV = log2(N^2 / t) where N = f-stop, t = shutter time
|
|
var shutterTime = 1.0 / shutterSpeed;
|
|
var ev = Math.log(fStop * fStop / shutterTime) / Math.log(2.0);
|
|
var isoAdjust = Math.log(iso / 100.0) / Math.log(2.0);
|
|
ev += isoAdjust;
|
|
|
|
return ev;
|
|
}
|
|
|
|
function fovToFocalLength(fovDegrees: Float): Float {
|
|
var fovRadians = fovDegrees * (Math.PI / 180.0);
|
|
var focalLength = sensorSize / (2.0 * Math.tan(fovRadians / 2.0));
|
|
return focalLength;
|
|
}
|
|
|
|
function resetToDefaults() {
|
|
fStop = defaultFStop;
|
|
shutterSpeed = defaultShutterSpeed;
|
|
iso = defaultISO;
|
|
fov = defaultFOV;
|
|
autoExposure = defaultAutoExposure;
|
|
autoFocus = defaultAutoFocus;
|
|
focusDistance = defaultFocusDistance;
|
|
expComp = exposureCompensation;
|
|
applyToRenderPipeline();
|
|
applyFOVToCamera();
|
|
}
|
|
|
|
function formatValue(value: Float, decimals: Int): String {
|
|
var multiplier = Math.pow(10, decimals);
|
|
var rounded = Math.round(value * multiplier) / multiplier;
|
|
return Std.string(rounded);
|
|
}
|
|
}
|