source: trunk/source/visualization/OpenGL/src/G4OpenGLViewer.cc@ 925

Last change on this file since 925 was 925, checked in by garnier, 17 years ago

marche, mais en cours de debug sur OI

  • Property svn:mime-type set to text/cpp
File size: 35.3 KB
RevLine 
[529]1//
2// ********************************************************************
3// * License and Disclaimer *
4// * *
5// * The Geant4 software is copyright of the Copyright Holders of *
6// * the Geant4 Collaboration. It is provided under the terms and *
7// * conditions of the Geant4 Software License, included in the file *
8// * LICENSE and available at http://cern.ch/geant4/license . These *
9// * include a list of copyright holders. *
10// * *
11// * Neither the authors of this software system, nor their employing *
12// * institutes,nor the agencies providing financial support for this *
13// * work make any representation or warranty, express or implied, *
14// * regarding this software system or assume any liability for its *
15// * use. Please see the license in the file LICENSE and URL above *
16// * for the full disclaimer and the limitation of liability. *
17// * *
18// * This code implementation is the result of the scientific and *
19// * technical work of the GEANT4 collaboration. *
20// * By using, copying, modifying or distributing the software (or *
21// * any work based on the software) you agree to acknowledge its *
22// * use in resulting scientific publications, and indicate your *
23// * acceptance of all terms of the Geant4 Software license. *
24// ********************************************************************
25//
26//
[924]27// $Id: G4OpenGLViewer.cc,v 1.48 2009/02/16 15:31:05 lgarnier Exp $
[873]28// GEANT4 tag $Name: $
[529]29//
30//
31// Andrew Walkden 27th March 1996
32// OpenGL view - opens window, hard copy, etc.
33
34#ifdef G4VIS_BUILD_OPENGL_DRIVER
35
36#include "G4ios.hh"
37#include "G4OpenGLViewer.hh"
38#include "G4OpenGLSceneHandler.hh"
39#include "G4OpenGLTransform3D.hh"
[923]40#include "G4OpenGL2PSAction.hh"
[529]41
42#include "G4Scene.hh"
43#include "G4VisExtent.hh"
44#include "G4LogicalVolume.hh"
45#include "G4VSolid.hh"
46#include "G4Point3D.hh"
47#include "G4Normal3D.hh"
48#include "G4Plane3D.hh"
[593]49#include "G4AttHolder.hh"
50#include "G4AttCheck.hh"
[918]51
[923]52// GL2PS
53#include "Geant4_gl2ps.h"
54
[593]55#include <sstream>
[529]56
[593]57static const char* gouraudtriangleEPS[] =
58{
59 "/bd{bind def}bind def /triangle { aload pop setrgbcolor aload pop 5 3",
60 "roll 4 2 roll 3 2 roll exch moveto lineto lineto closepath fill } bd",
61 "/computediff1 { 2 copy sub abs threshold ge {pop pop pop true} { exch 2",
62 "index sub abs threshold ge { pop pop true} { sub abs threshold ge } ifelse",
63 "} ifelse } bd /computediff3 { 3 copy 0 get 3 1 roll 0 get 3 1 roll 0 get",
64 "computediff1 {true} { 3 copy 1 get 3 1 roll 1 get 3 1 roll 1 get",
65 "computediff1 {true} { 3 copy 2 get 3 1 roll 2 get 3 1 roll 2 get",
66 "computediff1 } ifelse } ifelse } bd /middlecolor { aload pop 4 -1 roll",
67 "aload pop 4 -1 roll add 2 div 5 1 roll 3 -1 roll add 2 div 3 1 roll add 2",
68 "div 3 1 roll exch 3 array astore } bd /gouraudtriangle { computediff3 { 4",
69 "-1 roll aload 7 1 roll 6 -1 roll pop 3 -1 roll pop add 2 div 3 1 roll add",
70 "2 div exch 3 -1 roll aload 7 1 roll exch pop 4 -1 roll pop add 2 div 3 1",
71 "roll add 2 div exch 3 -1 roll aload 7 1 roll pop 3 -1 roll pop add 2 div 3",
72 "1 roll add 2 div exch 7 3 roll 10 -3 roll dup 3 index middlecolor 4 1 roll",
73 "2 copy middlecolor 4 1 roll 3 copy pop middlecolor 4 1 roll 13 -1 roll",
74 "aload pop 17 index 6 index 15 index 19 index 6 index 17 index 6 array",
75 "astore 10 index 10 index 14 index gouraudtriangle 17 index 5 index 17",
76 "index 19 index 5 index 19 index 6 array astore 10 index 9 index 13 index",
77 "gouraudtriangle 13 index 16 index 5 index 15 index 18 index 5 index 6",
78 "array astore 12 index 12 index 9 index gouraudtriangle 17 index 16 index",
79 "15 index 19 index 18 index 17 index 6 array astore 10 index 12 index 14",
80 "index gouraudtriangle 18 {pop} repeat } { aload pop 5 3 roll aload pop 7 3",
81 "roll aload pop 9 3 roll 4 index 6 index 4 index add add 3 div 10 1 roll 7",
82 "index 5 index 3 index add add 3 div 10 1 roll 6 index 4 index 2 index add",
83 "add 3 div 10 1 roll 9 {pop} repeat 3 array astore triangle } ifelse } bd",
84 NULL
85};
86
[529]87G4OpenGLViewer::G4OpenGLViewer (G4OpenGLSceneHandler& scene):
88G4VViewer (scene, -1),
[916]89fPrintFilename ("G4OpenGL.eps"),
90fPrintColour (true),
91fVectoredPs (true),
[593]92fOpenGLSceneHandler(scene),
[529]93background (G4Colour(0.,0.,0.)),
94transparency_enabled (true),
95antialiasing_enabled (false),
96haloing_enabled (false),
[789]97fStartTime(-DBL_MAX),
98fEndTime(DBL_MAX),
[529]99fFadeFactor(0.),
100fDisplayHeadTime(false),
101fDisplayHeadTimeX(-0.9),
102fDisplayHeadTimeY(-0.9),
103fDisplayHeadTimeSize(24.),
104fDisplayHeadTimeRed(0.),
105fDisplayHeadTimeGreen(1.),
106fDisplayHeadTimeBlue(1.),
107fDisplayLightFront(false),
108fDisplayLightFrontX(0.),
109fDisplayLightFrontY(0.),
110fDisplayLightFrontZ(0.),
111fDisplayLightFrontT(0.),
112fDisplayLightFrontRed(0.),
113fDisplayLightFrontGreen(1.),
[916]114fDisplayLightFrontBlue(0.),
115fPointSize (0)
[529]116{
117 // Make changes to view parameters for OpenGL...
118 fVP.SetAutoRefresh(true);
119 fDefaultVP.SetAutoRefresh(true);
[897]120 fWinSize_x = fVP.GetWindowSizeHintX();
121 fWinSize_y = fVP.GetWindowSizeHintY();
[529]122
[921]123 fGL2PSAction = new G4OpenGL2PSAction();
124
[529]125 // glClearColor (0.0, 0.0, 0.0, 0.0);
126 // glClearDepth (1.0);
127 // glDisable (GL_BLEND);
128 // glDisable (GL_LINE_SMOOTH);
129 // glDisable (GL_POLYGON_SMOOTH);
130
131}
132
133G4OpenGLViewer::~G4OpenGLViewer () {}
134
135void G4OpenGLViewer::InitializeGLView ()
136{
137 glClearColor (0.0, 0.0, 0.0, 0.0);
138 glClearDepth (1.0);
139 glDisable (GL_BLEND);
140 glDisable (GL_LINE_SMOOTH);
141 glDisable (GL_POLYGON_SMOOTH);
142}
143
144void G4OpenGLViewer::ClearView () {
145 glClearColor (background.GetRed(),
146 background.GetGreen(),
147 background.GetBlue(),
148 1.);
149 glClearDepth (1.0);
150 //Below line does not compile with Mesa includes.
151 //glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
152 glClear (GL_COLOR_BUFFER_BIT);
153 glClear (GL_DEPTH_BUFFER_BIT);
154 glClear (GL_STENCIL_BUFFER_BIT);
155 glFlush ();
156}
157
[906]158
159/**
160 * Set the viewport of the scene
161 */
162void G4OpenGLViewer::ResizeGLView()
163{
164 int side = fWinSize_x;
165 if (fWinSize_y < fWinSize_x) side = fWinSize_y;
166 glViewport((fWinSize_x - side) / 2, (fWinSize_y - side) / 2, side, side);
167}
168
169
[529]170void G4OpenGLViewer::SetView () {
[858]171
172 if (!fSceneHandler.GetScene()) {
[908]173 G4cerr << "G4OpenGLStoredViewer: Creating a Viewer without a scene is not allowed. \nPlease use /vis/scene/create before /vis/open/.... "
[858]174 << G4endl;
175 return;
176 }
[529]177 // Calculates view representation based on extent of object being
178 // viewed and (initial) viewpoint. (Note: it can change later due
179 // to user interaction via visualization system's GUI.)
180
181 // Lighting.
182 GLfloat lightPosition [4];
183 lightPosition [0] = fVP.GetActualLightpointDirection().x();
184 lightPosition [1] = fVP.GetActualLightpointDirection().y();
185 lightPosition [2] = fVP.GetActualLightpointDirection().z();
186 lightPosition [3] = 0.;
187 // Light position is "true" light direction, so must come after gluLookAt.
188 GLfloat ambient [] = { 0.2, 0.2, 0.2, 1.};
189 GLfloat diffuse [] = { 0.8, 0.8, 0.8, 1.};
190 glEnable (GL_LIGHT0);
191 glLightfv (GL_LIGHT0, GL_AMBIENT, ambient);
192 glLightfv (GL_LIGHT0, GL_DIFFUSE, diffuse);
193
194 // Get radius of scene, etc.
195 // Note that this procedure properly takes into account zoom, dolly and pan.
196 const G4Point3D targetPoint
197 = fSceneHandler.GetScene()->GetStandardTargetPoint()
198 + fVP.GetCurrentTargetPoint ();
199 G4double radius = fSceneHandler.GetScene()->GetExtent().GetExtentRadius();
200 if(radius<=0.) radius = 1.;
201 const G4double cameraDistance = fVP.GetCameraDistance (radius);
202 const G4Point3D cameraPosition =
203 targetPoint + cameraDistance * fVP.GetViewpointDirection().unit();
[906]204 const GLdouble pnear = fVP.GetNearDistance (cameraDistance, radius);
205 const GLdouble pfar = fVP.GetFarDistance (cameraDistance, pnear, radius);
[529]206 const GLdouble right = fVP.GetFrontHalfHeight (pnear, radius);
207 const GLdouble left = -right;
208 const GLdouble bottom = left;
209 const GLdouble top = right;
210
[906]211 // FIXME
212 ResizeGLView();
[908]213 //SHOULD SetWindowsSizeHint()...
[906]214
[529]215 glMatrixMode (GL_PROJECTION); // set up Frustum.
216 glLoadIdentity();
217
[906]218 const G4Vector3D scaleFactor = fVP.GetScaleFactor();
219 glScaled(scaleFactor.x(),scaleFactor.y(),scaleFactor.z());
[529]220
221 if (fVP.GetFieldHalfAngle() == 0.) {
222 glOrtho (left, right, bottom, top, pnear, pfar);
223 }
224 else {
225 glFrustum (left, right, bottom, top, pnear, pfar);
[908]226 }
[906]227
[529]228 glMatrixMode (GL_MODELVIEW); // apply further transformations to scene.
229 glLoadIdentity();
230
231 const G4Normal3D& upVector = fVP.GetUpVector ();
232 G4Point3D gltarget;
233 if (cameraDistance > 1.e-6 * radius) {
234 gltarget = targetPoint;
235 }
236 else {
237 gltarget = targetPoint - radius * fVP.GetViewpointDirection().unit();
238 }
239
240 const G4Point3D& pCamera = cameraPosition; // An alias for brevity.
241 gluLookAt (pCamera.x(), pCamera.y(), pCamera.z(), // Viewpoint.
242 gltarget.x(), gltarget.y(), gltarget.z(), // Target point.
243 upVector.x(), upVector.y(), upVector.z()); // Up vector.
[906]244
[529]245 // Light position is "true" light direction, so must come after gluLookAt.
246 glLightfv (GL_LIGHT0, GL_POSITION, lightPosition);
247
248 // OpenGL no longer seems to reconstruct clipped edges, so, when the
249 // BooleanProcessor is up to it, abandon this and use generic
250 // clipping in G4OpenGLSceneHandler::CreateSectionPolyhedron. Also,
251 // force kernel visit on change of clipping plane in
252 // G4OpenGLStoredViewer::CompareForKernelVisit.
253 if (fVP.IsSection () ) { // pair of back to back clip planes.
254 const G4Plane3D& s = fVP.GetSectionPlane ();
255 double sArray[4];
256 sArray[0] = s.a();
257 sArray[1] = s.b();
258 sArray[2] = s.c();
259 sArray[3] = s.d() + radius * 1.e-05;
260 glClipPlane (GL_CLIP_PLANE0, sArray);
261 glEnable (GL_CLIP_PLANE0);
262 sArray[0] = -s.a();
263 sArray[1] = -s.b();
264 sArray[2] = -s.c();
265 sArray[3] = -s.d() + radius * 1.e-05;
266 glClipPlane (GL_CLIP_PLANE1, sArray);
267 glEnable (GL_CLIP_PLANE1);
268 } else {
269 glDisable (GL_CLIP_PLANE0);
270 glDisable (GL_CLIP_PLANE1);
271 }
272
273 const G4Planes& cutaways = fVP.GetCutawayPlanes();
274 size_t nPlanes = cutaways.size();
275 if (fVP.IsCutaway() &&
276 fVP.GetCutawayMode() == G4ViewParameters::cutawayIntersection &&
277 nPlanes > 0) {
278 double a[4];
279 a[0] = cutaways[0].a();
280 a[1] = cutaways[0].b();
281 a[2] = cutaways[0].c();
282 a[3] = cutaways[0].d();
283 glClipPlane (GL_CLIP_PLANE2, a);
284 glEnable (GL_CLIP_PLANE2);
285 if (nPlanes > 1) {
286 a[0] = cutaways[1].a();
287 a[1] = cutaways[1].b();
288 a[2] = cutaways[1].c();
289 a[3] = cutaways[1].d();
290 glClipPlane (GL_CLIP_PLANE3, a);
291 glEnable (GL_CLIP_PLANE3);
292 }
293 if (nPlanes > 2) {
294 a[0] = cutaways[2].a();
295 a[1] = cutaways[2].b();
296 a[2] = cutaways[2].c();
297 a[3] = cutaways[2].d();
298 glClipPlane (GL_CLIP_PLANE4, a);
299 glEnable (GL_CLIP_PLANE4);
300 }
301 } else {
302 glDisable (GL_CLIP_PLANE2);
303 glDisable (GL_CLIP_PLANE3);
304 glDisable (GL_CLIP_PLANE4);
305 }
306
307 // Background.
308 background = fVP.GetBackgroundColour ();
309
310}
311
312void G4OpenGLViewer::HaloingFirstPass () {
313
314 //To perform haloing, first Draw all information to the depth buffer
315 //alone, using a chunky line width, and then Draw all info again, to
316 //the colour buffer, setting a thinner line width an the depth testing
317 //function to less than or equal, so if two lines cross, the one
318 //passing behind the other will not pass the depth test, and so not
319 //get rendered either side of the infront line for a short distance.
320
321 //First, disable writing to the colo(u)r buffer...
322 glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
323
324 //Now enable writing to the depth buffer...
325 glDepthMask (GL_TRUE);
326 glDepthFunc (GL_LESS);
327 glClearDepth (1.0);
328
329 //Finally, set the line width to something wide...
330 glLineWidth (3.0);
331
332}
333
334void G4OpenGLViewer::HaloingSecondPass () {
335
336 //And finally, turn the colour buffer back on with a sesible line width...
337 glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
338 glDepthFunc (GL_LEQUAL);
339 glLineWidth (1.0);
340
341}
342
[593]343void G4OpenGLViewer::Pick(GLdouble x, GLdouble y)
344{
345 //G4cout << "X: " << x << ", Y: " << y << G4endl;
346 const G4int BUFSIZE = 512;
347 GLuint selectBuffer[BUFSIZE];
348 glSelectBuffer(BUFSIZE, selectBuffer);
349 glRenderMode(GL_SELECT);
350 glInitNames();
351 glPushName(0);
352 glMatrixMode(GL_PROJECTION);
353 G4double currentProjectionMatrix[16];
354 glGetDoublev(GL_PROJECTION_MATRIX, currentProjectionMatrix);
355 glPushMatrix();
356 glLoadIdentity();
357 GLint viewport[4];
358 glGetIntegerv(GL_VIEWPORT, viewport);
359 // Define 5x5 pixel pick area
360 gluPickMatrix(x, viewport[3] - y, 5., 5., viewport);
361 glMultMatrixd(currentProjectionMatrix);
362 glMatrixMode(GL_MODELVIEW);
363 DrawView();
364 GLint hits = glRenderMode(GL_RENDER);
365 if (hits < 0)
366 G4cout << "Too many hits. Zoom in to reduce overlaps." << G4cout;
367 else if (hits > 0) {
368 //G4cout << hits << " hit(s)" << G4endl;
369 GLuint* p = selectBuffer;
370 for (GLint i = 0; i < hits; ++i) {
371 GLuint nnames = *p++;
372 *p++; //OR GLuint zmin = *p++;
373 *p++; //OR GLuint zmax = *p++;
374 //G4cout << "Hit " << i << ": " << nnames << " names"
375 // << "\nzmin: " << zmin << ", zmax: " << zmax << G4endl;
376 for (GLuint j = 0; j < nnames; ++j) {
377 GLuint name = *p++;
378 //G4cout << "Name " << j << ": PickName: " << name << G4endl;
379 std::map<GLuint, G4AttHolder*>::iterator iter =
380 fOpenGLSceneHandler.fPickMap.find(name);
381 if (iter != fOpenGLSceneHandler.fPickMap.end()) {
382 G4AttHolder* attHolder = iter->second;
383 if(attHolder && attHolder->GetAttDefs().size()) {
384 for (size_t i = 0; i < attHolder->GetAttDefs().size(); ++i) {
385 G4cout << G4AttCheck(attHolder->GetAttValues()[i],
386 attHolder->GetAttDefs()[i]);
387 }
388 }
389 }
390 }
391 G4cout << G4endl;
392 }
393 }
394 glMatrixMode(GL_PROJECTION);
395 glPopMatrix();
396 glMatrixMode(GL_MODELVIEW);
397}
398
[917]399void G4OpenGLViewer::printVectoredEPS() {
[593]400
401 // Print vectored PostScript
402
403 G4int size = 5000000;
[754]404 GLfloat* feedback_buffer = new GLfloat[size];
[593]405 glFeedbackBuffer (size, GL_3D_COLOR, feedback_buffer);
406 glRenderMode (GL_FEEDBACK);
407
408 DrawView();
[754]409
410 GLint returned;
[593]411 returned = glRenderMode (GL_RENDER);
412
[754]413 FILE* file;
[916]414 if (!fPrintFilename.empty()) {
415 file = fopen (fPrintFilename.c_str(), "w");
[593]416 if (file) {
417 spewWireframeEPS (file, returned, feedback_buffer, "rendereps");
[918]418 fclose(file);
[593]419 } else {
[916]420 printf("Could not open %s\n", fPrintFilename.c_str());
[593]421 }
422 } else {
423 printBuffer (returned, feedback_buffer);
424 }
425
426 delete[] feedback_buffer;
427}
428
429void G4OpenGLViewer::print3DcolorVertex(GLint size, GLint * count, GLfloat * buffer)
430{
431 G4int i;
432
433 printf(" ");
434 for (i = 0; i < 7; i++) {
435 printf("%4.2f ", buffer[size - (*count)]);
436 *count = *count - 1;
437 }
438 printf("\n");
439}
440
441void G4OpenGLViewer::spewWireframeEPS (FILE* file, GLint size, GLfloat* buffer, const char* cr) {
442
443 GLfloat EPS_GOURAUD_THRESHOLD=0.1;
444
445 GLfloat clearColor[4], viewport[4];
446 GLfloat lineWidth;
447 G4int i;
448
449 glGetFloatv (GL_VIEWPORT, viewport);
450 glGetFloatv (GL_COLOR_CLEAR_VALUE, clearColor);
451 glGetFloatv (GL_LINE_WIDTH, &lineWidth);
[916]452 glGetFloatv (GL_POINT_SIZE, &fPointSize);
[593]453
454 fputs ("%!PS-Adobe-2.0 EPSF-2.0\n", file);
455 fprintf (file, "%%%%Creator: %s (using OpenGL feedback)\n", cr);
456 fprintf (file, "%%%%BoundingBox: %g %g %g %g\n", viewport[0], viewport[1], viewport[2], viewport[3]);
457 fputs ("%%EndComments\n", file);
458 fputs ("\n", file);
459 fputs ("gsave\n", file);
460 fputs ("\n", file);
461
462 fputs ("% the gouraudtriangle PostScript fragment below is free\n", file);
463 fputs ("% written by Frederic Delhoume (delhoume@ilog.fr)\n", file);
464 fprintf (file, "/threshold %g def\n", EPS_GOURAUD_THRESHOLD);
465 for (i=0; gouraudtriangleEPS[i]; i++) {
466 fprintf (file, "%s\n", gouraudtriangleEPS[i]);
467 }
468
469 fprintf(file, "\n%g setlinewidth\n", lineWidth);
470
471 fprintf (file, "%g %g %g setrgbcolor\n", clearColor[0], clearColor[1], clearColor[2]);
472 fprintf (file, "%g %g %g %g rectfill\n\n", viewport[0], viewport[1], viewport[2], viewport[3]);
473
474 spewSortedFeedback (file, size, buffer);
475
476 fputs ("grestore\n\n", file);
477 fputs ("showpage\n", file);
478
479 fclose(file);
480}
481
482void G4OpenGLViewer::printBuffer (GLint size, GLfloat* buffer) {
483
484 GLint count;
485 G4int token, nvertices;
486
487 count=size;
488 while(count) {
489 token=G4int (buffer[size-count]);
490 count--;
491 switch (token) {
492
493 case GL_PASS_THROUGH_TOKEN:
494 printf ("GL_PASS_THROUGH_TOKEN\n");
495 printf (" %4.2f\n", buffer[size-count]);
496 count--;
497 break;
498
499 case GL_POINT_TOKEN:
500 printf ("GL_POINT_TOKEN\n");
501 print3DcolorVertex (size, &count, buffer);
502 break;
503
504 case GL_LINE_TOKEN:
505 printf ("GL_LINE_TOKEN\n");
506 print3DcolorVertex (size, &count, buffer);
507 print3DcolorVertex (size, &count, buffer);
508 break;
509
510 case GL_LINE_RESET_TOKEN:
511 printf ("GL_LINE_RESET_TOKEN\n");
512 print3DcolorVertex (size, &count, buffer);
513 print3DcolorVertex (size, &count, buffer);
514 break;
515
516 case GL_POLYGON_TOKEN:
517 printf ("GL_POLYGON_TOKEN\n");
518 nvertices=G4int (buffer[size-count]);
519 count--;
520 for (; nvertices>0; nvertices--) {
521 print3DcolorVertex (size, &count, buffer);
522 }
523 }
524 }
525}
526
527G4float* G4OpenGLViewer::spewPrimitiveEPS (FILE* file, GLfloat* loc) {
528
529 G4int token;
530 G4int nvertices, i;
531 GLfloat red, green, blue, intensity;
532 G4int smooth;
533 GLfloat dx, dy, dr, dg, db, absR, absG, absB, colormax;
534 G4int steps;
535 Feedback3Dcolor *vertex;
536 GLfloat xstep(0.), ystep(0.), rstep(0.), gstep(0.), bstep(0.);
537 GLfloat xnext(0.), ynext(0.), rnext(0.), gnext(0.), bnext(0.), distance(0.);
538
539 token=G4int (*loc);
540 loc++;
541 switch (token) {
542 case GL_LINE_RESET_TOKEN:
543 case GL_LINE_TOKEN:
544 vertex=(Feedback3Dcolor*)loc;
545 dr=vertex[1].red - vertex[0].red;
546 dg=vertex[1].green - vertex[0].green;
547 db=vertex[1].blue - vertex[0].blue;
548
[916]549 if (!fPrintColour) {
[593]550 dr+=(dg+db);
551 dr/=3.0;
552 dg=dr;
553 db=dr;
554 }
555
556 if (dr!=0 || dg!=0 || db!=0) {
557 dx=vertex[1].x - vertex[0].x;
558 dy=vertex[1].y - vertex[0].y;
559 distance=std::sqrt(dx*dx + dy*dy);
560
561 absR=std::fabs(dr);
562 absG=std::fabs(dg);
563 absB=std::fabs(db);
564
565 #define Max(a, b) (((a)>(b))?(a):(b))
566
567 #define EPS_SMOOTH_LINE_FACTOR 0.06
568
569 colormax=Max(absR, Max(absG, absB));
570 steps=Max(1, G4int (colormax*distance*EPS_SMOOTH_LINE_FACTOR));
571
572 xstep=dx/steps;
573 ystep=dy/steps;
574
575 rstep=dr/steps;
576 gstep=dg/steps;
577 bstep=db/steps;
578
579 xnext=vertex[0].x;
580 ynext=vertex[0].y;
581 rnext=vertex[0].red;
582 gnext=vertex[0].green;
583 bnext=vertex[0].blue;
584
[916]585 if (!fPrintColour) {
[593]586 rnext+=(gnext+bnext);
587 rnext/=3.0;
588 gnext=rnext;
589 bnext=rnext;
590 }
591
592 xnext -= xstep/2.0;
593 ynext -= ystep/2.0;
594 rnext -= rstep/2.0;
595 gnext -= gstep/2.0;
596 bnext -= bstep/2.0;
597 } else {
598 steps=0;
599 }
[916]600 if (fPrintColour) {
[593]601 fprintf (file, "%g %g %g setrgbcolor\n",
602 vertex[0].red, vertex[0].green, vertex[0].blue);
603 } else {
604 intensity = (vertex[0].red + vertex[0].green + vertex[0].blue) / 3.0;
605 fprintf (file, "%g %g %g setrgbcolor\n",
606 intensity, intensity, intensity);
607 }
608 fprintf (file, "%g %g moveto\n", vertex[0].x, vertex[0].y);
609
610 for (i=0; i<steps; i++) {
611
612 xnext += xstep;
613 ynext += ystep;
614 rnext += rstep;
615 gnext += gstep;
616 bnext += bstep;
617
618 fprintf (file, "%g %g lineto stroke\n", xnext, ynext);
619 fprintf (file, "%g %g %g setrgbcolor\n", rnext, gnext, bnext);
620 fprintf (file, "%g %g moveto\n", xnext, ynext);
621 }
622 fprintf (file, "%g %g lineto stroke\n", vertex[1].x, vertex[1].y);
623
624 loc += 14;
625 break;
626
627 case GL_POLYGON_TOKEN:
628 nvertices = G4int (*loc);
629 loc++;
630 vertex=(Feedback3Dcolor*)loc;
631 if (nvertices>0) {
632 red=vertex[0].red;
633 green=vertex[0].green;
634 blue=vertex[0].blue;
635 smooth=0;
636
[916]637 if (!fPrintColour) {
[593]638 red+=(green+blue);
639 red/=3.0;
640 green=red;
641 blue=red;
642 }
643
[916]644 if (fPrintColour) {
[593]645 for (i=1; i<nvertices; i++) {
646 if (red!=vertex[i].red || green!=vertex[i].green || blue!=vertex[i].blue) {
647 smooth=1;
648 break;
649 }
650 }
651 } else {
652 for (i=1; i<nvertices; i++) {
653 intensity = vertex[i].red + vertex[i].green + vertex[i].blue;
654 intensity/=3.0;
655 if (red!=intensity) {
656 smooth=1;
657 break;
658 }
659 }
660 }
661
662 if (smooth) {
663 G4int triOffset;
664 for (i=0; i<nvertices-2; i++) {
665 triOffset = i*7;
666 fprintf (file, "[%g %g %g %g %g %g]",
667 vertex[0].x, vertex[i+1].x, vertex[i+2].x,
668 vertex[0].y, vertex[i+1].y, vertex[i+2].y);
[916]669 if (fPrintColour) {
[593]670 fprintf (file, " [%g %g %g] [%g %g %g] [%g %g %g] gouraudtriangle\n",
671 vertex[0].red, vertex[0].green, vertex[0].blue,
672 vertex[i+1].red, vertex[i+1].green, vertex[i+1].blue,
673 vertex[i+2].red, vertex[i+2].green, vertex[i+2].blue);
674 } else {
675
676 intensity = vertex[0].red + vertex[0].green + vertex[0].blue;
677 intensity/=3.0;
678 fprintf (file, " [%g %g %g]", intensity, intensity, intensity);
679
680 intensity = vertex[1].red + vertex[1].green + vertex[1].blue;
681 intensity/=3.0;
682 fprintf (file, " [%g %g %g]", intensity, intensity, intensity);
683
684 intensity = vertex[2].red + vertex[2].green + vertex[2].blue;
685 intensity/=3.0;
686 fprintf (file, " [%g %g %g] gouraudtriangle\n", intensity, intensity, intensity);
687 }
688 }
689 } else {
690 fprintf (file, "newpath\n");
691 fprintf (file, "%g %g %g setrgbcolor\n", red, green, blue);
692 fprintf (file, "%g %g moveto\n", vertex[0].x, vertex[0].y);
693 for (i=1; i<nvertices; i++) {
694 fprintf (file, "%g %g lineto\n", vertex[i].x, vertex[i].y);
695 }
696 fprintf (file, "closepath fill\n\n");
697 }
698 }
699 loc += nvertices*7;
700 break;
701
702 case GL_POINT_TOKEN:
703 vertex=(Feedback3Dcolor*)loc;
[916]704 if (fPrintColour) {
[593]705 fprintf (file, "%g %g %g setrgbcolor\n", vertex[0].red, vertex[0].green, vertex[0].blue);
706 } else {
707 intensity = vertex[0].red + vertex[0].green + vertex[0].blue;
708 intensity/=3.0;
709 fprintf (file, "%g %g %g setrgbcolor\n", intensity, intensity, intensity);
710 }
[916]711 fprintf(file, "%g %g %g 0 360 arc fill\n\n", vertex[0].x, vertex[0].y, fPointSize / 2.0);
[593]712 loc += 7; /* Each vertex element in the feedback
713 buffer is 7 GLfloats. */
714 break;
715 default:
716 /* XXX Left as an excersie to the reader. */
717 static G4bool spewPrimitiveEPSWarned = false;
718 if (!spewPrimitiveEPSWarned) {
719 std::ostringstream oss;
720 oss <<
721 "Incomplete implementation. Unexpected token (" << token << ")."
722 "\n (Seems to be caused by text.)";
723 G4Exception("G4OpenGLViewer::spewPrimitiveEPS",
724 "Unexpected token",
725 JustWarning,
726 oss.str().c_str());
727 spewPrimitiveEPSWarned = true;
728 }
729 }
730 return loc;
731}
732
733typedef struct G4OpenGLViewerDepthIndex {
734 GLfloat *ptr;
735 GLfloat depth;
736} DepthIndex;
737
738extern "C" {
739 int G4OpenGLViewercompare(const void *a, const void *b)
740 {
741 const DepthIndex *p1 = (DepthIndex *) a;
742 const DepthIndex *p2 = (DepthIndex *) b;
743 GLfloat diff = p2->depth - p1->depth;
744
745 if (diff > 0.0) {
746 return 1;
747 } else if (diff < 0.0) {
748 return -1;
749 } else {
750 return 0;
751 }
752 }
753}
754
[914]755GLubyte* G4OpenGLViewer::grabPixels (int inColor, unsigned int width, unsigned int height) {
756
757 GLubyte* buffer;
758 GLint swapbytes, lsbfirst, rowlength;
759 GLint skiprows, skippixels, alignment;
760 GLenum format;
761 int size;
762
763 if (inColor) {
764 format = GL_RGB;
765 size = width*height*3;
766 } else {
767 format = GL_LUMINANCE;
768 size = width*height*1;
769 }
770
771 buffer = new GLubyte[size];
772 if (buffer == NULL)
773 return NULL;
774
775 glGetIntegerv (GL_UNPACK_SWAP_BYTES, &swapbytes);
776 glGetIntegerv (GL_UNPACK_LSB_FIRST, &lsbfirst);
777 glGetIntegerv (GL_UNPACK_ROW_LENGTH, &rowlength);
778
779 glGetIntegerv (GL_UNPACK_SKIP_ROWS, &skiprows);
780 glGetIntegerv (GL_UNPACK_SKIP_PIXELS, &skippixels);
781 glGetIntegerv (GL_UNPACK_ALIGNMENT, &alignment);
782
783 glPixelStorei (GL_UNPACK_SWAP_BYTES, GL_FALSE);
784 glPixelStorei (GL_UNPACK_LSB_FIRST, GL_FALSE);
785 glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
786
787 glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
788 glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
789 glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
790
791 glReadPixels (0, 0, (GLsizei)width, (GLsizei)height, format, GL_UNSIGNED_BYTE, (GLvoid*) buffer);
792
793 glPixelStorei (GL_UNPACK_SWAP_BYTES, swapbytes);
794 glPixelStorei (GL_UNPACK_LSB_FIRST, lsbfirst);
795 glPixelStorei (GL_UNPACK_ROW_LENGTH, rowlength);
796
797 glPixelStorei (GL_UNPACK_SKIP_ROWS, skiprows);
798 glPixelStorei (GL_UNPACK_SKIP_PIXELS, skippixels);
799 glPixelStorei (GL_UNPACK_ALIGNMENT, alignment);
800
801 return buffer;
802}
803
804int G4OpenGLViewer::generateEPS (const char* filnam,
805 int inColour,
806 unsigned int width,
807 unsigned int height) {
808
809 FILE* fp;
810 GLubyte* pixels;
811 GLubyte* curpix;
812 int components, pos, i;
813
[925]814 WritePostScript("AAAAAAAAAAAAAAA.ps");
[914]815 pixels = grabPixels (inColour, width, height);
816
817 if (pixels == NULL)
818 return 1;
819 if (inColour) {
820 components = 3;
821 } else {
822 components = 1;
823 }
824
825 fp = fopen (filnam, "w");
826 if (fp == NULL) {
827 return 2;
828 }
829
830 fprintf (fp, "%%!PS-Adobe-2.0 EPSF-1.2\n");
831 fprintf (fp, "%%%%Title: %s\n", filnam);
832 fprintf (fp, "%%%%Creator: OpenGL pixmap render output\n");
833 fprintf (fp, "%%%%BoundingBox: 0 0 %d %d\n", width, height);
834 fprintf (fp, "%%%%EndComments\n");
835 fprintf (fp, "gsave\n");
836 fprintf (fp, "/bwproc {\n");
837 fprintf (fp, " rgbproc\n");
838 fprintf (fp, " dup length 3 idiv string 0 3 0 \n");
839 fprintf (fp, " 5 -1 roll {\n");
840 fprintf (fp, " add 2 1 roll 1 sub dup 0 eq\n");
841 fprintf (fp, " { pop 3 idiv 3 -1 roll dup 4 -1 roll dup\n");
842 fprintf (fp, " 3 1 roll 5 -1 roll } put 1 add 3 0 \n");
843 fprintf (fp, " { 2 1 roll } ifelse\n");
844 fprintf (fp, " }forall\n");
845 fprintf (fp, " pop pop pop\n");
846 fprintf (fp, "} def\n");
847 fprintf (fp, "systemdict /colorimage known not {\n");
848 fprintf (fp, " /colorimage {\n");
849 fprintf (fp, " pop\n");
850 fprintf (fp, " pop\n");
851 fprintf (fp, " /rgbproc exch def\n");
852 fprintf (fp, " { bwproc } image\n");
853 fprintf (fp, " } def\n");
854 fprintf (fp, "} if\n");
855 fprintf (fp, "/picstr %d string def\n", width * components);
856 fprintf (fp, "%d %d scale\n", width, height);
857 fprintf (fp, "%d %d %d\n", width, height, 8);
858 fprintf (fp, "[%d 0 0 %d 0 0]\n", width, height);
859 fprintf (fp, "{currentfile picstr readhexstring pop}\n");
860 fprintf (fp, "false %d\n", components);
861 fprintf (fp, "colorimage\n");
862
863 curpix = (GLubyte*) pixels;
864 pos = 0;
865 for (i = width*height*components; i>0; i--) {
866 fprintf (fp, "%02hx ", *(curpix++));
867 if (++pos >= 32) {
868 fprintf (fp, "\n");
869 pos = 0;
870 }
871 }
872 if (pos)
873 fprintf (fp, "\n");
874
875 fprintf (fp, "grestore\n");
876 fprintf (fp, "showpage\n");
877 delete pixels;
878 fclose (fp);
879 return 0;
880}
881
[918]882
883void G4OpenGLViewer::WritePostScript(const char *aFile) {
884
[921]885 if (!fGL2PSAction) return;
886
[923]887 fGL2PSAction->setFileName("PostScriptViaGL2PS.ps");
[921]888 if (fGL2PSAction->enableFileWriting()) {
889 DrawView();
890 fGL2PSAction->disableFileWriting();
891 }
[924]892 FILE *fFile = fopen(aFile,"w");
[918]893 if(!fFile) {
894 G4cout << "G4OpenGLViewer::WritePostScript. Cannot open file " <<aFile << G4endl;
895 return;
896 }
897
898 // Get the viewport
899 GLint viewport[4];
900 glGetIntegerv(GL_VIEWPORT, viewport);
901
902
903
904 int psformat;
905 // psformat = GL2PS_PDF;
906 psformat = GL2PS_PS;
907 // psformat = GL2PS_SVG;
908 // psformat = GL2PS_EPS;
909
910 // int old_bg_gradient = CTX.bg_gradient;
911 // if(!CTX.print.eps_background) CTX.bg_gradient = 0;
912
913// PixelBuffer buffer(width, height, GL_RGB, GL_FLOAT);
914
915// if(CTX.print.eps_quality == 0)
916// buffer.Fill(CTX.batch);
917
918 int pssort = GL2PS_SIMPLE_SORT;
919 // int pssort =
920 // (CTX.print.eps_quality == 3) ? GL2PS_NO_SORT :
921 // (CTX.print.eps_quality == 2) ? GL2PS_BSP_SORT :
922 // GL2PS_SIMPLE_SORT;
923 int psoptions = GL2PS_SIMPLE_LINE_OFFSET | GL2PS_DRAW_BACKGROUND;
924 // GL2PS_SIMPLE_LINE_OFFSET | GL2PS_SILENT |
925 // (CTX.print.eps_occlusion_culling ? GL2PS_OCCLUSION_CULL : 0) |
926 // (CTX.print.eps_best_root ? GL2PS_BEST_ROOT : 0) |
927 // (CTX.print.eps_background ? GL2PS_DRAW_BACKGROUND : 0) |
928 // (CTX.print.eps_compress ? GL2PS_COMPRESS : 0) |
929 // (CTX.print.eps_ps3shading ? 0 : GL2PS_NO_PS3_SHADING);
930
931 GLint buffsize = 0;
932 int res = GL2PS_OVERFLOW;
933 while(res == GL2PS_OVERFLOW) {
934 buffsize += 2048 * 2048;
935 gl2psBeginPage("MyTitle", "Geant4", viewport,
936 psformat, pssort, psoptions, GL_RGBA, 0, NULL,
937 15, 20, 10, buffsize, fFile, aFile);
938 DrawView();
939 res = gl2psEndPage();
940 }
941
942 // CTX.bg_gradient = old_bg_gradient;
943 fclose(fFile);
944
945}
946
[712]947GLdouble G4OpenGLViewer::getSceneNearWidth()
948{
949 const G4Point3D targetPoint
950 = fSceneHandler.GetScene()->GetStandardTargetPoint()
951 + fVP.GetCurrentTargetPoint ();
952 G4double radius = fSceneHandler.GetScene()->GetExtent().GetExtentRadius();
953 if(radius<=0.) radius = 1.;
954 const G4double cameraDistance = fVP.GetCameraDistance (radius);
955 const GLdouble pnear = fVP.GetNearDistance (cameraDistance, radius);
956 return 2 * fVP.GetFrontHalfHeight (pnear, radius);
957}
958
959GLdouble G4OpenGLViewer::getSceneFarWidth()
960{
961 const G4Point3D targetPoint
962 = fSceneHandler.GetScene()->GetStandardTargetPoint()
963 + fVP.GetCurrentTargetPoint ();
964 G4double radius = fSceneHandler.GetScene()->GetExtent().GetExtentRadius();
965 if(radius<=0.) radius = 1.;
966 const G4double cameraDistance = fVP.GetCameraDistance (radius);
967 const GLdouble pnear = fVP.GetNearDistance (cameraDistance, radius);
968 const GLdouble pfar = fVP.GetFarDistance (cameraDistance, pnear, radius);
969 return 2 * fVP.GetFrontHalfHeight (pfar, radius);
970}
971
972
973GLdouble G4OpenGLViewer::getSceneDepth()
974{
975 const G4Point3D targetPoint
976 = fSceneHandler.GetScene()->GetStandardTargetPoint()
977 + fVP.GetCurrentTargetPoint ();
978 G4double radius = fSceneHandler.GetScene()->GetExtent().GetExtentRadius();
979 if(radius<=0.) radius = 1.;
980 const G4double cameraDistance = fVP.GetCameraDistance (radius);
981 const GLdouble pnear = fVP.GetNearDistance (cameraDistance, radius);
982 return fVP.GetFarDistance (cameraDistance, pnear, radius)- pnear;
983}
984
985
[593]986void G4OpenGLViewer::spewSortedFeedback(FILE * file, GLint size, GLfloat * buffer)
987{
988 int token;
989 GLfloat *loc, *end;
990 Feedback3Dcolor *vertex;
991 GLfloat depthSum;
992 int nprimitives, item;
993 DepthIndex *prims;
994 int nvertices, i;
995
996 end = buffer + size;
997
998 /* Count how many primitives there are. */
999 nprimitives = 0;
1000 loc = buffer;
1001 while (loc < end) {
1002 token = int (*loc);
1003 loc++;
1004 switch (token) {
1005 case GL_LINE_TOKEN:
1006 case GL_LINE_RESET_TOKEN:
1007 loc += 14;
1008 nprimitives++;
1009 break;
1010 case GL_POLYGON_TOKEN:
1011 nvertices = int (*loc);
1012 loc++;
1013 loc += (7 * nvertices);
1014 nprimitives++;
1015 break;
1016 case GL_POINT_TOKEN:
1017 loc += 7;
1018 nprimitives++;
1019 break;
1020 default:
1021 /* XXX Left as an excersie to the reader. */
1022 static G4bool spewSortedFeedbackWarned = false;
1023 if (!spewSortedFeedbackWarned) {
1024 std::ostringstream oss;
1025 oss <<
1026 "Incomplete implementation. Unexpected token (" << token << ")."
1027 "\n (Seems to be caused by text.)";
1028 G4Exception("G4OpenGLViewer::spewSortedFeedback",
1029 "Unexpected token",
1030 JustWarning,
1031 oss.str().c_str());
1032 spewSortedFeedbackWarned = true;
1033 }
1034 nprimitives++;
1035 }
1036 }
1037
1038 /* Allocate an array of pointers that will point back at
1039 primitives in the feedback buffer. There will be one
1040 entry per primitive. This array is also where we keep the
1041 primitive's average depth. There is one entry per
1042 primitive in the feedback buffer. */
1043 prims = (DepthIndex *) malloc(sizeof(DepthIndex) * nprimitives);
1044
1045 item = 0;
1046 loc = buffer;
1047 while (loc < end) {
1048 prims[item].ptr = loc; /* Save this primitive's location. */
1049 token = int (*loc);
1050 loc++;
1051 switch (token) {
1052 case GL_LINE_TOKEN:
1053 case GL_LINE_RESET_TOKEN:
1054 vertex = (Feedback3Dcolor *) loc;
1055 depthSum = vertex[0].z + vertex[1].z;
1056 prims[item].depth = depthSum / 2.0;
1057 loc += 14;
1058 break;
1059 case GL_POLYGON_TOKEN:
1060 nvertices = int (*loc);
1061 loc++;
1062 vertex = (Feedback3Dcolor *) loc;
1063 depthSum = vertex[0].z;
1064 for (i = 1; i < nvertices; i++) {
1065 depthSum += vertex[i].z;
1066 }
1067 prims[item].depth = depthSum / nvertices;
1068 loc += (7 * nvertices);
1069 break;
1070 case GL_POINT_TOKEN:
1071 vertex = (Feedback3Dcolor *) loc;
1072 prims[item].depth = vertex[0].z;
1073 loc += 7;
1074 break;
1075 default:
1076 /* XXX Left as an excersie to the reader. */
1077 assert(1);
1078 }
1079 item++;
1080 }
1081 assert(item == nprimitives);
1082
1083 /* Sort the primitives back to front. */
1084 qsort(prims, nprimitives, sizeof(DepthIndex), G4OpenGLViewercompare);
1085
1086 /* Understand that sorting by a primitives average depth
1087 doesn't allow us to disambiguate some cases like self
1088 intersecting polygons. Handling these cases would require
1089 breaking up the primitives. That's too involved for this
1090 example. Sorting by depth is good enough for lots of
1091 applications. */
1092
1093 /* Emit the Encapsulated PostScript for the primitives in
1094 back to front order. */
1095 for (item = 0; item < nprimitives; item++) {
1096 (void) spewPrimitiveEPS(file, prims[item].ptr);
1097 }
1098
1099 free(prims);
1100}
1101
[798]1102void G4OpenGLViewer::rotateScene(G4double dx, G4double dy,G4double deltaRotation)
1103{
1104
1105 G4Vector3D vp;
1106 G4Vector3D up;
1107
1108 G4Vector3D xprime;
1109 G4Vector3D yprime;
1110 G4Vector3D zprime;
1111
1112 G4double delta_alpha;
1113 G4double delta_theta;
1114
1115 G4Vector3D new_vp;
1116 G4Vector3D new_up;
1117
1118 G4double cosalpha;
1119 G4double sinalpha;
1120
1121 G4Vector3D a1;
1122 G4Vector3D a2;
1123 G4Vector3D delta;
1124 G4Vector3D viewPoint;
1125
1126
1127 //phi spin stuff here
1128
1129 vp = fVP.GetViewpointDirection ().unit ();
1130 up = fVP.GetUpVector ().unit ();
1131
1132 yprime = (up.cross(vp)).unit();
1133 zprime = (vp.cross(yprime)).unit();
1134
1135 if (fVP.GetLightsMoveWithCamera()) {
1136 delta_alpha = dy * deltaRotation;
1137 delta_theta = -dx * deltaRotation;
1138 } else {
1139 delta_alpha = -dy * deltaRotation;
1140 delta_theta = dx * deltaRotation;
1141 }
1142
1143 delta_alpha *= deg;
1144 delta_theta *= deg;
1145
1146 new_vp = std::cos(delta_alpha) * vp + std::sin(delta_alpha) * zprime;
1147
1148 // to avoid z rotation flipping
1149 // to allow more than 360° rotation
[847]1150
[801]1151 const G4Point3D targetPoint
1152 = fSceneHandler.GetScene()->GetStandardTargetPoint()
1153 + fVP.GetCurrentTargetPoint ();
1154 G4double radius = fSceneHandler.GetScene()->GetExtent().GetExtentRadius();
1155 if(radius<=0.) radius = 1.;
1156 const G4double cameraDistance = fVP.GetCameraDistance (radius);
1157 const G4Point3D cameraPosition =
1158 targetPoint + cameraDistance * fVP.GetViewpointDirection().unit();
1159
[798]1160 if (fVP.GetLightsMoveWithCamera()) {
1161 new_up = (new_vp.cross(yprime)).unit();
1162 if (new_vp.z()*vp.z() <0) {
1163 new_up.set(new_up.x(),-new_up.y(),new_up.z());
1164 }
1165 } else {
1166 new_up = up;
1167 if (new_vp.z()*vp.z() <0) {
1168 new_up.set(new_up.x(),-new_up.y(),new_up.z());
1169 }
1170 }
1171 fVP.SetUpVector(new_up);
1172 ////////////////
1173 // Rotates by fixed azimuthal angle delta_theta.
1174
1175 cosalpha = new_up.dot (new_vp.unit());
1176 sinalpha = std::sqrt (1. - std::pow (cosalpha, 2));
1177 yprime = (new_up.cross (new_vp.unit())).unit ();
1178 xprime = yprime.cross (new_up);
1179 // Projection of vp on plane perpendicular to up...
1180 a1 = sinalpha * xprime;
1181 // Required new projection...
1182 a2 = sinalpha * (std::cos (delta_theta) * xprime + std::sin (delta_theta) * yprime);
1183 // Required Increment vector...
1184 delta = a2 - a1;
1185 // So new viewpoint is...
1186 viewPoint = new_vp.unit() + delta;
1187
1188 fVP.SetViewAndLights (viewPoint);
1189}
1190
[529]1191#endif
Note: See TracBrowser for help on using the repository browser.