reinit OpenGL state on redraw properly
[divverent/netradiant.git] / radiant / camwindow.cpp
1 /*
2 Copyright (C) 1999-2006 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 "camwindow.h"
29
30 #include "debugging/debugging.h"
31
32 #include "iscenegraph.h"
33 #include "irender.h"
34 #include "igl.h"
35 #include "icamera.h"
36 #include "cullable.h"
37 #include "renderable.h"
38 #include "preferencesystem.h"
39
40 #include "signal/signal.h"
41 #include "container/array.h"
42 #include "scenelib.h"
43 #include "render.h"
44 #include "cmdlib.h"
45 #include "math/frustum.h"
46
47 #include "gtkutil/widget.h"
48 #include "gtkutil/button.h"
49 #include "gtkutil/toolbar.h"
50 #include "gtkutil/glwidget.h"
51 #include "gtkutil/xorrectangle.h"
52 #include "gtkmisc.h"
53 #include "selection.h"
54 #include "mainframe.h"
55 #include "preferences.h"
56 #include "commands.h"
57 #include "xywindow.h"
58 #include "windowobservers.h"
59 #include "renderstate.h"
60
61 #include "timer.h"
62
63 Signal0 g_cameraMoved_callbacks;
64
65 void AddCameraMovedCallback(const SignalHandler& handler)
66 {
67   g_cameraMoved_callbacks.connectLast(handler);
68 }
69
70 void CameraMovedNotify()
71 {
72   g_cameraMoved_callbacks();
73 }
74
75
76 struct camwindow_globals_private_t
77 {
78   int m_nMoveSpeed;
79   bool m_bCamLinkSpeed;
80   int m_nAngleSpeed;
81   bool m_bCamInverseMouse;
82   bool m_bCamDiscrete;
83   bool m_bCubicClipping;
84   bool m_showStats;
85   int m_nStrafeMode;
86
87   camwindow_globals_private_t() :
88     m_nMoveSpeed(100),
89     m_bCamLinkSpeed(true),
90     m_nAngleSpeed(3),
91     m_bCamInverseMouse(false),
92     m_bCamDiscrete(true),
93     m_bCubicClipping(true),
94     m_showStats(true),
95         m_nStrafeMode(0)
96   {
97   }
98
99 };
100
101 camwindow_globals_private_t g_camwindow_globals_private;
102
103
104 const Matrix4 g_opengl2radiant(
105   0, 0,-1, 0,
106  -1, 0, 0, 0,
107   0, 1, 0, 0,
108   0, 0, 0, 1
109 );
110
111 const Matrix4 g_radiant2opengl(
112   0,-1, 0, 0,
113   0, 0, 1, 0,
114  -1, 0, 0, 0,
115   0, 0, 0, 1
116 );
117
118 struct camera_t;
119 void Camera_mouseMove(camera_t& camera, int x, int y);
120
121 enum camera_draw_mode
122 {
123   cd_wire,
124   cd_solid,
125   cd_texture,
126   cd_lighting
127 };
128
129 struct camera_t
130 {
131   int width, height;
132
133   bool timing;
134
135   Vector3 origin;
136   Vector3 angles;
137
138   Vector3 color;   // background
139
140   Vector3  forward, right; // move matrix (TTimo: used to have up but it was not updated)
141   Vector3 vup, vpn, vright; // view matrix (taken from the modelview matrix)
142
143   Matrix4 projection;
144   Matrix4 modelview;
145
146   bool m_strafe; // true when in strafemode toggled by the ctrl-key
147   bool m_strafe_forward; // true when in strafemode by ctrl-key and shift is pressed for forward strafing
148
149   unsigned int movementflags;  // movement flags
150   Timer m_keycontrol_timer;
151   guint m_keymove_handler;
152
153
154   float fieldOfView;
155
156   DeferredMotionDelta m_mouseMove;
157
158   static void motionDelta(int x, int y, void* data)
159   {
160     Camera_mouseMove(*reinterpret_cast<camera_t*>(data), x, y);
161   }
162
163   View* m_view;
164   Callback m_update;
165
166   static camera_draw_mode draw_mode;
167
168   camera_t(View* view, const Callback& update)
169     : width(0),
170     height(0),
171     timing(false),
172     origin(0, 0, 0),
173     angles(0, 0, 0),
174     color(0, 0, 0),
175     movementflags(0),
176     m_keymove_handler(0),
177     fieldOfView(90.0f),
178     m_mouseMove(motionDelta, this),
179     m_view(view),
180     m_update(update)
181   {
182   }
183 };
184
185 camera_draw_mode camera_t::draw_mode = cd_texture;
186
187 inline Matrix4 projection_for_camera(float near_z, float far_z, float fieldOfView, int width, int height)
188 {
189   const float half_width = static_cast<float>(near_z * tan(degrees_to_radians(fieldOfView * 0.5)));
190   const float half_height = half_width * (static_cast<float>(height) / static_cast<float>(width));
191
192   return matrix4_frustum(
193     -half_width,
194     half_width,
195     -half_height,
196     half_height,
197     near_z,
198     far_z
199   );
200 }
201
202 float Camera_getFarClipPlane(camera_t& camera)
203 {
204   return (g_camwindow_globals_private.m_bCubicClipping)? pow(2.0, (g_camwindow_globals.m_nCubicScale + 7) / 2.0) : 32768.0f;
205 }
206
207 void Camera_updateProjection(camera_t& camera)
208 {
209   float farClip = Camera_getFarClipPlane(camera);
210   camera.projection = projection_for_camera(farClip / 4096.0f, farClip, camera.fieldOfView, camera.width, camera.height);
211
212   camera.m_view->Construct(camera.projection, camera.modelview, camera.width, camera.height);
213 }
214
215 void Camera_updateVectors(camera_t& camera)
216 {
217   for (int i=0 ; i<3 ; i++)
218   {
219     camera.vright[i] = camera.modelview[(i<<2)+0];
220     camera.vup[i] = camera.modelview[(i<<2)+1];
221     camera.vpn[i] = camera.modelview[(i<<2)+2];
222   }
223 }
224
225 void Camera_updateModelview(camera_t& camera)
226 {
227   camera.modelview = g_matrix4_identity;
228
229   // roll, pitch, yaw
230   Vector3 radiant_eulerXYZ(0, -camera.angles[CAMERA_PITCH], camera.angles[CAMERA_YAW]);
231
232   matrix4_translate_by_vec3(camera.modelview, camera.origin);
233   matrix4_rotate_by_euler_xyz_degrees(camera.modelview, radiant_eulerXYZ);
234   matrix4_multiply_by_matrix4(camera.modelview, g_radiant2opengl);
235   matrix4_affine_invert(camera.modelview);
236
237   Camera_updateVectors(camera);
238
239   camera.m_view->Construct(camera.projection, camera.modelview, camera.width, camera.height);
240 }
241
242
243 void Camera_Move_updateAxes(camera_t& camera)
244 {
245   double ya = degrees_to_radians(camera.angles[CAMERA_YAW]);
246
247   // the movement matrix is kept 2d
248   camera.forward[0] = static_cast<float>(cos(ya));
249   camera.forward[1] = static_cast<float>(sin(ya));
250   camera.forward[2] = 0;
251   camera.right[0] = camera.forward[1];
252   camera.right[1] = -camera.forward[0];
253 }
254
255 void Camera_Freemove_updateAxes(camera_t& camera)
256 {
257   camera.right = camera.vright;
258   camera.forward = vector3_negated(camera.vpn);
259 }
260
261 const Vector3& Camera_getOrigin(camera_t& camera)
262 {
263   return camera.origin;
264 }
265
266 void Camera_setOrigin(camera_t& camera, const Vector3& origin)
267 {
268   camera.origin = origin;
269   Camera_updateModelview(camera);
270   camera.m_update();
271   CameraMovedNotify();
272 }
273
274 const Vector3& Camera_getAngles(camera_t& camera)
275 {
276   return camera.angles;
277 }
278
279 void Camera_setAngles(camera_t& camera, const Vector3& angles)
280 {
281   camera.angles = angles;
282   Camera_updateModelview(camera);
283   camera.m_update();
284   CameraMovedNotify();
285 }
286
287
288 void Camera_FreeMove(camera_t& camera, int dx, int dy)
289 {
290   // free strafe mode, toggled by the ctrl key with optional shift for forward movement
291   if(camera.m_strafe)
292   {
293     float strafespeed = 0.65f;
294
295     if(g_camwindow_globals_private.m_bCamLinkSpeed)
296     {
297       strafespeed = (float)g_camwindow_globals_private.m_nMoveSpeed / 100;
298     }
299
300           camera.origin -= camera.vright * strafespeed * dx;
301           if(camera.m_strafe_forward)
302             camera.origin += camera.vpn * strafespeed * dy;
303           else
304             camera.origin += camera.vup * strafespeed * dy;
305   }
306   else// free rotation
307   {
308     const float dtime = 0.1f;
309
310     if (g_camwindow_globals_private.m_bCamInverseMouse)
311         camera.angles[CAMERA_PITCH] -= dy * dtime * g_camwindow_globals_private.m_nAngleSpeed;
312     else
313         camera.angles[CAMERA_PITCH] += dy * dtime * g_camwindow_globals_private.m_nAngleSpeed;
314
315     camera.angles[CAMERA_YAW] += dx * dtime * g_camwindow_globals_private.m_nAngleSpeed;
316
317     if (camera.angles[CAMERA_PITCH] > 90)
318         camera.angles[CAMERA_PITCH] = 90;
319     else if (camera.angles[CAMERA_PITCH] < -90)
320         camera.angles[CAMERA_PITCH] = -90;
321
322     if (camera.angles[CAMERA_YAW] >= 360)
323         camera.angles[CAMERA_YAW] -=360;
324     else if (camera.angles[CAMERA_YAW] <= 0)
325         camera.angles[CAMERA_YAW] +=360;
326   }
327
328   Camera_updateModelview(camera);
329   Camera_Freemove_updateAxes(camera);
330 }
331
332 void Cam_MouseControl(camera_t& camera, int x, int y)
333 {
334   int   xl, xh;
335   int yl, yh;
336   float xf, yf;
337
338   xf = (float)(x - camera.width/2) / (camera.width/2);
339   yf = (float)(y - camera.height/2) / (camera.height/2);
340
341   xl = camera.width/3;
342   xh = xl*2;
343   yl = camera.height/3;
344   yh = yl*2;
345
346   xf *= 1.0f - fabsf(yf);
347   if (xf < 0)
348   {
349     xf += 0.1f;
350     if (xf > 0)
351       xf = 0;
352   }
353   else
354   {
355     xf -= 0.1f;
356     if (xf < 0)
357       xf = 0;
358   }
359
360   vector3_add(camera.origin, vector3_scaled(camera.forward, yf * 0.1f* g_camwindow_globals_private.m_nMoveSpeed));
361   camera.angles[CAMERA_YAW] += xf * -0.1f * g_camwindow_globals_private.m_nAngleSpeed;
362
363   Camera_updateModelview(camera);
364 }
365
366 void Camera_mouseMove(camera_t& camera, int x, int y)
367 {
368   //globalOutputStream() << "mousemove... ";
369   Camera_FreeMove(camera, -x, -y);
370   camera.m_update();
371   CameraMovedNotify();
372 }
373
374 const unsigned int MOVE_NONE = 0;
375 const unsigned int MOVE_FORWARD = 1 << 0;
376 const unsigned int MOVE_BACK = 1 << 1;
377 const unsigned int MOVE_ROTRIGHT = 1 << 2;
378 const unsigned int MOVE_ROTLEFT = 1 << 3;
379 const unsigned int MOVE_STRAFERIGHT = 1 << 4;
380 const unsigned int MOVE_STRAFELEFT = 1 << 5;
381 const unsigned int MOVE_UP = 1 << 6;
382 const unsigned int MOVE_DOWN = 1 << 7;
383 const unsigned int MOVE_PITCHUP = 1 << 8;
384 const unsigned int MOVE_PITCHDOWN = 1 << 9;
385 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;
386
387 void Cam_KeyControl(camera_t& camera, float dtime)
388 {
389   // Update angles
390   if (camera.movementflags & MOVE_ROTLEFT)
391     camera.angles[CAMERA_YAW] += 15 * dtime* g_camwindow_globals_private.m_nAngleSpeed;
392   if (camera.movementflags & MOVE_ROTRIGHT)
393     camera.angles[CAMERA_YAW] -= 15 * dtime * g_camwindow_globals_private.m_nAngleSpeed;
394   if (camera.movementflags & MOVE_PITCHUP)
395   {
396     camera.angles[CAMERA_PITCH] += 15 * dtime* g_camwindow_globals_private.m_nAngleSpeed;
397     if(camera.angles[CAMERA_PITCH] > 90)
398       camera.angles[CAMERA_PITCH] = 90;
399   }
400   if (camera.movementflags & MOVE_PITCHDOWN)
401   {
402     camera.angles[CAMERA_PITCH] -= 15 * dtime * g_camwindow_globals_private.m_nAngleSpeed;
403     if(camera.angles[CAMERA_PITCH] < -90)
404       camera.angles[CAMERA_PITCH] = -90;
405   }
406
407   Camera_updateModelview(camera);
408   Camera_Freemove_updateAxes(camera);
409
410   // Update position
411   if (camera.movementflags & MOVE_FORWARD)
412     vector3_add(camera.origin, vector3_scaled(camera.forward, dtime * g_camwindow_globals_private.m_nMoveSpeed));
413   if (camera.movementflags & MOVE_BACK)
414     vector3_add(camera.origin, vector3_scaled(camera.forward, -dtime * g_camwindow_globals_private.m_nMoveSpeed));
415   if (camera.movementflags & MOVE_STRAFELEFT)
416     vector3_add(camera.origin, vector3_scaled(camera.right, -dtime * g_camwindow_globals_private.m_nMoveSpeed));
417   if (camera.movementflags & MOVE_STRAFERIGHT)
418     vector3_add(camera.origin, vector3_scaled(camera.right, dtime * g_camwindow_globals_private.m_nMoveSpeed));
419   if (camera.movementflags & MOVE_UP)
420     vector3_add(camera.origin, vector3_scaled(g_vector3_axis_z, dtime * g_camwindow_globals_private.m_nMoveSpeed));
421   if (camera.movementflags & MOVE_DOWN)
422     vector3_add(camera.origin, vector3_scaled(g_vector3_axis_z, -dtime * g_camwindow_globals_private.m_nMoveSpeed));
423
424   Camera_updateModelview(camera);
425 }
426
427 void Camera_keyMove(camera_t& camera)
428 {
429   camera.m_mouseMove.flush();
430
431   //globalOutputStream() << "keymove... ";
432   float time_seconds = camera.m_keycontrol_timer.elapsed_msec() / static_cast<float>(msec_per_sec);
433   camera.m_keycontrol_timer.start();
434   if(time_seconds > 0.05f)
435   {
436     time_seconds = 0.05f; // 20fps
437   }
438   Cam_KeyControl(camera, time_seconds * 5.0f);
439
440   camera.m_update();
441   CameraMovedNotify();
442 }
443
444 gboolean camera_keymove(gpointer data)
445 {
446   Camera_keyMove(*reinterpret_cast<camera_t*>(data));
447   return TRUE;
448 }
449
450 void Camera_setMovementFlags(camera_t& camera, unsigned int mask)
451 {
452   if((~camera.movementflags & mask) != 0 && camera.movementflags == 0)
453   {
454     camera.m_keymove_handler = g_idle_add(camera_keymove, &camera);
455   }
456   camera.movementflags |= mask;
457 }
458 void Camera_clearMovementFlags(camera_t& camera, unsigned int mask)
459 {
460   if((camera.movementflags & ~mask) == 0 && camera.movementflags != 0)
461   {
462     g_source_remove(camera.m_keymove_handler);
463     camera.m_keymove_handler = 0;
464   }
465   camera.movementflags &= ~mask;
466 }
467
468 void Camera_MoveForward_KeyDown(camera_t& camera)
469 {
470   Camera_setMovementFlags(camera, MOVE_FORWARD);
471 }
472 void Camera_MoveForward_KeyUp(camera_t& camera)
473 {
474   Camera_clearMovementFlags(camera, MOVE_FORWARD);
475 }
476 void Camera_MoveBack_KeyDown(camera_t& camera)
477 {
478   Camera_setMovementFlags(camera, MOVE_BACK);
479 }
480 void Camera_MoveBack_KeyUp(camera_t& camera)
481 {
482   Camera_clearMovementFlags(camera, MOVE_BACK);
483 }
484
485 void Camera_MoveLeft_KeyDown(camera_t& camera)
486 {
487   Camera_setMovementFlags(camera, MOVE_STRAFELEFT);
488 }
489 void Camera_MoveLeft_KeyUp(camera_t& camera)
490 {
491   Camera_clearMovementFlags(camera, MOVE_STRAFELEFT);
492 }
493 void Camera_MoveRight_KeyDown(camera_t& camera)
494 {
495   Camera_setMovementFlags(camera, MOVE_STRAFERIGHT);
496 }
497 void Camera_MoveRight_KeyUp(camera_t& camera)
498 {
499   Camera_clearMovementFlags(camera, MOVE_STRAFERIGHT);
500 }
501
502 void Camera_MoveUp_KeyDown(camera_t& camera)
503 {
504   Camera_setMovementFlags(camera, MOVE_UP);
505 }
506 void Camera_MoveUp_KeyUp(camera_t& camera)
507 {
508   Camera_clearMovementFlags(camera, MOVE_UP);
509 }
510 void Camera_MoveDown_KeyDown(camera_t& camera)
511 {
512   Camera_setMovementFlags(camera, MOVE_DOWN);
513 }
514 void Camera_MoveDown_KeyUp(camera_t& camera)
515 {
516   Camera_clearMovementFlags(camera, MOVE_DOWN);
517 }
518
519 void Camera_RotateLeft_KeyDown(camera_t& camera)
520 {
521   Camera_setMovementFlags(camera, MOVE_ROTLEFT);
522 }
523 void Camera_RotateLeft_KeyUp(camera_t& camera)
524 {
525   Camera_clearMovementFlags(camera, MOVE_ROTLEFT);
526 }
527 void Camera_RotateRight_KeyDown(camera_t& camera)
528 {
529   Camera_setMovementFlags(camera, MOVE_ROTRIGHT);
530 }
531 void Camera_RotateRight_KeyUp(camera_t& camera)
532 {
533   Camera_clearMovementFlags(camera, MOVE_ROTRIGHT);
534 }
535
536 void Camera_PitchUp_KeyDown(camera_t& camera)
537 {
538   Camera_setMovementFlags(camera, MOVE_PITCHUP);
539 }
540 void Camera_PitchUp_KeyUp(camera_t& camera)
541 {
542   Camera_clearMovementFlags(camera, MOVE_PITCHUP);
543 }
544 void Camera_PitchDown_KeyDown(camera_t& camera)
545 {
546   Camera_setMovementFlags(camera, MOVE_PITCHDOWN);
547 }
548 void Camera_PitchDown_KeyUp(camera_t& camera)
549 {
550   Camera_clearMovementFlags(camera, MOVE_PITCHDOWN);
551 }
552
553
554 typedef ReferenceCaller<camera_t, &Camera_MoveForward_KeyDown> FreeMoveCameraMoveForwardKeyDownCaller;
555 typedef ReferenceCaller<camera_t, &Camera_MoveForward_KeyUp> FreeMoveCameraMoveForwardKeyUpCaller;
556 typedef ReferenceCaller<camera_t, &Camera_MoveBack_KeyDown> FreeMoveCameraMoveBackKeyDownCaller;
557 typedef ReferenceCaller<camera_t, &Camera_MoveBack_KeyUp> FreeMoveCameraMoveBackKeyUpCaller;
558 typedef ReferenceCaller<camera_t, &Camera_MoveLeft_KeyDown> FreeMoveCameraMoveLeftKeyDownCaller;
559 typedef ReferenceCaller<camera_t, &Camera_MoveLeft_KeyUp> FreeMoveCameraMoveLeftKeyUpCaller;
560 typedef ReferenceCaller<camera_t, &Camera_MoveRight_KeyDown> FreeMoveCameraMoveRightKeyDownCaller;
561 typedef ReferenceCaller<camera_t, &Camera_MoveRight_KeyUp> FreeMoveCameraMoveRightKeyUpCaller;
562 typedef ReferenceCaller<camera_t, &Camera_MoveUp_KeyDown> FreeMoveCameraMoveUpKeyDownCaller;
563 typedef ReferenceCaller<camera_t, &Camera_MoveUp_KeyUp> FreeMoveCameraMoveUpKeyUpCaller;
564 typedef ReferenceCaller<camera_t, &Camera_MoveDown_KeyDown> FreeMoveCameraMoveDownKeyDownCaller;
565 typedef ReferenceCaller<camera_t, &Camera_MoveDown_KeyUp> FreeMoveCameraMoveDownKeyUpCaller;
566
567
568 #define SPEED_MOVE 32
569 #define SPEED_TURN 22.5
570 #define MIN_CAM_SPEED 10
571 #define MAX_CAM_SPEED 610
572 #define CAM_SPEED_STEP 50
573
574 void Camera_MoveForward_Discrete(camera_t& camera)
575 {
576   Camera_Move_updateAxes(camera);
577   Camera_setOrigin(camera, vector3_added(Camera_getOrigin(camera), vector3_scaled(camera.forward, SPEED_MOVE)));
578 }
579 void Camera_MoveBack_Discrete(camera_t& camera)
580 {
581   Camera_Move_updateAxes(camera);
582   Camera_setOrigin(camera, vector3_added(Camera_getOrigin(camera), vector3_scaled(camera.forward, -SPEED_MOVE)));
583 }
584
585 void Camera_MoveUp_Discrete(camera_t& camera)
586 {
587   Vector3 origin(Camera_getOrigin(camera));
588   origin[2] += SPEED_MOVE;
589   Camera_setOrigin(camera, origin);
590 }
591 void Camera_MoveDown_Discrete(camera_t& camera)
592 {
593   Vector3 origin(Camera_getOrigin(camera));
594   origin[2] -= SPEED_MOVE;
595   Camera_setOrigin(camera, origin);
596 }
597
598 void Camera_MoveLeft_Discrete(camera_t& camera)
599 {
600   Camera_Move_updateAxes(camera);
601   Camera_setOrigin(camera, vector3_added(Camera_getOrigin(camera), vector3_scaled(camera.right, -SPEED_MOVE)));
602 }
603 void Camera_MoveRight_Discrete(camera_t& camera)
604 {
605   Camera_Move_updateAxes(camera);
606   Camera_setOrigin(camera, vector3_added(Camera_getOrigin(camera), vector3_scaled(camera.right, SPEED_MOVE)));
607 }
608
609 void Camera_RotateLeft_Discrete(camera_t& camera)
610 {
611   Vector3 angles(Camera_getAngles(camera));
612   angles[CAMERA_YAW] += SPEED_TURN;
613   Camera_setAngles(camera, angles);
614 }
615 void Camera_RotateRight_Discrete(camera_t& camera)
616 {
617   Vector3 angles(Camera_getAngles(camera));
618   angles[CAMERA_YAW] -= SPEED_TURN;
619   Camera_setAngles(camera, angles);
620 }
621
622 void Camera_PitchUp_Discrete(camera_t& camera)
623 {
624   Vector3 angles(Camera_getAngles(camera));
625   angles[CAMERA_PITCH] += SPEED_TURN;
626   if (angles[CAMERA_PITCH] > 90)
627     angles[CAMERA_PITCH] = 90;
628   Camera_setAngles(camera, angles);
629 }
630 void Camera_PitchDown_Discrete(camera_t& camera)
631 {
632   Vector3 angles(Camera_getAngles(camera));
633   angles[CAMERA_PITCH] -= SPEED_TURN;
634   if (angles[CAMERA_PITCH] < -90)
635     angles[CAMERA_PITCH] = -90;
636   Camera_setAngles(camera, angles);
637 }
638
639
640 class RadiantCameraView : public CameraView
641 {
642   camera_t& m_camera;
643   View* m_view;
644   Callback m_update;
645 public:
646   RadiantCameraView(camera_t& camera, View* view, const Callback& update) : m_camera(camera), m_view(view), m_update(update)
647   {
648   }
649   void update()
650   {
651     m_view->Construct(m_camera.projection, m_camera.modelview, m_camera.width, m_camera.height);
652     m_update();
653   }
654   void setModelview(const Matrix4& modelview)
655   {
656     m_camera.modelview = modelview;
657     matrix4_multiply_by_matrix4(m_camera.modelview, g_radiant2opengl);
658     matrix4_affine_invert(m_camera.modelview);
659     Camera_updateVectors(m_camera);
660     update();
661   }
662   void setFieldOfView(float fieldOfView)
663   {
664     float farClip = Camera_getFarClipPlane(m_camera);
665     m_camera.projection = projection_for_camera(farClip / 4096.0f, farClip, fieldOfView, m_camera.width, m_camera.height);
666     update();
667   }
668 };
669
670
671 void Camera_motionDelta(int x, int y, unsigned int state, void* data)
672 {
673   camera_t* cam = reinterpret_cast<camera_t*>(data);
674
675   cam->m_mouseMove.motion_delta(x, y, state);
676
677         switch(g_camwindow_globals_private.m_nStrafeMode)
678         {
679                 case 0:
680                         cam->m_strafe = (state & GDK_CONTROL_MASK) != 0;
681                         if(cam->m_strafe)
682                                 cam->m_strafe_forward = (state & GDK_SHIFT_MASK) != 0;
683                         else
684                                 cam->m_strafe_forward = false;
685                         break;
686                 case 1:
687                         cam->m_strafe = (state & GDK_CONTROL_MASK) != 0 && (state & GDK_SHIFT_MASK) == 0;
688                         cam->m_strafe_forward = false;
689                         break;
690                 case 2:
691                         cam->m_strafe = (state & GDK_CONTROL_MASK) != 0 && (state & GDK_SHIFT_MASK) == 0;
692                         cam->m_strafe_forward = cam->m_strafe;
693                         break;
694         }
695 }
696
697 class CamWnd
698 {
699   View m_view;
700   camera_t m_Camera;
701   RadiantCameraView m_cameraview;
702 #if 0
703   int m_PositionDragCursorX;
704   int m_PositionDragCursorY;
705 #endif
706
707   guint m_freemove_handle_focusout;
708
709   static Shader* m_state_select1;
710   static Shader* m_state_select2;
711
712   FreezePointer m_freezePointer;
713
714 public:
715   GtkWidget* m_gl_widget;
716   GtkWindow* m_parent;
717
718   SelectionSystemWindowObserver* m_window_observer;
719   XORRectangle m_XORRectangle;
720
721   DeferredDraw m_deferredDraw;
722   DeferredMotion m_deferred_motion;
723
724   guint m_selection_button_press_handler;
725   guint m_selection_button_release_handler;
726   guint m_selection_motion_handler;
727
728   guint m_freelook_button_press_handler;
729
730   guint m_sizeHandler;
731   guint m_exposeHandler;
732
733   CamWnd();
734   ~CamWnd();
735
736   bool m_drawing;
737   void queue_draw()
738   {
739     //ASSERT_MESSAGE(!m_drawing, "CamWnd::queue_draw(): called while draw is already in progress");
740     if(m_drawing)
741     {
742       return;
743     }
744     //globalOutputStream() << "queue... ";
745     m_deferredDraw.draw();
746   }
747   void draw();
748
749   static void captureStates()
750   {
751     m_state_select1 = GlobalShaderCache().capture("$CAM_HIGHLIGHT");
752     m_state_select2 = GlobalShaderCache().capture("$CAM_OVERLAY");
753   }
754   static void releaseStates()
755   {
756     GlobalShaderCache().release("$CAM_HIGHLIGHT");
757     GlobalShaderCache().release("$CAM_OVERLAY");
758   }
759
760   camera_t& getCamera()
761   {
762     return m_Camera;
763   };
764
765   void BenchMark();
766   void Cam_ChangeFloor(bool up);
767
768   void DisableFreeMove();
769   void EnableFreeMove();
770   bool m_bFreeMove;
771
772   CameraView& getCameraView()
773   {
774     return m_cameraview;
775   }
776
777 private:
778   void Cam_Draw();
779 };
780
781 typedef MemberCaller<CamWnd, &CamWnd::queue_draw> CamWndQueueDraw;
782
783 Shader* CamWnd::m_state_select1 = 0;
784 Shader* CamWnd::m_state_select2 = 0;
785
786 CamWnd* NewCamWnd()
787 {
788   return new CamWnd;
789 }
790 void DeleteCamWnd(CamWnd* camwnd)
791 {
792   delete camwnd;
793 }
794
795 void CamWnd_constructStatic()
796 {
797   CamWnd::captureStates();
798 }
799
800 void CamWnd_destroyStatic()
801 {
802   CamWnd::releaseStates();
803 }
804
805 static CamWnd* g_camwnd = 0;
806
807 void GlobalCamera_setCamWnd(CamWnd& camwnd)
808 {
809   g_camwnd = &camwnd;
810 }
811
812
813 GtkWidget* CamWnd_getWidget(CamWnd& camwnd)
814 {
815   return camwnd.m_gl_widget;
816 }
817
818 GtkWindow* CamWnd_getParent(CamWnd& camwnd)
819 {
820   return camwnd.m_parent;
821 }
822
823 ToggleShown g_camera_shown(true);
824
825 void CamWnd_setParent(CamWnd& camwnd, GtkWindow* parent)
826 {
827   camwnd.m_parent = parent;
828   g_camera_shown.connect(GTK_WIDGET(camwnd.m_parent));
829 }
830
831 void CamWnd_Update(CamWnd& camwnd)
832 {
833   camwnd.queue_draw();
834 }
835
836
837
838 camwindow_globals_t g_camwindow_globals;
839
840 const Vector3& Camera_getOrigin(CamWnd& camwnd)
841 {
842   return Camera_getOrigin(camwnd.getCamera());
843 }
844
845 void Camera_setOrigin(CamWnd& camwnd, const Vector3& origin)
846 {
847   Camera_setOrigin(camwnd.getCamera(), origin);
848 }
849
850 const Vector3& Camera_getAngles(CamWnd& camwnd)
851 {
852   return Camera_getAngles(camwnd.getCamera());
853 }
854
855 void Camera_setAngles(CamWnd& camwnd, const Vector3& angles)
856 {
857   Camera_setAngles(camwnd.getCamera(), angles);
858 }
859
860
861 // =============================================================================
862 // CamWnd class
863
864 gboolean enable_freelook_button_press(GtkWidget* widget, GdkEventButton* event, CamWnd* camwnd)
865 {
866   if(event->type == GDK_BUTTON_PRESS && event->button == 3)
867   {
868     camwnd->EnableFreeMove();
869     return TRUE;
870   }
871   return FALSE;
872 }
873
874 gboolean disable_freelook_button_press(GtkWidget* widget, GdkEventButton* event, CamWnd* camwnd)
875 {
876   if(event->type == GDK_BUTTON_PRESS && event->button == 3)
877   {
878     camwnd->DisableFreeMove();
879     return TRUE;
880   }
881   return FALSE;
882 }
883
884 #if 0
885 gboolean mousecontrol_button_press(GtkWidget* widget, GdkEventButton* event, CamWnd* camwnd)
886 {
887   if(event->type == GDK_BUTTON_PRESS && event->button == 3)
888   {
889     Cam_MouseControl(camwnd->getCamera(), event->x, widget->allocation.height - 1 - event->y);
890   }
891   return FALSE;
892 }
893 #endif
894
895 void camwnd_update_xor_rectangle(CamWnd& self, rect_t area)
896 {
897   if(GTK_WIDGET_VISIBLE(self.m_gl_widget))
898   {
899     self.m_XORRectangle.set(rectangle_from_area(area.min, area.max, self.getCamera().width, self.getCamera().height));
900   }
901 }
902
903
904 gboolean selection_button_press(GtkWidget* widget, GdkEventButton* event, WindowObserver* observer)
905 {
906   if(event->type == GDK_BUTTON_PRESS)
907   {
908     observer->onMouseDown(WindowVector_forDouble(event->x, event->y), button_for_button(event->button), modifiers_for_state(event->state));
909   }
910   return FALSE;
911 }
912
913 gboolean selection_button_release(GtkWidget* widget, GdkEventButton* event, WindowObserver* observer)
914 {
915   if(event->type == GDK_BUTTON_RELEASE)
916   {
917     observer->onMouseUp(WindowVector_forDouble(event->x, event->y), button_for_button(event->button), modifiers_for_state(event->state));
918   }
919   return FALSE;
920 }
921
922 void selection_motion(gdouble x, gdouble y, guint state, void* data)
923 {
924   //globalOutputStream() << "motion... ";
925   reinterpret_cast<WindowObserver*>(data)->onMouseMotion(WindowVector_forDouble(x, y), modifiers_for_state(state));
926 }
927
928 inline WindowVector windowvector_for_widget_centre(GtkWidget* widget)
929 {
930   return WindowVector(static_cast<float>(widget->allocation.width / 2), static_cast<float>(widget->allocation.height / 2));
931 }
932
933 gboolean selection_button_press_freemove(GtkWidget* widget, GdkEventButton* event, WindowObserver* observer)
934 {
935   if(event->type == GDK_BUTTON_PRESS)
936   {
937     observer->onMouseDown(windowvector_for_widget_centre(widget), button_for_button(event->button), modifiers_for_state(event->state));
938   }
939   return FALSE;
940 }
941
942 gboolean selection_button_release_freemove(GtkWidget* widget, GdkEventButton* event, WindowObserver* observer)
943 {
944   if(event->type == GDK_BUTTON_RELEASE)
945   {
946     observer->onMouseUp(windowvector_for_widget_centre(widget), button_for_button(event->button), modifiers_for_state(event->state));
947   }
948   return FALSE;
949 }
950
951 gboolean selection_motion_freemove(GtkWidget *widget, GdkEventMotion *event, WindowObserver* observer)
952 {
953   observer->onMouseMotion(windowvector_for_widget_centre(widget), modifiers_for_state(event->state));
954   return FALSE;
955 }
956
957 gboolean wheelmove_scroll(GtkWidget* widget, GdkEventScroll* event, CamWnd* camwnd)
958 {
959   if(event->direction == GDK_SCROLL_UP)
960   {
961     Camera_Freemove_updateAxes(camwnd->getCamera());
962     Camera_setOrigin(*camwnd, vector3_added(Camera_getOrigin(*camwnd), vector3_scaled(camwnd->getCamera().forward, static_cast<float>(g_camwindow_globals_private.m_nMoveSpeed))));
963   }
964   else if(event->direction == GDK_SCROLL_DOWN)
965   {
966     Camera_Freemove_updateAxes(camwnd->getCamera());
967     Camera_setOrigin(*camwnd, vector3_added(Camera_getOrigin(*camwnd), vector3_scaled(camwnd->getCamera().forward, -static_cast<float>(g_camwindow_globals_private.m_nMoveSpeed))));
968   }
969
970   return FALSE;
971 }
972
973 gboolean camera_size_allocate(GtkWidget* widget, GtkAllocation* allocation, CamWnd* camwnd)
974 {
975   camwnd->getCamera().width = allocation->width;
976   camwnd->getCamera().height = allocation->height;
977   Camera_updateProjection(camwnd->getCamera());
978   camwnd->m_window_observer->onSizeChanged(camwnd->getCamera().width, camwnd->getCamera().height);
979   camwnd->queue_draw();
980   return FALSE;
981 }
982
983 gboolean camera_expose(GtkWidget* widget, GdkEventExpose* event, gpointer data)
984 {
985   reinterpret_cast<CamWnd*>(data)->draw();
986   return FALSE;
987 }
988
989 void KeyEvent_connect(const char* name)
990 {
991   const KeyEvent& keyEvent = GlobalKeyEvents_find(name);
992   keydown_accelerators_add(keyEvent.m_accelerator, keyEvent.m_keyDown);
993   keyup_accelerators_add(keyEvent.m_accelerator, keyEvent.m_keyUp);
994 }
995
996 void KeyEvent_disconnect(const char* name)
997 {
998   const KeyEvent& keyEvent = GlobalKeyEvents_find(name);
999   keydown_accelerators_remove(keyEvent.m_accelerator);
1000   keyup_accelerators_remove(keyEvent.m_accelerator);
1001 }
1002
1003 void CamWnd_registerCommands(CamWnd& camwnd)
1004 {
1005   GlobalKeyEvents_insert("CameraForward", Accelerator(GDK_Up),
1006     ReferenceCaller<camera_t, Camera_MoveForward_KeyDown>(camwnd.getCamera()),
1007     ReferenceCaller<camera_t, Camera_MoveForward_KeyUp>(camwnd.getCamera())
1008   );
1009   GlobalKeyEvents_insert("CameraBack", Accelerator(GDK_Down),
1010     ReferenceCaller<camera_t, Camera_MoveBack_KeyDown>(camwnd.getCamera()),
1011     ReferenceCaller<camera_t, Camera_MoveBack_KeyUp>(camwnd.getCamera())
1012   );
1013   GlobalKeyEvents_insert("CameraLeft", Accelerator(GDK_Left),
1014     ReferenceCaller<camera_t, Camera_RotateLeft_KeyDown>(camwnd.getCamera()),
1015     ReferenceCaller<camera_t, Camera_RotateLeft_KeyUp>(camwnd.getCamera())
1016   );
1017   GlobalKeyEvents_insert("CameraRight", Accelerator(GDK_Right),
1018     ReferenceCaller<camera_t, Camera_RotateRight_KeyDown>(camwnd.getCamera()),
1019     ReferenceCaller<camera_t, Camera_RotateRight_KeyUp>(camwnd.getCamera())
1020   );
1021   GlobalKeyEvents_insert("CameraStrafeRight", Accelerator(GDK_period),
1022     ReferenceCaller<camera_t, Camera_MoveRight_KeyDown>(camwnd.getCamera()),
1023     ReferenceCaller<camera_t, Camera_MoveRight_KeyUp>(camwnd.getCamera())
1024   );
1025   GlobalKeyEvents_insert("CameraStrafeLeft", Accelerator(GDK_comma),
1026     ReferenceCaller<camera_t, Camera_MoveLeft_KeyDown>(camwnd.getCamera()),
1027     ReferenceCaller<camera_t, Camera_MoveLeft_KeyUp>(camwnd.getCamera())
1028   );
1029   GlobalKeyEvents_insert("CameraUp", Accelerator('D'),
1030     ReferenceCaller<camera_t, Camera_MoveUp_KeyDown>(camwnd.getCamera()),
1031     ReferenceCaller<camera_t, Camera_MoveUp_KeyUp>(camwnd.getCamera())
1032   );
1033   GlobalKeyEvents_insert("CameraDown", Accelerator('C'),
1034     ReferenceCaller<camera_t, Camera_MoveDown_KeyDown>(camwnd.getCamera()),
1035     ReferenceCaller<camera_t, Camera_MoveDown_KeyUp>(camwnd.getCamera())
1036   );
1037   GlobalKeyEvents_insert("CameraAngleDown", Accelerator('A'),
1038     ReferenceCaller<camera_t, Camera_PitchDown_KeyDown>(camwnd.getCamera()),
1039     ReferenceCaller<camera_t, Camera_PitchDown_KeyUp>(camwnd.getCamera())
1040   );
1041   GlobalKeyEvents_insert("CameraAngleUp", Accelerator('Z'),
1042     ReferenceCaller<camera_t, Camera_PitchUp_KeyDown>(camwnd.getCamera()),
1043     ReferenceCaller<camera_t, Camera_PitchUp_KeyUp>(camwnd.getCamera())
1044   );
1045
1046   GlobalKeyEvents_insert("CameraFreeMoveForward", Accelerator(GDK_Up),
1047     FreeMoveCameraMoveForwardKeyDownCaller(camwnd.getCamera()),
1048     FreeMoveCameraMoveForwardKeyUpCaller(camwnd.getCamera())
1049   );
1050   GlobalKeyEvents_insert("CameraFreeMoveBack", Accelerator(GDK_Down),
1051     FreeMoveCameraMoveBackKeyDownCaller(camwnd.getCamera()),
1052     FreeMoveCameraMoveBackKeyUpCaller(camwnd.getCamera())
1053   );
1054   GlobalKeyEvents_insert("CameraFreeMoveLeft", Accelerator(GDK_Left),
1055     FreeMoveCameraMoveLeftKeyDownCaller(camwnd.getCamera()),
1056     FreeMoveCameraMoveLeftKeyUpCaller(camwnd.getCamera())
1057   );
1058   GlobalKeyEvents_insert("CameraFreeMoveRight", Accelerator(GDK_Right),
1059     FreeMoveCameraMoveRightKeyDownCaller(camwnd.getCamera()),
1060     FreeMoveCameraMoveRightKeyUpCaller(camwnd.getCamera())
1061   );
1062   GlobalKeyEvents_insert("CameraFreeMoveUp", Accelerator('D'),
1063     FreeMoveCameraMoveUpKeyDownCaller(camwnd.getCamera()),
1064     FreeMoveCameraMoveUpKeyUpCaller(camwnd.getCamera())
1065   );
1066   GlobalKeyEvents_insert("CameraFreeMoveDown", Accelerator('C'),
1067     FreeMoveCameraMoveDownKeyDownCaller(camwnd.getCamera()),
1068     FreeMoveCameraMoveDownKeyUpCaller(camwnd.getCamera())
1069   );
1070
1071   GlobalCommands_insert("CameraForward", ReferenceCaller<camera_t, Camera_MoveForward_Discrete>(camwnd.getCamera()), Accelerator(GDK_Up));
1072   GlobalCommands_insert("CameraBack", ReferenceCaller<camera_t, Camera_MoveBack_Discrete>(camwnd.getCamera()), Accelerator(GDK_Down));
1073   GlobalCommands_insert("CameraLeft", ReferenceCaller<camera_t, Camera_RotateLeft_Discrete>(camwnd.getCamera()), Accelerator(GDK_Left));
1074   GlobalCommands_insert("CameraRight", ReferenceCaller<camera_t, Camera_RotateRight_Discrete>(camwnd.getCamera()), Accelerator(GDK_Right));
1075   GlobalCommands_insert("CameraStrafeRight", ReferenceCaller<camera_t, Camera_MoveRight_Discrete>(camwnd.getCamera()), Accelerator(GDK_period));
1076   GlobalCommands_insert("CameraStrafeLeft", ReferenceCaller<camera_t, Camera_MoveLeft_Discrete>(camwnd.getCamera()), Accelerator(GDK_comma));
1077
1078   GlobalCommands_insert("CameraUp", ReferenceCaller<camera_t, Camera_MoveUp_Discrete>(camwnd.getCamera()), Accelerator('D'));
1079   GlobalCommands_insert("CameraDown", ReferenceCaller<camera_t, Camera_MoveDown_Discrete>(camwnd.getCamera()), Accelerator('C'));
1080   GlobalCommands_insert("CameraAngleUp", ReferenceCaller<camera_t, Camera_PitchUp_Discrete>(camwnd.getCamera()), Accelerator('A'));
1081   GlobalCommands_insert("CameraAngleDown", ReferenceCaller<camera_t, Camera_PitchDown_Discrete>(camwnd.getCamera()), Accelerator('Z'));
1082 }
1083
1084 void CamWnd_Move_Enable(CamWnd& camwnd)
1085 {
1086   KeyEvent_connect("CameraForward");
1087   KeyEvent_connect("CameraBack");
1088   KeyEvent_connect("CameraLeft");
1089   KeyEvent_connect("CameraRight");
1090   KeyEvent_connect("CameraStrafeRight");
1091   KeyEvent_connect("CameraStrafeLeft");
1092   KeyEvent_connect("CameraUp");
1093   KeyEvent_connect("CameraDown");
1094   KeyEvent_connect("CameraAngleUp");
1095   KeyEvent_connect("CameraAngleDown");
1096 }
1097
1098 void CamWnd_Move_Disable(CamWnd& camwnd)
1099 {
1100   KeyEvent_disconnect("CameraForward");
1101   KeyEvent_disconnect("CameraBack");
1102   KeyEvent_disconnect("CameraLeft");
1103   KeyEvent_disconnect("CameraRight");
1104   KeyEvent_disconnect("CameraStrafeRight");
1105   KeyEvent_disconnect("CameraStrafeLeft");
1106   KeyEvent_disconnect("CameraUp");
1107   KeyEvent_disconnect("CameraDown");
1108   KeyEvent_disconnect("CameraAngleUp");
1109   KeyEvent_disconnect("CameraAngleDown");
1110 }
1111
1112 void CamWnd_Move_Discrete_Enable(CamWnd& camwnd)
1113 {
1114   command_connect_accelerator("CameraForward");
1115   command_connect_accelerator("CameraBack");
1116   command_connect_accelerator("CameraLeft");
1117   command_connect_accelerator("CameraRight");
1118   command_connect_accelerator("CameraStrafeRight");
1119   command_connect_accelerator("CameraStrafeLeft");
1120   command_connect_accelerator("CameraUp");
1121   command_connect_accelerator("CameraDown");
1122   command_connect_accelerator("CameraAngleUp");
1123   command_connect_accelerator("CameraAngleDown");
1124 }
1125
1126 void CamWnd_Move_Discrete_Disable(CamWnd& camwnd)
1127 {
1128   command_disconnect_accelerator("CameraForward");
1129   command_disconnect_accelerator("CameraBack");
1130   command_disconnect_accelerator("CameraLeft");
1131   command_disconnect_accelerator("CameraRight");
1132   command_disconnect_accelerator("CameraStrafeRight");
1133   command_disconnect_accelerator("CameraStrafeLeft");
1134   command_disconnect_accelerator("CameraUp");
1135   command_disconnect_accelerator("CameraDown");
1136   command_disconnect_accelerator("CameraAngleUp");
1137   command_disconnect_accelerator("CameraAngleDown");
1138 }
1139
1140 void CamWnd_Move_Discrete_Import(CamWnd& camwnd, bool value)
1141 {
1142   if(g_camwindow_globals_private.m_bCamDiscrete)
1143   {
1144     CamWnd_Move_Discrete_Disable(camwnd);
1145   }
1146   else
1147   {
1148     CamWnd_Move_Disable(camwnd);
1149   }
1150
1151   g_camwindow_globals_private.m_bCamDiscrete = value;
1152
1153   if(g_camwindow_globals_private.m_bCamDiscrete)
1154   {
1155     CamWnd_Move_Discrete_Enable(camwnd);
1156   }
1157   else
1158   {
1159     CamWnd_Move_Enable(camwnd);
1160   }
1161 }
1162
1163 void CamWnd_Move_Discrete_Import(bool value)
1164 {
1165   if(g_camwnd != 0)
1166   {
1167     CamWnd_Move_Discrete_Import(*g_camwnd, value);
1168   }
1169   else
1170   {
1171     g_camwindow_globals_private.m_bCamDiscrete = value;
1172   }
1173 }
1174
1175
1176
1177 void CamWnd_Add_Handlers_Move(CamWnd& camwnd)
1178 {
1179   camwnd.m_selection_button_press_handler = g_signal_connect(G_OBJECT(camwnd.m_gl_widget), "button_press_event", G_CALLBACK(selection_button_press), camwnd.m_window_observer);
1180   camwnd.m_selection_button_release_handler = g_signal_connect(G_OBJECT(camwnd.m_gl_widget), "button_release_event", G_CALLBACK(selection_button_release), camwnd.m_window_observer);
1181   camwnd.m_selection_motion_handler = g_signal_connect(G_OBJECT(camwnd.m_gl_widget), "motion_notify_event", G_CALLBACK(DeferredMotion::gtk_motion), &camwnd.m_deferred_motion);
1182
1183   camwnd.m_freelook_button_press_handler = g_signal_connect(G_OBJECT(camwnd.m_gl_widget), "button_press_event", G_CALLBACK(enable_freelook_button_press), &camwnd);
1184
1185   if(g_camwindow_globals_private.m_bCamDiscrete)
1186   {
1187     CamWnd_Move_Discrete_Enable(camwnd);
1188   }
1189   else
1190   {
1191     CamWnd_Move_Enable(camwnd);
1192   }
1193 }
1194
1195 void CamWnd_Remove_Handlers_Move(CamWnd& camwnd)
1196 {
1197   g_signal_handler_disconnect(G_OBJECT(camwnd.m_gl_widget), camwnd.m_selection_button_press_handler);
1198   g_signal_handler_disconnect(G_OBJECT(camwnd.m_gl_widget), camwnd.m_selection_button_release_handler);
1199   g_signal_handler_disconnect(G_OBJECT(camwnd.m_gl_widget), camwnd.m_selection_motion_handler);
1200
1201   g_signal_handler_disconnect(G_OBJECT(camwnd.m_gl_widget), camwnd.m_freelook_button_press_handler);
1202
1203   if(g_camwindow_globals_private.m_bCamDiscrete)
1204   {
1205     CamWnd_Move_Discrete_Disable(camwnd);
1206   }
1207   else
1208   {
1209     CamWnd_Move_Disable(camwnd);
1210   }
1211 }
1212
1213 void CamWnd_Add_Handlers_FreeMove(CamWnd& camwnd)
1214 {
1215   camwnd.m_selection_button_press_handler = g_signal_connect(G_OBJECT(camwnd.m_gl_widget), "button_press_event", G_CALLBACK(selection_button_press_freemove), camwnd.m_window_observer);
1216   camwnd.m_selection_button_release_handler = g_signal_connect(G_OBJECT(camwnd.m_gl_widget), "button_release_event", G_CALLBACK(selection_button_release_freemove), camwnd.m_window_observer);
1217   camwnd.m_selection_motion_handler = g_signal_connect(G_OBJECT(camwnd.m_gl_widget), "motion_notify_event", G_CALLBACK(selection_motion_freemove), camwnd.m_window_observer);
1218
1219   camwnd.m_freelook_button_press_handler = g_signal_connect(G_OBJECT(camwnd.m_gl_widget), "button_press_event", G_CALLBACK(disable_freelook_button_press), &camwnd);
1220
1221   KeyEvent_connect("CameraFreeMoveForward");
1222   KeyEvent_connect("CameraFreeMoveBack");
1223   KeyEvent_connect("CameraFreeMoveLeft");
1224   KeyEvent_connect("CameraFreeMoveRight");
1225   KeyEvent_connect("CameraFreeMoveUp");
1226   KeyEvent_connect("CameraFreeMoveDown");
1227 }
1228
1229 void CamWnd_Remove_Handlers_FreeMove(CamWnd& camwnd)
1230 {
1231   KeyEvent_disconnect("CameraFreeMoveForward");
1232   KeyEvent_disconnect("CameraFreeMoveBack");
1233   KeyEvent_disconnect("CameraFreeMoveLeft");
1234   KeyEvent_disconnect("CameraFreeMoveRight");
1235   KeyEvent_disconnect("CameraFreeMoveUp");
1236   KeyEvent_disconnect("CameraFreeMoveDown");
1237
1238   g_signal_handler_disconnect(G_OBJECT(camwnd.m_gl_widget), camwnd.m_selection_button_press_handler);
1239   g_signal_handler_disconnect(G_OBJECT(camwnd.m_gl_widget), camwnd.m_selection_button_release_handler);
1240   g_signal_handler_disconnect(G_OBJECT(camwnd.m_gl_widget), camwnd.m_selection_motion_handler);
1241
1242   g_signal_handler_disconnect(G_OBJECT(camwnd.m_gl_widget), camwnd.m_freelook_button_press_handler);
1243 }
1244
1245 CamWnd::CamWnd() :
1246   m_view(true),
1247   m_Camera(&m_view, CamWndQueueDraw(*this)),
1248   m_cameraview(m_Camera, &m_view, ReferenceCaller<CamWnd, CamWnd_Update>(*this)),
1249   m_gl_widget(glwidget_new(TRUE)),
1250   m_window_observer(NewWindowObserver()),
1251   m_XORRectangle(m_gl_widget),
1252   m_deferredDraw(WidgetQueueDrawCaller(*m_gl_widget)),
1253   m_deferred_motion(selection_motion, m_window_observer),
1254   m_selection_button_press_handler(0),
1255   m_selection_button_release_handler(0),
1256   m_selection_motion_handler(0),
1257   m_freelook_button_press_handler(0),
1258   m_drawing(false)
1259 {
1260   m_bFreeMove = false;
1261
1262   GlobalWindowObservers_add(m_window_observer);
1263   GlobalWindowObservers_connectWidget(m_gl_widget);
1264
1265   m_window_observer->setRectangleDrawCallback(ReferenceCaller1<CamWnd, rect_t, camwnd_update_xor_rectangle>(*this));
1266   m_window_observer->setView(m_view);
1267
1268   gtk_widget_ref(m_gl_widget);
1269
1270   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);
1271   GTK_WIDGET_SET_FLAGS (m_gl_widget, GTK_CAN_FOCUS);
1272
1273   m_sizeHandler = g_signal_connect(G_OBJECT(m_gl_widget), "size_allocate", G_CALLBACK(camera_size_allocate), this);
1274   m_exposeHandler = g_signal_connect(G_OBJECT(m_gl_widget), "expose_event", G_CALLBACK(camera_expose), this);
1275
1276   Map_addValidCallback(g_map, DeferredDrawOnMapValidChangedCaller(m_deferredDraw));
1277
1278   CamWnd_registerCommands(*this);
1279
1280   CamWnd_Add_Handlers_Move(*this);
1281
1282   g_signal_connect(G_OBJECT(m_gl_widget), "scroll_event", G_CALLBACK(wheelmove_scroll), this);
1283
1284   AddSceneChangeCallback(ReferenceCaller<CamWnd, CamWnd_Update>(*this));
1285
1286   PressedButtons_connect(g_pressedButtons, m_gl_widget);
1287 }
1288
1289 CamWnd::~CamWnd()
1290 {
1291   if(m_bFreeMove)
1292   {
1293     DisableFreeMove();
1294   }
1295
1296   CamWnd_Remove_Handlers_Move(*this);
1297
1298   g_signal_handler_disconnect(G_OBJECT(m_gl_widget), m_sizeHandler);
1299   g_signal_handler_disconnect(G_OBJECT(m_gl_widget), m_exposeHandler);
1300
1301   gtk_widget_unref(m_gl_widget);
1302
1303   m_window_observer->release();
1304 }
1305
1306 class FloorHeightWalker : public scene::Graph::Walker
1307 {
1308   float m_current;
1309   float& m_bestUp;
1310   float& m_bestDown;
1311 public:
1312   FloorHeightWalker(float current, float& bestUp, float& bestDown) :
1313       m_current(current), m_bestUp(bestUp), m_bestDown(bestDown)
1314   {
1315     bestUp = g_MaxWorldCoord;
1316     bestDown = -g_MaxWorldCoord;
1317   }
1318   bool pre(const scene::Path& path, scene::Instance& instance) const
1319   {
1320     if(path.top().get().visible()
1321       && Node_isBrush(path.top())) // this node is a floor
1322     {
1323       const AABB& aabb = instance.worldAABB();
1324       float floorHeight = aabb.origin.z() + aabb.extents.z();
1325       if(floorHeight > m_current && floorHeight < m_bestUp)
1326       {
1327         m_bestUp = floorHeight;
1328       }
1329       if(floorHeight < m_current && floorHeight > m_bestDown)
1330       {
1331         m_bestDown = floorHeight;
1332       }
1333     }
1334     return true;
1335   }
1336 };
1337
1338 void CamWnd::Cam_ChangeFloor(bool up)
1339 {
1340   float current = m_Camera.origin[2] - 48;
1341   float bestUp;
1342   float bestDown;
1343   GlobalSceneGraph().traverse(FloorHeightWalker(current, bestUp, bestDown));
1344
1345   if(up && bestUp != g_MaxWorldCoord)
1346   {
1347     current = bestUp;
1348   }
1349   if(!up && bestDown != -g_MaxWorldCoord)
1350   {
1351     current = bestDown;
1352   }
1353
1354   m_Camera.origin[2] = current + 48;
1355   Camera_updateModelview(getCamera());
1356   CamWnd_Update(*this);
1357   CameraMovedNotify();
1358 }
1359
1360
1361 #if 0
1362
1363 // button_press
1364   Sys_GetCursorPos(&m_PositionDragCursorX, &m_PositionDragCursorY);
1365
1366 // motion
1367   if ( (m_bFreeMove && (buttons == (RAD_CONTROL|RAD_SHIFT)))
1368     || (!m_bFreeMove && (buttons == (RAD_RBUTTON|RAD_CONTROL))) )
1369   {
1370     Cam_PositionDrag();
1371     CamWnd_Update(camwnd);
1372     CameraMovedNotify();
1373     return;
1374   }
1375
1376 void CamWnd::Cam_PositionDrag()
1377 {
1378   int x, y;
1379
1380   Sys_GetCursorPos(GTK_WINDOW(m_gl_widget), &x, &y);
1381   if (x != m_PositionDragCursorX || y != m_PositionDragCursorY)
1382   {
1383     x -= m_PositionDragCursorX;
1384     vector3_add(m_Camera.origin, vector3_scaled(m_Camera.vright, x));
1385     y -= m_PositionDragCursorY;
1386     m_Camera.origin[2] -= y;
1387     Camera_updateModelview();
1388     CamWnd_Update(camwnd);
1389     CameraMovedNotify();
1390
1391     Sys_SetCursorPos(GTK_WINDOW(m_parent), m_PositionDragCursorX, m_PositionDragCursorY);
1392   }
1393 }
1394 #endif
1395
1396
1397 // NOTE TTimo if there's an OS-level focus out of the application
1398 //   then we can release the camera cursor grab
1399 static gboolean camwindow_freemove_focusout(GtkWidget* widget, GdkEventFocus* event, gpointer data)
1400 {
1401   reinterpret_cast<CamWnd*>(data)->DisableFreeMove();
1402   return FALSE;
1403 }
1404
1405 void CamWnd::EnableFreeMove()
1406 {
1407   //globalOutputStream() << "EnableFreeMove\n";
1408
1409   ASSERT_MESSAGE(!m_bFreeMove, "EnableFreeMove: free-move was already enabled");
1410   m_bFreeMove = true;
1411   Camera_clearMovementFlags(getCamera(), MOVE_ALL);
1412
1413   CamWnd_Remove_Handlers_Move(*this);
1414   CamWnd_Add_Handlers_FreeMove(*this);
1415
1416   gtk_window_set_focus(m_parent, m_gl_widget);
1417   m_freemove_handle_focusout = g_signal_connect(G_OBJECT(m_gl_widget), "focus_out_event", G_CALLBACK(camwindow_freemove_focusout), this);
1418   m_freezePointer.freeze_pointer(m_parent, Camera_motionDelta, &m_Camera);
1419
1420   CamWnd_Update(*this);
1421 }
1422
1423 void CamWnd::DisableFreeMove()
1424 {
1425   //globalOutputStream() << "DisableFreeMove\n";
1426
1427   ASSERT_MESSAGE(m_bFreeMove, "DisableFreeMove: free-move was not enabled");
1428   m_bFreeMove = false;
1429   Camera_clearMovementFlags(getCamera(), MOVE_ALL);
1430
1431   CamWnd_Remove_Handlers_FreeMove(*this);
1432   CamWnd_Add_Handlers_Move(*this);
1433
1434   m_freezePointer.unfreeze_pointer(m_parent);
1435   g_signal_handler_disconnect(G_OBJECT(m_gl_widget), m_freemove_handle_focusout);
1436
1437   CamWnd_Update(*this);
1438 }
1439
1440
1441 #include "renderer.h"
1442
1443 class CamRenderer: public Renderer
1444 {
1445   struct state_type
1446   {
1447     state_type() : m_highlight(0), m_state(0), m_lights(0)
1448     {
1449     }
1450     unsigned int m_highlight;
1451     Shader* m_state;
1452     const LightList* m_lights;
1453   };
1454
1455   std::vector<state_type> m_state_stack;
1456   RenderStateFlags m_globalstate;
1457   Shader* m_state_select0;
1458   Shader* m_state_select1;
1459   const Vector3& m_viewer;
1460
1461 public:
1462   CamRenderer(RenderStateFlags globalstate, Shader* select0, Shader* select1, const Vector3& viewer) :
1463     m_globalstate(globalstate),
1464     m_state_select0(select0),
1465     m_state_select1(select1),
1466     m_viewer(viewer)
1467   {
1468     ASSERT_NOTNULL(select0);
1469     ASSERT_NOTNULL(select1);
1470     m_state_stack.push_back(state_type());
1471   }
1472
1473   void SetState(Shader* state, EStyle style)
1474   {
1475     ASSERT_NOTNULL(state);
1476     if(style == eFullMaterials)
1477     {
1478       m_state_stack.back().m_state = state;
1479     }
1480   }
1481   const EStyle getStyle() const
1482   {
1483     return eFullMaterials;
1484   }
1485   void PushState()
1486   {
1487     m_state_stack.push_back(m_state_stack.back());
1488   }
1489   void PopState()
1490   {
1491     ASSERT_MESSAGE(!m_state_stack.empty(), "popping empty stack");
1492     m_state_stack.pop_back();
1493   }
1494   void Highlight(EHighlightMode mode, bool bEnable = true)
1495   {
1496     (bEnable)
1497       ? m_state_stack.back().m_highlight |= mode
1498       : m_state_stack.back().m_highlight &= ~mode;
1499   }
1500   void setLights(const LightList& lights)
1501   {
1502     m_state_stack.back().m_lights = &lights;
1503   }
1504   void addRenderable(const OpenGLRenderable& renderable, const Matrix4& world)
1505   {
1506     if(m_state_stack.back().m_highlight & ePrimitive)
1507     {
1508       m_state_select0->addRenderable(renderable, world, m_state_stack.back().m_lights);
1509     }
1510     if(m_state_stack.back().m_highlight & eFace)
1511     {
1512       m_state_select1->addRenderable(renderable, world, m_state_stack.back().m_lights);
1513     }
1514
1515     m_state_stack.back().m_state->addRenderable(renderable, world, m_state_stack.back().m_lights);
1516   }
1517
1518   void render(const Matrix4& modelview, const Matrix4& projection)
1519   {
1520     GlobalShaderCache().render(m_globalstate, modelview, projection, m_viewer);
1521   }
1522 };
1523
1524 /*
1525 ==============
1526 Cam_Draw
1527 ==============
1528 */
1529
1530 void ShowStatsToggle()
1531 {
1532   g_camwindow_globals_private.m_showStats ^= 1;
1533 }
1534 typedef FreeCaller<ShowStatsToggle> ShowStatsToggleCaller;
1535
1536 void ShowStatsExport(const BoolImportCallback& importer)
1537 {
1538   importer(g_camwindow_globals_private.m_showStats);
1539 }
1540 typedef FreeCaller1<const BoolImportCallback&, ShowStatsExport> ShowStatsExportCaller;
1541
1542 ShowStatsExportCaller g_show_stats_caller;
1543 BoolExportCallback g_show_stats_callback(g_show_stats_caller);
1544 ToggleItem g_show_stats(g_show_stats_callback);
1545
1546 void CamWnd::Cam_Draw()
1547 {
1548   glViewport(0, 0, m_Camera.width, m_Camera.height);
1549 #if 0
1550   GLint viewprt[4];
1551   glGetIntegerv (GL_VIEWPORT, viewprt);
1552 #endif
1553
1554   // initialize OpenGL state for glClear to work
1555   GlobalShaderCache().render(RENDER_DEFAULT, m_Camera.modelview, m_Camera.projection, m_view.getViewer());
1556
1557   // enable depth buffer writes
1558   glDepthMask(GL_TRUE);
1559
1560   Vector3 clearColour(0, 0, 0);
1561   if(m_Camera.draw_mode != cd_lighting)
1562   {
1563     clearColour = g_camwindow_globals.color_cameraback;
1564   }
1565
1566   glClearColor(clearColour[0], clearColour[1], clearColour[2], 0);
1567   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1568
1569   extern void Renderer_ResetStats();
1570   Renderer_ResetStats();
1571   extern void Cull_ResetStats();
1572   Cull_ResetStats();
1573
1574   glMatrixMode(GL_PROJECTION);
1575   glLoadMatrixf(reinterpret_cast<const float*>(&m_Camera.projection));
1576
1577   glMatrixMode(GL_MODELVIEW);
1578   glLoadMatrixf(reinterpret_cast<const float*>(&m_Camera.modelview));
1579
1580
1581   // one directional light source directly behind the viewer
1582   {
1583     GLfloat inverse_cam_dir[4], ambient[4], diffuse[4];//, material[4];
1584
1585     ambient[0] = ambient[1] = ambient[2] = 0.4f;
1586     ambient[3] = 1.0f;
1587     diffuse[0] = diffuse[1] = diffuse[2] = 0.4f;
1588     diffuse[3] = 1.0f;
1589     //material[0] = material[1] = material[2] = 0.8f;
1590     //material[3] = 1.0f;
1591
1592     inverse_cam_dir[0] = m_Camera.vpn[0];
1593     inverse_cam_dir[1] = m_Camera.vpn[1];
1594     inverse_cam_dir[2] = m_Camera.vpn[2];
1595     inverse_cam_dir[3] = 0;
1596
1597     glLightfv(GL_LIGHT0, GL_POSITION, inverse_cam_dir);
1598
1599     glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
1600     glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
1601
1602     glEnable(GL_LIGHT0);
1603   }
1604
1605
1606   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;
1607   switch (m_Camera.draw_mode)
1608   {
1609   case cd_wire:
1610     break;
1611   case cd_solid:
1612     globalstate |= RENDER_FILL
1613       | RENDER_LIGHTING
1614       | RENDER_SMOOTH
1615       | RENDER_SCALED;
1616     break;
1617   case cd_texture:
1618     globalstate |= RENDER_FILL
1619       | RENDER_LIGHTING
1620       | RENDER_TEXTURE
1621       | RENDER_SMOOTH
1622       | RENDER_SCALED;
1623     break;
1624   case cd_lighting:
1625     globalstate |= RENDER_FILL
1626       | RENDER_LIGHTING
1627       | RENDER_TEXTURE
1628       | RENDER_SMOOTH
1629       | RENDER_SCALED
1630       | RENDER_BUMP
1631       | RENDER_PROGRAM
1632       | RENDER_SCREEN;
1633     break;
1634   default:
1635     globalstate = 0;
1636     break;
1637   }
1638
1639   if(!g_xywindow_globals.m_bNoStipple)
1640   {
1641     globalstate |= RENDER_LINESTIPPLE|RENDER_POLYGONSTIPPLE;
1642   }
1643
1644   {
1645     CamRenderer renderer(globalstate, m_state_select2, m_state_select1, m_view.getViewer());
1646
1647     Scene_Render(renderer, m_view);
1648
1649     renderer.render(m_Camera.modelview, m_Camera.projection);
1650   }
1651
1652   // prepare for 2d stuff
1653   glColor4f(1, 1, 1, 1);
1654   glDisable(GL_BLEND);
1655   glMatrixMode(GL_PROJECTION);
1656   glLoadIdentity();
1657   glOrtho(0, (float)m_Camera.width, 0, (float)m_Camera.height, -100, 100);
1658   glScalef(1, -1, 1);
1659   glTranslatef(0, -(float)m_Camera.height, 0);
1660   glMatrixMode(GL_MODELVIEW);
1661   glLoadIdentity();
1662
1663   if(GlobalOpenGL().GL_1_3())
1664   {
1665     glClientActiveTexture(GL_TEXTURE0);
1666     glActiveTexture(GL_TEXTURE0);
1667   }
1668
1669   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1670   glDisableClientState(GL_NORMAL_ARRAY);
1671   glDisableClientState(GL_COLOR_ARRAY);
1672
1673   glDisable(GL_TEXTURE_2D);
1674   glDisable(GL_LIGHTING);
1675   glDisable(GL_COLOR_MATERIAL);
1676   glDisable(GL_DEPTH_TEST);
1677   glColor3f( 1.f, 1.f, 1.f );
1678   glLineWidth(1);
1679
1680   // draw the crosshair
1681   if (m_bFreeMove)
1682   {
1683     glBegin( GL_LINES );
1684     glVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f + 6 );
1685     glVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f + 2 );
1686     glVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f - 6 );
1687     glVertex2f( (float)m_Camera.width / 2.f, (float)m_Camera.height / 2.f - 2 );
1688     glVertex2f( (float)m_Camera.width / 2.f + 6, (float)m_Camera.height / 2.f );
1689     glVertex2f( (float)m_Camera.width / 2.f + 2, (float)m_Camera.height / 2.f );
1690     glVertex2f( (float)m_Camera.width / 2.f - 6, (float)m_Camera.height / 2.f );
1691     glVertex2f( (float)m_Camera.width / 2.f - 2, (float)m_Camera.height / 2.f );
1692     glEnd();
1693   }
1694
1695   if(g_camwindow_globals_private.m_showStats)
1696   {
1697     glRasterPos3f(1.0f, static_cast<float>(m_Camera.height) - GlobalOpenGL().m_fontDescent, 0.0f);
1698     extern const char* Renderer_GetStats();
1699     GlobalOpenGL().drawString(Renderer_GetStats());
1700
1701     glRasterPos3f(1.0f, static_cast<float>(m_Camera.height) - GlobalOpenGL().m_fontDescent - GlobalOpenGL().m_fontHeight, 0.0f);
1702     extern const char* Cull_GetStats();
1703     GlobalOpenGL().drawString(Cull_GetStats());
1704   }
1705
1706   // bind back to the default texture so that we don't have problems
1707   // elsewhere using/modifying texture maps between contexts
1708   glBindTexture( GL_TEXTURE_2D, 0 );
1709 }
1710
1711 void CamWnd::draw()
1712 {
1713   m_drawing = true;
1714
1715   //globalOutputStream() << "draw...\n";
1716   if (glwidget_make_current(m_gl_widget) != FALSE)
1717   {
1718     if(Map_Valid(g_map) && ScreenUpdates_Enabled())
1719     {
1720       GlobalOpenGL_debugAssertNoErrors();
1721       Cam_Draw();
1722       GlobalOpenGL_debugAssertNoErrors();
1723       //qglFinish();
1724
1725       m_XORRectangle.set(rectangle_t());
1726     }
1727
1728     glwidget_swap_buffers(m_gl_widget);
1729   }
1730
1731   m_drawing = false;
1732 }
1733
1734 void CamWnd::BenchMark()
1735 {
1736   double dStart = Sys_DoubleTime();
1737   for (int i=0 ; i < 100 ; i++)
1738   {
1739     Vector3 angles;
1740     angles[CAMERA_ROLL] = 0;
1741     angles[CAMERA_PITCH] = 0;
1742     angles[CAMERA_YAW] = static_cast<float>(i * (360.0 / 100.0));
1743     Camera_setAngles(*this, angles);
1744   }
1745   double dEnd = Sys_DoubleTime();
1746   globalOutputStream() << FloatFormat(dEnd - dStart, 5, 2) << " seconds\n";
1747 }
1748
1749
1750 void fill_view_camera_menu(GtkMenu* menu)
1751 {
1752   create_check_menu_item_with_mnemonic(menu, "Camera View", "ToggleCamera");
1753 }
1754
1755 void GlobalCamera_ResetAngles()
1756 {
1757   CamWnd& camwnd = *g_camwnd;
1758   Vector3 angles;
1759   angles[CAMERA_ROLL] = angles[CAMERA_PITCH] = 0;
1760   angles[CAMERA_YAW] = static_cast<float>(22.5 * floor((Camera_getAngles(camwnd)[CAMERA_YAW]+11)/22.5));
1761   Camera_setAngles(camwnd, angles);
1762 }
1763
1764 void Camera_ChangeFloorUp()
1765 {
1766   CamWnd& camwnd = *g_camwnd;
1767   camwnd.Cam_ChangeFloor (true);
1768 }
1769
1770 void Camera_ChangeFloorDown()
1771 {
1772   CamWnd& camwnd = *g_camwnd;
1773   camwnd.Cam_ChangeFloor (false);
1774 }
1775
1776 void Camera_CubeIn()
1777 {
1778   CamWnd& camwnd = *g_camwnd;
1779   g_camwindow_globals.m_nCubicScale--;
1780   if (g_camwindow_globals.m_nCubicScale < 1)
1781     g_camwindow_globals.m_nCubicScale = 1;
1782   Camera_updateProjection(camwnd.getCamera());
1783   CamWnd_Update(camwnd);
1784   g_pParentWnd->SetGridStatus();
1785 }
1786
1787 void Camera_CubeOut()
1788 {
1789   CamWnd& camwnd = *g_camwnd;
1790   g_camwindow_globals.m_nCubicScale++;
1791   if (g_camwindow_globals.m_nCubicScale > 23)
1792     g_camwindow_globals.m_nCubicScale = 23;
1793   Camera_updateProjection(camwnd.getCamera());
1794   CamWnd_Update(camwnd);
1795   g_pParentWnd->SetGridStatus();
1796 }
1797
1798 bool Camera_GetFarClip()
1799 {
1800   return g_camwindow_globals_private.m_bCubicClipping;
1801 }
1802
1803 BoolExportCaller g_getfarclip_caller(g_camwindow_globals_private.m_bCubicClipping);
1804 ToggleItem g_getfarclip_item(g_getfarclip_caller);
1805
1806 void Camera_SetFarClip(bool value)
1807 {
1808   CamWnd& camwnd = *g_camwnd;
1809   g_camwindow_globals_private.m_bCubicClipping = value;
1810   g_getfarclip_item.update();
1811   Camera_updateProjection(camwnd.getCamera());
1812   CamWnd_Update(camwnd);
1813 }
1814
1815 void Camera_ToggleFarClip()
1816 {
1817   Camera_SetFarClip(!Camera_GetFarClip());
1818 }
1819
1820
1821 void CamWnd_constructToolbar(GtkToolbar* toolbar)
1822 {
1823   toolbar_append_toggle_button(toolbar, "Cubic clip the camera view (\\)", "view_cubicclipping.bmp", "ToggleCubicClip");
1824 }
1825
1826 void CamWnd_registerShortcuts()
1827 {
1828   toggle_add_accelerator("ToggleCubicClip");
1829
1830   if(g_pGameDescription->mGameType == "doom3")
1831   {
1832     command_connect_accelerator("TogglePreview");
1833   }
1834
1835   command_connect_accelerator("CameraSpeedInc");
1836   command_connect_accelerator("CameraSpeedDec");
1837 }
1838
1839
1840 void GlobalCamera_Benchmark()
1841 {
1842   CamWnd& camwnd = *g_camwnd;
1843   camwnd.BenchMark();
1844 }
1845
1846 void GlobalCamera_Update()
1847 {
1848   CamWnd& camwnd = *g_camwnd;
1849   CamWnd_Update(camwnd);
1850 }
1851
1852 camera_draw_mode CamWnd_GetMode()
1853 {
1854   return camera_t::draw_mode;
1855 }
1856 void CamWnd_SetMode(camera_draw_mode mode)
1857 {
1858   ShaderCache_setBumpEnabled(mode == cd_lighting);
1859   camera_t::draw_mode = mode;
1860   if(g_camwnd != 0)
1861   {
1862     CamWnd_Update(*g_camwnd);
1863   }
1864 }
1865
1866 void CamWnd_TogglePreview(void)
1867 {
1868   // gametype must be doom3 for this function to work
1869   // if the gametype is not doom3 something is wrong with the
1870   // global command list or somebody else calls this function.
1871   ASSERT_MESSAGE(g_pGameDescription->mGameType == "doom3", "CamWnd_TogglePreview called although mGameType is not doom3 compatible");
1872
1873   // switch between textured and lighting mode
1874   CamWnd_SetMode((CamWnd_GetMode() == cd_lighting) ? cd_texture : cd_lighting);
1875 }
1876
1877
1878 CameraModel* g_camera_model = 0;
1879
1880 void CamWnd_LookThroughCamera(CamWnd& camwnd)
1881 {
1882   if(g_camera_model != 0)
1883   {
1884     CamWnd_Add_Handlers_Move(camwnd);
1885     g_camera_model->setCameraView(0, Callback());
1886     g_camera_model = 0;
1887     Camera_updateModelview(camwnd.getCamera());
1888     Camera_updateProjection(camwnd.getCamera());
1889     CamWnd_Update(camwnd);
1890   }
1891 }
1892
1893 inline CameraModel* Instance_getCameraModel(scene::Instance& instance)
1894 {
1895   return InstanceTypeCast<CameraModel>::cast(instance);
1896 }
1897
1898 void CamWnd_LookThroughSelected(CamWnd& camwnd)
1899 {
1900   if(g_camera_model != 0)
1901   {
1902     CamWnd_LookThroughCamera(camwnd);
1903   }
1904
1905   if(GlobalSelectionSystem().countSelected() != 0)
1906   {
1907     scene::Instance& instance = GlobalSelectionSystem().ultimateSelected();
1908     CameraModel* cameraModel = Instance_getCameraModel(instance);
1909     if(cameraModel != 0)
1910     {
1911       CamWnd_Remove_Handlers_Move(camwnd);
1912       g_camera_model = cameraModel;
1913       g_camera_model->setCameraView(&camwnd.getCameraView(), ReferenceCaller<CamWnd, CamWnd_LookThroughCamera>(camwnd));
1914     }
1915   }
1916 }
1917
1918 void GlobalCamera_LookThroughSelected()
1919 {
1920   CamWnd_LookThroughSelected(*g_camwnd);
1921 }
1922
1923 void GlobalCamera_LookThroughCamera()
1924 {
1925   CamWnd_LookThroughCamera(*g_camwnd);
1926 }
1927
1928
1929 void RenderModeImport(int value)
1930 {
1931   switch(value)
1932   {
1933   case 0:
1934     CamWnd_SetMode(cd_wire);
1935     break;
1936   case 1:
1937     CamWnd_SetMode(cd_solid);
1938     break;
1939   case 2:
1940     CamWnd_SetMode(cd_texture);
1941     break;
1942   case 3:
1943     CamWnd_SetMode(cd_lighting);
1944     break;
1945   default:
1946     CamWnd_SetMode(cd_texture);
1947   }
1948 }
1949 typedef FreeCaller1<int, RenderModeImport> RenderModeImportCaller;
1950
1951 void RenderModeExport(const IntImportCallback& importer)
1952 {
1953   switch(CamWnd_GetMode())
1954   {
1955   case cd_wire:
1956     importer(0);
1957     break;
1958   case cd_solid:
1959     importer(1);
1960     break;
1961   case cd_texture:
1962     importer(2);
1963     break;
1964   case cd_lighting:
1965     importer(3);
1966     break;
1967   }
1968 }
1969 typedef FreeCaller1<const IntImportCallback&, RenderModeExport> RenderModeExportCaller;
1970
1971 void Camera_constructPreferences(PreferencesPage& page)
1972 {
1973   page.appendSlider("Movement Speed", g_camwindow_globals_private.m_nMoveSpeed, TRUE, 0, 0, 100, MIN_CAM_SPEED, MAX_CAM_SPEED, 1, 10);
1974   page.appendCheckBox("", "Link strafe speed to movement speed", g_camwindow_globals_private.m_bCamLinkSpeed);
1975   page.appendSlider("Rotation Speed", g_camwindow_globals_private.m_nAngleSpeed, TRUE, 0, 0, 3, 1, 180, 1, 10);
1976   page.appendCheckBox("", "Invert mouse vertical axis", g_camwindow_globals_private.m_bCamInverseMouse);
1977   page.appendCheckBox(
1978     "", "Discrete movement",
1979     FreeCaller1<bool, CamWnd_Move_Discrete_Import>(),
1980     BoolExportCaller(g_camwindow_globals_private.m_bCamDiscrete)
1981   );
1982   page.appendCheckBox(
1983     "", "Enable far-clip plane",
1984     FreeCaller1<bool, Camera_SetFarClip>(),
1985     BoolExportCaller(g_camwindow_globals_private.m_bCubicClipping)
1986   );
1987
1988   if(g_pGameDescription->mGameType == "doom3")
1989   {
1990     const char* render_mode[] = { "Wireframe", "Flatshade", "Textured", "Lighting" };
1991
1992     page.appendCombo(
1993       "Render Mode",
1994       STRING_ARRAY_RANGE(render_mode),
1995       IntImportCallback(RenderModeImportCaller()),
1996       IntExportCallback(RenderModeExportCaller())
1997     );
1998   }
1999   else
2000   {
2001     const char* render_mode[] = { "Wireframe", "Flatshade", "Textured" };
2002
2003     page.appendCombo(
2004       "Render Mode",
2005       STRING_ARRAY_RANGE(render_mode),
2006       IntImportCallback(RenderModeImportCaller()),
2007       IntExportCallback(RenderModeExportCaller())
2008     );
2009   }
2010
2011     const char* strafe_mode[] = { "Both", "Forward", "Up" };
2012
2013     page.appendCombo(
2014       "Strafe Mode",
2015       g_camwindow_globals_private.m_nStrafeMode,
2016       STRING_ARRAY_RANGE(strafe_mode)
2017     );
2018 }
2019 void Camera_constructPage(PreferenceGroup& group)
2020 {
2021   PreferencesPage page(group.createPage("Camera", "Camera View Preferences"));
2022   Camera_constructPreferences(page);
2023 }
2024 void Camera_registerPreferencesPage()
2025 {
2026   PreferencesDialog_addSettingsPage(FreeCaller1<PreferenceGroup&, Camera_constructPage>());
2027 }
2028
2029 #include "preferencesystem.h"
2030 #include "stringio.h"
2031 #include "dialog.h"
2032
2033 typedef FreeCaller1<bool, CamWnd_Move_Discrete_Import> CamWndMoveDiscreteImportCaller;
2034
2035 void CameraSpeed_increase()
2036 {
2037   if(g_camwindow_globals_private.m_nMoveSpeed <= (MAX_CAM_SPEED - CAM_SPEED_STEP - 10))
2038   {
2039     g_camwindow_globals_private.m_nMoveSpeed += CAM_SPEED_STEP;
2040   } else {
2041     g_camwindow_globals_private.m_nMoveSpeed = MAX_CAM_SPEED - 10;
2042   }
2043 }
2044
2045 void CameraSpeed_decrease()
2046 {
2047   if(g_camwindow_globals_private.m_nMoveSpeed >= (MIN_CAM_SPEED + CAM_SPEED_STEP))
2048   {
2049     g_camwindow_globals_private.m_nMoveSpeed -= CAM_SPEED_STEP;
2050   } else {
2051     g_camwindow_globals_private.m_nMoveSpeed = MIN_CAM_SPEED;
2052   }
2053 }
2054
2055 /// \brief Initialisation for things that have the same lifespan as this module.
2056 void CamWnd_Construct()
2057 {
2058   GlobalCommands_insert("CenterView", FreeCaller<GlobalCamera_ResetAngles>(), Accelerator(GDK_End));
2059
2060   GlobalToggles_insert("ToggleCubicClip", FreeCaller<Camera_ToggleFarClip>(), ToggleItem::AddCallbackCaller(g_getfarclip_item), Accelerator('\\', (GdkModifierType)GDK_CONTROL_MASK));
2061   GlobalCommands_insert("CubicClipZoomIn", FreeCaller<Camera_CubeIn>(), Accelerator('[', (GdkModifierType)GDK_CONTROL_MASK));
2062   GlobalCommands_insert("CubicClipZoomOut", FreeCaller<Camera_CubeOut>(), Accelerator(']', (GdkModifierType)GDK_CONTROL_MASK));
2063
2064   GlobalCommands_insert("UpFloor", FreeCaller<Camera_ChangeFloorUp>(), Accelerator(GDK_Prior));
2065   GlobalCommands_insert("DownFloor", FreeCaller<Camera_ChangeFloorDown>(), Accelerator(GDK_Next));
2066
2067   GlobalToggles_insert("ToggleCamera", ToggleShown::ToggleCaller(g_camera_shown), ToggleItem::AddCallbackCaller(g_camera_shown.m_item), Accelerator('C', (GdkModifierType)(GDK_SHIFT_MASK|GDK_CONTROL_MASK)));
2068   GlobalCommands_insert("LookThroughSelected", FreeCaller<GlobalCamera_LookThroughSelected>());
2069   GlobalCommands_insert("LookThroughCamera", FreeCaller<GlobalCamera_LookThroughCamera>());
2070
2071   if(g_pGameDescription->mGameType == "doom3")
2072   {
2073     GlobalCommands_insert("TogglePreview", FreeCaller<CamWnd_TogglePreview>(), Accelerator(GDK_F3));
2074   }
2075
2076   GlobalCommands_insert("CameraSpeedInc", FreeCaller<CameraSpeed_increase>(), Accelerator(GDK_KP_Add, (GdkModifierType)GDK_SHIFT_MASK));
2077   GlobalCommands_insert("CameraSpeedDec", FreeCaller<CameraSpeed_decrease>(), Accelerator(GDK_KP_Subtract, (GdkModifierType)GDK_SHIFT_MASK));
2078
2079   GlobalShortcuts_insert("CameraForward", Accelerator(GDK_Up));
2080   GlobalShortcuts_insert("CameraBack", Accelerator(GDK_Down));
2081   GlobalShortcuts_insert("CameraLeft", Accelerator(GDK_Left));
2082   GlobalShortcuts_insert("CameraRight", Accelerator(GDK_Right));
2083   GlobalShortcuts_insert("CameraStrafeRight", Accelerator(GDK_period));
2084   GlobalShortcuts_insert("CameraStrafeLeft", Accelerator(GDK_comma));
2085
2086   GlobalShortcuts_insert("CameraUp", Accelerator('D'));
2087   GlobalShortcuts_insert("CameraDown", Accelerator('C'));
2088   GlobalShortcuts_insert("CameraAngleUp", Accelerator('A'));
2089   GlobalShortcuts_insert("CameraAngleDown", Accelerator('Z'));
2090
2091   GlobalShortcuts_insert("CameraFreeMoveForward", Accelerator(GDK_Up));
2092   GlobalShortcuts_insert("CameraFreeMoveBack", Accelerator(GDK_Down));
2093   GlobalShortcuts_insert("CameraFreeMoveLeft", Accelerator(GDK_Left));
2094   GlobalShortcuts_insert("CameraFreeMoveRight", Accelerator(GDK_Right));
2095
2096   GlobalToggles_insert("ShowStats", ShowStatsToggleCaller(), ToggleItem::AddCallbackCaller(g_show_stats));
2097
2098   GlobalPreferenceSystem().registerPreference("ShowStats", BoolImportStringCaller(g_camwindow_globals_private.m_showStats), BoolExportStringCaller(g_camwindow_globals_private.m_showStats));
2099   GlobalPreferenceSystem().registerPreference("MoveSpeed", IntImportStringCaller(g_camwindow_globals_private.m_nMoveSpeed), IntExportStringCaller(g_camwindow_globals_private.m_nMoveSpeed));
2100   GlobalPreferenceSystem().registerPreference("CamLinkSpeed", BoolImportStringCaller(g_camwindow_globals_private.m_bCamLinkSpeed), BoolExportStringCaller(g_camwindow_globals_private.m_bCamLinkSpeed));
2101   GlobalPreferenceSystem().registerPreference("AngleSpeed", IntImportStringCaller(g_camwindow_globals_private.m_nAngleSpeed), IntExportStringCaller(g_camwindow_globals_private.m_nAngleSpeed));
2102   GlobalPreferenceSystem().registerPreference("CamInverseMouse", BoolImportStringCaller(g_camwindow_globals_private.m_bCamInverseMouse), BoolExportStringCaller(g_camwindow_globals_private.m_bCamInverseMouse));
2103   GlobalPreferenceSystem().registerPreference("CamDiscrete", makeBoolStringImportCallback(CamWndMoveDiscreteImportCaller()), BoolExportStringCaller(g_camwindow_globals_private.m_bCamDiscrete));
2104   GlobalPreferenceSystem().registerPreference("CubicClipping", BoolImportStringCaller(g_camwindow_globals_private.m_bCubicClipping), BoolExportStringCaller(g_camwindow_globals_private.m_bCubicClipping));
2105   GlobalPreferenceSystem().registerPreference("CubicScale", IntImportStringCaller(g_camwindow_globals.m_nCubicScale), IntExportStringCaller(g_camwindow_globals.m_nCubicScale));
2106   GlobalPreferenceSystem().registerPreference("SI_Colors4", Vector3ImportStringCaller(g_camwindow_globals.color_cameraback), Vector3ExportStringCaller(g_camwindow_globals.color_cameraback));
2107   GlobalPreferenceSystem().registerPreference("SI_Colors12", Vector3ImportStringCaller(g_camwindow_globals.color_selbrushes3d), Vector3ExportStringCaller(g_camwindow_globals.color_selbrushes3d));
2108   GlobalPreferenceSystem().registerPreference("CameraRenderMode", makeIntStringImportCallback(RenderModeImportCaller()), makeIntStringExportCallback(RenderModeExportCaller()));
2109   GlobalPreferenceSystem().registerPreference("StrafeMode", IntImportStringCaller(g_camwindow_globals_private.m_nStrafeMode), IntExportStringCaller(g_camwindow_globals_private.m_nStrafeMode));
2110
2111   CamWnd_constructStatic();
2112
2113   Camera_registerPreferencesPage();
2114 }
2115 void CamWnd_Destroy()
2116 {
2117   CamWnd_destroyStatic();
2118 }