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