0025854: Visualization, TKOpenGl - add option to request Core profile 3.2+
[occt.git] / src / NIS / NIS_View.cxx
1 // Created on: 2007-07-06
2 // Created by: Alexander GRIGORIEV
3 // Copyright (c) 2007-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <NIS_View.hxx>
17 #include <NIS_InteractiveContext.hxx>
18 #include <NIS_InteractiveObject.hxx>
19 #include <gp_Ax1.hxx>
20 #include <Visual3d_View.hxx>
21 #include <Bnd_B2f.hxx>
22 #include <TColStd_MapIteratorOfPackedMapOfInteger.hxx>
23
24 #include <OpenGl_GlCore11.hxx>
25
26 IMPLEMENT_STANDARD_HANDLE  (NIS_View, V3d_View)
27 IMPLEMENT_STANDARD_RTTIEXT (NIS_View, V3d_View)
28
29 //=======================================================================
30 //function : NIS_View()
31 //purpose  : Constructor
32 //=======================================================================
33
34 NIS_View::NIS_View (const Handle(V3d_Viewer)&    theViewer,
35                     const Handle(Aspect_Window)& theWindow)
36   : V3d_View (theViewer),
37     myIsTopHilight(Standard_False),
38     myDoHilightSelected(Standard_True)
39 {
40   if (!theWindow.IsNull())
41     V3d_View::SetWindow (theWindow, NULL, &MyCallback, this);
42 }
43
44 //=======================================================================
45 //function : SetWindow
46 //purpose  :
47 //=======================================================================
48
49 void NIS_View::SetWindow(const Handle(Aspect_Window) &theWindow)
50 {
51   V3d_View::SetWindow (theWindow, NULL, &MyCallback, this);
52 }
53
54 // //=======================================================================
55 // //function : ~NIS_View
56 // //purpose  : Destructor
57 // //=======================================================================
58
59 // NIS_View::~NIS_View()
60 // {}
61
62
63 //=======================================================================
64 //function : AddContext
65 //purpose  :
66 //=======================================================================
67
68 void  NIS_View::AddContext (NIS_InteractiveContext * theCtx)
69 {
70   // Check that the given context is not among already attached contexts
71   NCollection_List<NIS_InteractiveContext *>::Iterator anIter (myContexts);
72   for (; anIter.More(); anIter.Next())
73     if (anIter.Value() == theCtx)
74       break;
75   if (anIter.More() == Standard_False)
76     myContexts.Append (theCtx);
77 }
78
79 //=======================================================================
80 //function : RemoveContext
81 //purpose  :
82 //=======================================================================
83
84 void NIS_View::RemoveContext (NIS_InteractiveContext * theCtx)
85 {
86   NCollection_List<NIS_InteractiveContext *>::Iterator anIter (myContexts);
87   for (; anIter.More(); anIter.Next())
88     if (anIter.Value() == theCtx) {
89       myContexts.Remove (anIter);
90       break;
91     }
92
93   NCollection_Map<Handle(NIS_Drawer)>::Iterator anIterD (theCtx->GetDrawers ());
94   for (; anIterD.More(); anIterD.Next()) {
95     const Handle(NIS_Drawer)& aDrawer = anIterD.Value();
96     if (aDrawer.IsNull() == Standard_False) {
97       aDrawer->UpdateExListId(this);
98     }
99   }
100 }
101
102 //=======================================================================
103 //function : FitAll3d
104 //purpose  :
105 //=======================================================================
106
107 Standard_Boolean NIS_View::FitAll3d (const Quantity_Coefficient theCoef)
108 {
109   Bnd_B3f aB3fBox = GetBndBox();
110
111   if (aB3fBox.IsVoid() || MyView->IsDefined() == Standard_False)
112   {
113     return Standard_False;
114   }
115
116   Bnd_Box aBox;
117   aBox.Add (gp_Pnt (aB3fBox.CornerMin()));
118   aBox.Add (gp_Pnt (aB3fBox.CornerMax()));
119
120   if (!FitMinMax (myCamera, aBox, theCoef, 0.0, Standard_False))
121   {
122     return Standard_False;
123   }
124
125   View()->AutoZFit();
126
127   ImmediateUpdate();
128
129   return Standard_True;
130 }
131
132 //=======================================================================
133 //function : GetBndBox
134 //purpose  :
135 //=======================================================================
136
137 Bnd_B3f NIS_View::GetBndBox() const
138 {
139   // Calculate the 3D bounding box of visible objects
140   // in all interactive contexts
141   Bnd_B3f aBox;
142   NCollection_List<NIS_InteractiveContext *>::Iterator anIterC (myContexts);
143   for (; anIterC.More(); anIterC.Next()) {
144     NCollection_Map<Handle(NIS_Drawer)>::Iterator anIterD
145       (anIterC.Value()->myDrawers);
146     for (; anIterD.More(); anIterD.Next()) {
147       const Handle(NIS_Drawer)& aDrawer = anIterD.Value();
148       Bnd_B3f aBoxD = aDrawer->GetBox (this);
149       aBox.Add (aBoxD);
150     }
151   }
152
153   // Take the bounding box of AIS objects displayed in the view
154   Bnd_Box aVal = View()->MinMaxValues();
155   if (!aVal.IsVoid())
156   {
157     aBox.Add (aVal.CornerMin());
158     aBox.Add (aVal.CornerMax());
159   }
160
161   return aBox;
162 }
163
164 //=======================================================================
165 //function : GetBndBox
166 //purpose  :
167 //=======================================================================
168
169 void NIS_View::GetBndBox( Standard_Integer& theXMin, Standard_Integer& theXMax,
170                           Standard_Integer& theYMin, Standard_Integer& theYMax ) const
171 {
172   theXMin = theYMin = 0;
173   theXMax = theYMax = -1;
174
175   Bnd_B3f aBox = GetBndBox();
176
177   // Check that the box is not empty
178   if (aBox.IsVoid() == Standard_False) {
179     // Convert the 3D box to 2D representation in pixel coordinates
180     gp_XYZ aCoord;
181     Standard_Integer anXp, anYp;
182     const gp_XYZ aCorner[2] = { aBox.CornerMin(), aBox.CornerMax() };
183     Standard_Integer aLimp[4] = { 1000000, -1000000, 1000000, -1000000 };
184     for (Standard_Integer i = 0; i < 8; i++) {
185       if (i & 0x1) aCoord.SetX (aCorner[0].X());
186       else         aCoord.SetX (aCorner[1].X());
187       if (i & 0x2) aCoord.SetY (aCorner[0].Y());
188       else         aCoord.SetY (aCorner[1].Y());
189       if (i & 0x4) aCoord.SetZ (aCorner[0].Z());
190       else         aCoord.SetZ (aCorner[1].Z());
191       Convert( aCoord.X(), aCoord.Y(), aCoord.Z(), anXp, anYp );
192       if (aLimp[0] > anXp) aLimp[0] = anXp;
193       if (aLimp[1] < anXp) aLimp[1] = anXp;
194       if (aLimp[2] > anYp) aLimp[2] = anYp;
195       if (aLimp[3] < anYp) aLimp[3] = anYp;
196     }
197     if (aLimp[0] < aLimp[1] && aLimp[2] < aLimp[3])
198     {
199       // Scale the view
200       // WindowFit (aLimp[0], aLimp[2], aLimp[1], aLimp[3]);
201       theXMin = aLimp[0];
202       theXMax = aLimp[1];
203       theYMin = aLimp[2];
204       theYMax = aLimp[3];
205     }
206   }
207 }
208
209
210 //=======================================================================
211 //function : MyCallback
212 //purpose  :
213 //=======================================================================
214
215 int NIS_View::MyCallback (Aspect_Drawable                /* Window ID */,
216                           void*                          ptrData,
217                           Aspect_GraphicCallbackStruct*  callData /* call data */)
218 {
219   // Avoid multiple rendering of the scene ( accordingly with update of
220   // callback mechanism, that invokes additional callbacks before
221   // underlay and overlay redrawing with OCC_PRE_REDRAW and OCC_PRE_OVERLAY
222   // bits added to the "reason" value of the callback data structure;
223   // see comments to OCC_REDRAW_ADDITIONAL_CALLBACKS definition )
224   if (callData->reason & OCC_REDRAW_ADDITIONAL_CALLBACKS)
225     return 0;
226
227   if (callData->IsCoreProfile)
228   {
229     return 0; // not supported
230   }
231
232   const Handle(NIS_View) thisView (static_cast<NIS_View *> (ptrData));
233   NCollection_List<NIS_InteractiveContext *>::Iterator anIter;
234 #ifdef CLIP
235   // Find the bounding box of all displayed objects by summing the boxes stored
236   // in the relevant DrawList instances.
237   Bnd_B3f aBndBox;
238   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
239     anIter.Value()->GetBox (aBndBox, pView);
240
241   if (aBndBox.IsVoid() == Standard_False) {
242     const gp_XYZ aBoxSize   = 0.5 * (aBndBox.CornerMax() - aBndBox.CornerMin());
243     const gp_XYZ aBoxCenter = 0.5 * (aBndBox.CornerMax() + aBndBox.CornerMin());
244
245     // Find the ray passing through the clicked point in the view window.
246     Standard_Real anX, anY, aZ;
247     thisView->Convert(0, 0, anX, anY, aZ);  // 3D point for the 3D coordinates
248     const gp_Pnt anEye (anX, anY, aZ);
249     thisView->Proj (anX, anY, aZ);  // vector orthogonal to the view plane
250     const gp_Dir aProj (anX, anY, aZ);
251     const gp_Ax1 anAxis (anEye, aProj);
252
253     const Standard_Real aCenterDist = (anEye.XYZ() - aBoxCenter) * aProj.XYZ();
254     const Standard_Real aBoxExtent /*(fabs(aBoxSize.X() * anX) +
255                                     fabs(aBoxSize.Y() * anY) +
256                                     fabs(aBoxSize.Z() * aZ))*/(100.);
257
258 #define FRONT_CLIPPING_PLANE (GL_CLIP_PLANE0 + 0)
259 #define BACK_CLIPPING_PLANE  (GL_CLIP_PLANE0 + 1)
260     Standard_Real arr[4] = {
261       0.0,  /* Nx */
262       0.0,  /* Ny */
263       1.0,  /* Nz */
264       0.
265     };
266     arr[3] = aBoxExtent + 1.;
267     glClipPlane (BACK_CLIPPING_PLANE, arr);
268     glEnable (BACK_CLIPPING_PLANE);
269     arr[2] = -1.0;
270     arr[3] = aBoxExtent + 1.;
271     glClipPlane (FRONT_CLIPPING_PLANE, arr);
272     glEnable (FRONT_CLIPPING_PLANE);
273   }
274 #endif //IS_DISABLED
275
276   GLboolean isDepthWriteMask, isDepthTest;
277   glGetBooleanv(GL_DEPTH_WRITEMASK,&isDepthWriteMask);
278   glGetBooleanv(GL_DEPTH_TEST,&isDepthTest);
279 //   printf ("GlDepthMask=%d; GlDepthTest=%d\n", depthwritemask, depthtest);
280   glDisableClientState(GL_COLOR_ARRAY);
281   glDisableClientState(GL_EDGE_FLAG_ARRAY);
282   glDisableClientState(GL_INDEX_ARRAY);
283   glDisableClientState(GL_NORMAL_ARRAY);
284   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
285   if (!isDepthTest) {
286     glEnable(GL_DEPTH_TEST);
287     glDepthFunc(GL_LESS);
288     glClearDepth(1.);
289     glClear(GL_DEPTH_BUFFER_BIT);
290   }
291
292   TColStd_MapIteratorOfPackedMapOfInteger anIterM(thisView->myExListId);
293   for (; anIterM.More(); anIterM.Next())
294     if (anIterM.Key() != 0) {
295 #ifdef ARRAY_LISTS
296       glDeleteLists (anIterM.Key(), 5);
297 #else
298       glDeleteLists (anIterM.Key(), 1);
299     }
300 #endif
301   thisView->myExListId.Clear();
302
303   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
304     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Normal);
305
306   // #818151 - selected object is hidden by covered unselected one
307   // display hilighted objects always above the rest ones
308   if (thisView->myIsTopHilight == Standard_True) {
309     glDepthFunc(GL_ALWAYS);
310   }
311
312   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
313     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Hilighted);
314   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
315     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_DynHilighted);
316   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
317     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Transparent);
318
319   // draw top objects always above
320   if (thisView->myIsTopHilight == Standard_False) {
321     glDepthFunc(GL_ALWAYS);
322   }
323
324   for (anIter = thisView->myContexts; anIter.More(); anIter.Next())
325     anIter.Value()->redraw (thisView, NIS_Drawer::Draw_Top);
326
327   return 0;
328 }
329
330 //=======================================================================
331 //function : DynamicHilight
332 //purpose  :
333 //=======================================================================
334
335 void NIS_View::DynamicHilight  (const Standard_Integer theX,
336                                 const Standard_Integer theY)
337 {
338   myDetected.Clear();
339   const Handle(NIS_InteractiveObject) aSelected = Pick (theX, theY);
340
341   // ASV: if at least one Context returns IsSelectable()==False,
342   // hilight is canceled, this method returns
343   if (aSelected.IsNull() == Standard_False) {
344     if (aSelected->IsSelectable() == Standard_False)
345       return;
346   }
347   if (aSelected != myDynHilighted) {
348     const Handle(NIS_View) aView (this);
349     if (myDynHilighted.IsNull() == Standard_False)
350       if (myDynHilighted->GetDrawer().IsNull() == Standard_False)
351         myDynHilighted->GetDrawer()->SetDynamicHilighted(Standard_False,
352                                                          myDynHilighted, aView);
353
354     // 30.07.10 - NKV - synchronize behaviour with AIS interactive context (if need)
355     if (aSelected.IsNull() ||
356         (myDoHilightSelected == Standard_False &&
357          aSelected->GetDrawer()->GetContext()->IsSelected(aSelected)))
358     {
359       myDynHilighted.Nullify();
360     }
361     else {
362       aSelected->GetDrawer()->SetDynamicHilighted (Standard_True,
363                                                    aSelected, aView);
364       myDynHilighted = aSelected;
365     }
366     Redraw();
367   }
368 }
369
370 //=======================================================================
371 //function : DynamicUnhilight
372 //purpose  :
373 //=======================================================================
374
375 void NIS_View::DynamicUnhilight(const Handle(NIS_InteractiveObject)& theObj)
376 {
377   if (theObj == myDynHilighted && theObj.IsNull() == Standard_False) {
378     const Handle(NIS_View) aView (this);
379     if (myDynHilighted->GetDrawer().IsNull() == Standard_False)
380       myDynHilighted->GetDrawer()->SetDynamicHilighted (Standard_False,
381                                                         myDynHilighted, aView);
382     myDynHilighted.Nullify();
383     Redraw();
384   }
385 }
386
387 //=======================================================================
388 //function : Select
389 //purpose  : selection by single click
390 //=======================================================================
391
392 void NIS_View::Select (const Standard_Integer theX,
393                        const Standard_Integer theY,
394                        const Standard_Boolean isForceMultiple,
395                        const Standard_Boolean theRedraw)
396 {
397   myDetected.Clear();
398   const Handle(NIS_InteractiveObject) aSelected = Pick (theX, theY);
399   NCollection_List<NIS_InteractiveContext *>::Iterator anIter (myContexts);
400   for (; anIter.More(); anIter.Next())
401     anIter.Value()->ProcessSelection (aSelected, isForceMultiple);
402   if (aSelected == myDynHilighted && aSelected.IsNull() == Standard_False)
403   {
404     myDynHilighted.Nullify();
405     const Handle(NIS_Drawer)& aDrawer = aSelected->GetDrawer();
406     aDrawer->SetDynamicHilighted (Standard_False, aSelected, this);
407   }
408   if (theRedraw) Redraw();
409 }
410
411 //=======================================================================
412 //function : Select
413 //purpose  : selection by rectange
414 //=======================================================================
415
416 void NIS_View::Select (const Standard_Integer  theXmin,
417                        const Standard_Integer  theYmin,
418                        const Standard_Integer  theXmax,
419                        const Standard_Integer  theYmax,
420                        const Standard_Boolean  isForceMult,
421                        const Standard_Boolean  isFullyIncluded,
422                        const Standard_Boolean  theRedraw)
423 {
424   myDetected.Clear();
425   Standard_Real anX, anY, aZ;
426   if (theXmin == theXmax || theYmin == theYmax)
427     return;
428
429   //Transformed box corresponding to the selected rectangle
430   Proj (anX, anY, aZ);                  // vector orthogonal to the view plane
431   const gp_Dir aProj (anX, anY, aZ);
432
433   Convert(theXmin, theYmin, anX, anY, aZ); // 3D point for the 3D coordinates
434   const gp_Pnt anEye (anX, anY, aZ);
435
436   Convert(theXmax, theYmin, anX, anY, aZ); // 3D point for the 3D coordinates
437   const gp_XYZ anXdir (gp_XYZ(anX, anY, aZ) - anEye.XYZ());
438   const gp_Ax3 anAx3 (anEye, aProj, anXdir);
439   gp_Trsf aTrf;
440   aTrf.SetTransformation (anAx3);
441   const gp_Trsf aTrfInv = aTrf.Inverted();
442
443   Convert(theXmax, theYmax, anX, anY, aZ); // 3D point for the 3D coordinates
444   gp_XYZ anUpperCorner (anX, anY, aZ);
445   aTrf.Transforms(anUpperCorner);
446
447   // Selecting box
448   Bnd_B3f aBoxSel;
449   aBoxSel.Add (gp_XYZ(0., 0., -10000.));
450   aBoxSel.Add (anUpperCorner);
451
452   TColStd_PackedMapOfInteger mapSelected;
453   NCollection_List<NIS_InteractiveContext *>::Iterator anIterC (myContexts);
454   for (; anIterC.More(); anIterC.Next()) {
455     NIS_InteractiveContext * pCtx = anIterC.Value();
456     mapSelected.Clear();
457     pCtx->selectObjects (mapSelected, aBoxSel, aTrfInv, aTrf, isFullyIncluded);
458     pCtx->ProcessSelection (mapSelected, isForceMult);
459   }
460   if (theRedraw) Redraw();
461 }
462
463 //=======================================================================
464 //function : Select
465 //purpose  : Selection by polygon
466 //=======================================================================
467
468 void  NIS_View::Select (const NCollection_List<gp_XY> &thePolygon,
469                         const Standard_Boolean         isForceMult,
470                         const Standard_Boolean         isFullyIncluded,
471                         const Standard_Boolean         theRedraw)
472 {
473   myDetected.Clear();
474   if (thePolygon.IsEmpty())
475     return;
476
477   Standard_Real anX, anY, aZ;
478
479   //Transformed box corresponding to the selected rectangle
480   Proj (anX, anY, aZ);                  // vector orthogonal to the view plane
481   const gp_Dir aProj (anX, anY, aZ);
482
483   const gp_XY &aPf = thePolygon.First();
484   // 3D point for the 3D coordinates
485   Convert((Standard_Integer) aPf.X(), (Standard_Integer) aPf.Y(), anX, anY, aZ);
486   const gp_Pnt anEye (anX, anY, aZ);
487
488   // 3D point for the 3D coordinates
489   const gp_XY &aPl = thePolygon.Last();
490
491   Convert((Standard_Integer) aPl.X(), (Standard_Integer) aPl.Y(), anX, anY, aZ);
492
493   // Compute transformation.
494   const gp_XYZ anXdir (gp_XYZ(anX, anY, aZ) - anEye.XYZ());
495   if (anXdir.Modulus() <= gp::Resolution())
496   {
497     return;
498   }
499
500   const gp_Ax3 anAx3 (anEye, aProj, anXdir);
501   gp_Trsf aTrf;
502   aTrf.SetTransformation (anAx3);
503
504   // Prepare list of 2d points of selection polygon.
505   NCollection_List<gp_XY>           aPoints;
506   NCollection_List<gp_XY>::Iterator anIter(thePolygon);
507   Bnd_B2f                           aPolyBox;
508
509   for (; anIter.More(); anIter.Next()) {
510     const gp_XY &aP = anIter.Value();
511
512     Convert((Standard_Integer) aP.X(), (Standard_Integer) aP.Y(), anX, anY, aZ);
513     gp_XYZ aP3d(anX, anY, aZ);
514
515     aTrf.Transforms(aP3d);
516
517     gp_XY aP2d(aP3d.X(), aP3d.Y());
518
519     aPoints.Append(aP2d);
520     aPolyBox.Add(aP2d);
521   }
522
523   TColStd_PackedMapOfInteger                           mapSelected;
524   NCollection_List<NIS_InteractiveContext *>::Iterator anIterC(myContexts);
525
526   for (; anIterC.More(); anIterC.Next()) {
527     NIS_InteractiveContext * pCtx = anIterC.Value();
528     mapSelected.Clear();
529     pCtx->selectObjects (mapSelected, aPoints, aPolyBox, aTrf, isFullyIncluded);
530     pCtx->ProcessSelection (mapSelected, isForceMult);
531   }
532
533   if (theRedraw) Redraw();
534 }
535
536 //=======================================================================
537 //function : Pick
538 //purpose  :
539 //=======================================================================
540
541 Handle(NIS_InteractiveObject) NIS_View::Pick (const Standard_Integer theX,
542                                              const Standard_Integer theY)
543 {
544   // Find the ray passing through the clicked point in the view window.
545   Standard_Real anX, anY, aZ, anOver;
546   Convert(theX, theY, anX, anY, aZ);  // 3D point for the 3D coordinates
547   const gp_Pnt anEye (anX, anY, aZ);
548   Proj (anX, anY, aZ);                // vector orthogonal to the view plane
549   const gp_Dir aProj (-anX, -anY, -aZ);
550   const gp_Ax1 anAxis (anEye, aProj);
551
552   Convert (theX+1, theY+1, anX, anY, aZ);
553   anOver = ((gp_XYZ(anX, anY, aZ) - anEye.XYZ()) ^ aProj.XYZ()).Modulus() * 1.5;
554
555   return Pick(anAxis, anOver, Standard_True);
556 }
557
558 //=======================================================================
559 //function : Pick
560 //purpose  :
561 //=======================================================================
562
563 Handle(NIS_InteractiveObject) NIS_View::Pick
564                                 (const gp_Ax1&          theAxis,
565                                  const Standard_Real    theOver,
566                                  const Standard_Boolean isOnlySelectable)
567 {
568   typedef NCollection_List<NIS_InteractiveContext::DetectedEnt> LstDetected;
569   Standard_Real                 aDistance (0.1 * RealLast());
570   Handle(NIS_InteractiveObject) aSelected, aTmpSel;
571   LstDetected aDetected;
572
573   NCollection_List<NIS_InteractiveContext *>::Iterator anIterC (myContexts);
574   for (; anIterC.More(); anIterC.Next()) {
575     const Standard_Real aDist =
576       anIterC.Value()->selectObject (aTmpSel, aDetected, theAxis, theOver,
577                                      isOnlySelectable);
578     if (aDist < aDistance) {
579       aDistance = aDist;
580       aSelected = aTmpSel;
581     }
582   }
583
584   // simple iterating is enough to create list of detected objects
585   // in the order of increasing distance
586   myDetected.Clear();
587   for (LstDetected::Iterator anIt(aDetected); anIt.More(); anIt.Next())
588     myDetected.Append(anIt.Value().PObj);
589
590   return aSelected;
591 }
592