/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // // Camera Window // // Leonardo Zide (leo@lokigames.com) // #include "camwindow.h" #include #include #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( data ), x, y ); } View* m_view; Callback m_update; static camera_draw_mode draw_mode; camera_t( View* view, const Callback& update ) : width( 0 ), height( 0 ), timing( false ), origin( 0, 0, 0 ), angles( 0, 0, 0 ), color( 0, 0, 0 ), projection( g_matrix4_identity ), modelview( g_matrix4_identity ), movementflags( 0 ), m_keycontrol_timer(), 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( near_z * tan( degrees_to_radians( fieldOfView * 0.5 ) ) ); const float half_height = half_width * ( static_cast( height ) / static_cast( 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( cos( ya ) ); camera.forward[1] = static_cast( 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( 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( 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 FreeMoveCameraMoveForwardKeyDownCaller; typedef ReferenceCaller FreeMoveCameraMoveForwardKeyUpCaller; typedef ReferenceCaller FreeMoveCameraMoveBackKeyDownCaller; typedef ReferenceCaller FreeMoveCameraMoveBackKeyUpCaller; typedef ReferenceCaller FreeMoveCameraMoveLeftKeyDownCaller; typedef ReferenceCaller FreeMoveCameraMoveLeftKeyUpCaller; typedef ReferenceCaller FreeMoveCameraMoveRightKeyDownCaller; typedef ReferenceCaller FreeMoveCameraMoveRightKeyUpCaller; typedef ReferenceCaller FreeMoveCameraMoveUpKeyDownCaller; typedef ReferenceCaller FreeMoveCameraMoveUpKeyUpCaller; typedef ReferenceCaller FreeMoveCameraMoveDownKeyDownCaller; typedef ReferenceCaller 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 m_update; public: RadiantCameraView( camera_t& camera, View* view, const Callback& 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( 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 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( 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( allocation.width / 2 ), static_cast(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; } gboolean selection_button_release_freemove( ui::Widget widget, GdkEventButton* event, WindowObserver* observer ){ if ( event->type == GDK_BUTTON_RELEASE ) { observer->onMouseUp( windowvector_for_widget_centre( widget ), button_for_button( event->button ), modifiers_for_state( event->state ) ); } return FALSE; } gboolean selection_motion_freemove( ui::Widget widget, GdkEventMotion *event, WindowObserver* observer ){ observer->onMouseMotion( windowvector_for_widget_centre( widget ), modifiers_for_state( event->state ) ); return FALSE; } gboolean wheelmove_scroll( ui::Widget widget, GdkEventScroll* event, CamWnd* camwnd ){ if ( event->direction == GDK_SCROLL_UP ) { Camera_Freemove_updateAxes( camwnd->getCamera() ); Camera_setOrigin( *camwnd, vector3_added( Camera_getOrigin( *camwnd ), vector3_scaled( camwnd->getCamera().forward, static_cast( g_camwindow_globals_private.m_nMoveSpeed ) ) ) ); } else if ( event->direction == GDK_SCROLL_DOWN ) { Camera_Freemove_updateAxes( camwnd->getCamera() ); Camera_setOrigin( *camwnd, vector3_added( Camera_getOrigin( *camwnd ), vector3_scaled( camwnd->getCamera().forward, -static_cast( g_camwindow_globals_private.m_nMoveSpeed ) ) ) ); } return FALSE; } gboolean camera_size_allocate( ui::Widget widget, GtkAllocation* allocation, CamWnd* camwnd ){ camwnd->getCamera().width = allocation->width; camwnd->getCamera().height = allocation->height; Camera_updateProjection( camwnd->getCamera() ); camwnd->m_window_observer->onSizeChanged( camwnd->getCamera().width, camwnd->getCamera().height ); camwnd->queue_draw(); return FALSE; } gboolean camera_expose( ui::Widget widget, GdkEventExpose* event, gpointer data ){ reinterpret_cast( data )->draw(); return FALSE; } void KeyEvent_connect( const char* name ){ const KeyEvent& keyEvent = GlobalKeyEvents_find( name ); keydown_accelerators_add( keyEvent.m_accelerator, keyEvent.m_keyDown ); keyup_accelerators_add( keyEvent.m_accelerator, keyEvent.m_keyUp ); } void KeyEvent_disconnect( const char* name ){ const KeyEvent& keyEvent = GlobalKeyEvents_find( name ); keydown_accelerators_remove( keyEvent.m_accelerator ); keyup_accelerators_remove( keyEvent.m_accelerator ); } void CamWnd_registerCommands( CamWnd& camwnd ){ GlobalKeyEvents_insert( "CameraForward", Accelerator( GDK_KEY_Up ), ReferenceCaller( camwnd.getCamera() ), ReferenceCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraBack", Accelerator( GDK_KEY_Down ), ReferenceCaller( camwnd.getCamera() ), ReferenceCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraLeft", Accelerator( GDK_KEY_Left ), ReferenceCaller( camwnd.getCamera() ), ReferenceCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraRight", Accelerator( GDK_KEY_Right ), ReferenceCaller( camwnd.getCamera() ), ReferenceCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraStrafeRight", Accelerator( GDK_KEY_period ), ReferenceCaller( camwnd.getCamera() ), ReferenceCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraStrafeLeft", Accelerator( GDK_KEY_comma ), ReferenceCaller( camwnd.getCamera() ), ReferenceCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraUp", Accelerator( 'D' ), ReferenceCaller( camwnd.getCamera() ), ReferenceCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraDown", Accelerator( 'C' ), ReferenceCaller( camwnd.getCamera() ), ReferenceCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraAngleDown", Accelerator( 'A' ), ReferenceCaller( camwnd.getCamera() ), ReferenceCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraAngleUp", Accelerator( 'Z' ), ReferenceCaller( camwnd.getCamera() ), ReferenceCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraFreeMoveForward", Accelerator( GDK_KEY_Up ), FreeMoveCameraMoveForwardKeyDownCaller( camwnd.getCamera() ), FreeMoveCameraMoveForwardKeyUpCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraFreeMoveBack", Accelerator( GDK_KEY_Down ), FreeMoveCameraMoveBackKeyDownCaller( camwnd.getCamera() ), FreeMoveCameraMoveBackKeyUpCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraFreeMoveLeft", Accelerator( GDK_KEY_Left ), FreeMoveCameraMoveLeftKeyDownCaller( camwnd.getCamera() ), FreeMoveCameraMoveLeftKeyUpCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraFreeMoveRight", Accelerator( GDK_KEY_Right ), FreeMoveCameraMoveRightKeyDownCaller( camwnd.getCamera() ), FreeMoveCameraMoveRightKeyUpCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraFreeMoveUp", Accelerator( 'D' ), FreeMoveCameraMoveUpKeyDownCaller( camwnd.getCamera() ), FreeMoveCameraMoveUpKeyUpCaller( camwnd.getCamera() ) ); GlobalKeyEvents_insert( "CameraFreeMoveDown", Accelerator( 'C' ), FreeMoveCameraMoveDownKeyDownCaller( camwnd.getCamera() ), FreeMoveCameraMoveDownKeyUpCaller( camwnd.getCamera() ) ); GlobalCommands_insert( "CameraForward", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_Up ) ); GlobalCommands_insert( "CameraBack", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_Down ) ); GlobalCommands_insert( "CameraLeft", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_Left ) ); GlobalCommands_insert( "CameraRight", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_Right ) ); GlobalCommands_insert( "CameraStrafeRight", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_period ) ); GlobalCommands_insert( "CameraStrafeLeft", ReferenceCaller( camwnd.getCamera() ), Accelerator( GDK_KEY_comma ) ); GlobalCommands_insert( "CameraUp", ReferenceCaller( camwnd.getCamera() ), Accelerator( 'D' ) ); GlobalCommands_insert( "CameraDown", ReferenceCaller( camwnd.getCamera() ), Accelerator( 'C' ) ); GlobalCommands_insert( "CameraAngleUp", ReferenceCaller( camwnd.getCamera() ), Accelerator( 'A' ) ); GlobalCommands_insert( "CameraAngleDown", ReferenceCaller( camwnd.getCamera() ), Accelerator( 'Z' ) ); } void CamWnd_Move_Enable( CamWnd& camwnd ){ KeyEvent_connect( "CameraForward" ); KeyEvent_connect( "CameraBack" ); KeyEvent_connect( "CameraLeft" ); KeyEvent_connect( "CameraRight" ); KeyEvent_connect( "CameraStrafeRight" ); KeyEvent_connect( "CameraStrafeLeft" ); KeyEvent_connect( "CameraUp" ); KeyEvent_connect( "CameraDown" ); KeyEvent_connect( "CameraAngleUp" ); KeyEvent_connect( "CameraAngleDown" ); } void CamWnd_Move_Disable( CamWnd& camwnd ){ KeyEvent_disconnect( "CameraForward" ); KeyEvent_disconnect( "CameraBack" ); KeyEvent_disconnect( "CameraLeft" ); KeyEvent_disconnect( "CameraRight" ); KeyEvent_disconnect( "CameraStrafeRight" ); KeyEvent_disconnect( "CameraStrafeLeft" ); KeyEvent_disconnect( "CameraUp" ); KeyEvent_disconnect( "CameraDown" ); KeyEvent_disconnect( "CameraAngleUp" ); KeyEvent_disconnect( "CameraAngleDown" ); } void CamWnd_Move_Discrete_Enable( CamWnd& camwnd ){ command_connect_accelerator( "CameraForward" ); command_connect_accelerator( "CameraBack" ); command_connect_accelerator( "CameraLeft" ); command_connect_accelerator( "CameraRight" ); command_connect_accelerator( "CameraStrafeRight" ); command_connect_accelerator( "CameraStrafeLeft" ); command_connect_accelerator( "CameraUp" ); command_connect_accelerator( "CameraDown" ); command_connect_accelerator( "CameraAngleUp" ); command_connect_accelerator( "CameraAngleDown" ); } void CamWnd_Move_Discrete_Disable( CamWnd& camwnd ){ command_disconnect_accelerator( "CameraForward" ); command_disconnect_accelerator( "CameraBack" ); command_disconnect_accelerator( "CameraLeft" ); command_disconnect_accelerator( "CameraRight" ); command_disconnect_accelerator( "CameraStrafeRight" ); command_disconnect_accelerator( "CameraStrafeLeft" ); command_disconnect_accelerator( "CameraUp" ); command_disconnect_accelerator( "CameraDown" ); command_disconnect_accelerator( "CameraAngleUp" ); command_disconnect_accelerator( "CameraAngleDown" ); } struct CamWnd_Move_Discrete { static void Export(const Callback &returnz) { returnz(g_camwindow_globals_private.m_bCamDiscrete); } static void Import(bool value) { if (g_camwnd) { Import_(*g_camwnd, value); } else { g_camwindow_globals_private.m_bCamDiscrete = value; } } static void Import_(CamWnd &camwnd, bool value) { if (g_camwindow_globals_private.m_bCamDiscrete) { CamWnd_Move_Discrete_Disable(camwnd); } else { CamWnd_Move_Disable(camwnd); } g_camwindow_globals_private.m_bCamDiscrete = value; if (g_camwindow_globals_private.m_bCamDiscrete) { CamWnd_Move_Discrete_Enable(camwnd); } else { CamWnd_Move_Enable(camwnd); } } }; void CamWnd_Add_Handlers_Move( CamWnd& camwnd ){ camwnd.m_selection_button_press_handler = camwnd.m_gl_widget.connect( "button_press_event", G_CALLBACK( selection_button_press ), camwnd.m_window_observer ); camwnd.m_selection_button_release_handler = camwnd.m_gl_widget.connect( "button_release_event", G_CALLBACK( selection_button_release ), camwnd.m_window_observer ); camwnd.m_selection_motion_handler = camwnd.m_gl_widget.connect( "motion_notify_event", G_CALLBACK( DeferredMotion::gtk_motion ), &camwnd.m_deferred_motion ); camwnd.m_freelook_button_press_handler = camwnd.m_gl_widget.connect( "button_press_event", G_CALLBACK( enable_freelook_button_press ), &camwnd ); if ( g_camwindow_globals_private.m_bCamDiscrete ) { CamWnd_Move_Discrete_Enable( camwnd ); } else { CamWnd_Move_Enable( camwnd ); } } void CamWnd_Remove_Handlers_Move( CamWnd& camwnd ){ g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_selection_button_press_handler ); g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_selection_button_release_handler ); g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_selection_motion_handler ); g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_freelook_button_press_handler ); if ( g_camwindow_globals_private.m_bCamDiscrete ) { CamWnd_Move_Discrete_Disable( camwnd ); } else { CamWnd_Move_Disable( camwnd ); } } void CamWnd_Add_Handlers_FreeMove( CamWnd& camwnd ){ camwnd.m_selection_button_press_handler = camwnd.m_gl_widget.connect( "button_press_event", G_CALLBACK( selection_button_press_freemove ), camwnd.m_window_observer ); camwnd.m_selection_button_release_handler = camwnd.m_gl_widget.connect( "button_release_event", G_CALLBACK( selection_button_release_freemove ), camwnd.m_window_observer ); camwnd.m_selection_motion_handler = camwnd.m_gl_widget.connect( "motion_notify_event", G_CALLBACK( selection_motion_freemove ), camwnd.m_window_observer ); camwnd.m_freelook_button_press_handler = camwnd.m_gl_widget.connect( "button_press_event", G_CALLBACK( disable_freelook_button_press ), &camwnd ); KeyEvent_connect( "CameraFreeMoveForward" ); KeyEvent_connect( "CameraFreeMoveBack" ); KeyEvent_connect( "CameraFreeMoveLeft" ); KeyEvent_connect( "CameraFreeMoveRight" ); KeyEvent_connect( "CameraFreeMoveUp" ); KeyEvent_connect( "CameraFreeMoveDown" ); } void CamWnd_Remove_Handlers_FreeMove( CamWnd& camwnd ){ KeyEvent_disconnect( "CameraFreeMoveForward" ); KeyEvent_disconnect( "CameraFreeMoveBack" ); KeyEvent_disconnect( "CameraFreeMoveLeft" ); KeyEvent_disconnect( "CameraFreeMoveRight" ); KeyEvent_disconnect( "CameraFreeMoveUp" ); KeyEvent_disconnect( "CameraFreeMoveDown" ); g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_selection_button_press_handler ); g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_selection_button_release_handler ); g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_selection_motion_handler ); g_signal_handler_disconnect( G_OBJECT( camwnd.m_gl_widget ), camwnd.m_freelook_button_press_handler ); } CamWnd::CamWnd() : m_view( true ), m_Camera( &m_view, CamWndQueueDraw( *this ) ), m_cameraview( m_Camera, &m_view, ReferenceCaller( *this ) ), m_gl_widget( glwidget_new( TRUE ) ), m_window_observer( NewWindowObserver() ), m_XORRectangle( m_gl_widget ), m_deferredDraw( WidgetQueueDrawCaller( m_gl_widget ) ), m_deferred_motion( selection_motion, m_window_observer ), m_selection_button_press_handler( 0 ), m_selection_button_release_handler( 0 ), m_selection_motion_handler( 0 ), m_freelook_button_press_handler( 0 ), m_drawing( false ){ m_bFreeMove = false; GlobalWindowObservers_add( m_window_observer ); GlobalWindowObservers_connectWidget( m_gl_widget ); m_window_observer->setRectangleDrawCallback( ReferenceCaller( *this ) ); m_window_observer->setView( m_view ); g_object_ref( m_gl_widget._handle ); gtk_widget_set_events( m_gl_widget, GDK_DESTROY | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK ); gtk_widget_set_can_focus( m_gl_widget, true ); m_sizeHandler = m_gl_widget.connect( "size_allocate", G_CALLBACK( camera_size_allocate ), this ); m_exposeHandler = m_gl_widget.on_render( G_CALLBACK( camera_expose ), this ); Map_addValidCallback( g_map, DeferredDrawOnMapValidChangedCaller( m_deferredDraw ) ); CamWnd_registerCommands( *this ); CamWnd_Add_Handlers_Move( *this ); m_gl_widget.connect( "scroll_event", G_CALLBACK( wheelmove_scroll ), this ); AddSceneChangeCallback( ReferenceCaller( *this ) ); PressedButtons_connect( g_pressedButtons, m_gl_widget ); } CamWnd::~CamWnd(){ if ( m_bFreeMove ) { DisableFreeMove(); } CamWnd_Remove_Handlers_Move( *this ); g_signal_handler_disconnect( G_OBJECT( m_gl_widget ), m_sizeHandler ); g_signal_handler_disconnect( G_OBJECT( m_gl_widget ), m_exposeHandler ); m_gl_widget.unref(); m_window_observer->release(); } class FloorHeightWalker : public scene::Graph::Walker { float m_current; float& m_bestUp; float& m_bestDown; public: FloorHeightWalker( float current, float& bestUp, float& bestDown ) : m_current( current ), m_bestUp( bestUp ), m_bestDown( bestDown ){ bestUp = g_MaxWorldCoord; bestDown = -g_MaxWorldCoord; } bool pre( const scene::Path& path, scene::Instance& instance ) const { if ( path.top().get().visible() && Node_isBrush( path.top() ) ) { // this node is a floor const AABB& aabb = instance.worldAABB(); float floorHeight = aabb.origin.z() + aabb.extents.z(); if ( floorHeight > m_current && floorHeight < m_bestUp ) { m_bestUp = floorHeight; } if ( floorHeight < m_current && floorHeight > m_bestDown ) { m_bestDown = floorHeight; } } return true; } }; void CamWnd::Cam_ChangeFloor( bool up ){ float current = m_Camera.origin[2] - 48; float bestUp; float bestDown; GlobalSceneGraph().traverse( FloorHeightWalker( current, bestUp, bestDown ) ); if ( up && bestUp != g_MaxWorldCoord ) { current = bestUp; } if ( !up && bestDown != -g_MaxWorldCoord ) { current = bestDown; } m_Camera.origin[2] = current + 48; Camera_updateModelview( getCamera() ); CamWnd_Update( *this ); CameraMovedNotify(); } #if 0 // button_press Sys_GetCursorPos( &m_PositionDragCursorX, &m_PositionDragCursorY ); // motion if ( ( m_bFreeMove && ( buttons == ( RAD_CONTROL | RAD_SHIFT ) ) ) || ( !m_bFreeMove && ( buttons == ( RAD_RBUTTON | RAD_CONTROL ) ) ) ) { Cam_PositionDrag(); CamWnd_Update( camwnd ); CameraMovedNotify(); return; } void CamWnd::Cam_PositionDrag(){ int x, y; Sys_GetCursorPos( m_gl_widget, &x, &y ); if ( x != m_PositionDragCursorX || y != m_PositionDragCursorY ) { x -= m_PositionDragCursorX; vector3_add( m_Camera.origin, vector3_scaled( m_Camera.vright, x ) ); y -= m_PositionDragCursorY; m_Camera.origin[2] -= y; Camera_updateModelview(); CamWnd_Update( camwnd ); CameraMovedNotify(); Sys_SetCursorPos( m_gl_widget, m_PositionDragCursorX, m_PositionDragCursorY ); } } #endif // NOTE TTimo if there's an OS-level focus out of the application // then we can release the camera cursor grab static gboolean camwindow_freemove_focusout( ui::Widget widget, GdkEventFocus* event, gpointer data ){ reinterpret_cast( data )->DisableFreeMove(); return FALSE; } void CamWnd::EnableFreeMove(){ //globalOutputStream() << "EnableFreeMove\n"; ASSERT_MESSAGE( !m_bFreeMove, "EnableFreeMove: free-move was already enabled" ); m_bFreeMove = true; Camera_clearMovementFlags( getCamera(), MOVE_ALL ); CamWnd_Remove_Handlers_Move( *this ); CamWnd_Add_Handlers_FreeMove( *this ); gtk_window_set_focus( m_parent, m_gl_widget ); m_freemove_handle_focusout = m_gl_widget.connect( "focus_out_event", G_CALLBACK( camwindow_freemove_focusout ), this ); m_freezePointer.freeze_pointer( m_gl_widget, Camera_motionDelta, &m_Camera ); CamWnd_Update( *this ); } void CamWnd::DisableFreeMove(){ //globalOutputStream() << "DisableFreeMove\n"; ASSERT_MESSAGE( m_bFreeMove, "DisableFreeMove: free-move was not enabled" ); m_bFreeMove = false; Camera_clearMovementFlags( getCamera(), MOVE_ALL ); CamWnd_Remove_Handlers_FreeMove( *this ); CamWnd_Add_Handlers_Move( *this ); m_freezePointer.unfreeze_pointer( m_gl_widget ); g_signal_handler_disconnect( G_OBJECT( m_gl_widget ), m_freemove_handle_focusout ); CamWnd_Update( *this ); } #include "renderer.h" class CamRenderer : public Renderer { struct state_type { state_type() : m_highlight( 0 ), m_state( 0 ), m_lights( 0 ){ } unsigned int m_highlight; Shader* m_state; const LightList* m_lights; }; std::vector m_state_stack; RenderStateFlags m_globalstate; Shader* m_state_select0; Shader* m_state_select1; const Vector3& m_viewer; public: CamRenderer( RenderStateFlags globalstate, Shader* select0, Shader* select1, const Vector3& viewer ) : m_globalstate( globalstate ), m_state_select0( select0 ), m_state_select1( select1 ), m_viewer( viewer ){ ASSERT_NOTNULL( select0 ); ASSERT_NOTNULL( select1 ); m_state_stack.push_back( state_type() ); } void SetState( Shader* state, EStyle style ){ ASSERT_NOTNULL( state ); if ( style == eFullMaterials ) { m_state_stack.back().m_state = state; } } EStyle getStyle() const { return eFullMaterials; } void PushState(){ m_state_stack.push_back( m_state_stack.back() ); } void PopState(){ ASSERT_MESSAGE( !m_state_stack.empty(), "popping empty stack" ); m_state_stack.pop_back(); } void Highlight( EHighlightMode mode, bool bEnable = true ){ if ( bEnable ) { m_state_stack.back().m_highlight |= mode; } else { m_state_stack.back().m_highlight &= ~mode; } } void setLights( const LightList& lights ){ m_state_stack.back().m_lights = &lights; } void addRenderable( const OpenGLRenderable& renderable, const Matrix4& world ){ if ( m_state_stack.back().m_highlight & ePrimitive ) { m_state_select0->addRenderable( renderable, world, m_state_stack.back().m_lights ); } if ( m_state_stack.back().m_highlight & eFace ) { m_state_select1->addRenderable( renderable, world, m_state_stack.back().m_lights ); } m_state_stack.back().m_state->addRenderable( renderable, world, m_state_stack.back().m_lights ); } void render( const Matrix4& modelview, const Matrix4& projection ){ GlobalShaderCache().render( m_globalstate, modelview, projection, m_viewer ); } }; /* ============== Cam_Draw ============== */ void ShowStatsToggle(){ g_camwindow_globals_private.m_showStats ^= 1; } void ShowStatsExport( const Callback &importer ){ importer( g_camwindow_globals_private.m_showStats ); } FreeCaller&), ShowStatsExport> g_show_stats_caller; Callback &)> g_show_stats_callback( g_show_stats_caller ); ToggleItem g_show_stats( g_show_stats_callback ); void CamWnd::Cam_Draw(){ glViewport( 0, 0, m_Camera.width, m_Camera.height ); #if 0 GLint viewprt[4]; glGetIntegerv( GL_VIEWPORT, viewprt ); #endif // enable depth buffer writes glDepthMask( GL_TRUE ); glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); Vector3 clearColour( 0, 0, 0 ); if ( m_Camera.draw_mode != cd_lighting ) { clearColour = g_camwindow_globals.color_cameraback; } glClearColor( clearColour[0], clearColour[1], clearColour[2], 0 ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); extern void Renderer_ResetStats(); Renderer_ResetStats(); extern void Cull_ResetStats(); Cull_ResetStats(); glMatrixMode( GL_PROJECTION ); glLoadMatrixf( reinterpret_cast( &m_Camera.projection ) ); glMatrixMode( GL_MODELVIEW ); glLoadMatrixf( reinterpret_cast( &m_Camera.modelview ) ); // one directional light source directly behind the viewer { GLfloat inverse_cam_dir[4], ambient[4], diffuse[4]; //, material[4]; ambient[0] = ambient[1] = ambient[2] = 0.4f; ambient[3] = 1.0f; diffuse[0] = diffuse[1] = diffuse[2] = 0.4f; diffuse[3] = 1.0f; //material[0] = material[1] = material[2] = 0.8f; //material[3] = 1.0f; inverse_cam_dir[0] = m_Camera.vpn[0]; inverse_cam_dir[1] = m_Camera.vpn[1]; inverse_cam_dir[2] = m_Camera.vpn[2]; inverse_cam_dir[3] = 0; glLightfv( GL_LIGHT0, GL_POSITION, inverse_cam_dir ); glLightfv( GL_LIGHT0, GL_AMBIENT, ambient ); glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse ); glEnable( GL_LIGHT0 ); } unsigned int globalstate = RENDER_DEPTHTEST | RENDER_COLOURWRITE | RENDER_DEPTHWRITE | RENDER_ALPHATEST | RENDER_BLEND | RENDER_CULLFACE | RENDER_COLOURARRAY | RENDER_OFFSETLINE | RENDER_POLYGONSMOOTH | RENDER_LINESMOOTH | RENDER_FOG | RENDER_COLOURCHANGE; switch ( m_Camera.draw_mode ) { case cd_wire: break; case cd_solid: globalstate |= RENDER_FILL | RENDER_LIGHTING | RENDER_SMOOTH | RENDER_SCALED; break; case cd_texture: globalstate |= RENDER_FILL | RENDER_LIGHTING | RENDER_TEXTURE | RENDER_SMOOTH | RENDER_SCALED; break; case cd_lighting: globalstate |= RENDER_FILL | RENDER_LIGHTING | RENDER_TEXTURE | RENDER_SMOOTH | RENDER_SCALED | RENDER_BUMP | RENDER_PROGRAM | RENDER_SCREEN; break; default: globalstate = 0; break; } if ( !g_xywindow_globals.m_bNoStipple ) { globalstate |= RENDER_LINESTIPPLE | RENDER_POLYGONSTIPPLE; } { CamRenderer renderer( globalstate, m_state_select2, m_state_select1, m_view.getViewer() ); Scene_Render( renderer, m_view ); renderer.render( m_Camera.modelview, m_Camera.projection ); } // prepare for 2d stuff glColor4f( 1, 1, 1, 1 ); glDisable( GL_BLEND ); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( 0, (float)m_Camera.width, 0, (float)m_Camera.height, -100, 100 ); glScalef( 1, -1, 1 ); glTranslatef( 0, -(float)m_Camera.height, 0 ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); if ( GlobalOpenGL().GL_1_3() ) { glClientActiveTexture( GL_TEXTURE0 ); glActiveTexture( GL_TEXTURE0 ); } glDisableClientState( GL_TEXTURE_COORD_ARRAY ); glDisableClientState( GL_NORMAL_ARRAY ); glDisableClientState( GL_COLOR_ARRAY ); glDisable( GL_TEXTURE_2D ); glDisable( GL_LIGHTING ); glDisable( GL_COLOR_MATERIAL ); glDisable( GL_DEPTH_TEST ); glColor3f( 1.f, 1.f, 1.f ); glLineWidth( 1 ); // draw the crosshair if ( m_bFreeMove ) { glBegin( GL_LINES ); glVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f + 6 ); glVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f + 2 ); glVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f - 6 ); glVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f - 2 ); glVertex2f( (float)m_Camera.width / 2.f + 6, (float)m_Camera.height / 2.f ); glVertex2f( (float)m_Camera.width / 2.f + 2, (float)m_Camera.height / 2.f ); glVertex2f( (float)m_Camera.width / 2.f - 6, (float)m_Camera.height / 2.f ); glVertex2f( (float)m_Camera.width / 2.f - 2, (float)m_Camera.height / 2.f ); glEnd(); } if ( g_camwindow_globals_private.m_showStats ) { glRasterPos3f( 1.0f, static_cast( m_Camera.height ) - GlobalOpenGL().m_font->getPixelDescent(), 0.0f ); extern const char* Renderer_GetStats(); GlobalOpenGL().drawString( Renderer_GetStats() ); glRasterPos3f( 1.0f, static_cast( m_Camera.height ) - GlobalOpenGL().m_font->getPixelDescent() - GlobalOpenGL().m_font->getPixelHeight(), 0.0f ); extern const char* Cull_GetStats(); GlobalOpenGL().drawString( Cull_GetStats() ); } // bind back to the default texture so that we don't have problems // elsewhere using/modifying texture maps between contexts glBindTexture( GL_TEXTURE_2D, 0 ); } void CamWnd::draw(){ m_drawing = true; //globalOutputStream() << "draw...\n"; if ( glwidget_make_current( m_gl_widget ) != FALSE ) { if ( Map_Valid( g_map ) && ScreenUpdates_Enabled() ) { GlobalOpenGL_debugAssertNoErrors(); Cam_Draw(); GlobalOpenGL_debugAssertNoErrors(); //qglFinish(); m_XORRectangle.set( rectangle_t() ); } glwidget_swap_buffers( m_gl_widget ); } m_drawing = false; } void CamWnd::BenchMark(){ double dStart = Sys_DoubleTime(); for ( int i = 0 ; i < 100 ; i++ ) { Vector3 angles; angles[CAMERA_ROLL] = 0; angles[CAMERA_PITCH] = 0; angles[CAMERA_YAW] = i * 360.0f / 100.0f; Camera_setAngles( *this, angles ); } double dEnd = Sys_DoubleTime(); globalOutputStream() << FloatFormat( dEnd - dStart, 5, 2 ) << " seconds\n"; } void fill_view_camera_menu( ui::Menu menu ){ create_check_menu_item_with_mnemonic( menu, "Camera View", "ToggleCamera" ); } void GlobalCamera_ResetAngles(){ CamWnd& camwnd = *g_camwnd; Vector3 angles; angles[CAMERA_ROLL] = angles[CAMERA_PITCH] = 0; angles[CAMERA_YAW] = 22.5f * floorf( ( Camera_getAngles( camwnd )[CAMERA_YAW] + 11 ) / 22.5f ); Camera_setAngles( camwnd, angles ); } void Camera_ChangeFloorUp(){ CamWnd& camwnd = *g_camwnd; camwnd.Cam_ChangeFloor( true ); } void Camera_ChangeFloorDown(){ CamWnd& camwnd = *g_camwnd; camwnd.Cam_ChangeFloor( false ); } void Camera_CubeIn(){ CamWnd& camwnd = *g_camwnd; g_camwindow_globals.m_nCubicScale--; if ( g_camwindow_globals.m_nCubicScale < 1 ) { g_camwindow_globals.m_nCubicScale = 1; } Camera_updateProjection( camwnd.getCamera() ); CamWnd_Update( camwnd ); g_pParentWnd->SetGridStatus(); } void Camera_CubeOut(){ CamWnd& camwnd = *g_camwnd; g_camwindow_globals.m_nCubicScale++; if ( g_camwindow_globals.m_nCubicScale > 23 ) { g_camwindow_globals.m_nCubicScale = 23; } Camera_updateProjection( camwnd.getCamera() ); CamWnd_Update( camwnd ); g_pParentWnd->SetGridStatus(); } bool Camera_GetFarClip(){ return g_camwindow_globals_private.m_bCubicClipping; } ConstReferenceCaller &), PropertyImpl::Export> g_getfarclip_caller( g_camwindow_globals_private.m_bCubicClipping ); ToggleItem g_getfarclip_item( g_getfarclip_caller ); void Camera_SetFarClip( bool value ){ CamWnd& camwnd = *g_camwnd; g_camwindow_globals_private.m_bCubicClipping = value; g_getfarclip_item.update(); Camera_updateProjection( camwnd.getCamera() ); CamWnd_Update( camwnd ); } struct Camera_FarClip { static void Export(const Callback &returnz) { returnz(g_camwindow_globals_private.m_bCubicClipping); } static void Import(bool value) { Camera_SetFarClip(value); } }; void Camera_ToggleFarClip(){ Camera_SetFarClip( !Camera_GetFarClip() ); } void CamWnd_constructToolbar( ui::Toolbar toolbar ){ toolbar_append_toggle_button( toolbar, "Cubic clip the camera view (\\)", "view_cubicclipping.png", "ToggleCubicClip" ); } void CamWnd_registerShortcuts(){ toggle_add_accelerator( "ToggleCubicClip" ); if ( g_pGameDescription->mGameType == "doom3" ) { command_connect_accelerator( "TogglePreview" ); } command_connect_accelerator( "CameraSpeedInc" ); command_connect_accelerator( "CameraSpeedDec" ); } void GlobalCamera_Benchmark(){ CamWnd& camwnd = *g_camwnd; camwnd.BenchMark(); } void GlobalCamera_Update(){ CamWnd& camwnd = *g_camwnd; CamWnd_Update( camwnd ); } camera_draw_mode CamWnd_GetMode(){ return camera_t::draw_mode; } void CamWnd_SetMode( camera_draw_mode mode ){ ShaderCache_setBumpEnabled( mode == cd_lighting ); camera_t::draw_mode = mode; if ( g_camwnd != 0 ) { CamWnd_Update( *g_camwnd ); } } void CamWnd_TogglePreview( void ){ // gametype must be doom3 for this function to work // if the gametype is not doom3 something is wrong with the // global command list or somebody else calls this function. ASSERT_MESSAGE( g_pGameDescription->mGameType == "doom3", "CamWnd_TogglePreview called although mGameType is not doom3 compatible" ); // switch between textured and lighting mode CamWnd_SetMode( ( CamWnd_GetMode() == cd_lighting ) ? cd_texture : cd_lighting ); } CameraModel* g_camera_model = 0; void CamWnd_LookThroughCamera( CamWnd& camwnd ){ if ( g_camera_model != 0 ) { CamWnd_Add_Handlers_Move( camwnd ); g_camera_model->setCameraView( 0, Callback() ); g_camera_model = 0; Camera_updateModelview( camwnd.getCamera() ); Camera_updateProjection( camwnd.getCamera() ); CamWnd_Update( camwnd ); } } inline CameraModel* Instance_getCameraModel( scene::Instance& instance ){ return InstanceTypeCast::cast( instance ); } void CamWnd_LookThroughSelected( CamWnd& camwnd ){ if ( g_camera_model != 0 ) { CamWnd_LookThroughCamera( camwnd ); } if ( GlobalSelectionSystem().countSelected() != 0 ) { scene::Instance& instance = GlobalSelectionSystem().ultimateSelected(); CameraModel* cameraModel = Instance_getCameraModel( instance ); if ( cameraModel != 0 ) { CamWnd_Remove_Handlers_Move( camwnd ); g_camera_model = cameraModel; g_camera_model->setCameraView( &camwnd.getCameraView(), ReferenceCaller( camwnd ) ); } } } void GlobalCamera_LookThroughSelected(){ CamWnd_LookThroughSelected( *g_camwnd ); } void GlobalCamera_LookThroughCamera(){ CamWnd_LookThroughCamera( *g_camwnd ); } struct RenderMode { static void Export(const Callback &returnz) { switch (CamWnd_GetMode()) { case cd_wire: returnz(0); break; case cd_solid: returnz(1); break; case cd_texture: returnz(2); break; case cd_lighting: returnz(3); break; } } static void Import(int value) { switch (value) { case 0: CamWnd_SetMode(cd_wire); break; case 1: CamWnd_SetMode(cd_solid); break; case 2: CamWnd_SetMode(cd_texture); break; case 3: CamWnd_SetMode(cd_lighting); break; default: CamWnd_SetMode(cd_texture); } } }; void Camera_constructPreferences( PreferencesPage& page ){ page.appendSlider( "Movement Speed", g_camwindow_globals_private.m_nMoveSpeed, TRUE, 0, 0, 100, MIN_CAM_SPEED, MAX_CAM_SPEED, 1, 10 ); page.appendCheckBox( "", "Link strafe speed to movement speed", g_camwindow_globals_private.m_bCamLinkSpeed ); page.appendSlider( "Rotation Speed", g_camwindow_globals_private.m_nAngleSpeed, TRUE, 0, 0, 3, 1, 180, 1, 10 ); page.appendCheckBox( "", "Invert mouse vertical axis", g_camwindow_globals_private.m_bCamInverseMouse ); page.appendCheckBox( "", "Discrete movement", make_property() ); page.appendCheckBox( "", "Enable far-clip plane", make_property() ); if ( g_pGameDescription->mGameType == "doom3" ) { const char* render_mode[] = { "Wireframe", "Flatshade", "Textured", "Lighting" }; page.appendCombo( "Render Mode", STRING_ARRAY_RANGE( render_mode ), make_property() ); } else { const char* render_mode[] = { "Wireframe", "Flatshade", "Textured" }; page.appendCombo( "Render Mode", STRING_ARRAY_RANGE( render_mode ), make_property() ); } const char* strafe_mode[] = { "Both", "Forward", "Up" }; page.appendCombo( "Strafe Mode", g_camwindow_globals_private.m_nStrafeMode, STRING_ARRAY_RANGE( strafe_mode ) ); } void Camera_constructPage( PreferenceGroup& group ){ PreferencesPage page( group.createPage( "Camera", "Camera View Preferences" ) ); Camera_constructPreferences( page ); } void Camera_registerPreferencesPage(){ PreferencesDialog_addSettingsPage( makeCallbackF(Camera_constructPage) ); } #include "preferencesystem.h" #include "stringio.h" #include "dialog.h" void CameraSpeed_increase(){ if ( g_camwindow_globals_private.m_nMoveSpeed <= ( MAX_CAM_SPEED - CAM_SPEED_STEP - 10 ) ) { g_camwindow_globals_private.m_nMoveSpeed += CAM_SPEED_STEP; } else { g_camwindow_globals_private.m_nMoveSpeed = MAX_CAM_SPEED - 10; } } void CameraSpeed_decrease(){ if ( g_camwindow_globals_private.m_nMoveSpeed >= ( MIN_CAM_SPEED + CAM_SPEED_STEP ) ) { g_camwindow_globals_private.m_nMoveSpeed -= CAM_SPEED_STEP; } else { g_camwindow_globals_private.m_nMoveSpeed = MIN_CAM_SPEED; } } /// \brief Initialisation for things that have the same lifespan as this module. void CamWnd_Construct(){ GlobalCommands_insert( "CenterView", makeCallbackF(GlobalCamera_ResetAngles), Accelerator( GDK_KEY_End ) ); GlobalToggles_insert( "ToggleCubicClip", makeCallbackF(Camera_ToggleFarClip), ToggleItem::AddCallbackCaller( g_getfarclip_item ), Accelerator( '\\', (GdkModifierType)GDK_CONTROL_MASK ) ); GlobalCommands_insert( "CubicClipZoomIn", makeCallbackF(Camera_CubeIn), Accelerator( '[', (GdkModifierType)GDK_CONTROL_MASK ) ); GlobalCommands_insert( "CubicClipZoomOut", makeCallbackF(Camera_CubeOut), Accelerator( ']', (GdkModifierType)GDK_CONTROL_MASK ) ); GlobalCommands_insert( "UpFloor", makeCallbackF(Camera_ChangeFloorUp), Accelerator( GDK_KEY_Prior ) ); GlobalCommands_insert( "DownFloor", makeCallbackF(Camera_ChangeFloorDown), Accelerator( GDK_KEY_Next ) ); GlobalToggles_insert( "ToggleCamera", ToggleShown::ToggleCaller( g_camera_shown ), ToggleItem::AddCallbackCaller( g_camera_shown.m_item ), Accelerator( 'C', (GdkModifierType)( GDK_SHIFT_MASK | GDK_CONTROL_MASK ) ) ); GlobalCommands_insert( "LookThroughSelected", makeCallbackF(GlobalCamera_LookThroughSelected) ); GlobalCommands_insert( "LookThroughCamera", makeCallbackF(GlobalCamera_LookThroughCamera) ); if ( g_pGameDescription->mGameType == "doom3" ) { GlobalCommands_insert( "TogglePreview", makeCallbackF(CamWnd_TogglePreview), Accelerator( GDK_KEY_F3 ) ); } GlobalCommands_insert( "CameraSpeedInc", makeCallbackF(CameraSpeed_increase), Accelerator( GDK_KEY_KP_Add, (GdkModifierType)GDK_SHIFT_MASK ) ); GlobalCommands_insert( "CameraSpeedDec", makeCallbackF(CameraSpeed_decrease), Accelerator( GDK_KEY_KP_Subtract, (GdkModifierType)GDK_SHIFT_MASK ) ); GlobalShortcuts_insert( "CameraForward", Accelerator( GDK_KEY_Up ) ); GlobalShortcuts_insert( "CameraBack", Accelerator( GDK_KEY_Down ) ); GlobalShortcuts_insert( "CameraLeft", Accelerator( GDK_KEY_Left ) ); GlobalShortcuts_insert( "CameraRight", Accelerator( GDK_KEY_Right ) ); GlobalShortcuts_insert( "CameraStrafeRight", Accelerator( GDK_KEY_period ) ); GlobalShortcuts_insert( "CameraStrafeLeft", Accelerator( GDK_KEY_comma ) ); GlobalShortcuts_insert( "CameraUp", Accelerator( 'D' ) ); GlobalShortcuts_insert( "CameraDown", Accelerator( 'C' ) ); GlobalShortcuts_insert( "CameraAngleUp", Accelerator( 'A' ) ); GlobalShortcuts_insert( "CameraAngleDown", Accelerator( 'Z' ) ); GlobalShortcuts_insert( "CameraFreeMoveForward", Accelerator( GDK_KEY_Up ) ); GlobalShortcuts_insert( "CameraFreeMoveBack", Accelerator( GDK_KEY_Down ) ); GlobalShortcuts_insert( "CameraFreeMoveLeft", Accelerator( GDK_KEY_Left ) ); GlobalShortcuts_insert( "CameraFreeMoveRight", Accelerator( GDK_KEY_Right ) ); GlobalToggles_insert( "ShowStats", makeCallbackF(ShowStatsToggle), ToggleItem::AddCallbackCaller( g_show_stats ) ); GlobalPreferenceSystem().registerPreference( "ShowStats", make_property_string( g_camwindow_globals_private.m_showStats ) ); GlobalPreferenceSystem().registerPreference( "MoveSpeed", make_property_string( g_camwindow_globals_private.m_nMoveSpeed ) ); GlobalPreferenceSystem().registerPreference( "CamLinkSpeed", make_property_string( g_camwindow_globals_private.m_bCamLinkSpeed ) ); GlobalPreferenceSystem().registerPreference( "AngleSpeed", make_property_string( g_camwindow_globals_private.m_nAngleSpeed ) ); GlobalPreferenceSystem().registerPreference( "CamInverseMouse", make_property_string( g_camwindow_globals_private.m_bCamInverseMouse ) ); GlobalPreferenceSystem().registerPreference( "CamDiscrete", make_property_string()); GlobalPreferenceSystem().registerPreference( "CubicClipping", make_property_string( g_camwindow_globals_private.m_bCubicClipping ) ); GlobalPreferenceSystem().registerPreference( "CubicScale", make_property_string( g_camwindow_globals.m_nCubicScale ) ); GlobalPreferenceSystem().registerPreference( "SI_Colors4", make_property_string( g_camwindow_globals.color_cameraback ) ); GlobalPreferenceSystem().registerPreference( "SI_Colors12", make_property_string( g_camwindow_globals.color_selbrushes3d ) ); GlobalPreferenceSystem().registerPreference( "CameraRenderMode", make_property_string() ); GlobalPreferenceSystem().registerPreference( "StrafeMode", make_property_string( g_camwindow_globals_private.m_nStrafeMode ) ); CamWnd_constructStatic(); Camera_registerPreferencesPage(); } void CamWnd_Destroy(){ CamWnd_destroyStatic(); }