Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TGLPhysicalShape.cxx
Go to the documentation of this file.
1 // @(#)root/gl:$Id$
2 // Author: Richard Maunder 25/05/2005
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "TGLPhysicalShape.h"
13 #include "TGLLogicalShape.h"
14 #include "TGLPShapeRef.h"
15 #include "TGLCamera.h"
16 #include "TGLRnrCtx.h"
17 #include "TGLIncludes.h"
18 
19 #include "TGLScene.h"
20 
21 #include "TColor.h"
22 #include "TROOT.h"
23 
24 #include <cmath>
25 
26 // For debug tracing
27 #include "TClass.h"
28 #include "TError.h"
29 
30 /** \class TGLPhysicalShape
31 \ingroup opengl
32 Concrete physical shape - a GL drawable. Physical shapes are the
33 objects the user can actually see, select, move in the viewer. It is
34 a placement of the associated local frame TGLLogicaShape into the
35 world frame. The draw process is:
36 
37 Load attributes - material colors etc
38 Load translation matrix - placement
39 Load gl name (for selection)
40 Call our associated logical shape Draw() to draw placed shape
41 
42 The physical shape supports translation, scaling and rotation,
43 selection, color changes, and permitted modification flags etc.
44 A physical shape cannot modify or be bound to another (or no)
45 logical shape - hence const & handle. It can perform mutable
46 reference counting on the logical to enable purging.
47 
48 Physical shape also maintains a list of references to it and
49 provides notifications of change and destruction.
50 See class TGLPShapeRef which needs to be sub-classes for real use.
51 
52 See base/src/TVirtualViewer3D for description of common external 3D
53 viewer architecture and how external viewer clients use it.
54 */
55 
56 ClassImp(TGLPhysicalShape);
57 
58 ////////////////////////////////////////////////////////////////////////////////
59 /// Construct a physical shape using arguments:
60 /// - ID - unique drawable id.
61 /// - logicalShape - bound logical shape
62 /// - transform - transform for placement of logical drawing
63 /// - invertedWind - use inverted face polygon winding?
64 /// - rgba - basic four component (RGBA) diffuse color
65 
66 TGLPhysicalShape::TGLPhysicalShape(UInt_t id, const TGLLogicalShape & logicalShape,
67  const TGLMatrix & transform, Bool_t invertedWind,
68  const Float_t rgba[4]) :
69  fLogicalShape (&logicalShape),
70  fNextPhysical (0),
71  fFirstPSRef (0),
72  fID (id),
73  fTransform (transform),
74  fManip (kManipAll),
75  fSelected (0),
76  fInvertedWind (invertedWind),
77  fModified (kFALSE),
78  fIsScaleForRnr(kFALSE)
79 {
80  fLogicalShape->AddRef(this);
81  UpdateBoundingBox();
82 
83  // Initialise color
84  InitColor(rgba);
85 }
86 
87 ////////////////////////////////////////////////////////////////////////////////
88 /// Construct a physical shape using arguments:
89 /// - id - unique drawable id.
90 /// - logicalShape - bound logical shape
91 /// - transform - 16 Double_t component transform for placement of logical drawing
92 /// - invertedWind - use inverted face polygon winding?
93 /// - rgba - basic four component (RGBA) diffuse color
94 
95 TGLPhysicalShape::TGLPhysicalShape(UInt_t id, const TGLLogicalShape & logicalShape,
96  const Double_t * transform, Bool_t invertedWind,
97  const Float_t rgba[4]) :
98  fLogicalShape (&logicalShape),
99  fNextPhysical (0),
100  fFirstPSRef (0),
101  fID (id),
102  fTransform (transform),
103  fManip (kManipAll),
104  fSelected (0),
105  fInvertedWind (invertedWind),
106  fModified (kFALSE),
107  fIsScaleForRnr(kFALSE)
108 {
109  fLogicalShape->AddRef(this);
110 
111  // Temporary hack - invert the 3x3 part of matrix as TGeo sends this
112  // in opp layout to shear/translation parts. Speak to Andrei about best place
113  // to fix - probably when filling TBuffer3D - should always be OGL convention?
114  fTransform.Transpose3x3();
115  UpdateBoundingBox();
116 
117  // Initialise color
118  InitColor(rgba);
119 }
120 
121 ////////////////////////////////////////////////////////////////////////////////
122 /// Destroy the physical shape.
123 
124 TGLPhysicalShape::~TGLPhysicalShape()
125 {
126  // If destroyed from the logical shape itself the pointer has already
127  // been cleared.
128  if (fLogicalShape) fLogicalShape->SubRef(this);
129 
130  // Remove all references.
131  while (fFirstPSRef) {
132  fFirstPSRef->SetPShape(0);
133  }
134 }
135 
136 ////////////////////////////////////////////////////////////////////////////////
137 /// Add reference ref.
138 
139 void TGLPhysicalShape::AddReference(TGLPShapeRef* ref)
140 {
141  assert(ref != 0);
142 
143  ref->fNextPSRef = fFirstPSRef;
144  fFirstPSRef = ref;
145 }
146 
147 ////////////////////////////////////////////////////////////////////////////////
148 /// Remove reference ref.
149 
150 void TGLPhysicalShape::RemoveReference(TGLPShapeRef* ref)
151 {
152  assert(ref != 0);
153 
154  Bool_t found = kFALSE;
155  if (fFirstPSRef == ref) {
156  fFirstPSRef = ref->fNextPSRef;
157  found = kTRUE;
158  } else {
159  TGLPShapeRef *shp1 = fFirstPSRef, *shp2;
160  while ((shp2 = shp1->fNextPSRef) != 0) {
161  if (shp2 == ref) {
162  shp1->fNextPSRef = shp2->fNextPSRef;
163  found = kTRUE;
164  break;
165  }
166  shp1 = shp2;
167  }
168  }
169  if (found) {
170  ref->fNextPSRef = 0;
171  } else {
172  Error("TGLPhysicalShape::RemoveReference", "Attempt to un-ref an unregistered shape-ref.");
173  }
174 }
175 
176 ////////////////////////////////////////////////////////////////////////////////
177 /// Call this after modifying the physical so that the information
178 /// can be propagated to the object referencing it.
179 
180 void TGLPhysicalShape::Modified()
181 {
182  fModified = kTRUE;
183  TGLPShapeRef * ref = fFirstPSRef;
184  while (ref) {
185  ref->PShapeModified();
186  ref = ref->fNextPSRef;
187  }
188 }
189 
190 ////////////////////////////////////////////////////////////////////////////////
191 /// Update our internal bounding box (in global frame).
192 
193 void TGLPhysicalShape::UpdateBoundingBox()
194 {
195  fBoundingBox.Set(fLogicalShape->BoundingBox());
196  fBoundingBox.Transform(fTransform);
197 
198  fIsScaleForRnr = fTransform.IsScalingForRender();
199 
200  if (fLogicalShape->GetScene())
201  fLogicalShape->GetScene()->InvalidateBoundingBox();
202 }
203 
204 ////////////////////////////////////////////////////////////////////////////////
205 /// Initialise the colors, using basic RGBA diffuse material color supplied
206 
207 void TGLPhysicalShape::InitColor(const Float_t rgba[4])
208 {
209  // TODO: Make a color class
210  fColor[0] = rgba[0];
211  fColor[1] = rgba[1];
212  fColor[2] = rgba[2];
213  fColor[3] = rgba[3];
214 
215  fColor[4] = fColor[5] = fColor[6] = 0.0f; //ambient
216  fColor[8] = fColor[9] = fColor[10] = 0.7f; //specular
217  fColor[12] = fColor[13] = fColor[14] = 0.0f; //emission
218  fColor[7] = fColor[11] = fColor[15] = 1.0f; //alpha
219  fColor[16] = 60.0f; //shininess
220 }
221 
222 ////////////////////////////////////////////////////////////////////////////////
223 /// Set full color attributes - see OpenGL material documentation
224 /// for full description.
225 /// 0->3 diffuse, 4->7 ambient, 8->11 specular, 12->15 emission, 16 shininess
226 
227 void TGLPhysicalShape::SetColor(const Float_t color[17])
228 {
229  // TODO: Make a color class
230  for (UInt_t i = 0; i < 17; i++) {
231  fColor[i] = color[i];
232  }
233 
234  Modified();
235 }
236 
237 ////////////////////////////////////////////////////////////////////////////////
238 /// Set full color attributes to all physicals sharing the same
239 /// logical with this object.
240 
241 void TGLPhysicalShape::SetColorOnFamily(const Float_t color[17])
242 {
243  TGLPhysicalShape* pshp = const_cast<TGLPhysicalShape*>(fLogicalShape->GetFirstPhysical());
244  while (pshp)
245  {
246  pshp->SetColor(color);
247  pshp = pshp->fNextPhysical;
248  }
249 }
250 
251 ////////////////////////////////////////////////////////////////////////////////
252 /// Set color from ROOT color index and transparency [0,100].
253 
254 void TGLPhysicalShape::SetDiffuseColor(const Float_t rgba[4])
255 {
256  for (Int_t i=0; i<4; ++i)
257  fColor[i] = rgba[i];
258  Modified();
259 }
260 
261 ////////////////////////////////////////////////////////////////////////////////
262 /// Set color from RGBA quadruplet.
263 
264 void TGLPhysicalShape::SetDiffuseColor(const UChar_t rgba[4])
265 {
266  for (Int_t i=0; i<4; ++i)
267  fColor[i] = rgba[i]/255.0f;
268  Modified();
269 }
270 
271 ////////////////////////////////////////////////////////////////////////////////
272 /// Set color from standard ROOT representation, that is color index
273 /// + transparency in range [0, 100].
274 
275 void TGLPhysicalShape::SetDiffuseColor(Color_t ci, UChar_t transparency)
276 {
277  if (ci < 0) ci = 1;
278  TColor* c = gROOT->GetColor(ci);
279  if (c) {
280  fColor[0] = c->GetRed();
281  fColor[1] = c->GetGreen();
282  fColor[2] = c->GetBlue();
283  fColor[3] = 1.0f - 0.01*transparency;
284  }
285  Modified();
286 }
287 
288 ////////////////////////////////////////////////////////////////////////////////
289 /// Setup colors - avoid setting things not required
290 /// for current draw flags.
291 
292 void TGLPhysicalShape::SetupGLColors(TGLRnrCtx & rnrCtx, const Float_t* color) const
293 {
294  if (color == 0) color = fColor;
295 
296  switch (rnrCtx.DrawPass()) {
297  case TGLRnrCtx::kPassWireFrame:
298  {
299  // Wireframe needs basic color only
300  glColor4fv(color);
301  break;
302  }
303  case TGLRnrCtx::kPassFill:
304  case TGLRnrCtx::kPassOutlineFill:
305  {
306  // Both need material colors
307 
308  // Set back diffuse only for clipping where inner (back) faces
309  // are shown. Don't set shininess or specular as we want
310  // back face to appear as 'flat' as possible as crude visual
311  // approximation to proper capped clipped solid
312  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color);
313  glMaterialfv(GL_FRONT, GL_AMBIENT, color + 4);
314  glMaterialfv(GL_FRONT, GL_SPECULAR, color + 8);
315  glMaterialfv(GL_FRONT, GL_EMISSION, color + 12);
316  glMaterialf(GL_FRONT, GL_SHININESS, color[16]);
317  // Some objects use point/line graphics. Material mode disabled.
318  glColor4fv(color);
319  break;
320  }
321  case TGLRnrCtx::kPassOutlineLine:
322  {
323  // Outline also needs grey wireframe but respecting
324  // transparency of main diffuse color.
325  TGLUtil::ColorAlpha(rnrCtx.ColorSet().Outline(), 0.5f*color[3]);
326  break;
327  }
328  default:
329  {
330  assert(kFALSE);
331  }
332  }
333 }
334 
335 ////////////////////////////////////////////////////////////////////////////////
336 /// Draw physical shape, using LOD flags, potential from display list cache
337 
338 void TGLPhysicalShape::Draw(TGLRnrCtx & rnrCtx) const
339 {
340  // Debug tracing
341  if (gDebug > 4) {
342  Info("TGLPhysicalShape::Draw", "this %ld (class %s) LOD %d",
343  (Long_t)this, IsA()->GetName(), rnrCtx.ShapeLOD());
344  }
345 
346  // If LOD is pixel or less can draw pixel(point) directly, skipping
347  // any logical call, caching etc.
348  if (rnrCtx.ShapeLOD() == TGLRnrCtx::kLODPixel)
349  {
350  if (!rnrCtx.IsDrawPassOutlineLine())
351  {
352  glColor4fv(fColor);
353  glBegin(GL_POINTS);
354  glVertex3dv(&fTransform.CArr()[12]);
355  glEnd();
356  }
357  return;
358  }
359 
360  if (gDebug > 4) {
361  Info("TGLPhysicalShape::Draw", "this %ld (class %s) LOD %d",
362  (Long_t)this, IsA()->GetName(), rnrCtx.ShapeLOD());
363  }
364 
365  glPushMatrix();
366  glMultMatrixd(fTransform.CArr());
367  if (fIsScaleForRnr) glEnable(GL_NORMALIZE);
368  if (fInvertedWind) glFrontFace(GL_CW);
369  if (rnrCtx.Highlight())
370  {
371  glPushAttrib(GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT);
372 
373  glDisable(GL_LIGHTING);
374  glDisable(GL_DEPTH_TEST);
375 
376  if (rnrCtx.HighlightOutline())
377  {
378  static const Int_t offsets[20][2] =
379  { {-1,-1}, { 1,-1}, { 1, 1}, {-1, 1},
380  { 1, 0}, { 0, 1}, {-1, 0}, { 0,-1},
381  { 0,-2}, { 2, 0}, { 0, 2}, {-2, 0},
382  {-2,-2}, { 2,-2}, { 2, 2}, {-2, 2},
383  { 0,-3}, { 3, 0}, { 0, 3}, {-3, 0} };
384  static const Int_t max_off =
385  TGLUtil::GetScreenScalingFactor() > 1.5 ? 20 : 12;
386 
387  const TGLRect& vp = rnrCtx.RefCamera().RefViewport();
388 
389  for (int i = 0; i < max_off; ++i)
390  {
391  glViewport(vp.X() + offsets[i][0], vp.Y() + offsets[i][1], vp.Width(), vp.Height());
392  fLogicalShape->DrawHighlight(rnrCtx, this);
393  }
394 
395  glViewport(vp.X(), vp.Y(), vp.Width(), vp.Height());
396  }
397  else
398  {
399  fLogicalShape->DrawHighlight(rnrCtx, this);
400  }
401 
402  glPopAttrib();
403  }
404  else
405  {
406  SetupGLColors(rnrCtx);
407  if (rnrCtx.IsDrawPassOutlineLine())
408  TGLUtil::LockColor();
409  fLogicalShape->Draw(rnrCtx);
410  if (rnrCtx.IsDrawPassOutlineLine())
411  TGLUtil::UnlockColor();
412  }
413  if (fInvertedWind) glFrontFace(GL_CCW);
414  if (fIsScaleForRnr) glDisable(GL_NORMALIZE);
415  glPopMatrix();
416 }
417 
418 ////////////////////////////////////////////////////////////////////////////////
419 /// Calculate shape-lod, suitable for use under
420 /// projection defined by 'rnrCtx', taking account of which local
421 /// axes of the shape support LOD adjustment, and the global
422 /// 'sceneFlags' passed.
423 ///
424 /// Returned shapeLOD component is from 0 (kLODPixel - lowest
425 /// quality) to 100 (kLODHigh - highest quality).
426 ///
427 /// Scene flags are not used. LOD quantization is not done. RnrCtx
428 /// is not modified as this is called via lodification stage of
429 /// rendering.
430 
431 void TGLPhysicalShape::CalculateShapeLOD(TGLRnrCtx& rnrCtx, Float_t& pixSize, Short_t& shapeLOD) const
432 {
433  TGLLogicalShape::ELODAxes lodAxes = fLogicalShape->SupportedLODAxes();
434 
435  if (lodAxes == TGLLogicalShape::kLODAxesNone)
436  { // Shape doesn't support LOD along any axes return special
437  // unsupported LOD draw/cache flag.
438  // TODO: Still ... could check for kLODPixel when very small,
439  // by using diagonal from bounding-box and some special camera foo.
440  pixSize = 100; // Make up something / irrelevant.
441  shapeLOD = TGLRnrCtx::kLODHigh;
442  return;
443  }
444 
445  std::vector <Double_t> boxViewportDiags;
446  const TGLBoundingBox & box = BoundingBox();
447  const TGLCamera & camera = rnrCtx.RefCamera();
448 
449  if (lodAxes == TGLLogicalShape::kLODAxesAll) {
450  // Shape supports LOD along all axes - basis LOD hint on diagonal of viewport
451  // projection rect round whole bounding box
452  boxViewportDiags.push_back(camera.ViewportRect(box).Diagonal());
453  } else if (lodAxes == (TGLLogicalShape::kLODAxesY | TGLLogicalShape::kLODAxesZ)) {
454  // Shape supports LOD along Y/Z axes (not X). LOD hint based on longest
455  // diagonal (largest rect) of either of the X axis end faces
456  boxViewportDiags.push_back(camera.ViewportRect(box, TGLBoundingBox::kFaceLowX).Diagonal());
457  boxViewportDiags.push_back(camera.ViewportRect(box, TGLBoundingBox::kFaceHighX).Diagonal());
458  } else if (lodAxes == (TGLLogicalShape::kLODAxesX | TGLLogicalShape::kLODAxesZ)) {
459  // Shape supports LOD along X/Z axes (not Y). See above for Y/Z
460  boxViewportDiags.push_back(camera.ViewportRect(box, TGLBoundingBox::kFaceLowY).Diagonal());
461  boxViewportDiags.push_back(camera.ViewportRect(box, TGLBoundingBox::kFaceHighY).Diagonal());
462  } else if (lodAxes == (TGLLogicalShape::kLODAxesX | TGLLogicalShape::kLODAxesY)) {
463  // Shape supports LOD along X/Y axes (not Z). See above for Y/Z
464  boxViewportDiags.push_back(camera.ViewportRect(box, TGLBoundingBox::kFaceLowZ).Diagonal());
465  boxViewportDiags.push_back(camera.ViewportRect(box, TGLBoundingBox::kFaceHighZ).Diagonal());
466  } else {
467  // Don't bother to implement LOD calc for shapes supporting LOD along single
468  // axis only. Not needed at present + unlikely case - but could be done based
469  // on longest of projection of 4 edges of BBox along LOD axis. However this would
470  // probably be more costly than just using whole BB projection (as for all axes)
471  Error("TGLPhysicalShape::CalcPhysicalLOD", "LOD calculation for single axis not implemented presently");
472  shapeLOD = TGLRnrCtx::kLODMed;
473  return;
474  }
475 
476  // Find largest of the projected diagonals
477  Double_t largestDiagonal = 0.0;
478  for (UInt_t i = 0; i < boxViewportDiags.size(); i++) {
479  if (boxViewportDiags[i] > largestDiagonal) {
480  largestDiagonal = boxViewportDiags[i];
481  }
482  }
483  pixSize = largestDiagonal;
484 
485  if (largestDiagonal <= 1.0) {
486  shapeLOD = TGLRnrCtx::kLODPixel;
487  } else {
488  // TODO: Get real screen size - assuming 2000 pixel screen at present
489  // Calculate a non-linear sizing hint for this shape based on diagonal.
490  // Needs more experimenting with...
491  UInt_t lodApp = static_cast<UInt_t>(std::pow(largestDiagonal,0.4) * 100.0 / std::pow(2000.0,0.4));
492  if (lodApp > 1000) lodApp = 1000;
493  shapeLOD = (Short_t) lodApp;
494  }
495 }
496 
497 ////////////////////////////////////////////////////////////////////////////////
498 /// Factor in scene/vierer LOD and Quantize ... forward to
499 /// logical shape.
500 
501 void TGLPhysicalShape::QuantizeShapeLOD(Short_t shapeLOD, Short_t combiLOD, Short_t& quantLOD) const
502 {
503  quantLOD = fLogicalShape->QuantizeShapeLOD(shapeLOD, combiLOD);
504 }
505 
506 ////////////////////////////////////////////////////////////////////////////////
507 /// Request creation of context menu on shape, attached to 'menu' at screen position
508 /// 'x' 'y'
509 
510 void TGLPhysicalShape::InvokeContextMenu(TContextMenu & menu, UInt_t x, UInt_t y) const
511 {
512  // Just defer to our logical at present
513  fLogicalShape->InvokeContextMenu(menu, x, y);
514 }