+#include <gdk/gdkkeysyms.h>
+
+#include "debugging/debugging.h"
+
+#include "iscenegraph.h"
+#include "irender.h"
+#include "igl.h"
+#include "icamera.h"
+#include "cullable.h"
+#include "renderable.h"
+#include "preferencesystem.h"
+
+#include "signal/signal.h"
+#include "container/array.h"
+#include "scenelib.h"
+#include "render.h"
+#include "cmdlib.h"
+#include "math/frustum.h"
+
+#include "gtkutil/widget.h"
+#include "gtkutil/button.h"
+#include "gtkutil/toolbar.h"
+#include "gtkutil/glwidget.h"
+#include "gtkutil/xorrectangle.h"
+#include "gtkmisc.h"
+#include "selection.h"
+#include "mainframe.h"
+#include "preferences.h"
+#include "commands.h"
+#include "xywindow.h"
+#include "windowobservers.h"
+#include "renderstate.h"
+
+#include "timer.h"
+
+Signal0 g_cameraMoved_callbacks;
+
+void AddCameraMovedCallback(const SignalHandler &handler)
+{
+ g_cameraMoved_callbacks.connectLast(handler);
+}
+
+void CameraMovedNotify()
+{
+ g_cameraMoved_callbacks();
+}
+
+
+struct camwindow_globals_private_t {
+ int m_nMoveSpeed;
+ bool m_bCamLinkSpeed;
+ int m_nAngleSpeed;
+ bool m_bCamInverseMouse;
+ bool m_bCamDiscrete;
+ bool m_bCubicClipping;
+ bool m_showStats;
+ int m_nStrafeMode;
+
+ camwindow_globals_private_t() :
+ m_nMoveSpeed(100),
+ m_bCamLinkSpeed(true),
+ m_nAngleSpeed(3),
+ m_bCamInverseMouse(false),
+ m_bCamDiscrete(true),
+ m_bCubicClipping(true),
+ m_showStats(true),
+ m_nStrafeMode(0)
+ {
+ }
+
+};
+
+camwindow_globals_private_t g_camwindow_globals_private;
+
+
+const Matrix4 g_opengl2radiant(
+ 0, 0, -1, 0,
+ -1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 0, 1
+);
+
+const Matrix4 g_radiant2opengl(
+ 0, -1, 0, 0,
+ 0, 0, 1, 0,
+ -1, 0, 0, 0,
+ 0, 0, 0, 1
+);
+
+struct camera_t;
+
+void Camera_mouseMove(camera_t &camera, int x, int y);
+
+enum camera_draw_mode {
+ cd_wire,
+ cd_solid,
+ cd_texture,
+ cd_lighting
+};
+
+struct camera_t {
+ int width, height;
+
+ bool timing;
+
+ Vector3 origin;
+ Vector3 angles;
+
+ Vector3 color; // background
+
+ Vector3 forward, right; // move matrix (TTimo: used to have up but it was not updated)
+ Vector3 vup, vpn, vright; // view matrix (taken from the modelview matrix)
+
+ Matrix4 projection;
+ Matrix4 modelview;
+
+ bool m_strafe; // true when in strafemode toggled by the ctrl-key
+ bool m_strafe_forward; // true when in strafemode by ctrl-key and shift is pressed for forward strafing
+
+ unsigned int movementflags; // movement flags
+ Timer m_keycontrol_timer;
+ guint m_keymove_handler;
+
+
+ float fieldOfView;
+
+ DeferredMotionDelta m_mouseMove;
+
+ static void motionDelta(int x, int y, void *data)
+ {
+ Camera_mouseMove(*reinterpret_cast<camera_t *>( data ), x, y);
+ }
+
+ View *m_view;
+ Callback<void()> m_update;
+
+ static camera_draw_mode draw_mode;
+
+ camera_t(View *view, const Callback<void()> &update)
+ : width(0),
+ height(0),
+ timing(false),
+ origin(0, 0, 0),
+ angles(0, 0, 0),
+ color(0, 0, 0),
+ movementflags(0),
+ m_keymove_handler(0),
+ fieldOfView(90.0f),
+ m_mouseMove(motionDelta, this),
+ m_view(view),
+ m_update(update)
+ {
+ }
+};
+
+camera_draw_mode camera_t::draw_mode = cd_texture;
+
+inline Matrix4 projection_for_camera(float near_z, float far_z, float fieldOfView, int width, int height)
+{
+ const float half_width = static_cast<float>( near_z * tan(degrees_to_radians(fieldOfView * 0.5)));
+ const float half_height = half_width * (static_cast<float>( height ) / static_cast<float>( width ));
+
+ return matrix4_frustum(
+ -half_width,
+ half_width,
+ -half_height,
+ half_height,
+ near_z,
+ far_z
+ );
+}
+
+float Camera_getFarClipPlane(camera_t &camera)
+{
+ return (g_camwindow_globals_private.m_bCubicClipping) ? pow(2.0, (g_camwindow_globals.m_nCubicScale + 7) / 2.0)
+ : 32768.0f;
+}
+
+void Camera_updateProjection(camera_t &camera)
+{
+ float farClip = Camera_getFarClipPlane(camera);
+ camera.projection = projection_for_camera(farClip / 4096.0f, farClip, camera.fieldOfView, camera.width,
+ camera.height);
+
+ camera.m_view->Construct(camera.projection, camera.modelview, camera.width, camera.height);
+}
+
+void Camera_updateVectors(camera_t &camera)
+{
+ for (int i = 0; i < 3; i++) {
+ camera.vright[i] = camera.modelview[(i << 2) + 0];
+ camera.vup[i] = camera.modelview[(i << 2) + 1];
+ camera.vpn[i] = camera.modelview[(i << 2) + 2];
+ }
+}
+
+void Camera_updateModelview(camera_t &camera)
+{
+ camera.modelview = g_matrix4_identity;
+
+ // roll, pitch, yaw
+ Vector3 radiant_eulerXYZ(0, -camera.angles[CAMERA_PITCH], camera.angles[CAMERA_YAW]);
+
+ matrix4_translate_by_vec3(camera.modelview, camera.origin);
+ matrix4_rotate_by_euler_xyz_degrees(camera.modelview, radiant_eulerXYZ);
+ matrix4_multiply_by_matrix4(camera.modelview, g_radiant2opengl);
+ matrix4_affine_invert(camera.modelview);
+
+ Camera_updateVectors(camera);
+
+ camera.m_view->Construct(camera.projection, camera.modelview, camera.width, camera.height);
+}
+
+
+void Camera_Move_updateAxes(camera_t &camera)
+{
+ double ya = degrees_to_radians(camera.angles[CAMERA_YAW]);
+
+ // the movement matrix is kept 2d
+ camera.forward[0] = static_cast<float>( cos(ya));
+ camera.forward[1] = static_cast<float>( sin(ya));
+ camera.forward[2] = 0;
+ camera.right[0] = camera.forward[1];
+ camera.right[1] = -camera.forward[0];
+}
+
+void Camera_Freemove_updateAxes(camera_t &camera)
+{
+ camera.right = camera.vright;
+ camera.forward = vector3_negated(camera.vpn);
+}
+
+const Vector3 &Camera_getOrigin(camera_t &camera)
+{
+ return camera.origin;
+}
+
+void Camera_setOrigin(camera_t &camera, const Vector3 &origin)
+{
+ camera.origin = origin;
+ Camera_updateModelview(camera);
+ camera.m_update();
+ CameraMovedNotify();
+}
+
+const Vector3 &Camera_getAngles(camera_t &camera)
+{
+ return camera.angles;
+}
+
+void Camera_setAngles(camera_t &camera, const Vector3 &angles)
+{
+ camera.angles = angles;
+ Camera_updateModelview(camera);
+ camera.m_update();
+ CameraMovedNotify();
+}
+
+
+void Camera_FreeMove(camera_t &camera, int dx, int dy)
+{
+ // free strafe mode, toggled by the ctrl key with optional shift for forward movement
+ if (camera.m_strafe) {
+ float strafespeed = 0.65f;
+
+ if (g_camwindow_globals_private.m_bCamLinkSpeed) {
+ strafespeed = (float) g_camwindow_globals_private.m_nMoveSpeed / 100;
+ }
+
+ camera.origin -= camera.vright * strafespeed * dx;
+ if (camera.m_strafe_forward) {
+ camera.origin += camera.vpn * strafespeed * dy;
+ } else {
+ camera.origin += camera.vup * strafespeed * dy;
+ }
+ } else // free rotation
+ {
+ const float dtime = 0.1f;
+
+ if (g_camwindow_globals_private.m_bCamInverseMouse) {
+ camera.angles[CAMERA_PITCH] -= dy * dtime * g_camwindow_globals_private.m_nAngleSpeed;
+ } else {
+ camera.angles[CAMERA_PITCH] += dy * dtime * g_camwindow_globals_private.m_nAngleSpeed;
+ }
+
+ camera.angles[CAMERA_YAW] += dx * dtime * g_camwindow_globals_private.m_nAngleSpeed;
+
+ if (camera.angles[CAMERA_PITCH] > 90) {
+ camera.angles[CAMERA_PITCH] = 90;
+ } else if (camera.angles[CAMERA_PITCH] < -90) {
+ camera.angles[CAMERA_PITCH] = -90;
+ }
+
+ if (camera.angles[CAMERA_YAW] >= 360) {
+ camera.angles[CAMERA_YAW] -= 360;
+ } else if (camera.angles[CAMERA_YAW] <= 0) {
+ camera.angles[CAMERA_YAW] += 360;
+ }
+ }
+
+ Camera_updateModelview(camera);
+ Camera_Freemove_updateAxes(camera);
+}
+
+void Cam_MouseControl(camera_t &camera, int x, int y)
+{
+ float xf = (float) (x - camera.width / 2) / (camera.width / 2);
+ float yf = (float) (y - camera.height / 2) / (camera.height / 2);
+
+ xf *= 1.0f - fabsf(yf);
+ if (xf < 0) {
+ xf += 0.1f;
+ if (xf > 0) {
+ xf = 0;
+ }
+ } else {
+ xf -= 0.1f;
+ if (xf < 0) {
+ xf = 0;
+ }
+ }
+
+ vector3_add(camera.origin, vector3_scaled(camera.forward, yf * 0.1f * g_camwindow_globals_private.m_nMoveSpeed));
+ camera.angles[CAMERA_YAW] += xf * -0.1f * g_camwindow_globals_private.m_nAngleSpeed;
+
+ Camera_updateModelview(camera);
+}
+
+void Camera_mouseMove(camera_t &camera, int x, int y)
+{
+ //globalOutputStream() << "mousemove... ";
+ Camera_FreeMove(camera, -x, -y);
+ camera.m_update();
+ CameraMovedNotify();
+}
+
+const unsigned int MOVE_NONE = 0;
+const unsigned int MOVE_FORWARD = 1 << 0;
+const unsigned int MOVE_BACK = 1 << 1;
+const unsigned int MOVE_ROTRIGHT = 1 << 2;
+const unsigned int MOVE_ROTLEFT = 1 << 3;
+const unsigned int MOVE_STRAFERIGHT = 1 << 4;
+const unsigned int MOVE_STRAFELEFT = 1 << 5;
+const unsigned int MOVE_UP = 1 << 6;
+const unsigned int MOVE_DOWN = 1 << 7;
+const unsigned int MOVE_PITCHUP = 1 << 8;
+const unsigned int MOVE_PITCHDOWN = 1 << 9;
+const unsigned int MOVE_ALL =
+ MOVE_FORWARD | MOVE_BACK | MOVE_ROTRIGHT | MOVE_ROTLEFT | MOVE_STRAFERIGHT | MOVE_STRAFELEFT | MOVE_UP |
+ MOVE_DOWN | MOVE_PITCHUP | MOVE_PITCHDOWN;
+
+void Cam_KeyControl(camera_t &camera, float dtime)
+{
+ // Update angles
+ if (camera.movementflags & MOVE_ROTLEFT) {
+ camera.angles[CAMERA_YAW] += 15 * dtime * g_camwindow_globals_private.m_nAngleSpeed;
+ }
+ if (camera.movementflags & MOVE_ROTRIGHT) {
+ camera.angles[CAMERA_YAW] -= 15 * dtime * g_camwindow_globals_private.m_nAngleSpeed;
+ }
+ if (camera.movementflags & MOVE_PITCHUP) {
+ camera.angles[CAMERA_PITCH] += 15 * dtime * g_camwindow_globals_private.m_nAngleSpeed;
+ if (camera.angles[CAMERA_PITCH] > 90) {
+ camera.angles[CAMERA_PITCH] = 90;
+ }
+ }
+ if (camera.movementflags & MOVE_PITCHDOWN) {
+ camera.angles[CAMERA_PITCH] -= 15 * dtime * g_camwindow_globals_private.m_nAngleSpeed;
+ if (camera.angles[CAMERA_PITCH] < -90) {
+ camera.angles[CAMERA_PITCH] = -90;
+ }
+ }
+
+ Camera_updateModelview(camera);
+ Camera_Freemove_updateAxes(camera);
+
+ // Update position
+ if (camera.movementflags & MOVE_FORWARD) {
+ vector3_add(camera.origin, vector3_scaled(camera.forward, dtime * g_camwindow_globals_private.m_nMoveSpeed));
+ }
+ if (camera.movementflags & MOVE_BACK) {
+ vector3_add(camera.origin, vector3_scaled(camera.forward, -dtime * g_camwindow_globals_private.m_nMoveSpeed));
+ }
+ if (camera.movementflags & MOVE_STRAFELEFT) {
+ vector3_add(camera.origin, vector3_scaled(camera.right, -dtime * g_camwindow_globals_private.m_nMoveSpeed));
+ }
+ if (camera.movementflags & MOVE_STRAFERIGHT) {
+ vector3_add(camera.origin, vector3_scaled(camera.right, dtime * g_camwindow_globals_private.m_nMoveSpeed));
+ }
+ if (camera.movementflags & MOVE_UP) {
+ vector3_add(camera.origin, vector3_scaled(g_vector3_axis_z, dtime * g_camwindow_globals_private.m_nMoveSpeed));
+ }
+ if (camera.movementflags & MOVE_DOWN) {
+ vector3_add(camera.origin, vector3_scaled(g_vector3_axis_z, -dtime * g_camwindow_globals_private.m_nMoveSpeed));
+ }
+
+ Camera_updateModelview(camera);
+}
+
+void Camera_keyMove(camera_t &camera)
+{
+ camera.m_mouseMove.flush();
+
+ //globalOutputStream() << "keymove... ";
+ float time_seconds = camera.m_keycontrol_timer.elapsed_msec() / static_cast<float>( msec_per_sec );
+ camera.m_keycontrol_timer.start();
+ if (time_seconds > 0.05f) {
+ time_seconds = 0.05f; // 20fps
+ }
+ Cam_KeyControl(camera, time_seconds * 5.0f);
+
+ camera.m_update();
+ CameraMovedNotify();
+}
+
+gboolean camera_keymove(gpointer data)
+{
+ Camera_keyMove(*reinterpret_cast<camera_t *>( data ));
+ return TRUE;
+}
+
+void Camera_setMovementFlags(camera_t &camera, unsigned int mask)
+{
+ if ((~camera.movementflags & mask) != 0 && camera.movementflags == 0) {
+ camera.m_keymove_handler = g_idle_add(camera_keymove, &camera);
+ }
+ camera.movementflags |= mask;
+}
+
+void Camera_clearMovementFlags(camera_t &camera, unsigned int mask)
+{
+ if ((camera.movementflags & ~mask) == 0 && camera.movementflags != 0) {
+ g_source_remove(camera.m_keymove_handler);
+ camera.m_keymove_handler = 0;
+ }
+ camera.movementflags &= ~mask;
+}
+
+void Camera_MoveForward_KeyDown(camera_t &camera)
+{
+ Camera_setMovementFlags(camera, MOVE_FORWARD);
+}
+
+void Camera_MoveForward_KeyUp(camera_t &camera)
+{
+ Camera_clearMovementFlags(camera, MOVE_FORWARD);
+}
+
+void Camera_MoveBack_KeyDown(camera_t &camera)
+{
+ Camera_setMovementFlags(camera, MOVE_BACK);
+}
+
+void Camera_MoveBack_KeyUp(camera_t &camera)
+{
+ Camera_clearMovementFlags(camera, MOVE_BACK);
+}
+
+void Camera_MoveLeft_KeyDown(camera_t &camera)
+{
+ Camera_setMovementFlags(camera, MOVE_STRAFELEFT);
+}
+
+void Camera_MoveLeft_KeyUp(camera_t &camera)
+{
+ Camera_clearMovementFlags(camera, MOVE_STRAFELEFT);
+}
+
+void Camera_MoveRight_KeyDown(camera_t &camera)
+{
+ Camera_setMovementFlags(camera, MOVE_STRAFERIGHT);
+}
+
+void Camera_MoveRight_KeyUp(camera_t &camera)
+{
+ Camera_clearMovementFlags(camera, MOVE_STRAFERIGHT);
+}
+
+void Camera_MoveUp_KeyDown(camera_t &camera)
+{
+ Camera_setMovementFlags(camera, MOVE_UP);
+}
+
+void Camera_MoveUp_KeyUp(camera_t &camera)
+{
+ Camera_clearMovementFlags(camera, MOVE_UP);
+}
+
+void Camera_MoveDown_KeyDown(camera_t &camera)
+{
+ Camera_setMovementFlags(camera, MOVE_DOWN);
+}
+
+void Camera_MoveDown_KeyUp(camera_t &camera)
+{
+ Camera_clearMovementFlags(camera, MOVE_DOWN);
+}