]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/camwindow.cpp
fix unzip code
[xonotic/netradiant.git] / radiant / camwindow.cpp
1 /*
2    Copyright (C) 1999-2007 id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 //
23 // Camera Window
24 //
25 // Leonardo Zide (leo@lokigames.com)
26 //
27
28 #include "stdafx.h"
29 #include <gtk/gtk.h>
30 #include <GL/gl.h>
31
32 extern void DrawPathLines();
33 extern void Select_ShiftTexture( int x, int y );
34 extern void Select_RotateTexture( int amt );
35 extern void DrawAlternatePoint( vec3_t v, float scale );
36 //extern void Select_ScaleTexture(int x, int y);
37
38 extern int g_nPatchClickedView;
39
40 brush_t* g_pSplitList = NULL;
41
42 // =============================================================================
43 // CamWnd class
44
45 CamWnd::CamWnd ()
46         : GLWindow( TRUE ), m_XORRectangle( m_pWidget ){
47         m_nNumTransBrushes = 0;
48         memset( &m_Camera, 0, sizeof( camera_t ) );
49         m_pSide_select = NULL;
50         m_bClipMode = false;
51         m_bFreeMove = false;
52         Cam_Init();
53 }
54
55 CamWnd::~CamWnd (){
56 }
57
58 void CamWnd::OnCreate(){
59         if ( !MakeCurrent() ) {
60                 Error( "glMakeCurrent failed" );
61         }
62
63         // report OpenGL information
64         Sys_Printf( "GL_VENDOR: %s\n", qglGetString( GL_VENDOR ) );
65         Sys_Printf( "GL_RENDERER: %s\n", qglGetString( GL_RENDERER ) );
66         Sys_Printf( "GL_VERSION: %s\n", qglGetString( GL_VERSION ) );
67         Sys_Printf( "GL_EXTENSIONS: %s\n", qglGetString( GL_EXTENSIONS ) );
68
69         // Set off texture compression supported
70         g_qeglobals.bTextureCompressionSupported = 0;
71
72         // finalize OpenGL init
73         // NOTE
74         // why is this here? well .. the Gtk objects get constructed when you enter gtk_main
75         // and I wanted to have the extensions information in the editor startup console (avoid looking that up in the early console)
76         // RIANT
77         // I Split this up so as to add support for extension and user-friendly
78         // compression format selection.
79         // ADD new globals for your new format so as to minimise
80         // calls to Sys_QGL_ExtensionSupported
81         // NOTE TTimo: I don't really like this approach with globals. Frequent calls to Sys_QGL_ExtensionSupported don't sound like
82         //   a problem to me. If there is some caching to be done, then I think it should be inside Sys_QGL_ExtensionSupported
83         ///////////////////////////////////////////
84         // Check for default OpenGL
85         if ( Sys_QGL_ExtensionSupported( "GL_ARB_texture_compression" ) ) {
86                 g_qeglobals.bTextureCompressionSupported = 1;
87                 g_qeglobals.m_bOpenGLCompressionSupported = 1;
88         }
89
90         // INSERT PROPRIETARY EXTENSIONS HERE
91         // Check for S3 extensions
92         // create a bool global for extension supported
93         if ( Sys_QGL_ExtensionSupported( "GL_EXT_texture_compression_s3tc" ) ) {
94                 g_qeglobals.bTextureCompressionSupported = 1;
95                 g_qeglobals.m_bS3CompressionSupported = 1;
96         }
97
98         g_qeglobals.m_bOpenGLReady = true;
99
100         g_PrefsDlg.UpdateTextureCompression();
101
102 #ifdef ATIHACK_812
103         g_PrefsDlg.UpdateATIHack();
104 #endif
105
106         g_qeglobals_gui.d_camera = m_pWidget;
107 }
108
109 void CamWnd::Cam_Init(){
110         m_Camera.timing = false;
111         m_Camera.origin[0] = 0.f;
112         m_Camera.origin[1] = 20.f;
113         m_Camera.origin[2] = 46.f;
114         m_Camera.color[0] = 0.3f;
115         m_Camera.color[1] = 0.3f;
116         m_Camera.color[2] = 0.3f;
117         m_nCambuttonstate = 0;
118 }
119
120 void CamWnd::OnSize( int cx, int cy ){
121         m_Camera.width = cx;
122         m_Camera.height = cy;
123         gtk_widget_queue_draw( m_pWidget );
124 }
125
126 rectangle_t rectangle_from_area_cam(){
127         const float left = MIN( g_qeglobals.d_vAreaTL[0], g_qeglobals.d_vAreaBR[0] );
128         const float top = MAX( g_qeglobals.d_vAreaTL[1], g_qeglobals.d_vAreaBR[1] );
129         const float right = MAX( g_qeglobals.d_vAreaTL[0], g_qeglobals.d_vAreaBR[0] );
130         const float bottom = MIN( g_qeglobals.d_vAreaTL[1], g_qeglobals.d_vAreaBR[1] );
131         return rectangle_t( left, bottom, right - left, top - bottom );
132 }
133
134 void update_xor_rectangle( XORRectangle& xor_rectangle ){
135         rectangle_t rectangle;
136         if ( ( g_qeglobals.d_select_mode == sel_area ) ) {
137                 rectangle = rectangle_from_area_cam();
138         }
139         xor_rectangle.set( rectangle );
140 }
141
142 void CamWnd::OnMouseMove( guint32 flags, int pointx, int pointy ){
143         int height = m_pWidget->allocation.height;
144         // NOTE RR2DO2 this hasn't got any use anymore really. It is an old qeradiant feature
145         // that can be re-enabled by removing the checks for HasCapture and not shift/ctrl down
146         // but the scaling/rotating (unless done with the steps set in the surface inspector
147         // dialog) is way too sensitive to be of any use
148         if ( HasCapture() && Sys_AltDown() &&
149                  !( ( flags & MK_SHIFT ) || ( flags & MK_CONTROL ) ) ) {
150                 if ( flags & MK_CONTROL ) {
151                         Select_RotateTexture( pointy - m_ptLastCursorY );
152                 }
153                 else
154                 if ( flags & MK_SHIFT ) {
155                         Select_ScaleTexture( pointx - m_ptLastCursorX, m_ptLastCursorY - pointy );
156                 }
157                 else{
158                         Select_ShiftTexture( pointx - m_ptLastCursorX, m_ptLastCursorY - pointy );
159                 }
160         }
161         else
162         {
163                 Cam_MouseMoved( pointx, height - 1 - pointy, flags );
164         }
165         m_ptLastCursorX = pointx;
166         m_ptLastCursorY = pointy;
167
168         update_xor_rectangle( m_XORRectangle );
169 }
170
171 void CamWnd::OnMouseWheel( bool bUp ){
172         if ( bUp ) {
173                 VectorMA( m_Camera.origin, g_PrefsDlg.m_nMoveSpeed, m_Camera.forward, m_Camera.origin );
174         }
175         else{
176                 VectorMA( m_Camera.origin, -g_PrefsDlg.m_nMoveSpeed, m_Camera.forward, m_Camera.origin );
177         }
178
179         int nUpdate = ( g_PrefsDlg.m_bCamXYUpdate ) ? ( W_CAMERA | W_XY ) : ( W_CAMERA );
180         Sys_UpdateWindows( nUpdate );
181         g_pParentWnd->OnTimer();
182 }
183
184 void CamWnd::OnLButtonDown( guint32 nFlags, int pointx, int pointy ){
185         m_ptLastCursorX = pointx;
186         m_ptLastCursorY = pointy;
187         OriginalMouseDown( nFlags, pointx, pointy );
188 }
189
190 void CamWnd::OnLButtonUp( guint32 nFlags, int pointx, int pointy ){
191         OriginalMouseUp( nFlags, pointx, pointy );
192 }
193
194 void CamWnd::OnMButtonDown( guint32 nFlags, int pointx, int pointy ){
195         OriginalMouseDown( nFlags, pointx, pointy );
196 }
197
198 void CamWnd::OnMButtonUp( guint32 nFlags, int pointx, int pointy ){
199         OriginalMouseUp( nFlags, pointx, pointy );
200 }
201
202 void CamWnd::OnRButtonDown( guint32 nFlags, int pointx, int pointy ){
203         OriginalMouseDown( nFlags, pointx, pointy );
204 }
205
206 void CamWnd::OnRButtonUp( guint32 nFlags, int pointx, int pointy ){
207         OriginalMouseUp( nFlags, pointx, pointy );
208 }
209
210 void CamWnd::OriginalMouseUp( guint32 nFlags, int pointx, int pointy ){
211         int height = m_pWidget->allocation.height;
212
213         if ( g_qeglobals.d_select_mode == sel_facets_on || g_qeglobals.d_select_mode == sel_facets_off ) {
214                 g_qeglobals.d_select_mode = sel_brush;
215         }
216
217         Cam_MouseUp( pointx, height - 1 - pointy, nFlags );
218         ReleaseCapture();
219
220         update_xor_rectangle( m_XORRectangle );
221 }
222
223 void CamWnd::OriginalMouseDown( guint32 nFlags, int pointx, int pointy ){
224         int height = m_pWidget->allocation.height;
225
226         SetFocus();
227         SetCapture();
228         Cam_MouseDown( pointx, height - 1 - pointy, nFlags );
229
230         update_xor_rectangle( m_XORRectangle );
231 }
232
233 void CamWnd::Cam_BuildMatrix(){
234         float ya;
235         float matrix[4][4];
236         int i;
237
238         if ( !m_bFreeMove ) {
239                 ya = m_Camera.angles[1] / 180 * Q_PI;
240
241                 // the movement matrix is kept 2d
242                 m_Camera.forward[0] = cos( ya );
243                 m_Camera.forward[1] = sin( ya );
244                 m_Camera.forward[2] = 0;
245                 m_Camera.right[0] = m_Camera.forward[1];
246                 m_Camera.right[1] = -m_Camera.forward[0];
247         }
248         else
249         {
250                 AngleVectors( m_Camera.angles, m_Camera.forward, m_Camera.right, NULL );
251                 m_Camera.forward[2] = -m_Camera.forward[2];
252         }
253
254         memcpy( matrix, m_Camera.projection, sizeof( m4x4_t ) );
255         m4x4_multiply_by_m4x4( &matrix[0][0], &m_Camera.modelview[0][0] );
256
257         //qglGetFloatv (GL_PROJECTION_MATRIX, &matrix[0][0]);
258
259         for ( i = 0 ; i < 3 ; i++ )
260         {
261                 m_Camera.vright[i] = matrix[i][0];
262                 m_Camera.vup[i] = matrix[i][1];
263                 m_Camera.vpn[i] = matrix[i][2];
264         }
265
266         VectorNormalize( m_Camera.vright, m_Camera.vright );
267         VectorNormalize( m_Camera.vup, m_Camera.vup );
268         VectorNormalize( m_Camera.vpn, m_Camera.vpn );
269 }
270
271 void CamWnd::Cam_ChangeFloor( qboolean up ){
272         brush_t   *b;
273         float d, bestd, current;
274         vec3_t start, dir;
275
276         start[0] = m_Camera.origin[0];
277         start[1] = m_Camera.origin[1];
278         start[2] = g_MaxWorldCoord;
279         dir[0] = dir[1] = 0;
280         dir[2] = -1;
281
282         current = g_MaxWorldCoord - ( m_Camera.origin[2] - 48 );
283         if ( up ) {
284                 bestd = 0;
285         }
286         else{
287                 bestd = 2 * g_MaxWorldCoord;
288         }
289
290         for ( b = active_brushes.next ; b != &active_brushes ; b = b->next )
291         {
292                 if ( !Brush_Ray( start, dir, b, &d ) ) {
293                         continue;
294                 }
295                 if ( up && d < current && d > bestd ) {
296                         bestd = d;
297                 }
298                 if ( !up && d > current && d < bestd ) {
299                         bestd = d;
300                 }
301         }
302
303         if ( bestd == 0 || bestd == 2 * g_MaxWorldCoord ) {
304                 return;
305         }
306
307         m_Camera.origin[2] += current - bestd;
308         Sys_UpdateWindows( W_CAMERA | W_Z_OVERLAY );
309 }
310
311 void CamWnd::Cam_PositionDrag(){
312         int x, y;
313
314         Sys_GetCursorPos( &x, &y );
315         if ( x != m_ptCursorX || y != m_ptCursorY ) {
316                 x -= m_ptCursorX;
317                 VectorMA( m_Camera.origin, x, m_Camera.vright, m_Camera.origin );
318                 y -= m_ptCursorY;
319                 m_Camera.origin[2] -= y;
320                 Sys_SetCursorPos( m_ptCursorX, m_ptCursorY );
321                 Sys_UpdateWindows( W_CAMERA | W_XY_OVERLAY );
322         }
323 }
324
325 void CamWnd::Cam_MouseControl( float dtime ){
326         Cam_KeyControl( dtime );
327
328         if ( g_PrefsDlg.m_bCamFreeLook ) {
329                 int dx, dy;
330                 gint x, y;
331
332                 if ( !m_bFreeMove || m_nCambuttonstate == MK_CONTROL ) {
333                         return;
334                 }
335
336                 // Update angles
337                 Sys_GetCursorPos( &m_ptCursorX, &m_ptCursorY );
338
339                 dx = m_ptLastCamCursorX - m_ptCursorX;
340                 dy = m_ptLastCamCursorY - m_ptCursorY;
341
342                 gdk_window_get_origin( m_pWidget->window, &x, &y );
343
344                 m_ptLastCamCursorX = x + ( m_Camera.width / 2 );
345                 m_ptLastCamCursorY = y + ( m_Camera.height / 2 );
346
347                 Sys_SetCursorPos( m_ptLastCamCursorX, m_ptLastCamCursorY );
348
349                 // Don't use pitch
350                 if ( !g_PrefsDlg.m_bCamFreeLookStrafe ) {
351                         if ( g_PrefsDlg.m_bCamInverseMouse ) {
352                                 m_Camera.angles[PITCH] -= dy * dtime * g_PrefsDlg.m_nAngleSpeed;
353                         }
354                         else{
355                                 m_Camera.angles[PITCH] += dy * dtime * g_PrefsDlg.m_nAngleSpeed;
356                         }
357                 }
358                 else {
359                         VectorMA( m_Camera.origin, dy * (float) ( g_PrefsDlg.m_nMoveSpeed / 6.0f ), m_Camera.forward, m_Camera.origin );
360                 }
361
362                 m_Camera.angles[YAW] += dx * dtime * g_PrefsDlg.m_nAngleSpeed;
363
364                 if ( m_Camera.angles[PITCH] > 90 ) {
365                         m_Camera.angles[PITCH] = 90;
366                 }
367                 else if ( m_Camera.angles[PITCH] < -90 ) {
368                         m_Camera.angles[PITCH] = -90;
369                 }
370
371                 if ( m_Camera.angles[YAW] >= 360 ) {
372                         m_Camera.angles[YAW] = 0;
373                 }
374                 else if ( m_Camera.angles[YAW] <= -360 ) {
375                         m_Camera.angles[YAW] = 0;
376                 }
377
378                 if ( dx || dy || m_Camera.movementflags ) {
379                         int nUpdate = ( g_PrefsDlg.m_bCamXYUpdate ) ? ( W_CAMERA | W_XY ) : ( W_CAMERA );
380                         Sys_UpdateWindows( nUpdate );
381                         g_pParentWnd->OnTimer();
382                 }
383         }
384         else
385         {
386                 int xl, xh;
387                 int yl, yh;
388                 float xf, yf;
389
390                 if ( g_PrefsDlg.m_nMouseButtons == 2 ) {
391                         if ( m_nCambuttonstate != ( MK_RBUTTON | MK_SHIFT ) ) {
392                                 return;
393                         }
394                 }
395                 else
396                 {
397                         if ( m_nCambuttonstate != MK_RBUTTON ) {
398                                 return;
399                         }
400                 }
401
402                 xf = (float)( m_ptButtonX - m_Camera.width / 2 ) / ( m_Camera.width / 2 );
403                 yf = (float)( m_ptButtonY - m_Camera.height / 2 ) / ( m_Camera.height / 2 );
404
405                 xl = m_Camera.width / 3;
406                 xh = xl * 2;
407                 yl = m_Camera.height / 3;
408                 yh = yl * 2;
409
410                 xf *= 1.0 - fabs( yf );
411                 if ( xf < 0 ) {
412                         xf += 0.1f;
413                         if ( xf > 0 ) {
414                                 xf = 0;
415                         }
416                 }
417                 else
418                 {
419                         xf -= 0.1f;
420                         if ( xf < 0 ) {
421                                 xf = 0;
422                         }
423                 }
424
425                 VectorMA( m_Camera.origin, yf * dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.forward, m_Camera.origin );
426                 m_Camera.angles[YAW] += xf * -dtime * g_PrefsDlg.m_nAngleSpeed;
427
428                 int nUpdate = ( g_PrefsDlg.m_bCamXYUpdate ) ? ( W_CAMERA | W_XY ) : ( W_CAMERA );
429                 Sys_UpdateWindows( nUpdate );
430                 g_pParentWnd->OnTimer();
431         }
432 }
433
434 void CamWnd::Cam_KeyControl( float dtime ) {
435
436         // Update angles
437         if ( m_Camera.movementflags & MOVE_ROTLEFT ) {
438                 m_Camera.angles[YAW] += 15 * dtime * g_PrefsDlg.m_nAngleSpeed;
439         }
440         if ( m_Camera.movementflags & MOVE_ROTRIGHT ) {
441                 m_Camera.angles[YAW] -= 15 * dtime * g_PrefsDlg.m_nAngleSpeed;
442         }
443
444         // Update position
445         if ( m_Camera.movementflags & MOVE_FORWARD ) {
446                 VectorMA( m_Camera.origin, dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.forward, m_Camera.origin );
447         }
448         if ( m_Camera.movementflags & MOVE_BACK ) {
449                 VectorMA( m_Camera.origin, -dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.forward, m_Camera.origin );
450         }
451         if ( m_Camera.movementflags & MOVE_STRAFELEFT ) {
452                 VectorMA( m_Camera.origin, -dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.right, m_Camera.origin );
453         }
454         if ( m_Camera.movementflags & MOVE_STRAFERIGHT ) {
455                 VectorMA( m_Camera.origin, dtime * g_PrefsDlg.m_nMoveSpeed, m_Camera.right, m_Camera.origin );
456         }
457
458         // Save a screen update (when m_bFreeMove is enabled, mousecontrol does the update)
459         if ( !m_bFreeMove && m_Camera.movementflags ) {
460                 int nUpdate = ( g_PrefsDlg.m_bCamXYUpdate ) ? ( W_CAMERA | W_XY ) : ( W_CAMERA );
461                 Sys_UpdateWindows( nUpdate );
462                 g_pParentWnd->OnTimer();
463         }
464 }
465
466 // NOTE TTimo if there's an OS-level focus out of the application
467 //   then we can release the camera cursor grab
468 static gint camwindow_focusout( GtkWidget* widget, GdkEventKey* event, gpointer data ){
469         g_pParentWnd->GetCamWnd()->ToggleFreeMove();
470         return FALSE;
471 }
472
473 void CamWnd::ToggleFreeMove(){
474         GdkWindow *window;
475         GtkWidget *widget;
476
477         m_bFreeMove = !m_bFreeMove;
478         Camera()->movementflags = 0;
479         m_ptLastCamCursorX = m_ptCursorX;
480         m_ptLastCamCursorY = m_ptCursorY;
481
482         if ( g_pParentWnd->CurrentStyle() == MainFrame::eFloating ) {
483                 widget = g_pParentWnd->GetCamWnd()->m_pParent;
484                 window = widget->window;
485         }
486         else
487         {
488                 widget = g_pParentWnd->m_pWidget;
489                 window = widget->window;
490         }
491
492         if ( m_bFreeMove ) {
493
494                 SetFocus();
495                 SetCapture();
496
497                 {
498                         GdkPixmap *pixmap;
499                         GdkBitmap *mask;
500                         char buffer [( 32 * 32 ) / 8];
501                         memset( buffer, 0, ( 32 * 32 ) / 8 );
502                         GdkColor white = {0, 0xffff, 0xffff, 0xffff};
503                         GdkColor black = {0, 0x0000, 0x0000, 0x0000};
504                         pixmap = gdk_bitmap_create_from_data( NULL, buffer, 32, 32 );
505                         mask   = gdk_bitmap_create_from_data( NULL, buffer, 32, 32 );
506                         GdkCursor *cursor = gdk_cursor_new_from_pixmap( pixmap, mask, &white, &black, 1, 1 );
507
508                         gdk_window_set_cursor( window, cursor );
509                         gdk_cursor_unref( cursor );
510                         gdk_drawable_unref( pixmap );
511                         gdk_drawable_unref( mask );
512                 }
513
514                 // RR2DO2: FIXME why does this only work the 2nd and
515                 // further times the event is called? (floating windows
516                 // mode seems to work fine though...)
517                 m_FocusOutHandler_id = gtk_signal_connect( GTK_OBJECT( widget ), "focus_out_event",
518                                                                                                    GTK_SIGNAL_FUNC( camwindow_focusout ), g_pParentWnd );
519
520                 {
521                         GdkEventMask mask = (GdkEventMask)( GDK_POINTER_MOTION_MASK
522                                                                                                 | GDK_POINTER_MOTION_HINT_MASK
523                                                                                                 | GDK_BUTTON_MOTION_MASK
524                                                                                                 | GDK_BUTTON1_MOTION_MASK
525                                                                                                 | GDK_BUTTON2_MOTION_MASK
526                                                                                                 | GDK_BUTTON3_MOTION_MASK
527                                                                                                 | GDK_BUTTON_PRESS_MASK
528                                                                                                 | GDK_BUTTON_RELEASE_MASK );
529
530                         gdk_pointer_grab( widget->window, TRUE, mask, widget->window, NULL, GDK_CURRENT_TIME );
531                 }
532         }
533         else
534         {
535                 gdk_pointer_ungrab( GDK_CURRENT_TIME );
536
537                 gtk_signal_disconnect( GTK_OBJECT( widget ), m_FocusOutHandler_id );
538
539                 GdkCursor *cursor = gdk_cursor_new( GDK_LEFT_PTR );
540                 gdk_window_set_cursor( window, cursor );
541                 gdk_cursor_unref( cursor );
542
543                 ReleaseCapture();
544         }
545
546         int nUpdate = ( g_PrefsDlg.m_bCamXYUpdate ) ? ( W_CAMERA | W_XY ) : ( W_CAMERA );
547         Sys_UpdateWindows( nUpdate );
548         g_pParentWnd->OnTimer();
549 }
550
551 void CamWnd::Cam_MouseDown( int x, int y, int buttons ){
552         vec3_t dir;
553         float f, r, u;
554         int i;
555
556
557         //
558         // calc ray direction
559         //
560         u = (float)( y - ( m_Camera.height * .5f ) ) / ( m_Camera.width * .5f );
561         r = (float)( x - ( m_Camera.width * .5f ) ) / ( m_Camera.width * .5f );
562         f = 1;
563
564         for ( i = 0 ; i < 3 ; i++ )
565                 dir[i] = m_Camera.vpn[i] * f + m_Camera.vright[i] * r + m_Camera.vup[i] * u;
566         VectorNormalize( dir, dir );
567
568         Sys_GetCursorPos( &m_ptCursorX, &m_ptCursorY );
569
570         m_nCambuttonstate = buttons;
571         m_ptButtonX = x;
572         m_ptButtonY = y;
573
574         // LBUTTON = manipulate selection
575         // shift-LBUTTON = select
576         // middle button = grab texture
577         // ctrl-middle button = set entire brush to texture
578         // ctrl-shift-middle button = set single face to texture
579         int nMouseButton = g_PrefsDlg.m_nMouseButtons == 2 ? MK_RBUTTON : MK_MBUTTON;
580         if ( ( buttons == MK_LBUTTON )
581                  || ( buttons == ( MK_LBUTTON | MK_SHIFT ) )
582                  || ( buttons == ( MK_LBUTTON | MK_CONTROL ) )
583                  || ( buttons == ( MK_LBUTTON | MK_CONTROL | MK_SHIFT ) )
584                  || ( buttons == nMouseButton )
585                  || ( buttons == ( nMouseButton | MK_SHIFT ) )
586                  || ( buttons == ( nMouseButton | MK_CONTROL ) )
587                  || ( buttons == ( nMouseButton | MK_SHIFT | MK_CONTROL ) ) ) {
588                 if ( g_PrefsDlg.m_nMouseButtons == 2 && ( buttons == ( MK_RBUTTON | MK_SHIFT ) ) ) {
589                         if ( g_PrefsDlg.m_bCamFreeLook ) {
590                                 ToggleFreeMove();
591                         }
592                         else{
593                                 Cam_MouseControl( 0.1f );
594                         }
595                 }
596                 else
597                 {
598                         // something global needs to track which window is responsible for stuff
599                         Patch_SetView( W_CAMERA );
600                         Drag_Begin( x, y, buttons, m_Camera.vright, m_Camera.vup, m_Camera.origin, dir, true );
601                 }
602                 return;
603         }
604
605         if ( buttons == MK_RBUTTON ) {
606                 if ( g_PrefsDlg.m_bCamFreeLook ) {
607                         ToggleFreeMove();
608                 }
609                 else{
610                         Cam_MouseControl( 0.1f );
611                 }
612                 return;
613         }
614 }
615
616 void CamWnd::Cam_MouseUp( int x, int y, int buttons ){
617         m_nCambuttonstate = 0;
618         Drag_MouseUp( buttons );
619 }
620
621 void CamWnd::Cam_MouseMoved( int x, int y, int buttons ){
622         m_nCambuttonstate = buttons;
623         if ( !buttons ) {
624                 return;
625         }
626
627         if ( g_PrefsDlg.m_nCamDragMultiSelect ) {
628                 if ( g_qeglobals.d_select_mode == sel_brush_on  || g_qeglobals.d_select_mode == sel_brush_off ) {
629                         bool bDoDragMultiSelect = FALSE;
630
631                         if ( g_PrefsDlg.m_nCamDragMultiSelect == 1 && buttons == ( MK_LBUTTON | MK_SHIFT ) ) {
632                                 bDoDragMultiSelect = TRUE;
633                         }
634                         else if ( g_PrefsDlg.m_nCamDragMultiSelect == 2 && buttons == ( MK_LBUTTON | MK_CONTROL ) && Sys_AltDown() ) {
635                                 bDoDragMultiSelect = TRUE;
636                         }
637
638                         if ( bDoDragMultiSelect ) {
639                                 vec3_t dir;
640                                 float f, r, u;
641                                 int i;
642
643                                 //
644                                 // calc ray direction
645                                 //
646                                 u = (float)( y - ( m_Camera.height * .5f ) ) / ( m_Camera.width * .5f );
647                                 r = (float)( x - ( m_Camera.width * .5f ) ) / ( m_Camera.width * .5f );
648                                 f = 1;
649
650                                 for ( i = 0 ; i < 3 ; i++ )
651                                         dir[i] = m_Camera.vpn[i] * f + m_Camera.vright[i] * r + m_Camera.vup[i] * u;
652                                 VectorNormalize( dir,dir );
653
654                                 switch ( g_qeglobals.d_select_mode )
655                                 {
656                                 case sel_brush_on:
657                                         Select_Ray( m_Camera.origin, dir, ( SF_DRAG_ON | SF_CAMERA ) );
658                                         break;
659
660                                 case sel_brush_off:
661                                         Select_Ray( m_Camera.origin, dir, ( SF_DRAG_OFF | SF_CAMERA ) );
662                                         break;
663
664                                 default:
665                                         break;
666                                 }
667                                 return;
668                         }
669                 }
670                 else if ( g_qeglobals.d_select_mode == sel_facets_on || g_qeglobals.d_select_mode == sel_facets_off ) {
671                         if ( buttons == ( MK_LBUTTON | MK_CONTROL | MK_SHIFT ) ) {
672                                 vec3_t dir;
673                                 float f, r, u;
674                                 int i;
675
676                                 //
677                                 // calc ray direction
678                                 //
679                                 u = (float)( y - ( m_Camera.height * .5f ) ) / ( m_Camera.width * .5f );
680                                 r = (float)( x - ( m_Camera.width * .5f ) ) / ( m_Camera.width * .5f );
681                                 f = 1;
682
683                                 for ( i = 0 ; i < 3 ; i++ )
684                                         dir[i] = m_Camera.vpn[i] * f + m_Camera.vright[i] * r + m_Camera.vup[i] * u;
685                                 VectorNormalize( dir,dir );
686
687                                 switch ( g_qeglobals.d_select_mode )
688                                 {
689                                 case sel_facets_on:
690                                         Select_Ray( m_Camera.origin, dir, ( SF_SINGLEFACE | SF_DRAG_ON | SF_CAMERA ) );
691                                         break;
692
693                                 case sel_facets_off:
694                                         Select_Ray( m_Camera.origin, dir, ( SF_SINGLEFACE | SF_DRAG_OFF | SF_CAMERA ) );
695                                         break;
696
697                                 default:
698                                         break;
699                                 }
700                                 return;
701                         }
702                 }
703         }
704
705         m_ptButtonX = x;
706         m_ptButtonY = y;
707
708         if ( ( m_bFreeMove && ( buttons & MK_CONTROL ) && !( buttons & MK_SHIFT ) ) || ( !m_bFreeMove && ( buttons == ( MK_RBUTTON | MK_CONTROL ) ) ) ) {
709                 Cam_PositionDrag();
710                 Sys_UpdateWindows( W_XY | W_CAMERA | W_Z );
711                 return;
712         }
713
714         Sys_GetCursorPos( &m_ptCursorX, &m_ptCursorY );
715
716         if ( buttons & ( MK_LBUTTON | MK_MBUTTON ) ) {
717                 Drag_MouseMoved( x, y, buttons );
718                 if ( g_qeglobals.d_select_mode != sel_area ) {
719                         Sys_UpdateWindows( W_XY | W_CAMERA | W_Z );
720                 }
721         }
722 }
723
724 void CamWnd::InitCull(){
725         int i;
726
727         VectorSubtract( m_Camera.vpn, m_Camera.vright, m_vCull1 );
728         VectorAdd( m_Camera.vpn, m_Camera.vright, m_vCull2 );
729
730         for ( i = 0 ; i < 3 ; i++ )
731         {
732                 if ( m_vCull1[i] > 0 ) {
733                         m_nCullv1[i] = 3 + i;
734                 }
735                 else{
736                         m_nCullv1[i] = i;
737                 }
738                 if ( m_vCull2[i] > 0 ) {
739                         m_nCullv2[i] = 3 + i;
740                 }
741                 else{
742                         m_nCullv2[i] = i;
743                 }
744         }
745 }
746
747 qboolean CamWnd::CullBrush( brush_t *b ){
748         int i;
749         vec3_t point;
750         float d;
751
752         if ( g_PrefsDlg.m_bCubicClipping ) {
753                 float fLevel = g_PrefsDlg.m_nCubicScale * 64;
754
755                 point[0] = m_Camera.origin[0] - fLevel;
756                 point[1] = m_Camera.origin[1] - fLevel;
757                 point[2] = m_Camera.origin[2] - fLevel;
758
759                 for ( i = 0; i < 3; i++ )
760                         if ( b->mins[i] < point[i] && b->maxs[i] < point[i] ) {
761                                 return true;
762                         }
763
764                 point[0] = m_Camera.origin[0] + fLevel;
765                 point[1] = m_Camera.origin[1] + fLevel;
766                 point[2] = m_Camera.origin[2] + fLevel;
767
768                 for ( i = 0; i < 3; i++ )
769                         if ( b->mins[i] > point[i] && b->maxs[i] > point[i] ) {
770                                 return true;
771                         }
772         }
773
774         for ( i = 0 ; i < 3 ; i++ )
775                 point[i] = b->mins[m_nCullv1[i]] - m_Camera.origin[i];
776
777         d = DotProduct( point, m_vCull1 );
778         if ( d < -1 ) {
779                 return true;
780         }
781
782         for ( i = 0 ; i < 3 ; i++ )
783                 point[i] = b->mins[m_nCullv2[i]] - m_Camera.origin[i];
784
785         d = DotProduct( point, m_vCull2 );
786         if ( d < -1 ) {
787                 return true;
788         }
789
790         return false;
791 }
792
793 // project a 3D point onto the camera space
794 // we use the GL viewing matrixes
795 // this is the implementation of a glu function (I realized that afterwards): gluProject
796 void CamWnd::ProjectCamera( const vec3_t A, vec_t B[2] ){
797
798         vec_t P1[4],P2[4],P3[4];
799         VectorCopy( A,P1 ); P1[3] = 1;
800
801         GLMatMul( m_Camera.modelview, P1, P2 );
802         GLMatMul( m_Camera.projection, P2, P3 );
803
804         // we ASSUME that the view port is 0 0 m_Camera.width m_Camera.height (you can check in Cam_Draw)
805         B[0] = (float)m_Camera.width  * ( P3[0] + 1.0 ) / 2.0;
806         B[1] = (float)m_Camera.height * ( P3[1] + 1.0 ) / 2.0;
807
808 }
809
810 // vec defines a direction in geometric space and P an origin point
811 // the user is interacting from the camera view
812 // (for example with texture adjustment shortcuts)
813 // and intuitively if he hits left / right / up / down
814 //   what happens in geometric space should match the left/right/up/down move in camera space
815 // axis = 0: vec is along left/right
816 // axis = 1: vec is along up/down
817 // sgn = +1: same directions
818 // sgn = -1: opposite directions
819 // Implementation:
820 //   typical use case is giving a face center and a normalized vector
821 //   1) compute start and endpoint, project them in camera view, get the direction
822 //     depending on the situation, we might bump into precision issues with that
823 //   2) possible to compute the projected direction independently?
824 //     this solution would be better but right now I don't see how to do it..
825 void CamWnd::MatchViewAxes( const vec3_t P, const vec3_t vec, int &axis, float &sgn ){
826
827         vec_t A[2],B[2],V[2];
828         ProjectCamera( P,A );
829         vec3_t Q;
830         VectorAdd( P,vec,Q );
831         ProjectCamera( Q,B );
832         // V is the vector projected in camera space
833         V[0] = B[0] - A[0];
834         V[1] = B[1] - A[1];
835         if ( fabs( V[0] ) > fabs( V[1] ) ) {
836                 // best match is against right
837                 axis = 0;
838                 if ( V[0] > 0 ) {
839                         sgn = +1;
840                 }
841                 else{
842                         sgn = -1;
843                 }
844         }
845         else
846         {
847                 // best match is against up
848                 axis = 1;
849                 if ( V[1] > 0 ) {
850                         sgn = +1;
851                 }
852                 else{
853                         sgn = -1;
854                 }
855         }
856 }
857
858 #if 0
859 void CamWnd::DrawLightRadius( brush_t* pBrush ){
860         // if lighting
861         int nRadius = Brush_LightRadius( pBrush );
862         if ( nRadius > 0 ) {
863                 Brush_SetLightColor( pBrush );
864                 qglEnable( GL_BLEND );
865                 qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
866                 qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
867                 qglDisable( GL_TEXTURE_2D );
868
869                 qglEnable( GL_TEXTURE_2D );
870                 qglDisable( GL_BLEND );
871                 qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
872         }
873 }
874 #endif
875
876 extern void DrawPatchMesh( patchMesh_t *pm );
877 extern void DrawPatchControls( patchMesh_t *pm );
878 extern void Brush_DrawFacingAngle( brush_t *b, entity_t *e );
879 extern void Brush_DrawModel( brush_t *b, bool bTextured = false );
880 extern void DrawModelOrigin( brush_t *b );
881 extern void DrawModelBBox( brush_t *b );
882
883 void CamWnd::Cam_DrawBrush( brush_t *b, int mode ){
884         int nGLState = m_Camera.draw_glstate;
885         int nModelMode = g_PrefsDlg.m_nEntityShowState;
886
887         GLfloat material[4], identity[4];
888         VectorSet( identity, 0.8f, 0.8f, 0.8f );
889         IShader *pShader;
890
891         // lights
892         if ( b->owner->eclass->fixedsize && b->owner->eclass->nShowFlags & ECLASS_LIGHT && g_PrefsDlg.m_bNewLightDraw ) {
893                 switch ( mode )
894                 {
895                 case DRAW_SOLID:
896                         VectorCopy( b->owner->color, material );
897                         VectorScale( material, 0.8f, material );
898                         material[3] = 1.0f;
899
900                         qglColor4fv( material );
901
902                         if ( g_PrefsDlg.m_bNewLightDraw ) {
903                                 DrawLight( b->owner, nGLState, ( IsBrushSelected( b ) ) ? g_PrefsDlg.m_nLightRadiuses : 0, 0 );
904                         }
905
906                         break;
907                 }
908         }
909
910         // models
911         else if ( b->owner->eclass->fixedsize && b->owner->model.pRender
912                           && !( !IsBrushSelected( b ) && ( nModelMode & ENTITY_SELECTED_ONLY ) ) ) {
913                 switch ( mode )
914                 {
915                 case DRAW_TEXTURED:
916                         if ( !( nModelMode & ENTITY_WIREFRAME ) && nModelMode != ENTITY_BOX ) {
917                                 VectorCopy( b->owner->eclass->color, material );
918                                 material[3] = identity[3] = 1.0f;
919
920                                 qglEnable( GL_CULL_FACE );
921
922                                 if ( !( nGLState & DRAW_GL_TEXTURE_2D ) ) {
923                                         qglColor4fv( material );
924                                 }
925                                 else{ qglColor4fv( identity ); }
926                                 if ( nGLState & DRAW_GL_LIGHTING ) {
927                                         qglShadeModel( GL_SMOOTH );
928                                 }
929
930                                 b->owner->model.pRender->Draw( nGLState, DRAW_RF_CAM );
931                         }
932                         break;
933                 case DRAW_WIRE:
934                         VectorCopy( b->owner->eclass->color, material );
935                         material[3] = 1.0f;
936                         qglColor4fv( material );
937
938                         // model view mode "wireframe" or "selected wire"
939                         if ( nModelMode & ENTITY_WIREFRAME ) {
940                                 b->owner->model.pRender->Draw( nGLState, DRAW_RF_CAM );
941                         }
942
943                         // model view mode "skinned and boxed"
944                         if ( !( b->owner->eclass->nShowFlags & ECLASS_MISCMODEL ) ) {
945                                 qglColor4fv( material );
946                                 aabb_draw( b->owner->model.pRender->GetAABB(), DRAW_GL_WIRE );
947                         }
948                         else if ( nModelMode & ENTITY_BOXED ) {
949                                 aabb_draw( b->owner->model.pRender->GetAABB(), DRAW_GL_WIRE );
950                         }
951 /*
952       if(!(nModelMode & ENTITY_BOXED) && b->owner->eclass->nShowFlags & ECLASS_MISCMODEL)
953               DrawModelOrigin(b);
954  */
955                 }
956         }
957
958         // patches
959         else if ( b->patchBrush ) {
960                 bool bTrans = ( b->pPatch->pShader->getTrans() < 1.0f );
961                 switch ( mode )
962                 {
963                 case DRAW_TEXTURED:
964                         if ( !g_bPatchWireFrame && ( ( nGLState & DRAW_GL_BLEND && bTrans ) || ( !( nGLState & DRAW_GL_BLEND ) && !bTrans ) ) ) {
965                                 qglDisable( GL_CULL_FACE );
966
967                                 pShader = b->pPatch->pShader;
968                                 VectorCopy( pShader->getTexture()->color, material );
969                                 material[3] = identity[3] = pShader->getTrans();
970
971                                 if ( nGLState & DRAW_GL_TEXTURE_2D ) {
972                                         qglColor4fv( identity );
973                                         qglBindTexture( GL_TEXTURE_2D, pShader->getTexture()->texture_number );
974                                 }
975                                 else{
976                                         qglColor4fv( material );
977                                 }
978                                 if ( nGLState & DRAW_GL_LIGHTING ) {
979                                         qglShadeModel( GL_SMOOTH );
980                                 }
981
982                                 DrawPatchMesh( b->pPatch );
983                         }
984                         break;
985                 case DRAW_WIRE:
986                         if ( g_bPatchWireFrame ) {
987                                 VectorCopy( b->pPatch->pShader->getTexture()->color, material );
988                                 material[3] = 1.0;
989                                 qglColor4fv( material );
990                                 DrawPatchMesh( b->pPatch );
991                         }
992                         if ( b->pPatch->bSelected && ( g_qeglobals.d_select_mode == sel_curvepoint
993                                                                                    || g_qeglobals.d_select_mode == sel_area
994                                                                                    || g_bPatchBendMode ) ) {
995                                 DrawPatchControls( b->pPatch );
996                         }
997                 }
998         }
999
1000         // brushes
1001         else if ( b->owner->eclass->fixedsize ) {
1002                 switch ( mode )
1003                 {
1004                 case DRAW_SOLID:
1005                         VectorCopy( b->owner->eclass->color, material );
1006                         VectorScale( material, 0.8f, material );
1007                         material[3] = 1.0f;
1008                         qglColor4fv( material );
1009
1010                         qglEnable( GL_CULL_FACE );
1011                         qglShadeModel( GL_FLAT );
1012                         Brush_Draw( b );
1013                         break;
1014                 case DRAW_WIRE:
1015                         if ( ( g_qeglobals.d_savedinfo.include & INCLUDE_ANGLES )
1016                                  && ( b->owner->eclass->nShowFlags & ECLASS_ANGLE ) ) {
1017                                 Brush_DrawFacingAngle( b, b->owner );
1018                         }
1019                 }
1020         }
1021
1022         // brushes
1023         else
1024         {
1025                 switch ( mode )
1026                 {
1027                 case DRAW_TEXTURED:
1028                         qglEnable( GL_CULL_FACE );
1029                         qglShadeModel( GL_FLAT );
1030                         Brush_Draw( b );
1031                 }
1032         }
1033 }
1034
1035 void CamWnd::Cam_DrawBrushes( int mode ){
1036         brush_t *b;
1037         brush_t *pList = ( g_bClipMode && g_pSplitList ) ? g_pSplitList : &selected_brushes;
1038
1039         for ( b = active_brushes.next; b != &active_brushes; b = b->next )
1040                 if ( !b->bFiltered && !b->bCamCulled ) {
1041                         Cam_DrawBrush( b, mode );
1042                 }
1043         for ( b = pList->next; b != pList; b = b->next )
1044                 if ( !b->bFiltered && !b->bCamCulled ) {
1045                         Cam_DrawBrush( b, mode );
1046                 }
1047 }
1048
1049 void CamWnd::Cam_DrawStuff(){
1050         GLfloat identity[4];
1051         VectorSet( identity, 0.8f, 0.8f, 0.8f );
1052         brush_t *b;
1053
1054         for ( b = active_brushes.next; b != &active_brushes; b = b->next )
1055                 b->bCamCulled = CullBrush( b );
1056
1057         for ( b = selected_brushes.next; b != &selected_brushes; b = b->next )
1058                 b->bCamCulled = CullBrush( b );
1059
1060         switch ( m_Camera.draw_mode )
1061         {
1062         case cd_wire:
1063                 qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
1064                 qglDisable( GL_TEXTURE_2D );
1065                 qglDisable( GL_TEXTURE_1D );
1066                 qglDisable( GL_BLEND );
1067                 qglEnable( GL_DEPTH_TEST );
1068                 qglEnableClientState( GL_VERTEX_ARRAY );
1069                 qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
1070                 qglShadeModel( GL_FLAT );
1071                 if ( g_PrefsDlg.m_bGLLighting ) {
1072                         qglDisable( GL_LIGHTING );
1073                         qglDisable( GL_COLOR_MATERIAL );
1074                         qglDisableClientState( GL_NORMAL_ARRAY );
1075                 }
1076                 m_Camera.draw_glstate = DRAW_GL_WIRE;
1077                 break;
1078
1079         case cd_solid:
1080                 qglCullFace( GL_FRONT );
1081                 qglEnable( GL_CULL_FACE );
1082                 qglShadeModel( GL_FLAT );
1083                 qglPolygonMode( GL_FRONT, GL_LINE );
1084                 qglPolygonMode( GL_BACK, GL_FILL );
1085                 qglDisable( GL_TEXTURE_2D );
1086                 qglDisable( GL_BLEND );
1087                 qglEnable( GL_DEPTH_TEST );
1088                 qglEnableClientState( GL_VERTEX_ARRAY );
1089                 qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
1090                 qglPolygonOffset( -1.0, 2 );
1091                 if ( g_PrefsDlg.m_bGLLighting ) {
1092                         qglEnable( GL_LIGHTING );
1093                         qglEnable( GL_COLOR_MATERIAL );
1094 //    qglEnable(GL_RESCALE_NORMAL);
1095                         qglEnableClientState( GL_NORMAL_ARRAY );
1096                 }
1097                 m_Camera.draw_glstate = DRAW_GL_SOLID;
1098                 break;
1099
1100         case cd_texture:
1101                 qglCullFace( GL_FRONT );
1102                 qglEnable( GL_CULL_FACE );
1103                 qglShadeModel( GL_FLAT );
1104                 qglPolygonMode( GL_FRONT, GL_LINE );
1105                 qglPolygonMode( GL_BACK, GL_FILL );
1106                 qglEnable( GL_TEXTURE_2D );
1107                 qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
1108                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
1109                 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
1110                 qglDisable( GL_BLEND );
1111                 qglEnable( GL_DEPTH_TEST );
1112                 qglEnableClientState( GL_VERTEX_ARRAY );
1113                 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
1114                 if ( g_PrefsDlg.m_bGLLighting ) {
1115                         qglEnable( GL_LIGHTING );
1116                         qglDisable( GL_COLOR_MATERIAL );
1117                         qglMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, identity );
1118                         qglEnableClientState( GL_NORMAL_ARRAY );
1119 //    qglEnable(GL_RESCALE_NORMAL);
1120                 }
1121                 qglPolygonOffset( -1.0, 2 );
1122                 m_Camera.draw_glstate = DRAW_GL_TEXTURED;
1123                 break;
1124
1125         default: Sys_Printf( "CamWnd::Cam_DrawStuff:invalid render mode\n" );
1126         }
1127
1128         Cam_DrawBrushes( DRAW_TEXTURED );
1129
1130         // setup for solid stuff
1131         switch ( m_Camera.draw_mode )
1132         {
1133         case cd_texture:
1134                 qglDisable( GL_TEXTURE_2D );
1135                 m_Camera.draw_glstate &= ~DRAW_GL_TEXTURE_2D;
1136                 if ( g_PrefsDlg.m_bGLLighting ) {
1137                         qglEnable( GL_COLOR_MATERIAL );
1138                 }
1139                 qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
1140                 break;
1141         case cd_solid:
1142                 break;
1143         case cd_wire:
1144                 break;
1145         default: Sys_Printf( "CamWnd::Cam_DrawStuff:invalid render mode\n" );
1146         }
1147
1148         qglEnable( GL_CULL_FACE );
1149         qglShadeModel( GL_FLAT );
1150         Cam_DrawBrushes( DRAW_SOLID );
1151
1152         // setup for wireframe stuff
1153         switch ( m_Camera.draw_mode )
1154         {
1155         case cd_texture:
1156                 if ( g_PrefsDlg.m_bGLLighting ) {
1157                         qglDisable( GL_LIGHTING );
1158                         qglDisable( GL_COLOR_MATERIAL );
1159                         qglDisableClientState( GL_NORMAL_ARRAY );
1160 //      qglDisable(GL_RESCALE_NORMAL);
1161                 }
1162                 qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
1163                 break;
1164         case cd_solid:
1165                 if ( g_PrefsDlg.m_bGLLighting ) {
1166                         qglDisable( GL_LIGHTING );
1167                         qglDisable( GL_COLOR_MATERIAL );
1168                         qglDisableClientState( GL_NORMAL_ARRAY );
1169 //      qglDisable(GL_RESCALE_NORMAL);
1170                 }
1171                 qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
1172                 break;
1173         case cd_wire:
1174                 break;
1175         default: Sys_Printf( "CamWnd::Cam_DrawStuff:invalid render mode\n" );
1176         }
1177
1178         qglDisable( GL_CULL_FACE );
1179         Cam_DrawBrushes( DRAW_WIRE );
1180
1181         // setup for transparent texture stuff
1182         switch ( m_Camera.draw_mode )
1183         {
1184         case cd_texture:
1185                 qglPolygonMode( GL_FRONT, GL_LINE );
1186                 qglPolygonMode( GL_BACK, GL_FILL );
1187                 if ( g_PrefsDlg.m_bGLLighting ) {
1188                         qglEnable( GL_COLOR_MATERIAL );
1189                         qglEnableClientState( GL_NORMAL_ARRAY );
1190                         qglMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, identity );
1191                 }
1192                 qglEnable( GL_TEXTURE_2D );
1193                 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
1194                 m_Camera.draw_glstate = DRAW_GL_TEXTURED;
1195                 break;
1196         case cd_solid:
1197                 qglPolygonMode( GL_FRONT, GL_LINE );
1198                 qglPolygonMode( GL_BACK, GL_FILL );
1199                 if ( g_PrefsDlg.m_bGLLighting ) {
1200                         qglEnable( GL_LIGHTING );
1201                         qglEnable( GL_COLOR_MATERIAL );
1202                         qglEnableClientState( GL_NORMAL_ARRAY );
1203 //      qglEnable(GL_RESCALE_NORMAL);
1204                 }
1205                 m_Camera.draw_glstate = DRAW_GL_SOLID;
1206                 break;
1207         case cd_wire:
1208                 m_Camera.draw_glstate = DRAW_GL_WIRE;
1209                 break;
1210         default: Sys_Printf( "CamWnd::Cam_DrawStuff:invalid render mode\n" );
1211         }
1212
1213
1214         qglEnable( GL_BLEND );
1215         m_Camera.draw_glstate |= DRAW_GL_BLEND;
1216         qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1217         // FIXME: some .TGA are buggy, have a completely empty alpha channel
1218         // if such brushes are rendered in this loop they would be totally transparent with GL_MODULATE
1219         // so I decided using GL_DECAL instead
1220         // if an empty-alpha-channel or nearly-empty texture is used. It will be blank-transparent.
1221         // this could get better if you can get qglTexEnviv (GL_TEXTURE_ENV, to work .. patches are welcome
1222         // Arnout: empty alpha channels are now always filled with data. Don't set this anymore (would cause problems with qer_alphafunc too)
1223 //  qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
1224
1225         Cam_DrawBrushes( DRAW_TEXTURED );
1226
1227 //  qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1228         qglDisable( GL_BLEND );
1229
1230         // setup for wireframe stuff
1231         switch ( m_Camera.draw_mode )
1232         {
1233         case cd_texture:
1234                 if ( g_PrefsDlg.m_bGLLighting ) {
1235                         qglDisable( GL_COLOR_MATERIAL );
1236                         qglDisable( GL_LIGHTING );
1237 //      qglDisable(GL_RESCALE_NORMAL);
1238                 }
1239                 break;
1240         case cd_solid:
1241                 if ( g_PrefsDlg.m_bGLLighting ) {
1242                         qglDisable( GL_COLOR_MATERIAL );
1243                         qglDisable( GL_LIGHTING );
1244 //      qglDisable(GL_RESCALE_NORMAL);
1245                 }
1246                 break;
1247         case cd_wire:
1248                 break;
1249         default: Sys_Printf( "CamWnd::Cam_DrawStuff:invalid render mode\n" );
1250         }
1251
1252 }
1253
1254 /*
1255    ==============
1256    Cam_Draw
1257    ==============
1258  */
1259
1260 void QueueClear();
1261 void QueueDraw();
1262
1263 void CamWnd::Cam_Draw(){
1264         brush_t   *brush;
1265         face_t    *face;
1266         float screenaspect;
1267         float yfov;
1268         double start = 0.0, end;
1269         int i;
1270
1271         if ( !active_brushes.next ) {
1272                 return; // not valid yet
1273
1274         }
1275         if ( m_Camera.timing ) {
1276                 start = Sys_DoubleTime();
1277         }
1278
1279         //
1280         // clear
1281         //
1282         QE_CheckOpenGLForErrors();
1283
1284         qglViewport( 0, 0, m_Camera.width, m_Camera.height );
1285         qglClearColor( g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][0],
1286                                    g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][1],
1287                                    g_qeglobals.d_savedinfo.colors[COLOR_CAMERABACK][2], 0 );
1288         qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
1289
1290         //
1291         // set up viewpoint
1292         //
1293
1294
1295         qglMatrixMode( GL_PROJECTION );
1296         qglLoadIdentity();
1297
1298         screenaspect = (float)m_Camera.width / m_Camera.height;
1299         yfov = 2 * atan( (float)m_Camera.height / m_Camera.width ) * 180 / Q_PI;
1300         qgluPerspective( yfov,  screenaspect,  8,  32768 );
1301
1302         // we're too lazy to calc projection matrix ourselves!!!
1303         qglGetFloatv( GL_PROJECTION_MATRIX, &m_Camera.projection[0][0] );
1304
1305         vec3_t vec;
1306
1307         m4x4_identity( &m_Camera.modelview[0][0] );
1308         VectorSet( vec, -90, 0, 0 );
1309         m4x4_rotate_by_vec3( &m_Camera.modelview[0][0], vec, eXYZ );
1310         VectorSet( vec, 0, 0, 90 );
1311         m4x4_rotate_by_vec3( &m_Camera.modelview[0][0], vec, eXYZ );
1312         VectorSet( vec, 0, m_Camera.angles[0], 0 );
1313         m4x4_rotate_by_vec3( &m_Camera.modelview[0][0], vec, eXYZ );
1314         VectorSet( vec, 0, 0, -m_Camera.angles[1] );
1315         m4x4_rotate_by_vec3( &m_Camera.modelview[0][0], vec, eXYZ );
1316         VectorSet( vec, -m_Camera.origin[0],  -m_Camera.origin[1],  -m_Camera.origin[2] );
1317         m4x4_translate_by_vec3( &m_Camera.modelview[0][0], vec );
1318
1319         Cam_BuildMatrix();
1320
1321         qglMatrixMode( GL_MODELVIEW );
1322         qglLoadIdentity();
1323
1324         qglMultMatrixf( &m_Camera.modelview[0][0] );
1325
1326         // grab the GL_PROJECTION and GL_MODELVIEW matrixes
1327         //   used in GetRelativeAxes
1328         //qglGetFloatv (GL_PROJECTION_MATRIX, &m_Camera.projection[0][0]);
1329         //qglGetFloatv (GL_MODELVIEW_MATRIX, &m_Camera.modelview[0][0]);
1330
1331 #if 0
1332         // TTimo: this is not used, just for verification (0, 0, m_Camera.width, m_Camera.height)
1333         GLint viewprt[4];
1334         qglGetIntegerv( GL_VIEWPORT, viewprt );
1335 #endif
1336
1337         if ( g_PrefsDlg.m_bGLLighting ) {
1338                 GLfloat inverse_cam_dir[4], ambient[4], diffuse[4]; //, material[4];
1339
1340                 ambient[0] = ambient[1] = ambient[2] = 0.6f;
1341                 ambient[3] = 1.0f;
1342                 diffuse[0] = diffuse[1] = diffuse[2] = 0.4f;
1343                 diffuse[3] = 1.0f;
1344                 //material[0] = material[1] = material[2] = 0.8f;
1345                 //material[3] = 1.0f;
1346
1347                 vec3_t vCam, vRotate;
1348                 VectorSet( vCam, -1, 0, 0 ); //default cam pos
1349                 VectorSet( vRotate, 0, -m_Camera.angles[0], 0 );
1350                 VectorRotate( vCam, vRotate, vCam );
1351                 VectorSet( vRotate, 0, 0, m_Camera.angles[1] );
1352                 VectorRotate( vCam, vRotate, vCam );
1353
1354                 inverse_cam_dir[0] = vCam[0];
1355                 inverse_cam_dir[1] = vCam[1];
1356                 inverse_cam_dir[2] = vCam[2];
1357                 inverse_cam_dir[3] = 0;
1358
1359                 qglColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
1360
1361                 qglLightfv( GL_LIGHT0, GL_POSITION, inverse_cam_dir );
1362
1363                 qglLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
1364                 qglLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
1365
1366                 qglEnable( GL_LIGHT0 );
1367         }
1368
1369         InitCull();
1370
1371         //
1372         // draw stuff
1373         //
1374
1375         Cam_DrawStuff();
1376
1377         qglEnableClientState( GL_VERTEX_ARRAY );
1378         qglDisableClientState( GL_NORMAL_ARRAY );
1379         qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
1380         qglDisable( GL_TEXTURE_2D );
1381         qglDisable( GL_LIGHTING );
1382         qglDisable( GL_COLOR_MATERIAL );
1383
1384         qglEnable( GL_CULL_FACE );
1385
1386         brush_t* pList = ( g_bClipMode && g_pSplitList ) ? g_pSplitList : &selected_brushes;
1387
1388         if ( g_qeglobals.d_savedinfo.iSelectedOutlinesStyle & OUTLINE_BSEL ) {
1389                 qglColor4f( g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES3D][0], g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES3D][1], g_qeglobals.d_savedinfo.colors[COLOR_SELBRUSHES3D][2], 0.3f );
1390                 qglEnable( GL_BLEND );
1391                 qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1392                 qglDepthFunc( GL_LEQUAL );
1393                 for ( brush = pList->next ; brush != pList ; brush = brush->next )
1394                 {
1395                         if ( brush->bCamCulled ) { // draw selected faces of filtered brushes to remind that there is a selection
1396                                 continue;
1397                         }
1398
1399                         if ( brush->patchBrush && ( g_qeglobals.d_select_mode == sel_curvepoint || g_qeglobals.d_select_mode == sel_area ) ) {
1400                                 continue;
1401                         }
1402
1403                         if ( !g_PrefsDlg.m_bPatchBBoxSelect && brush->patchBrush ) {
1404                                 DrawPatchMesh( brush->pPatch );
1405                         }
1406                         else if ( brush->owner->model.pRender && g_PrefsDlg.m_nEntityShowState != ENTITY_BOX ) {
1407                                 brush->owner->model.pRender->Draw( DRAW_GL_FLAT, ( DRAW_RF_SEL_OUTLINE | DRAW_RF_CAM ) );
1408                         }
1409                         else
1410                         {
1411                                 for ( face = brush->brush_faces ; face ; face = face->next )
1412                                         Brush_FaceDraw( face, DRAW_GL_FLAT );
1413                         }
1414                 }
1415
1416
1417                 int nCount = g_ptrSelectedFaces.GetSize();
1418                 if ( nCount > 0 ) {
1419                         for ( int i = 0; i < nCount; i++ )
1420                         {
1421                                 face_t *selFace = reinterpret_cast<face_t*>( g_ptrSelectedFaces.GetAt( i ) );
1422                                 Brush_FaceDraw( selFace, DRAW_GL_FLAT );
1423                         }
1424                 }
1425
1426                 qglDisableClientState( GL_NORMAL_ARRAY );
1427                 qglDepthFunc( GL_LESS );
1428         }
1429
1430         if ( g_qeglobals.d_savedinfo.iSelectedOutlinesStyle & OUTLINE_ZBUF ) {
1431                 // non-zbuffered outline
1432                 qglDisable( GL_BLEND );
1433                 qglDisable( GL_DEPTH_TEST );
1434                 qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
1435                 qglColor3f( 1, 1, 1 );
1436                 for ( brush = pList->next ; brush != pList ; brush = brush->next )
1437                 {
1438                         if ( ( brush->patchBrush && ( g_qeglobals.d_select_mode == sel_curvepoint || g_qeglobals.d_select_mode == sel_area ) ) ) {
1439                                 continue;
1440                         }
1441
1442                         if ( !g_PrefsDlg.m_bPatchBBoxSelect && brush->patchBrush ) {
1443                                 DrawPatchMesh( brush->pPatch );
1444                         }
1445                         else if ( brush->owner->model.pRender && g_PrefsDlg.m_nEntityShowState != ENTITY_BOX ) {
1446                                 brush->owner->model.pRender->Draw( DRAW_GL_WIRE, ( DRAW_RF_SEL_FILL | DRAW_RF_CAM ) );
1447
1448                                 // Hydra : always draw bbox outline!
1449                                 aabb_draw( brush->owner->model.pRender->GetAABB(), DRAW_GL_WIRE );
1450                         }
1451                         else
1452                         {
1453                                 for ( face = brush->brush_faces ; face ; face = face->next )
1454                                         Brush_FaceDraw( face, DRAW_GL_WIRE );
1455                         }
1456                 }
1457         }
1458
1459         // edge / vertex flags
1460         if ( g_qeglobals.d_select_mode == sel_vertex ) {
1461                 // GL_POINTS on Kyro Workaround
1462                 if ( !g_PrefsDlg.m_bGlPtWorkaround ) {
1463                         // brush verts
1464                         qglPointSize( 4 );
1465                         qglColor3f( 0,1,0 );
1466                         qglBegin( GL_POINTS );
1467                         for ( i = 0 ; i < g_qeglobals.d_numpoints ; i++ )
1468                                 qglVertex3fv( g_qeglobals.d_points[i] );
1469                         qglEnd();
1470
1471                         if ( g_qeglobals.d_num_move_points ) {
1472                                 // selected brush verts
1473                                 qglPointSize( 5 );
1474                                 qglColor3f( 0,0,1 );
1475                                 qglBegin( GL_POINTS );
1476                                 for ( i = 0; i < g_qeglobals.d_num_move_points; i++ )
1477                                         qglVertex3fv( g_qeglobals.d_move_points[i] );
1478                                 qglEnd();
1479                         }
1480
1481                         qglPointSize( 1 );
1482                 }
1483                 else
1484                 {
1485                         // brush verts
1486                         qglColor3f( 0,1,0 );
1487                         qglLineWidth( 2.0 );
1488                         qglBegin( GL_LINES );
1489                         for ( i = 0; i < g_qeglobals.d_numpoints; i++ )
1490                                 DrawAlternatePoint( g_qeglobals.d_points[i], 1.5 );
1491                         qglEnd();
1492
1493                         if ( g_qeglobals.d_num_move_points ) {
1494                                 // selected brush verts
1495                                 qglColor3f( 0,0,1 );
1496                                 qglLineWidth( 3.0 );
1497                                 qglBegin( GL_LINES );
1498                                 for ( i = 0; i < g_qeglobals.d_num_move_points; i++ )
1499                                         qglVertex3fv( g_qeglobals.d_move_points[i] );
1500                                 qglEnd();
1501                         }
1502                         qglLineWidth( 1.0 );
1503                 }
1504         }
1505         else if ( g_qeglobals.d_select_mode == sel_edge ) {
1506                 float   *v1, *v2;
1507                 // GL_POINTS on Kyro Workaround
1508                 if ( !g_PrefsDlg.m_bGlPtWorkaround ) {
1509                         qglPointSize( 4 );
1510                         qglColor3f( 0,0,1 );
1511                         qglBegin( GL_POINTS );
1512                         for ( i = 0 ; i < g_qeglobals.d_numedges ; i++ )
1513                         {
1514                                 v1 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p1];
1515                                 v2 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p2];
1516                                 qglVertex3f( ( v1[0] + v2[0] ) * 0.5,( v1[1] + v2[1] ) * 0.5,( v1[2] + v2[2] ) * 0.5 );
1517                         }
1518                         qglEnd();
1519                         qglPointSize( 1 );
1520                 }
1521                 else {
1522                         qglColor3f( 0,0,1 );
1523                         qglLineWidth( 2.0 );
1524                         qglBegin( GL_LINES );
1525                         for ( i = 0; i < g_qeglobals.d_numedges; i++ )
1526                         {
1527                                 v1 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p1];
1528                                 v2 = g_qeglobals.d_points[g_qeglobals.d_edges[i].p2];
1529                                 vec3_t v3;
1530                                 v3[0] = ( v1[0] + v2[0] ) * 0.5;
1531                                 v3[1] = ( v1[1] + v2[1] ) * 0.5;
1532                                 v3[2] = ( v1[2] + v2[2] ) * 0.5;
1533                                 DrawAlternatePoint( v3, 1.5 );
1534                         }
1535                         qglEnd();
1536                         qglLineWidth( 1.0 );
1537                 }
1538         }
1539
1540         //
1541         // draw pointfile
1542         //
1543         qglEnable( GL_DEPTH_TEST );
1544         DrawPathLines();
1545
1546         if ( g_qeglobals.d_pointfile_display_list ) {
1547                 Pointfile_Draw();
1548         }
1549
1550         // call the drawing routine of plugin entities
1551         //++timo FIXME: we might need to hook in other places as well for transparency etc.
1552         //++timo FIXME: also needs a way to get some parameters about the view
1553         //++timo FIXME: maybe provide some culling API on Radiant side?
1554         Draw3DPluginEntities();
1555
1556         // draw the crosshair
1557         if ( m_bFreeMove ) {
1558                 // setup orthographic projection mode
1559                 qglMatrixMode( GL_PROJECTION );
1560                 //qglPushMatrix();
1561                 qglLoadIdentity();
1562                 qglDisable( GL_DEPTH_TEST );
1563                 qglOrtho( 0, (float)m_Camera.width, 0, (float)m_Camera.height, -100, 100 );
1564                 qglScalef( 1, -1, 1 );
1565                 qglTranslatef( 0, -(float)m_Camera.height, 0 );
1566                 qglMatrixMode( GL_MODELVIEW );
1567
1568                 // draw crosshair
1569                 //qglPushMatrix();
1570                 qglLoadIdentity();
1571                 qglColor3f( 1.f, 1.f, 1.f );
1572                 qglBegin( GL_LINES );
1573                 qglVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f + 6 );
1574                 qglVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f + 2 );
1575                 qglVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f - 6 );
1576                 qglVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f - 2 );
1577                 qglVertex2f( (float)m_Camera.width / 2.f + 6, (float)m_Camera.height / 2.f );
1578                 qglVertex2f( (float)m_Camera.width / 2.f + 2, (float)m_Camera.height / 2.f );
1579                 qglVertex2f( (float)m_Camera.width / 2.f - 6, (float)m_Camera.height / 2.f );
1580                 qglVertex2f( (float)m_Camera.width / 2.f - 2, (float)m_Camera.height / 2.f );
1581                 qglEnd();
1582                 //qglPopMatrix();
1583
1584                 // reset perspective projection
1585                 //qglMatrixMode(GL_PROJECTION);
1586                 //qglPopMatrix();
1587                 //qglMatrixMode(GL_MODELVIEW);
1588         }
1589
1590 #if 0
1591         if ( ( g_qeglobals.d_select_mode == sel_area ) && ( g_nPatchClickedView == W_CAMERA ) ) {
1592                 // setup orthographic projection mode
1593                 qglMatrixMode( GL_PROJECTION );
1594                 //qglPushMatrix();
1595                 qglLoadIdentity();
1596                 qglDisable( GL_DEPTH_TEST );
1597                 qglOrtho( 0, (float)m_Camera.width, 0, (float)m_Camera.height, -100, 100 );
1598                 //qglScalef(1, -1, 1);
1599                 //qglTranslatef(0, -(float)m_Camera.height, 0);
1600                 qglMatrixMode( GL_MODELVIEW );
1601
1602                 // area selection hack
1603                 qglLoadIdentity();
1604                 qglDisable( GL_CULL_FACE );
1605                 qglEnable( GL_BLEND );
1606                 qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
1607                 qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1608                 qglColor4f( 0.0, 0.0, 1.0, 0.25 );
1609                 qglRectf( g_qeglobals.d_vAreaTL[0], g_qeglobals.d_vAreaTL[1], g_qeglobals.d_vAreaBR[0], g_qeglobals.d_vAreaBR[1] );
1610                 qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
1611                 qglDisable( GL_BLEND );
1612                 qglEnable( GL_CULL_FACE );
1613         }
1614 #endif
1615
1616         // bind back to the default texture so that we don't have problems
1617         // elsewhere using/modifying texture maps between contexts
1618         qglBindTexture( GL_TEXTURE_2D, 0 );
1619
1620         qglFinish();
1621         QE_CheckOpenGLForErrors();
1622         //      Sys_EndWait();
1623         if ( m_Camera.timing ) {
1624                 end = Sys_DoubleTime();
1625                 Sys_Printf( "Camera: %i ms\n", (int)( 1000 * ( end - start ) ) );
1626         }
1627
1628         for ( brush = active_brushes.next ; brush != &active_brushes ; brush = brush->next )
1629                 brush->bCamCulled = false;
1630
1631         for ( brush = pList->next ; brush != pList ; brush = brush->next )
1632                 brush->bCamCulled = false;
1633 }
1634
1635 void CamWnd::OnExpose(){
1636         if ( !MakeCurrent() ) {
1637                 Sys_Printf( "ERROR: glXMakeCurrent failed..\n " );
1638                 Sys_Printf( "Please restart Radiant if the camera view is not working\n" );
1639         }
1640         else
1641         {
1642                 QE_CheckOpenGLForErrors();
1643                 g_pSplitList = NULL;
1644                 if ( g_bClipMode ) {
1645                         if ( g_Clip1.Set() && g_Clip2.Set() ) {
1646                                 g_pSplitList = ( g_bSwitch ) ?
1647                                                            &g_brBackSplits : &g_brFrontSplits;
1648                         }
1649                 }
1650
1651                 Patch_LODMatchAll(); // spog
1652
1653                 Cam_Draw();
1654                 QE_CheckOpenGLForErrors();
1655
1656                 m_XORRectangle.set( rectangle_t() );
1657                 SwapBuffers();
1658         }
1659 }
1660
1661 void CamWnd::BenchMark(){
1662         if ( !MakeCurrent() ) {
1663                 Error( "glXMakeCurrent failed in Benchmark" );
1664         }
1665
1666         qglDrawBuffer( GL_FRONT );
1667         double dStart = Sys_DoubleTime();
1668         for ( int i = 0 ; i < 100 ; i++ )
1669         {
1670                 m_Camera.angles[YAW] = i * 4;
1671                 Cam_Draw();
1672         }
1673         SwapBuffers();
1674         qglDrawBuffer( GL_BACK );
1675         double dEnd = Sys_DoubleTime();
1676         Sys_Printf( "%5.2f seconds\n", dEnd - dStart );
1677 }