Files
2018-07-17 14:15:37 +02:00

307 lines
6.9 KiB
ActionScript

//Adds functionality to the normal engine camera to make it
//more intelligent and context-sensitive
import double getElevation(double x, double z) from "navigation.elevation";
import double getAverageElevation() from "navigation.elevation";
import bool getElevationIntersect(const line3dd& line, vec3d& point) from "navigation.elevation";
import Object@ get_hoveredObject() from "obj_selection";
import saving;
bool CAM_PANNED = false;
bool CAM_ZOOMED = false;
bool CAM_ROTATED = false;
double OBJLOCK_ZOOMDIST = 5.0;
class SmartCamera : Savable {
//Main engine camera
Camera camera;
//Configuration
double camAnglesPerPixel;
double movePerPixel;
double tickZoom;
//Object locks
Object@ lockedObject;
vec3d lockOffset;
SmartCamera() {
camAnglesPerPixel = 0.22 * twopi / 360.0;
movePerPixel = 8.0;
tickZoom = 1.2;
camera.setPositionBound(vec3d(-200000), vec3d(200000));
}
void reset() {
//Set the camera to the normal angle
camera.resetRotation();
camera.resetZoom();
camera.zoom(5.0);
camera.pitch(0.7);
camera.snap();
}
vec3d get_lookAt() {
return camera.lookAt;
}
vec3d get_finalLookAt() {
return camera.finalLookAt;
}
double screenAngle(const vec3d& pos) {
return camera.screenAngle(pos);
}
double get_distance() {
return camera.distance;
}
//Rotate by a certain amount of screen pixels dragged
void rotate(int dx, int dy) {
CAM_ROTATED = true;
if(settings::bInvertHorizRot)
dx = -dx;
if(settings::bInvertVertRot)
dy = -dy;
if(settings::bFreeCamera) {
camera.yaw(clamp(double(dx) * camAnglesPerPixel, -pi * 0.5, pi * 0.5));
camera.pitch(clamp(double(dy) * camAnglesPerPixel, -pi * 0.5, pi * 0.5));
}
else {
camera.abs_yaw(clamp(double(dx) * camAnglesPerPixel, -pi * 0.5, pi * 0.5));
camera.pitch(clamp(double(dy) * camAnglesPerPixel, -pi * 0.5, pi * 0.5));
}
}
//Roll based on dragged script pixels
void roll(int delta) {
CAM_ROTATED = true;
camera.roll(double(delta) * camAnglesPerPixel);
}
//Zoom with world plane
void zoom(int delta) {
//Get zoom speed
double factor;
if(settings::bInvertZoom)
factor = pow(tickZoom, delta * settings::dZoomSpeed);
else
factor = pow(tickZoom, -delta * settings::dZoomSpeed);
CAM_ZOOMED = true;
if(lockedObject !is null) {
camera.zoom(factor);
return;
}
if(factor < 1.0 ? settings::bZoomToCursor : settings::bZoomFromCursor) {
if(hoveredObject !is null) {
double minDist = hoveredObject.radius * getMinZoomDist(hoveredObject);
vec3d point = hoveredObject.position;
camera.zoomTo(factor, point, minDist);
}
else {
//Interpolate plane position
line3dd ray = screenToRay(mousePos);
if(factor > 1.0)
ray.start -= ray.direction * 1.0;
vec3d point;
if(!getElevationIntersect(ray, point))
return;
double minDist = 0.0;
camera.zoomTo(factor, point, minDist);
}
}
else {
camera.zoom(factor);
}
}
void zoomTo(Object@ obj) {
clearObjectLock();
zoomTo(obj.position);
}
void zoomTo(const vec3d& pos) {
clearObjectLock();
vec3d finalPos = camera.finalLookAt;
vec3d movement;
movement.x = pos.x - finalPos.x;
movement.z = pos.z - finalPos.z;
movement.y = getElevation(pos.x, pos.z) - finalPos.y;
camera.move_abs(movement);
}
double getMinZoomDist(Object@ obj) {
switch(obj.type) {
case OT_Star:
return 1.4;
case OT_Planet:
return 1.6;
}
return 1.15;
}
//Pan on top of the world plane
void pan(int dx, int dy) {
if(!settings::bInvertPanX)
dx = -dx;
if(settings::bInvertPanY)
dy = -dy;
//Normal world plane movement
vec2d movement;
movement.x = movePerPixel * double(dx);
movement.y = movePerPixel * double(dy);
pan(movement);
}
void pan(vec2d movement) {
clearObjectLock();
//Speed based on zoom and modifiers
double speed = camera.distance / 2500.0;
if(settings::bModSpeedPan) {
if(shiftKey)
speed *= 3.0;
else if(altKey)
speed *= 0.333;
}
movement *= settings::dPanSpeed * speed;
//Normal world plane movement
vec3d prevPos = camera.finalLookAt;
camera.move_world_abs(vec3d(movement.x, 0, movement.y));
//Change in elevation
vec3d nextPos = camera.finalLookAt;
camera.move_abs(vec3d(0, getElevation(nextPos.x, nextPos.z) - prevPos.y, 0));
CAM_PANNED = true;
}
bool panTo(const vec3d& pos, double speed, double threshold = 10.0) {
clearObjectLock();
//Modify speed
speed *= max(camera.distance / 2500.0, 0.2);
if(settings::bModSpeedPan) {
if(shiftKey)
speed *= 3.0;
else if(altKey)
speed *= 0.333;
}
speed *= settings::dPanSpeed;
//Pan on a flat plane
vec3d cur = lookAt;
vec3d final = finalLookAt;
vec3d flatPos = pos;
flatPos.y = 0;
cur.y = 0;
final.y = 0;
vec3d panDir = flatPos - final;
double len = panDir.length;
if(len <= threshold) {
return true;
}
else {
if(len > speed)
panDir.length = speed;
vec3d prevPos = camera.finalLookAt;
camera.move_abs(panDir);
vec3d nextPos = camera.finalLookAt;
camera.move_abs(vec3d(0, getElevation(nextPos.x, nextPos.z) - prevPos.y, 0));
}
return false;
}
line3dd screenToRay(const vec2i& pos) {
vec2i size = screenSize;
return camera.screenToRay(double(pos.x) / double(size.x),
double(pos.y) / double(size.y));
}
vec3d screenToPoint(const vec2i& pos) {
line3dd ray = screenToRay(mousePos);
vec3d point;
if(!getElevationIntersect(ray, point))
ray.intersectY(point, getAverageElevation(), false);
return point;
}
void clearObjectLock() {
@lockedObject = null;
}
void zoomLockObject(Object& obj) {
zoomTo(obj.position);
camera.move_abs(obj.node_position - camera.finalLookAt);
if(!shiftKey)
camera.zoom((obj.radius * OBJLOCK_ZOOMDIST) / camera.distance);
setLocked(obj);
}
void setLocked(Object& obj) {
@lockedObject = obj;
lockOffset = camera.finalLookAt - obj.node_position;
}
void animateLocked(double time) {
if(lockedObject is null)
return;
if(!lockedObject.visible) {
@lockedObject = null;
return;
}
vec3d finalPos = camera.finalLookAt;
double tDiff = frameGameTime - lockedObject.lastTick;
vec3d objPos = lockedObject.position + (lockedObject.velocity + lockedObject.acceleration * (tDiff * 0.5)) * tDiff;
if(finalPos.distanceToSQ(objPos) > sqr(lockedObject.radius * 2.0 + lockedObject.velocity.length)) {
@lockedObject = null;
return;
}
objPos += lockOffset;
vec3d movement = objPos - finalPos;
bool snap = camera.lookAt.distanceTo(finalPos) < 15.0;
camera.move_abs(movement);
if(snap)
camera.snapTranslation();
}
void animate(double time) {
camera.animate(time);
if(lockedObject !is null)
animateLocked(time);
}
void save(SaveFile& file) {
file << camera.finalLookAt;
file << camera.radius;
}
void load(SaveFile& file) {
vec3d position;
file >> position;
if(file >= SV_0149) {
double radius = 0;
file >> radius;
camera.radius = radius;
}
camera.move_abs(position - camera.finalLookAt);
camera.snap();
}
}