1 // OGL_CAMERA.CPP
2
3 // Copyright (C) 1998 Tommi Hassinen.
4
5 // This package is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9
10 // This package is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14
15 // You should have received a copy of the GNU General Public License
16 // along with this package; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 /*################################################################################################*/
20
21 #include "ogl_camera.h" // config.h is here -> we get ENABLE-macros here...
22
23 #include "base_app.h"
24
25 #include <vector>
26 #include <algorithm> // for WIN32...
27 #include <sstream>
28 using namespace std;
29
30 // NOT_DEFINED is defined here the same way as in libghemical.
31
32 #ifndef NOT_DEFINED
33 #define NOT_DEFINED -1
34 #endif // NOT_DEFINED
35
36 /*################################################################################################*/
37
ogl_camera(const ogl_object_location & p1,GLfloat p2)38 ogl_camera::ogl_camera(const ogl_object_location & p1, GLfloat p2) :
39 ogl_dummy_object(p1)
40 {
41 focus = p2;
42 clipping = 0.90;
43
44 update_vdim = true;
45
46 use_local_lights = true;
47 use_global_lights = true;
48
49 ortho = false;
50
51 stereo_mode = false;
52 stereo_relaxed = false;
53
54 stereo_displacement = -0.25;
55 relaxed_separation = 0.60;
56
57 GetLD()->crd[2] = -focus;
58 }
59
60 // this is partial because only ogl_camera::OrbitObject() needs this!!!
61 // this is partial because only ogl_camera::OrbitObject() needs this!!!
62 // this is partial because only ogl_camera::OrbitObject() needs this!!!
63
ogl_camera(const ogl_camera & p1)64 ogl_camera::ogl_camera(const ogl_camera & p1) :
65 ogl_dummy_object(* p1.ol)
66 {
67 }
68
~ogl_camera(void)69 ogl_camera::~ogl_camera(void)
70 {
71 if (!obj_list.empty())
72 {
73 cout << "liboglappth : warning!!! ogl_camera::obj_list not empty!" << endl;
74 }
75
76 if (!wnd_vector.empty())
77 {
78 cout << "liboglappth : error!!! ogl_camera::wnd_vector not empty!" << endl;
79 exit(EXIT_FAILURE);
80 }
81 }
82
RegisterClient(base_wcl * wcl)83 void ogl_camera::RegisterClient(base_wcl * wcl)
84 {
85 vector<base_wcl *>::iterator it = find(wcl_vector.begin(), wcl_vector.end(), wcl);
86 if (it != wcl_vector.end())
87 {
88 cout << "liboglappth : duplicate wcl record!" << endl;
89 exit(EXIT_FAILURE);
90 }
91
92 wcl_vector.push_back(wcl);
93 }
94
UnregisterClient(base_wcl * wcl)95 void ogl_camera::UnregisterClient(base_wcl * wcl)
96 {
97 vector<base_wcl *>::iterator it = find(wcl_vector.begin(), wcl_vector.end(), wcl);
98 if (it == wcl_vector.end())
99 {
100 cout << "liboglappth : wcl record not found!" << endl;
101 exit(EXIT_FAILURE);
102 }
103
104 wcl_vector.erase(it);
105 }
106
RegisterWnd(base_wnd * wnd)107 void ogl_camera::RegisterWnd(base_wnd * wnd)
108 {
109 vector<base_wnd *>::iterator it = find(wnd_vector.begin(), wnd_vector.end(), wnd);
110 if (it != wnd_vector.end())
111 {
112 cout << "liboglappth : duplicate wnd record!" << endl;
113 exit(EXIT_FAILURE);
114 }
115
116 wnd_vector.push_back(wnd);
117 }
118
UnregisterWnd(base_wnd * wnd)119 void ogl_camera::UnregisterWnd(base_wnd * wnd)
120 {
121 vector<base_wnd *>::iterator it = find(wnd_vector.begin(), wnd_vector.end(), wnd);
122 if (it == wnd_vector.end())
123 {
124 cout << "liboglappth : wnd record not found!" << endl;
125 exit(EXIT_FAILURE);
126 }
127
128 wnd_vector.erase(it);
129 }
130
CopySettings(const ogl_camera * p1)131 bool ogl_camera::CopySettings(const ogl_camera * p1)
132 {
133 ogl_ol_static * ref = dynamic_cast<ogl_ol_static *>(ol);
134 if (ref == NULL) return false;
135
136 // now that we have verified that we can modify the ol-object, we will copy the settings...
137
138 focus = p1->focus; clipping = p1->clipping;
139 for (int n1 = 0;n1 < 3;n1++) GetLD()->crd[n1] = p1->GetSafeLD()->crd[n1];
140 GetLD()->zdir = p1->GetSafeLD()->zdir;
141 GetLD()->ydir = p1->GetSafeLD()->ydir;
142
143 // the local lights that are attached to the camera are not moved?!?!?!
144 // the local lights that are attached to the camera are not moved?!?!?!
145 // the local lights that are attached to the camera are not moved?!?!?!
146
147 return true;
148 }
149
RenderScene(base_wnd * wnd,bool accum,bool pick,int x,int y)150 void ogl_camera::RenderScene(base_wnd * wnd, bool accum, bool pick, int x, int y)
151 {
152 glMatrixMode(GL_PROJECTION);
153 glLoadIdentity();
154
155 // projection matrix is now initialized. if this is a "pick" operation, we must do
156 // some furter manipulations (gluPickMatrix() is used to "zoom" into the selection area).
157
158 if (pick)
159 {
160 glRenderMode(GL_SELECT);
161 GLint vp[4]; glGetIntegerv(GL_VIEWPORT, vp);
162
163 // is this dependent on screen resolution or what???
164 // there are some problems with selection in wireframe models...
165
166 const GLfloat region = 5.0;
167 gluPickMatrix(x, vp[3] - y, region, region, vp);
168 }
169
170 if (accum) glClear(GL_ACCUM_BUFFER_BIT);
171 else glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
172
173 // the stereo-modes related stuff start here...
174 // the stereo-modes related stuff start here...
175 // the stereo-modes related stuff start here...
176
177 // the stereo modes are implemented as a loop, that is cycled either once or twice.
178
179 int width = wnd->GetWidth(); if (stereo_mode && stereo_relaxed) width /= 2;
180 int height = wnd->GetHeight(); int shift = 0;
181
182 const GLfloat aspect = (GLfloat) width / (GLfloat) height;
183 const GLfloat fovy = (aspect > 1.0 ? 45.0 / aspect : 45.0);
184
185 if (update_vdim)
186 {
187 wnd->GetClient()->vdim[1] = focus * tan(M_PI * fovy / 360.0);
188 wnd->GetClient()->vdim[0] = aspect * wnd->GetClient()->vdim[1];
189 }
190
191 const int stereo_count = (stereo_mode ? 2 : 1);
192 for (int stereo_loop = 0;stereo_loop < stereo_count;stereo_loop++)
193 {
194 glViewport(shift, 0, width, height);
195 if (stereo_mode && stereo_relaxed)
196 {
197 // this will shift our viewport right at the next round...
198
199 shift += width;
200 }
201
202 const GLfloat nearplane = (1.0 - clipping) * focus;
203 const GLfloat farplane = (1.0 + clipping) * focus;
204
205 if (!ortho)
206 {
207 gluPerspective(fovy, aspect, nearplane, farplane);
208 }
209 else
210 {
211 const GLfloat vdimX = wnd->GetClient()->vdim[0];
212 const GLfloat vdimY = wnd->GetClient()->vdim[1];
213
214 glOrtho(-vdimX, +vdimX, -vdimY, +vdimY, nearplane, farplane);
215 }
216
217 const ogl_obj_loc_data * loc1 = GetSafeLD();
218
219 glMatrixMode(GL_MODELVIEW); glLoadIdentity();
220 oglv3d<GLfloat> target = oglv3d<GLfloat>(loc1->crd) + (loc1->zdir * focus);
221
222 const GLfloat * r1; const GLfloat * r2; const GLfloat * r3;
223
224 if (!stereo_mode)
225 {
226 // if not a stereo mode, then just use normal camera settings...
227
228 r1 = loc1->crd; // eye coordinates
229 r2 = target.data; // target coordinates
230 r3 = loc1->ydir.data; // y-direction vector
231 }
232 else
233 {
234 // for all stereo modes, translate the camera along camera's x-axis.
235 // y-direction will remain constant, but other directions won't. does it matter???
236
237 GLfloat displacement = stereo_displacement / 20.0;
238 if (!stereo_loop) displacement = -displacement;
239
240 oglv3d<GLfloat> xdir = loc1->ydir.vpr(loc1->zdir);
241
242 // displace each eye coordinate using camera's x-axis -> will be independent on camera's orientation!!!
243 // displace each eye coordinate using camera's x-axis -> will be independent on camera's orientation!!!
244 // displace each eye coordinate using camera's x-axis -> will be independent on camera's orientation!!!
245
246 static GLfloat tmp_crd[3];
247 tmp_crd[0] = loc1->crd[0] + displacement * xdir.data[0];
248 tmp_crd[1] = loc1->crd[1] + displacement * xdir.data[1];
249 tmp_crd[2] = loc1->crd[2] + displacement * xdir.data[2];
250
251 r1 = tmp_crd; // eye coordinates
252 r2 = target.data; // target coordinates
253 r3 = loc1->ydir.data; // y-direction vector
254
255 if (!stereo_relaxed)
256 {
257 // for the red-blue stereo mode, set the color masks...
258
259 if (!stereo_loop) glColorMask(GL_TRUE,GL_FALSE,GL_FALSE,GL_TRUE); // left_eye - red
260 else glColorMask(GL_FALSE,GL_FALSE,GL_TRUE,GL_TRUE); // right_eye - blue
261
262 // ...and clear the depth buffer!!!
263
264 glClear(GL_DEPTH_BUFFER_BIT);
265 }
266 }
267
268 gluLookAt(r1[0], r1[1], r1[2], r2[0], r2[1], r2[2], r3[0], r3[1], r3[2]);
269
270 if (stereo_mode && stereo_relaxed)
271 {
272 // this will do the "relaxed-eye stereo" separation effect.
273
274 // 20061005 ; this is not as good as it could be, since the
275 // effect is not a pure displacement but also a small rotation
276 // -> interferes with the stereo_displacement option!!!!!
277
278 GLfloat separation = relaxed_separation / 10.0;
279 if (!stereo_loop) separation = -separation;
280 else separation *= 2.0;
281
282 glMatrixMode(GL_PROJECTION);
283 glTranslatef(separation, 0.0, 0.0);
284 glMatrixMode(GL_MODELVIEW);
285 }
286
287 // now, it's time to set up the lights...
288
289 base_app::GetAppB()->UpdateLocalLightLocations(this);
290
291 // here, we are finally ready to actually render the view. for simple non-stereo views, just erase the background
292 // by calling glClear(), and then call prj->Render() to render the view. For relaxed-eye stereo views, two viewports
293 // are used and the view is therefore rendered twice, calling glClear only once. TODO(???): there is also a hardware
294 // stereo mode (not much supported yet, except SGI) that could be also used; that is a special graphics mode with two
295 // color buffers, and glDrawBuffer(???) is called to determine which is active.
296
297 // to make things more complicated, there is the "accumulation" effect that relies on glClear() call. to make all this
298 // work together, we must take all glClear() and glAccum() calls out from the prj->Render, and do them here.
299
300 // this "accumulation" stuff (used only in gt2 so far) is not tested after stereo mode changes...
301 // this "accumulation" stuff (used only in gt2 so far) is not tested after stereo mode changes...
302 // this "accumulation" stuff (used only in gt2 so far) is not tested after stereo mode changes...
303
304 glInitNames();
305 if (ogl_transformer::transform_in_progress)
306 {
307 // do the "transformation" effect:
308 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
309 // 1) render all non-selected parts of the scene.
310 // 2) render the selected parts translated/rotated as told by the transformer.
311
312 // accumulation should be DISABLED here!!!
313 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
314
315 wnd->GetClient()->RenderGL(Transform1);
316
317 glPushMatrix();
318 ogl_transformer::client->tc_object_ref->SetModelView();
319 wnd->GetClient()->RenderGL(Transform2);
320 glPopMatrix();
321 }
322 else
323 {
324 // just do the normal rendering...
325
326 wnd->GetClient()->RenderGL(Normal);
327 }
328
329 if (stereo_mode && !stereo_relaxed)
330 {
331 // reset the color mask...
332
333 glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
334 }
335 }
336
337 // end of stereo-modes related stuff...
338 // end of stereo-modes related stuff...
339 // end of stereo-modes related stuff...
340 }
341
342 // when rotating or translating a camera we must make all operations in reversed directions
343 // and also drag all those local lights along...
344
OrbitObject(const GLfloat * ang,const ogl_camera & cam)345 void ogl_camera::OrbitObject(const GLfloat * ang, const ogl_camera & cam)
346 {
347 GLfloat tmp_ang[3];
348 for (int n1 = 0;n1 < 3;n1++)
349 {
350 tmp_ang[n1] = -ang[n1];
351 }
352
353 base_app * app = base_app::GetAppB();
354 for (unsigned int n1 = 0;n1 < app->light_vector.size();n1++)
355 {
356 if (app->light_vector[n1]->owner != this) continue;
357 app->light_vector[n1]->OrbitObject(tmp_ang, cam);
358 }
359
360 ogl_dummy_object::OrbitObject(tmp_ang, cam);
361
362 DoCameraEvents();
363 }
364
365 // when rotating a camera, we must also make the local lights to orbit around the camera.
366 // dummy_object::OrbitObject() will orbit the lights around the camera's focus point, so here
367 // we must set the camera focus to zero before making the transformation...
368
RotateObject(const GLfloat * ang,const ogl_camera & cam)369 void ogl_camera::RotateObject(const GLfloat * ang, const ogl_camera & cam)
370 {
371 GLfloat tmp_ang[3];
372 for (int n1 = 0;n1 < 3;n1++)
373 {
374 tmp_ang[n1] = -ang[n1];
375 }
376
377 ogl_camera tmp_cam = cam;
378 tmp_cam.focus = 0.0;
379
380 base_app * app = base_app::GetAppB();
381 for (unsigned int n1 = 0;n1 < app->light_vector.size();n1++)
382 {
383 if (app->light_vector[n1]->owner != this) continue;
384 app->light_vector[n1]->OrbitObject(tmp_ang, tmp_cam);
385 }
386
387 ogl_dummy_object::RotateObject(tmp_ang, cam);
388
389 DoCameraEvents();
390 }
391
TranslateObject(const GLfloat * dst,const ogl_obj_loc_data * data)392 void ogl_camera::TranslateObject(const GLfloat * dst, const ogl_obj_loc_data * data)
393 {
394 GLfloat tmp_dst[3];
395 for (int n1 = 0;n1 < 3;n1++)
396 {
397 tmp_dst[n1] = -dst[n1];
398 }
399
400 base_app * app = base_app::GetAppB();
401 for (unsigned int n1 = 0;n1 < app->light_vector.size();n1++)
402 {
403 if (app->light_vector[n1]->owner != this) continue;
404 app->light_vector[n1]->TranslateObject(tmp_dst, data);
405 }
406
407 ogl_dummy_object::TranslateObject(tmp_dst, data);
408
409 DoCameraEvents();
410 }
411
DoCameraEvents(void)412 void ogl_camera::DoCameraEvents(void)
413 {
414 list<ogl_smart_object *>::iterator so_it;
415 for (so_it = obj_list.begin();so_it != obj_list.end();so_it++)
416 {
417 cout << "liboglappth : doing a camera_event..." << endl;
418 (* so_it)->CameraEvent(* this);
419 }
420 }
421
422 /*################################################################################################*/
423
424 ogl_transformer_client * ogl_transformer::client = NULL;
425 bool ogl_transformer::transform_in_progress = false;
426
ogl_transformer()427 ogl_transformer::ogl_transformer() :
428 ogl_dummy_object(true)
429 {
430 }
431
~ogl_transformer(void)432 ogl_transformer::~ogl_transformer(void)
433 {
434 }
435
Init(ogl_transformer_client * p1)436 void ogl_transformer::Init(ogl_transformer_client * p1)
437 {
438 client = p1;
439
440 ogl_obj_loc_data * data = GetLD();
441 for (int n1 = 0;n1 < 3;n1++) data->crd[n1] = 0.0;
442 data->zdir = oglv3d<GLfloat>(0.0, 0.0, 1.0);
443 data->ydir = oglv3d<GLfloat>(0.0, 1.0, 0.0);
444 }
445
GetMatrix(GLfloat * p1) const446 void ogl_transformer::GetMatrix(GLfloat * p1) const
447 {
448 glMatrixMode(GL_MODELVIEW);
449 glPushMatrix(); glLoadIdentity();
450 SetModelView(); glGetFloatv(GL_MODELVIEW_MATRIX, p1);
451 glPopMatrix();
452 }
453
BeginTransformation(void)454 bool ogl_transformer::BeginTransformation(void)
455 {
456 transform_in_progress = true;
457 client->BeginClientTransformation(this);
458 return true;
459 }
460
EndTransformation(void)461 bool ogl_transformer::EndTransformation(void)
462 {
463 transform_in_progress = false;
464 client->EndClientTransformation(this);
465 return true;
466 }
467
OrbitObject(const GLfloat * p1,const ogl_camera & p2)468 void ogl_transformer::OrbitObject(const GLfloat * p1, const ogl_camera & p2)
469 {
470 ogl_dummy_object::RotateObject(p1, p2);
471 }
472
RotateObject(const GLfloat * p1,const ogl_camera & p2)473 void ogl_transformer::RotateObject(const GLfloat * p1, const ogl_camera & p2)
474 {
475 ogl_dummy_object::OrbitObject(p1, p2);
476 }
477
478 /*################################################################################################*/
479
ogl_transformer_client(void)480 ogl_transformer_client::ogl_transformer_client(void)
481 {
482 tc_object_ref = NULL;
483 }
484
~ogl_transformer_client(void)485 ogl_transformer_client::~ogl_transformer_client(void)
486 {
487 }
488
489 /*################################################################################################*/
490
491 // eof
492