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