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