+#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 );
+}
+
+void Camera_RotateLeft_KeyDown( camera_t& camera ){
+ Camera_setMovementFlags( camera, MOVE_ROTLEFT );
+}
+void Camera_RotateLeft_KeyUp( camera_t& camera ){
+ Camera_clearMovementFlags( camera, MOVE_ROTLEFT );
+}
+void Camera_RotateRight_KeyDown( camera_t& camera ){
+ Camera_setMovementFlags( camera, MOVE_ROTRIGHT );
+}
+void Camera_RotateRight_KeyUp( camera_t& camera ){
+ Camera_clearMovementFlags( camera, MOVE_ROTRIGHT );
+}
+
+void Camera_PitchUp_KeyDown( camera_t& camera ){
+ Camera_setMovementFlags( camera, MOVE_PITCHUP );
+}
+void Camera_PitchUp_KeyUp( camera_t& camera ){
+ Camera_clearMovementFlags( camera, MOVE_PITCHUP );
+}
+void Camera_PitchDown_KeyDown( camera_t& camera ){
+ Camera_setMovementFlags( camera, MOVE_PITCHDOWN );
+}
+void Camera_PitchDown_KeyUp( camera_t& camera ){
+ Camera_clearMovementFlags( camera, MOVE_PITCHDOWN );
+}
+
+
+typedef ReferenceCaller<camera_t, void(), &Camera_MoveForward_KeyDown> FreeMoveCameraMoveForwardKeyDownCaller;
+typedef ReferenceCaller<camera_t, void(), &Camera_MoveForward_KeyUp> FreeMoveCameraMoveForwardKeyUpCaller;
+typedef ReferenceCaller<camera_t, void(), &Camera_MoveBack_KeyDown> FreeMoveCameraMoveBackKeyDownCaller;
+typedef ReferenceCaller<camera_t, void(), &Camera_MoveBack_KeyUp> FreeMoveCameraMoveBackKeyUpCaller;
+typedef ReferenceCaller<camera_t, void(), &Camera_MoveLeft_KeyDown> FreeMoveCameraMoveLeftKeyDownCaller;
+typedef ReferenceCaller<camera_t, void(), &Camera_MoveLeft_KeyUp> FreeMoveCameraMoveLeftKeyUpCaller;
+typedef ReferenceCaller<camera_t, void(), &Camera_MoveRight_KeyDown> FreeMoveCameraMoveRightKeyDownCaller;
+typedef ReferenceCaller<camera_t, void(), &Camera_MoveRight_KeyUp> FreeMoveCameraMoveRightKeyUpCaller;
+typedef ReferenceCaller<camera_t, void(), &Camera_MoveUp_KeyDown> FreeMoveCameraMoveUpKeyDownCaller;
+typedef ReferenceCaller<camera_t, void(), &Camera_MoveUp_KeyUp> FreeMoveCameraMoveUpKeyUpCaller;
+typedef ReferenceCaller<camera_t, void(), &Camera_MoveDown_KeyDown> FreeMoveCameraMoveDownKeyDownCaller;
+typedef ReferenceCaller<camera_t, void(), &Camera_MoveDown_KeyUp> FreeMoveCameraMoveDownKeyUpCaller;
+
+
+const float SPEED_MOVE = 32;
+const float SPEED_TURN = 22.5;
+const float MIN_CAM_SPEED = 10;
+const float MAX_CAM_SPEED = 610;
+const float CAM_SPEED_STEP = 50;
+
+void Camera_MoveForward_Discrete( camera_t& camera ){
+ Camera_Move_updateAxes( camera );
+ Camera_setOrigin( camera, vector3_added( Camera_getOrigin( camera ), vector3_scaled( camera.forward, SPEED_MOVE ) ) );
+}
+void Camera_MoveBack_Discrete( camera_t& camera ){
+ Camera_Move_updateAxes( camera );
+ Camera_setOrigin( camera, vector3_added( Camera_getOrigin( camera ), vector3_scaled( camera.forward, -SPEED_MOVE ) ) );
+}
+
+void Camera_MoveUp_Discrete( camera_t& camera ){
+ Vector3 origin( Camera_getOrigin( camera ) );
+ origin[2] += SPEED_MOVE;
+ Camera_setOrigin( camera, origin );
+}
+void Camera_MoveDown_Discrete( camera_t& camera ){
+ Vector3 origin( Camera_getOrigin( camera ) );
+ origin[2] -= SPEED_MOVE;
+ Camera_setOrigin( camera, origin );
+}
+
+void Camera_MoveLeft_Discrete( camera_t& camera ){
+ Camera_Move_updateAxes( camera );
+ Camera_setOrigin( camera, vector3_added( Camera_getOrigin( camera ), vector3_scaled( camera.right, -SPEED_MOVE ) ) );
+}
+void Camera_MoveRight_Discrete( camera_t& camera ){
+ Camera_Move_updateAxes( camera );
+ Camera_setOrigin( camera, vector3_added( Camera_getOrigin( camera ), vector3_scaled( camera.right, SPEED_MOVE ) ) );
+}
+
+void Camera_RotateLeft_Discrete( camera_t& camera ){
+ Vector3 angles( Camera_getAngles( camera ) );
+ angles[CAMERA_YAW] += SPEED_TURN;
+ Camera_setAngles( camera, angles );
+}
+void Camera_RotateRight_Discrete( camera_t& camera ){
+ Vector3 angles( Camera_getAngles( camera ) );
+ angles[CAMERA_YAW] -= SPEED_TURN;
+ Camera_setAngles( camera, angles );
+}
+
+void Camera_PitchUp_Discrete( camera_t& camera ){
+ Vector3 angles( Camera_getAngles( camera ) );
+ angles[CAMERA_PITCH] += SPEED_TURN;
+ if ( angles[CAMERA_PITCH] > 90 ) {
+ angles[CAMERA_PITCH] = 90;
+ }
+ Camera_setAngles( camera, angles );
+}
+void Camera_PitchDown_Discrete( camera_t& camera ){
+ Vector3 angles( Camera_getAngles( camera ) );
+ angles[CAMERA_PITCH] -= SPEED_TURN;
+ if ( angles[CAMERA_PITCH] < -90 ) {
+ angles[CAMERA_PITCH] = -90;
+ }
+ Camera_setAngles( camera, angles );
+}
+
+
+class RadiantCameraView : public CameraView
+{
+camera_t& m_camera;
+View* m_view;
+Callback<void()> m_update;
+public:
+RadiantCameraView( camera_t& camera, View* view, const Callback<void()>& update ) : m_camera( camera ), m_view( view ), m_update( update ){
+}
+void update(){
+ m_view->Construct( m_camera.projection, m_camera.modelview, m_camera.width, m_camera.height );
+ m_update();
+}
+void setModelview( const Matrix4& modelview ){
+ m_camera.modelview = modelview;
+ matrix4_multiply_by_matrix4( m_camera.modelview, g_radiant2opengl );
+ matrix4_affine_invert( m_camera.modelview );
+ Camera_updateVectors( m_camera );
+ update();
+}
+void setFieldOfView( float fieldOfView ){
+ float farClip = Camera_getFarClipPlane( m_camera );
+ m_camera.projection = projection_for_camera( farClip / 4096.0f, farClip, fieldOfView, m_camera.width, m_camera.height );
+ update();
+}
+};
+
+
+void Camera_motionDelta( int x, int y, unsigned int state, void* data ){
+ camera_t* cam = reinterpret_cast<camera_t*>( data );
+
+ cam->m_mouseMove.motion_delta( x, y, state );
+
+ switch ( g_camwindow_globals_private.m_nStrafeMode )
+ {
+ case 0:
+ cam->m_strafe = ( state & GDK_CONTROL_MASK ) != 0;
+ if ( cam->m_strafe ) {
+ cam->m_strafe_forward = ( state & GDK_SHIFT_MASK ) != 0;
+ }
+ else{
+ cam->m_strafe_forward = false;
+ }
+ break;
+ case 1:
+ cam->m_strafe = ( state & GDK_CONTROL_MASK ) != 0 && ( state & GDK_SHIFT_MASK ) == 0;
+ cam->m_strafe_forward = false;
+ break;
+ case 2:
+ cam->m_strafe = ( state & GDK_CONTROL_MASK ) != 0 && ( state & GDK_SHIFT_MASK ) == 0;
+ cam->m_strafe_forward = cam->m_strafe;
+ break;
+ }
+}
+
+class CamWnd
+{
+View m_view;
+camera_t m_Camera;
+RadiantCameraView m_cameraview;
+#if 0
+int m_PositionDragCursorX;
+int m_PositionDragCursorY;
+#endif
+
+guint m_freemove_handle_focusout;
+
+static Shader* m_state_select1;
+static Shader* m_state_select2;
+
+FreezePointer m_freezePointer;
+
+public:
+ui::GLArea m_gl_widget;
+ui::Window m_parent{ui::null};
+
+SelectionSystemWindowObserver* m_window_observer;
+XORRectangle m_XORRectangle;
+
+DeferredDraw m_deferredDraw;
+DeferredMotion m_deferred_motion;
+
+guint m_selection_button_press_handler;
+guint m_selection_button_release_handler;
+guint m_selection_motion_handler;
+
+guint m_freelook_button_press_handler;
+
+guint m_sizeHandler;
+guint m_exposeHandler;
+
+CamWnd();
+~CamWnd();
+
+bool m_drawing;
+void queue_draw(){
+ //ASSERT_MESSAGE(!m_drawing, "CamWnd::queue_draw(): called while draw is already in progress");
+ if ( m_drawing ) {
+ return;
+ }
+ //globalOutputStream() << "queue... ";
+ m_deferredDraw.draw();
+}
+void draw();
+
+static void captureStates(){
+ m_state_select1 = GlobalShaderCache().capture( "$CAM_HIGHLIGHT" );
+ m_state_select2 = GlobalShaderCache().capture( "$CAM_OVERLAY" );
+}
+static void releaseStates(){
+ GlobalShaderCache().release( "$CAM_HIGHLIGHT" );
+ GlobalShaderCache().release( "$CAM_OVERLAY" );
+}
+
+camera_t& getCamera(){
+ return m_Camera;
+};
+
+void BenchMark();
+void Cam_ChangeFloor( bool up );
+
+void DisableFreeMove();
+void EnableFreeMove();
+bool m_bFreeMove;
+
+CameraView& getCameraView(){
+ return m_cameraview;
+}
+
+private:
+void Cam_Draw();
+};
+
+typedef MemberCaller<CamWnd, void(), &CamWnd::queue_draw> CamWndQueueDraw;
+
+Shader* CamWnd::m_state_select1 = 0;
+Shader* CamWnd::m_state_select2 = 0;
+
+CamWnd* NewCamWnd(){
+ return new CamWnd;
+}
+void DeleteCamWnd( CamWnd* camwnd ){
+ delete camwnd;
+}
+
+void CamWnd_constructStatic(){
+ CamWnd::captureStates();
+}
+
+void CamWnd_destroyStatic(){
+ CamWnd::releaseStates();
+}
+
+static CamWnd* g_camwnd = 0;
+
+void GlobalCamera_setCamWnd( CamWnd& camwnd ){
+ g_camwnd = &camwnd;
+}
+
+
+ui::GLArea CamWnd_getWidget( CamWnd& camwnd ){
+ return camwnd.m_gl_widget;
+}
+
+ui::Window CamWnd_getParent( CamWnd& camwnd ){
+ return camwnd.m_parent;
+}
+
+ToggleShown g_camera_shown( true );
+
+void CamWnd_setParent( CamWnd& camwnd, ui::Window parent ){
+ camwnd.m_parent = parent;
+ g_camera_shown.connect( camwnd.m_parent );
+}
+
+void CamWnd_Update( CamWnd& camwnd ){
+ camwnd.queue_draw();
+}
+
+
+
+camwindow_globals_t g_camwindow_globals;
+
+const Vector3& Camera_getOrigin( CamWnd& camwnd ){
+ return Camera_getOrigin( camwnd.getCamera() );
+}
+
+void Camera_setOrigin( CamWnd& camwnd, const Vector3& origin ){
+ Camera_setOrigin( camwnd.getCamera(), origin );
+}
+
+const Vector3& Camera_getAngles( CamWnd& camwnd ){
+ return Camera_getAngles( camwnd.getCamera() );
+}
+
+void Camera_setAngles( CamWnd& camwnd, const Vector3& angles ){
+ Camera_setAngles( camwnd.getCamera(), angles );
+}
+
+
+// =============================================================================
+// CamWnd class
+
+gboolean enable_freelook_button_press( ui::Widget widget, GdkEventButton* event, CamWnd* camwnd ){
+ if ( event->type == GDK_BUTTON_PRESS && event->button == 3 ) {
+ camwnd->EnableFreeMove();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+gboolean disable_freelook_button_press( ui::Widget widget, GdkEventButton* event, CamWnd* camwnd ){
+ if ( event->type == GDK_BUTTON_PRESS && event->button == 3 ) {
+ camwnd->DisableFreeMove();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#if 0
+gboolean mousecontrol_button_press( ui::Widget widget, GdkEventButton* event, CamWnd* camwnd ){
+ if ( event->type == GDK_BUTTON_PRESS && event->button == 3 ) {
+ Cam_MouseControl( camwnd->getCamera(), event->x, widget->allocation.height - 1 - event->y );
+ }
+ return FALSE;
+}
+#endif
+
+void camwnd_update_xor_rectangle( CamWnd& self, rect_t area ){
+ if ( self.m_gl_widget.visible() ) {
+ self.m_XORRectangle.set( rectangle_from_area( area.min, area.max, self.getCamera().width, self.getCamera().height ) );
+ }
+}
+
+
+gboolean selection_button_press( ui::Widget widget, GdkEventButton* event, WindowObserver* observer ){
+ if ( event->type == GDK_BUTTON_PRESS ) {
+ observer->onMouseDown( WindowVector_forDouble( event->x, event->y ), button_for_button( event->button ), modifiers_for_state( event->state ) );
+ }
+ return FALSE;
+}
+
+gboolean selection_button_release( ui::Widget widget, GdkEventButton* event, WindowObserver* observer ){
+ if ( event->type == GDK_BUTTON_RELEASE ) {
+ observer->onMouseUp( WindowVector_forDouble( event->x, event->y ), button_for_button( event->button ), modifiers_for_state( event->state ) );
+ }
+ return FALSE;
+}
+
+void selection_motion( gdouble x, gdouble y, guint state, void* data ){
+ //globalOutputStream() << "motion... ";
+ reinterpret_cast<WindowObserver*>( data )->onMouseMotion( WindowVector_forDouble( x, y ), modifiers_for_state( state ) );
+}
+
+inline WindowVector windowvector_for_widget_centre( ui::Widget widget ){
+ auto allocation = widget.dimensions();
+ return WindowVector( static_cast<float>( allocation.width / 2 ), static_cast<float>(allocation.height / 2 ) );
+}
+
+gboolean selection_button_press_freemove( ui::Widget widget, GdkEventButton* event, WindowObserver* observer ){
+ if ( event->type == GDK_BUTTON_PRESS ) {
+ observer->onMouseDown( windowvector_for_widget_centre( widget ), button_for_button( event->button ), modifiers_for_state( event->state ) );
+ }
+ return FALSE;
+}