MBHaxe/src/CameraController.hx
2024-06-30 01:18:29 +05:30

452 lines
14 KiB
Haxe

package src;
import h3d.col.Bounds;
import h3d.col.Plane;
import h3d.mat.Material;
import h3d.prim.Cube;
import h3d.scene.Mesh;
import src.Settings;
import hxd.Key;
import src.Util;
import h3d.Quat;
#if hlsdl
import sdl.Cursor;
import sdl.Sdl;
#end
#if hldx
import dx.Cursor;
import dx.Window;
#end
import hxd.Window;
import hxd.Event;
import src.MarbleWorld;
import h3d.scene.Object;
import src.Marble;
import h3d.Camera;
import h3d.Vector;
import hxsl.Types.Matrix;
import h3d.scene.Scene;
import src.Gamepad;
import src.MarbleGame;
enum CameraMode {
FreeOrbit;
FixedOrbit;
}
class CameraController extends Object {
var marble:Marble;
var level:MarbleWorld;
var camZoomSpeed:Float;
public var CameraDistance:Float;
public var CameraPitch:Float;
public var CameraYaw:Float;
public var nextCameraYaw:Float;
public var nextCameraPitch:Float;
public var phi:Float;
public var theta:Float;
var lastTargetPos:Vector;
var lastCamPos:Vector;
var lastVertTranslation:Vector;
public var oob:Bool = false;
public var finish:Bool = false;
public var centeringCamera:Bool = false;
var radsLeftToCenter:Float = 0;
var radsStartingToCenter:Float = 0;
var _ignoreCursor:Bool = false;
public function new(marble:Marble) {
super();
this.marble = marble;
}
public function init(level:MarbleWorld) {
this.level = level;
this.CameraDistance = Settings.optionsSettings.cameraDistance;
if (this.CameraDistance <= 1 #if js || this.CameraDistance == null #end) {
this.CameraDistance = 2.5;
Settings.optionsSettings.cameraDistance = 2.5;
}
// level.scene.addEventListener(onEvent);
// Sdl.setRelativeMouseMode(true);
level.scene.camera.setFovX(Settings.optionsSettings.fovX, Settings.optionsSettings.screenWidth / Settings.optionsSettings.screenHeight);
lockCursor();
}
public function lockCursor() {
#if js
var jsCanvas = @:privateAccess Window.getInstance().canvas;
jsCanvas.focus();
var pointercontainer = js.Browser.document.querySelector("#pointercontainer");
pointercontainer.hidden = true;
#end
_ignoreCursor = true;
if (!Util.isTouchDevice())
Window.getInstance().lockPointer((x, y) -> orbit(x, y));
#if hl
Cursor.show(false);
#end
}
public function unlockCursor() {
if (!Util.isTouchDevice())
Window.getInstance().unlockPointer();
#if hl
Cursor.show(true);
#end
#if js
var jsCanvas = @:privateAccess Window.getInstance().canvas;
@:privateAccess Window.getInstance().lockCallback = null; // Fix cursorlock position shit
var pointercontainer = js.Browser.document.querySelector("#pointercontainer");
pointercontainer.hidden = false;
#end
}
public function orbit(mouseX:Float, mouseY:Float, isTouch:Bool = false) {
if (_ignoreCursor) {
_ignoreCursor = false;
return;
}
var scaleFactor = 1.0 / Window.getInstance().windowToPixelRatio;
#if js
scaleFactor = 1 / js.Browser.window.devicePixelRatio;
#end
var deltaposX = mouseX * scaleFactor;
var deltaposY = mouseY * (Settings.controlsSettings.invertYAxis ? -1 : 1) * scaleFactor;
if (deltaposX != 0 || deltaposY != 0) {
var absX = Math.abs(deltaposX);
var absY = Math.abs(deltaposY);
var len = Math.sqrt(deltaposX * deltaposX + deltaposY * deltaposY);
var max = Math.max(absX, absY);
if (max > 0.01) {
deltaposX *= len / max;
deltaposY *= len / max;
}
}
var factor = isTouch ? Util.lerp(1 / 250, 1 / 25,
Settings.controlsSettings.cameraSensitivity) : Util.lerp(1 / 2500, 1 / 100, Settings.controlsSettings.cameraSensitivity);
// CameraPitch += deltaposY * factor;
// CameraYaw += deltaposX * factor;
nextCameraPitch = CameraPitch + deltaposY * factor;
nextCameraYaw = CameraYaw + deltaposX * factor;
// var rotX = deltaposX * 0.001 * Settings.controlsSettings.cameraSensitivity * Math.PI * 2;
// var rotY = deltaposY * 0.001 * Settings.controlsSettings.cameraSensitivity * Math.PI * 2;
// CameraYaw -= rotX;
// CameraPitch -= rotY;
// // CameraYaw = Math.PI / 2;
// // CameraPitch = Math.PI / 4;
// if (CameraPitch > Math.PI / 2)
// CameraPitch = Math.PI / 2 - 0.001;
// if (CameraPitch < -Math.PI / 2)
// CameraPitch = -Math.PI / 2 + 0.001;
// if (CameraPitch > Math.PI)
// CameraPitch = 3.141;
// if (CameraPitch < 0)
// CameraPitch = 0.001;
}
function rescaleDeadZone(value:Float, deadZone:Float) {
if (deadZone >= value) {
if (-deadZone <= value)
return 0.0;
else
return (value + deadZone) / (1.0 - deadZone);
} else
return (value - deadZone) / (1.0 - deadZone);
}
function computePitchSpeedFromDelta(delta:Float) {
return Util.clamp(delta, Math.PI / 10, Math.PI / 2) * 4;
}
function applyNonlinearScale(value:Float) {
return Math.pow(Math.abs(value), 3.2) * (value >= 0 ? 1 : -1);
}
public function startCenterCamera() {
if (this.marble.velocity.lengthSq() >= 81) {
var marbAxis = this.marble.getMarbleAxis();
var motionDir = marbAxis[0].multiply(-1);
radsLeftToCenter = Math.atan2(marble.velocity.x, marble.velocity.y) - Math.atan2(motionDir.x, motionDir.y);
if (Math.abs(radsLeftToCenter) >= 0.5235987755982988) {
if (radsLeftToCenter <= Math.PI) {
if (radsLeftToCenter < 0.0 && radsLeftToCenter < -Math.PI)
radsLeftToCenter = radsLeftToCenter + Math.PI * 2;
} else
radsLeftToCenter = radsLeftToCenter - Math.PI * 2;
centeringCamera = true;
radsStartingToCenter = radsLeftToCenter;
}
}
}
public function update(currentTime:Float, dt:Float) {
// camera.position.set(marblePosition.x, marblePosition.y, marblePosition.z).sub(directionVector.clone().multiplyScalar(2.5));
// this.level.scene.camera.target = marblePosition.add(cameraVerticalTranslation);
// camera.position.add(cameraVerticalTranslation);
var camera = level.scene.camera;
var lerpt = Math.pow(0.5, dt / 0.032); // Math.min(1, 1 - Math.pow(0.6, dt / 0.032)); // hxd.Math.min(1, 1 - Math.pow(0.6, dt * 600));
var gamepadX = applyNonlinearScale(rescaleDeadZone(Gamepad.getAxis(Settings.gamepadSettings.cameraXAxis), 0.25));
var gamepadY = applyNonlinearScale(rescaleDeadZone(Gamepad.getAxis(Settings.gamepadSettings.cameraYAxis), 0.25));
var cameraPitchDelta = (Key.isDown(Settings.controlsSettings.camBackward) ? 1 : 0)
- (Key.isDown(Settings.controlsSettings.camForward) ? 1 : 0)
+ gamepadY;
if (Settings.gamepadSettings.invertYAxis || Settings.controlsSettings.invertYAxis)
cameraPitchDelta = -cameraPitchDelta;
var cameraYawDelta = (Key.isDown(Settings.controlsSettings.camRight) ? 1 : 0) - (Key.isDown(Settings.controlsSettings.camLeft) ? 1 : 0) + gamepadX;
if (Settings.gamepadSettings.invertXAxis)
cameraYawDelta = -cameraYawDelta;
if (MarbleGame.instance.paused) {
cameraYawDelta = 0;
cameraPitchDelta = 0;
}
var deltaX = 0.75 * 5 * cameraYawDelta * dt * Settings.gamepadSettings.cameraSensitivity;
var deltaY = 0.75 * 5 * cameraPitchDelta * dt * Settings.gamepadSettings.cameraSensitivity;
var deltaNew = deltaX;
if (false /*centeringCamera*/) { // This doesnt work
var yawDiff = Math.abs(Math.abs(deltaNew) - Math.abs(radsLeftToCenter));
if (yawDiff >= 0.15)
yawDiff = Math.sin(radsLeftToCenter / radsStartingToCenter) * 0.15;
else {
if (yawDiff >= 0.05)
yawDiff = 0.050000001;
else
centeringCamera = false;
}
if (radsLeftToCenter <= deltaNew) {
deltaNew = deltaNew - yawDiff;
radsLeftToCenter += yawDiff;
} else {
deltaNew = yawDiff + deltaNew;
radsLeftToCenter -= yawDiff;
}
deltaX = deltaNew;
}
deltaX = deltaNew;
// Center the pitch
if (!Settings.controlsSettings.alwaysFreeLook
&& !Key.isDown(Settings.controlsSettings.freelook)
&& !MarbleGame.instance.touchInput.cameraInput.pressed
&& deltaY == 0.0) {
var rescaledY = deltaY;
if (rescaledY <= 0.0)
rescaledY = 0.4 - rescaledY * -0.75;
else
rescaledY = rescaledY * 1.1 + 0.4;
var movePitchDelta = (rescaledY - CameraPitch);
var movePitchSpeed = computePitchSpeedFromDelta(Math.abs(movePitchDelta)) * dt * 0.8;
if (movePitchDelta <= 0.0) {
movePitchDelta = -movePitchDelta;
if (movePitchDelta < movePitchSpeed)
movePitchSpeed = movePitchDelta;
movePitchDelta = -movePitchSpeed;
movePitchSpeed = movePitchDelta;
} else if (movePitchSpeed > movePitchDelta) {
movePitchSpeed = movePitchDelta;
}
deltaY = movePitchSpeed;
}
nextCameraYaw += deltaX;
nextCameraPitch += deltaY;
nextCameraPitch = Math.max(-Math.PI / 2 + Math.PI / 4, Math.min(Math.PI / 2 - 0.0001, nextCameraPitch));
CameraYaw = Util.lerp(CameraYaw, nextCameraYaw, lerpt);
CameraPitch = Util.lerp(CameraPitch, nextCameraPitch, lerpt);
CameraPitch = Util.clamp(CameraPitch, -0.35, 1.5); // Util.clamp(CameraPitch, -Math.PI / 12, Math.PI / 2);
function getRotQuat(v1:Vector, v2:Vector) {
function orthogonal(v:Vector) {
var x = Math.abs(v.x);
var y = Math.abs(v.y);
var z = Math.abs(v.z);
var other = x < y ? (x < z ? new Vector(1, 0, 0) : new Vector(0, 0, 1)) : (y < z ? new Vector(0, 1, 0) : new Vector(0, 0, 1));
return v.cross(other);
}
var u = v1.normalized();
var v = v2.normalized();
if (u.multiply(-1).equals(v)) {
var q = new Quat();
var o = orthogonal(u).normalized();
q.x = o.x;
q.y = o.y;
q.z = o.z;
q.w = 0;
return q;
}
var half = u.add(v).normalized();
var q = new Quat();
q.w = u.dot(half);
var vr = u.cross(half);
q.x = vr.x;
q.y = vr.y;
q.z = vr.z;
return q;
}
var orientationQuat = level.getOrientationQuat(currentTime);
var cameraDistance = CameraDistance;
if (this.finish) {
// Make the camera spin around slowly
// CameraPitch = this.level.finishPitch;
// CameraYaw = this.level.finishYaw;
var effectTime = 1.0;
if (@:privateAccess this.marble.finishAnimTime >= 2.0)
effectTime = 1.0;
else
effectTime = @:privateAccess this.marble.finishAnimTime * 0.5;
effectTime *= 0.5 * CameraDistance;
cameraDistance += effectTime;
}
if (!this.level.isWatching) {
if (this.level.isRecording) {
this.level.replay.recordCameraState(CameraPitch, CameraYaw);
}
} else {
CameraPitch = this.level.replay.currentPlaybackFrame.cameraPitch;
CameraYaw = this.level.replay.currentPlaybackFrame.cameraYaw;
}
var marblePosition = this.finish ? level.marble.collider.transform.getPosition() : level.marble.getAbsPos().getPosition();
if (this.finish) {
// Move the target to the centre of the finish
if (@:privateAccess this.level.endPad != null) {
var padMat = @:privateAccess this.level.endPad.getAbsPos();
var offset = padMat.up();
var padPos = padMat.getPosition();
var focusPos = padPos.add(offset);
focusPos.scale(0.025);
focusPos = focusPos.add(lastTargetPos.multiply(0.975));
marblePosition = focusPos;
}
}
var up = new Vector(0, 0, 1);
up.transform(orientationQuat.toMatrix());
var directionVector = new Vector(1, 0, 0);
var cameraVerticalTranslation = new Vector(0, 0, 0.55);
var q1 = new Quat();
q1.initRotateAxis(0, 1, 0, CameraPitch);
directionVector.transform(q1.toMatrix());
// cameraVerticalTranslation.transform(q1.toMatrix());
q1.initRotateAxis(0, 0, 1, CameraYaw);
directionVector.transform(q1.toMatrix());
// cameraVerticalTranslation.transform(q1.toMatrix());
directionVector.transform(orientationQuat.toMatrix());
cameraVerticalTranslation.transform(orientationQuat.toMatrix());
camera.up = up;
camera.pos = marblePosition.sub(directionVector.multiply(cameraDistance)).add(cameraVerticalTranslation);
camera.target = marblePosition.add(cameraVerticalTranslation);
var closeness = 0.1;
var rayCastOrigin = marblePosition.add(level.marble.currentUp.multiply(marble._radius)).add(cameraVerticalTranslation);
for (pi in level.pathedInteriors) {
pi.pushTickState();
}
var processedShapes = [];
for (i in 0...3) {
var rayCastDirection = camera.pos.sub(rayCastOrigin);
rayCastDirection = rayCastDirection.add(rayCastDirection.normalized().multiply(2));
var rayCastLen = rayCastDirection.length();
var results = level.collisionWorld.rayCast(rayCastOrigin, rayCastDirection.normalized(), rayCastLen);
var firstHit:octree.IOctreeObject.RayIntersectionData = null;
var firstHitDistance = 1e8;
for (result in results) {
if (!processedShapes.contains(result.object)
&& (firstHit == null || (rayCastOrigin.distance(result.point) < firstHitDistance))) {
firstHit = result;
firstHitDistance = rayCastOrigin.distance(result.point);
processedShapes.push(result.object);
}
}
if (firstHit != null) {
if (firstHitDistance < cameraDistance) {
// camera.pos = marblePosition.sub(directionVector.multiply(firstHit.distance * 0.7));
var plane = new Plane(firstHit.normal.x, firstHit.normal.y, firstHit.normal.z, firstHit.point.dot(firstHit.normal));
var normal = firstHit.normal.multiply(-1);
// var position = firstHit.point;
var projected = plane.project(camera.pos.toPoint());
var dist = plane.distance(camera.pos.toPoint());
if (dist >= closeness)
break;
camera.pos = projected.toVector().add(normal.multiply(-closeness));
var forwardVec = marblePosition.sub(camera.pos).normalized();
var rightVec = camera.up.cross(forwardVec).normalized();
var upVec = forwardVec.cross(rightVec);
camera.target = marblePosition.add(cameraVerticalTranslation);
// camera.up = upVec;
continue;
}
}
break;
}
for (pi in level.pathedInteriors) {
pi.popTickState();
}
if (oob) {
camera.pos = lastCamPos;
camera.target = marblePosition.add(lastVertTranslation);
}
if (!oob) {
lastCamPos = camera.pos;
lastVertTranslation = cameraVerticalTranslation;
lastTargetPos = marblePosition.clone();
}
this.setPosition(camera.pos.x, camera.pos.y, camera.pos.z);
// camera.target = null;
// camera.target = targetpos.add(cameraVerticalTranslation);
// this.x = targetpos.x + directionVec.x;
// this.y = targetpos.y + directionVec.y;
// this.z = targetpos.z + directionVec.z;
// this.level.scene.camera.follow = {pos: this, target: this.marble};
}
}