1 /**********************************************************************
2 painterengine - Class Description
6 This file is part of the Software Architecture Description Language
7 (SADL) project. For more information, see <http://milkbox.net/sadl/>
9 SADL is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2 of the License, or (at your
12 option) any later version.
14 SADL is distributed in the hope that it will be useful, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 **********************************************************************/
25 #include "painterengine.h"
26 #include <avogadro/sphere.h>
27 #include <avogadro/cylinder.h>
28 #include <avogadro/textrenderer.h>
29 #include <avogadro/glwidget.h>
36 const int PAINTER_GLOBAL_QUALITY_SETTINGS
= 5;
37 const int DEFAULT_GLOBAL_QUALITY_SETTING
= PAINTER_GLOBAL_QUALITY_SETTINGS
- 3;
38 const int PAINTER_DETAIL_LEVELS
= 10;
39 // Sphere detail level array. Each row is a detail level.
40 // The first column is the sphere detail level at the furthest
41 // point and the last column is the detail level at the closest
43 const int PAINTER_SPHERES_LEVELS_ARRAY
[5][10]
45 { {0, 0, 1, 1, 2, 2, 3, 3, 4, 4},
46 {0, 1, 2, 3, 4, 4, 5, 5, 6, 6},
47 {1, 2, 3, 4, 5, 6, 7, 8, 9, 9},
48 {1, 2, 3, 4, 6, 7, 8, 9, 11, 12},
49 {2, 3, 4, 5, 7, 9, 12, 15, 18, 22}
51 const double PAINTER_SPHERES_LIMIT_MIN_LEVEL
= 0.005;
52 const double PAINTER_SPHERES_LIMIT_MAX_LEVEL
= 0.15;
54 // Cylinder detail level array. Each row is a detail level.
55 // The first column is the cylinder detail level at the furthest
56 // point and the last column is the detail level at the closest
58 const int PAINTER_CYLINDERS_LEVELS_ARRAY
[5][10]
60 { {0, 3, 5, 5, 8, 8, 12, 12, 16, 16},
61 {0, 4, 6, 9, 12, 12, 16, 16, 20, 20},
62 {0, 4, 6, 10, 14, 18, 22, 26, 32, 40},
63 {0, 4, 6, 12, 16, 20, 24, 28, 34, 42},
64 {0, 5, 10, 15, 20, 25, 30, 35, 40, 45}
66 const double PAINTER_CYLINDERS_LIMIT_MIN_LEVEL
= 0.001;
67 const double PAINTER_CYLINDERS_LIMIT_MAX_LEVEL
= 0.03;
68 const int PAINTER_MAX_DETAIL_LEVEL
= PAINTER_DETAIL_LEVELS
- 1;
69 const double PAINTER_SPHERES_SQRT_LIMIT_MIN_LEVEL
70 = sqrt ( PAINTER_SPHERES_LIMIT_MIN_LEVEL
);
71 const double PAINTER_SPHERES_SQRT_LIMIT_MAX_LEVEL
72 = sqrt ( PAINTER_SPHERES_LIMIT_MAX_LEVEL
);
73 const double PAINTER_SPHERES_DETAIL_COEFF
74 = static_cast<double> ( PAINTER_MAX_DETAIL_LEVEL
- 1 )
75 / ( PAINTER_SPHERES_SQRT_LIMIT_MAX_LEVEL
- PAINTER_SPHERES_SQRT_LIMIT_MIN_LEVEL
);
76 const double PAINTER_CYLINDERS_SQRT_LIMIT_MIN_LEVEL
77 = sqrt ( PAINTER_CYLINDERS_LIMIT_MIN_LEVEL
);
78 const double PAINTER_CYLINDERS_SQRT_LIMIT_MAX_LEVEL
79 = sqrt ( PAINTER_CYLINDERS_LIMIT_MAX_LEVEL
);
80 const double PAINTER_CYLINDERS_DETAIL_COEFF
81 = static_cast<double> ( PAINTER_MAX_DETAIL_LEVEL
- 1 )
82 / ( PAINTER_CYLINDERS_SQRT_LIMIT_MAX_LEVEL
- PAINTER_CYLINDERS_SQRT_LIMIT_MIN_LEVEL
);
83 const double PAINTER_FRUSTUM_CULL_TRESHOLD
= -0.8;
85 class PainterEnginePrivate
88 PainterEnginePrivate() : widget ( 0 ), quality ( 0 ), spheres ( 0 ), cylinders ( 0 ),
89 textRenderer ( new TextRenderer
), initialized ( false ), qualityChanged ( false ), sharing ( 0 ) {};
90 ~PainterEnginePrivate()
101 /** array of pointers to Spheres. You might ask, why not have
102 * a plain array of Spheres. The idea is that more than one global detail level
103 * may use a given sphere detail level. It is therefore interesting to be able
104 * to share that sphere, instead of having redundant spheres in memory.
107 /** array of pointers to Cylinders. You might ask, why not have
108 * a plain array of Cylinders. The idea is that more than one global detail level
109 * may use a given cylinder detail level. It is therefore interesting to be able
110 * to share that cylinder, instead of having redundant cylinder in memory.
112 Cylinder
**cylinders
;
114 TextRenderer
*textRenderer
;
119 void updateObjects();
120 void deleteObjects();
121 void createObjects();
124 * PainterEngines can be shared, we must keep track of this.
130 void PainterEnginePrivate::deleteObjects()
132 int level
, lastLevel
, n
;
133 // delete the spheres. One has to be wary that more than one sphere
134 // pointer may have the same value. One wants to avoid deleting twice the same sphere.
138 for ( n
= 0; n
< PAINTER_DETAIL_LEVELS
; n
++ )
140 level
= PAINTER_SPHERES_LEVELS_ARRAY
[quality
][n
];
141 if ( level
!= lastLevel
)
155 // delete the cylinders. One has to be wary that more than one cylinder
156 // pointer may have the same value. One wants to avoid deleting twice the same cylinder.
160 for ( n
= 0; n
< PAINTER_DETAIL_LEVELS
; n
++ )
162 level
= PAINTER_CYLINDERS_LEVELS_ARRAY
[quality
][n
];
163 if ( level
!= lastLevel
)
178 void PainterEnginePrivate::createObjects()
180 // create the spheres. More than one sphere detail level may have the same value.
181 // in that case we want to reuse the corresponding sphere by just copying the pointer,
182 // instead of creating redundant spheres.
185 spheres
= new Sphere
*[PAINTER_DETAIL_LEVELS
];
186 int level
, lastLevel
;
187 lastLevel
= PAINTER_SPHERES_LEVELS_ARRAY
[quality
][0];
188 spheres
[0] = new Sphere ( lastLevel
);
189 for ( int n
= 1; n
< PAINTER_DETAIL_LEVELS
; n
++ )
191 level
= PAINTER_SPHERES_LEVELS_ARRAY
[quality
][n
];
192 if ( level
== lastLevel
)
194 spheres
[n
] = spheres
[n
-1];
199 spheres
[n
] = new Sphere ( level
);
204 // create the cylinders. More than one cylinder detail level may have the same value.
205 // in that case we want to reuse the corresponding cylinder by just copying the pointer,
206 // instead of creating redundant cylinders.
207 if ( cylinders
== 0 )
209 cylinders
= new Cylinder
*[PAINTER_DETAIL_LEVELS
];
210 int level
, lastLevel
;
211 lastLevel
= PAINTER_SPHERES_LEVELS_ARRAY
[quality
][0];
212 cylinders
[0] = new Cylinder ( lastLevel
);
213 for ( int n
= 1; n
< PAINTER_DETAIL_LEVELS
; n
++ )
215 level
= PAINTER_CYLINDERS_LEVELS_ARRAY
[quality
][n
];
216 if ( level
== lastLevel
)
218 cylinders
[n
] = cylinders
[n
-1];
223 cylinders
[n
] = new Cylinder ( level
);
229 void PainterEnginePrivate::updateObjects()
231 if(newQuality
!= quality
)
234 quality
= newQuality
;
239 PainterEngine::PainterEngine( int quality
) : d ( new PainterEnginePrivate
)
241 if ( quality
< 0 || quality
>= PAINTER_MAX_DETAIL_LEVEL
)
243 quality
= DEFAULT_GLOBAL_QUALITY_SETTING
;
245 d
->quality
= quality
;
248 PainterEngine::~PainterEngine()
253 // void PainterEngine::setGLWidget( GLWidget * widget )
255 // d->widget = widget;
256 // d->textRenderer->setGLWidget(d->widget);
259 void PainterEngine::setQuality ( int quality
)
261 assert ( quality
>= 0 && quality
< PAINTER_GLOBAL_QUALITY_SETTINGS
);
263 d
->quality
= quality
;
264 d
->qualityChanged
= true;
267 int PainterEngine::quality() const
272 // void PainterEngine::initialize( GLWidget * widget, int quality )
274 // if(quality == -1) {
275 // quality = DEFAULT_GLOBAL_QUALITY_SETTING;
278 // assert( quality >= 0 && quality < PAINTER_GLOBAL_QUALITY_SETTINGS );
280 // d->initialized = true;
281 // setGLWidget(widget);
282 // setQuality(quality);
285 void PainterEngine::drawSphere ( const Eigen::Vector3d
& center
, double radius
, int detailLevel
) const
287 if ( d
->textRenderer
->isActive() )
289 d
->textRenderer
->end();
291 assert ( d
->widget
);
292 assert ( detailLevel
>= 0 && detailLevel
<= PAINTER_MAX_DETAIL_LEVEL
);
293 d
->spheres
[detailLevel
]->draw ( center
, radius
);
296 void PainterEngine::drawSphere ( const Eigen::Vector3d
& center
, double radius
) const
298 if ( d
->textRenderer
->isActive() )
300 d
->textRenderer
->end();
302 assert ( d
->widget
);
303 Eigen::Vector3d transformedCenter
= d
->widget
->camera()->modelview() * center
;
304 double distance
= transformedCenter
.norm();
306 // perform a rough form of frustum culling
307 double dot
= transformedCenter
.z() / distance
;
308 if ( dot
> PAINTER_FRUSTUM_CULL_TRESHOLD
) return;
310 double apparentRadius
= radius
/ distance
;
312 int detailLevel
= 1 + static_cast<int> ( floor (
313 PAINTER_SPHERES_DETAIL_COEFF
* ( sqrt ( apparentRadius
) - PAINTER_SPHERES_SQRT_LIMIT_MIN_LEVEL
)
315 if ( detailLevel
< 0 )
319 if ( detailLevel
> PAINTER_MAX_DETAIL_LEVEL
)
321 detailLevel
= PAINTER_MAX_DETAIL_LEVEL
;
323 d
->spheres
[detailLevel
]->draw ( center
, radius
);
326 void PainterEngine::drawCylinder ( const Eigen::Vector3d
&end1
, const Eigen::Vector3d
&end2
,
327 double radius
, int detailLevel
) const
329 if ( d
->textRenderer
->isActive() )
331 d
->textRenderer
->end();
333 assert ( d
->widget
);
334 assert ( detailLevel
>= 0 && detailLevel
<= PAINTER_MAX_DETAIL_LEVEL
);
335 d
->cylinders
[detailLevel
]->draw ( end1
, end2
, radius
);
338 void PainterEngine::drawCylinder ( const Eigen::Vector3d
&end1
, const Eigen::Vector3d
&end2
,
339 double radius
) const
341 if ( d
->textRenderer
->isActive() )
343 d
->textRenderer
->end();
345 assert ( d
->widget
);
346 Eigen::Vector3d transformedEnd1
= d
->widget
->camera()->modelview() * end1
;
347 double distance
= transformedEnd1
.norm();
349 // perform a rough form of frustum culling
350 double dot
= transformedEnd1
.z() / distance
;
351 if ( dot
> PAINTER_FRUSTUM_CULL_TRESHOLD
) return;
353 double apparentRadius
= radius
/ distance
;
354 int detailLevel
= 1 + static_cast<int> ( floor (
355 PAINTER_CYLINDERS_DETAIL_COEFF
356 * ( sqrt ( apparentRadius
) - PAINTER_CYLINDERS_SQRT_LIMIT_MIN_LEVEL
)
358 if ( detailLevel
< 0 )
362 if ( detailLevel
> PAINTER_MAX_DETAIL_LEVEL
)
364 detailLevel
= PAINTER_MAX_DETAIL_LEVEL
;
366 d
->cylinders
[detailLevel
]->draw ( end1
, end2
, radius
);
369 void PainterEngine::drawMultiCylinder ( const Eigen::Vector3d
&end1
, const Eigen::Vector3d
&end2
,
370 double radius
, int order
, double shift
, int detailLevel
) const
372 if ( d
->textRenderer
->isActive() )
374 d
->textRenderer
->end();
376 assert ( d
->widget
);
377 assert ( detailLevel
>= 0 && detailLevel
<= PAINTER_MAX_DETAIL_LEVEL
);
378 d
->cylinders
[detailLevel
]->drawMulti ( end1
, end2
, radius
, order
,
379 shift
, d
->widget
->normalVector() );
382 void PainterEngine::drawMultiCylinder ( const Eigen::Vector3d
&end1
, const Eigen::Vector3d
&end2
,
383 double radius
, int order
, double shift
) const
385 if ( d
->textRenderer
->isActive() )
387 d
->textRenderer
->end();
389 assert ( d
->widget
);
390 Eigen::Vector3d transformedEnd1
= d
->widget
->camera()->modelview() * end1
;
391 double distance
= transformedEnd1
.norm();
393 // perform a rough form of frustum culling
394 double dot
= transformedEnd1
.z() / distance
;
395 if ( dot
> PAINTER_FRUSTUM_CULL_TRESHOLD
) return;
397 double apparentRadius
= radius
/ distance
;
398 int detailLevel
= 1 + static_cast<int> ( floor (
399 PAINTER_CYLINDERS_DETAIL_COEFF
400 * ( sqrt ( apparentRadius
) - PAINTER_CYLINDERS_SQRT_LIMIT_MIN_LEVEL
)
402 if ( detailLevel
< 0 )
406 if ( detailLevel
> PAINTER_MAX_DETAIL_LEVEL
)
408 detailLevel
= PAINTER_MAX_DETAIL_LEVEL
;
410 d
->cylinders
[detailLevel
]->drawMulti ( end1
, end2
, radius
, order
,
411 shift
, d
->widget
->normalVector() );
414 int PainterEngine::drawText ( int x
, int y
, const QString
&string
) const
416 if ( !d
->textRenderer
->isActive() )
418 d
->textRenderer
->begin ( d
->widget
);
420 return d
->textRenderer
->draw ( x
, y
, string
);
423 int PainterEngine::drawText ( const QPoint
& pos
, const QString
&string
) const
425 if ( !d
->textRenderer
->isActive() )
427 d
->textRenderer
->begin( d
->widget
);
429 return d
->textRenderer
->draw ( pos
.x(), pos
.y(), string
);
432 int PainterEngine::drawText ( const Eigen::Vector3d
&pos
, const QString
&string
) const
434 if ( !d
->textRenderer
->isActive() )
436 d
->textRenderer
->begin ( d
->widget
);
438 Eigen::Vector3d transformedPos
= d
->widget
->camera()->modelview() * pos
;
440 // perform a rough form of frustum culling
441 double dot
= transformedPos
.z() / transformedPos
.norm();
442 if ( dot
> PAINTER_FRUSTUM_CULL_TRESHOLD
) return 0;
444 return d
->textRenderer
->draw ( pos
, string
);
447 void PainterEngine::begin ( GLWidget
*widget
)
451 qDebug() << "createObjects()";
453 d
->initialized
= true;
455 else if (d
->qualityChanged
)
457 qDebug() << "updateObjects()";
459 d
->qualityChanged
= false;
465 void PainterEngine::end()
467 if ( d
->textRenderer
->isActive() )
469 d
->textRenderer
->end();
474 int PainterEngine::defaultQuality()
476 return DEFAULT_GLOBAL_QUALITY_SETTING
;
479 int PainterEngine::maxQuality()
481 return PAINTER_GLOBAL_QUALITY_SETTINGS
-1;
484 bool PainterEngine::isShared()
489 void PainterEngine::incrementShare()
494 void PainterEngine::decrementShare()
498 } // end namespace Avogadro