source: experimental/distortionNG/DistortionManipulator.cpp @ 370

Last change on this file since 370 was 370, checked in by Torben Dannhauer, 12 years ago
File size: 18.6 KB
Line 
1/* osgVisual test. distortionNG, experimental.
2*
3*  Permission is hereby granted, free of charge, to any person obtaining a copy
4*  of this software and associated documentation files (the "Software"), to deal
5*  in the Software without restriction, including without limitation the rights
6*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7*  copies of the Software, and to permit persons to whom the Software is
8*  furnished to do so, subject to the following conditions:
9*
10*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16*  THE SOFTWARE.
17*/
18
19#include "DistortionManipulator.h"
20
21#include <osgViewer/Viewer>
22#include <osg/Point>
23#include <osg/PolygonMode>
24
25#include <osgDB/Registry>
26#include <osgDB/ReaderWriter>
27#include <osgDB/WriteFile>
28
29using namespace osg;
30using namespace osgViewer;
31
32
33DistortionManipulator::DistortionManipulator(DistortionSet* ds)
34 : _highlightColor( osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f) ), _distortionSet( ds )
35{
36        activeSetupMode = DISABLED;
37        activeDistortionMode = MESH;
38        activeManualSetupMode = DISTORTION;
39        activeVisualizationMode = NONE;
40
41        _highlighter = NULL;
42       
43        createVertexHighlighter();
44        createHUD();
45
46        _camera = 0;
47        //_distortionMesh(distortionMesh)
48
49        // Create Shader to vizualize intensitymap during blending setup.
50        osg::Shader* sh = osg::Shader::readShaderFile( osg::Shader::FRAGMENT, "shaderIntensityMapVis.frag" ); 
51        sh->setName("shaderIntensityMapVis");
52        ds->setShaderIntensityMapVis( sh );
53
54        //LF Get Screen Resolution for Cursor Position Scaling
55        osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface();
56        if (!wsi) { osg::notify(osg::NOTICE)<<"Error, no WindowSystemInterface available, cannot create windows."<<std::endl; }
57        wsi->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), _screenPixWidth, _screenPixHeight);
58
59
60}
61
62DistortionManipulator::~DistortionManipulator()
63{
64}
65
66void DistortionManipulator::getUsage(osg::ApplicationUsage& usage) const
67{
68        usage.addKeyboardMouseBinding("Keypad 0","Show/Hide distortion HUD.");
69        usage.addKeyboardMouseBinding("Keypad 1","Reset distortion.");
70        usage.addKeyboardMouseBinding("Keypad 2","Reset intensity blending.");
71        usage.addKeyboardMouseBinding("Keypad 4","Toggles Setup Mode between DISABLED, MANUAL & DELEGATED.");
72        usage.addKeyboardMouseBinding("Keypad 5","MANUAL Mode: Toggle between blending & distortion setup.");
73    usage.addKeyboardMouseBinding("Keypad 6","MANUAL Mode: Toggle if distortion drags affect mesh or rtt texture coordinates.");        // Defaults to Mesh
74        usage.addKeyboardMouseBinding("Keypad 7","Show distortion mesh / intensity map / none.");
75        usage.addKeyboardMouseBinding("Keypad 8","Save distortion set.");       // via plugin
76}
77
78bool DistortionManipulator::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa, osg::Object* obj, osg::NodeVisitor* nv)
79{
80
81
82        switch(ea.getEventType())
83    {
84        case(osgGA::GUIEventAdapter::MOVE): break;
85        case(osgGA::GUIEventAdapter::DRAG):
86                {
87                        if ( activeSetupMode == MANUAL)
88                        {
89                                OSG_ALWAYS<<std::endl<<"Drag:"<<std::endl<<"ea.getGraphicsContext()="<<ea.getGraphicsContext()<<std::endl;
90                                OSG_ALWAYS<<"ea.getXnormalized()="<<ea.getXnormalized()<<std::endl;
91                                OSG_ALWAYS<<"ea.getYnormalized()="<<ea.getYnormalized()<<std::endl;
92                                OSG_ALWAYS<<"ea.getX()="<<ea.getX()<<std::endl;
93                                OSG_ALWAYS<<"ea.getXin()="<<ea.getXmin()<<std::endl;
94                                OSG_ALWAYS<<"ea.getXmax()="<<ea.getXmax()<<std::endl;
95                                OSG_ALWAYS<<"ea.getY()="<<ea.getY()<<std::endl;
96                                OSG_ALWAYS<<"ea.getYin()="<<ea.getYmin()<<std::endl;
97                                OSG_ALWAYS<<"ea.getYmax()="<<ea.getYmax()<<std::endl;
98                               
99                                //LF get X,Y normalized to lowerleftcorner (0,0) / upperrightcorner (1,1)
100                                OSG_ALWAYS<<"getXnormalizedLF()="<<(ea.getXnormalized()+1.0)/2.0<<std::endl;
101                                OSG_ALWAYS<<"getYnormalizedLF()="<<(ea.getYnormalized()+1.0)/2.0<<std::endl;
102
103                                //LF get X,Y in pixels (lowerleftcorner (0,0) / upperrightcorner (screenwidth,screenheigth)
104                                OSG_ALWAYS<<std::endl<<"Screen Resolution: "<<_screenPixWidth<<" x "<<_screenPixHeight<<std::endl;
105                                OSG_ALWAYS<<"getXPixelspaceLF()="<<(int)((float)_screenPixWidth*(ea.getXnormalized()+1.0)/2.0)<<std::endl;
106                                OSG_ALWAYS<<"getYPixelspaceLF()="<<(int)((float)_screenPixHeight*(ea.getYnormalized()+1.0)/2.0)<<std::endl;
107
108                                if(activeDistortionMode == MESH)
109                                {
110                                        OSG_ALWAYS<<"MESH!"<<std::endl;
111                                }
112                                else if (activeDistortionMode == TEXCOORDINATES)
113                                {
114                                        OSG_ALWAYS<<"TEXCOORDINATES!"<<std::endl;
115                                }
116
117                                return true;    // true means event handled: not forwarded to the camera manipulator;
118                        }
119                        break;
120
121                }
122        case(osgGA::GUIEventAdapter::PUSH):
123                {
124                        OSG_ALWAYS<<"mouse click!"<<std::endl;
125                        if ( activeSetupMode == MANUAL /*&& ea.getModKeyMask()&osgGA::GUIEventAdapter::MODKEY_CTRL*/ && ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON )
126                        {
127                                osgViewer::View* viewer = dynamic_cast<osgViewer::View*>(&aa);
128                                if ( viewer )
129                                {
130                                        osg::notify(osg::ALWAYS)<<std::endl<<"Intersection:"<<std::endl<<"ea.getX()="<<ea.getX()<<std::endl;
131                                        osg::notify(osg::ALWAYS)<<"ea.getY()="<<ea.getY()<<std::endl;
132                                        osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector = new osgUtil::LineSegmentIntersector(osgUtil::Intersector::PROJECTION, ea.getX(), ea.getY());
133                                        osgUtil::IntersectionVisitor iv( intersector.get() );
134                                        _distortionSet->getDistortionCamera()->accept( iv );
135                               
136                                        if ( intersector->containsIntersections() )
137                                        {
138                                                //osgUtil::LineSegmentIntersector::Intersection& result = *(intersector->getIntersections().begin());
139                                                osgUtil::LineSegmentIntersector::Intersection *result;
140                                                result=&intersector->getFirstIntersection();
141                                                computeSelectedVertex( *result );
142                                                //computeSelectedVertex( result );
143                                        }
144                                }
145                        }
146                        break;
147                }
148        case(osgGA::GUIEventAdapter::RELEASE): break;
149        case(osgGA::GUIEventAdapter::KEYDOWN):
150                {
151                        if (ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Insert) // KP 0: Show/Hide HUD
152                        {
153                                bool oldValue = _distortionSet->getDistortionInternals()->getValue(DistortionSet::HUD);
154                                _distortionSet->getDistortionInternals()->setValue(DistortionSet::HUD, !oldValue);
155                                return(true);
156                        }
157                        if (ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_End)    // KP 1: reset distortion
158                        {
159                                resetDistortion();
160                                return(true);
161                        }
162                        if (ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Down)   // KP 2: reset intensity map
163                        {
164                                resetIntensityMap();
165                                return(true);
166                        }
167                        if (ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Left)   // KP 4: Toggles Setup Mode between DISABLED, MANUAL & DELEGATED.
168                        {
169                                switch(activeSetupMode)
170                                {
171                                        case DISABLED : 
172                                        {
173                                                activeSetupMode = MANUAL;
174                                                OSG_NOTICE<<"SetupMode MANUAL activated"<<std::endl;
175                                                _distortionSet->getDistortionInternals()->setValue(DistortionSet::HIGHLIGHTER, true);
176                                                break;
177                                        }
178                                        case MANUAL :
179                                        {
180                                                activeSetupMode = DELEGATED;
181                                                _distortionSet->getDistortionInternals()->setValue(DistortionSet::HIGHLIGHTER, false);
182                                                OSG_NOTICE<<"SetupMode DELEGATED activated"<<std::endl;
183                                                break;
184                                        }
185                                        case DELEGATED :
186                                        {
187                                                activeSetupMode = DISABLED;
188                                                _distortionSet->getDistortionInternals()->setValue(DistortionSet::HIGHLIGHTER, false); 
189                                                OSG_NOTICE<<"SetupMode DISABLED activated"<<std::endl;
190                                                break;
191                                        }
192                                }
193                                updateHUD();
194                                return(true);
195                        }
196                        if (ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Begin)  // KP 5: MANUAL Mode: Toggle between blending & distortion setup.
197                        {
198
199                                activeManualSetupMode = (activeManualSetupMode==DISTORTION?BLENDING:DISTORTION);
200                                updateHUD();
201                                return(true);
202                        }
203                        if (ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Right)  // KP 6: MANUAL Mode: Toggle if distortion drags affect mesh or rtt texture coordinates -> defaults to Mesh
204                        {
205                                activeDistortionMode = (activeDistortionMode==MESH?TEXCOORDINATES:MESH);
206                                updateHUD();
207                                return(true);
208                        }
209                        if (ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Home)   // KP 7: Show distortion mesh / intensity map / none.
210                        {
211                                switch(activeVisualizationMode)
212                                {       
213                                        case DISTORTION_MESH : 
214                                        {
215                                                activeVisualizationMode = INTENSITY_MAP;
216                                                showDistortionMesh(false);
217                                                showIntensityMap(true);
218                                                break;
219                                        }
220                                        case INTENSITY_MAP :
221                                        {
222                                                activeVisualizationMode = NONE;
223                                                showDistortionMesh(false);
224                                                showIntensityMap(false);
225                                                break;
226                                        }
227                                        case NONE : 
228                                        {
229                                                activeVisualizationMode = DISTORTION_MESH;
230                                                showDistortionMesh(true);
231                                                showIntensityMap(false);
232                                                break;
233                                        }
234                                }
235
236                                updateHUD();
237                                return(true);
238                        }
239                        if (ea.getKey()==osgGA::GUIEventAdapter::KEY_KP_Up)     // KP 8: Save distortion set     via plugin
240                        {
241                                OSG_ALWAYS<<"KEY_KP_8 : todo: Save DistortionContainer"<<std::endl;
242
243                                osgDB::writeObjectFile( *_distortionSet, "distortionset.dist" );
244                                return(true);
245                        }
246                       
247                        return false;   // Event ignored
248                }
249        case(osgGA::GUIEventAdapter::KEYUP): break;
250                case(osgGA::GUIEventAdapter::FRAME):
251                {
252                        if ( activeSetupMode == DELEGATED)
253                        {
254                                OSG_ALWAYS<<"Todo: Calling delegated class!"<<std::endl;
255                        }
256                        break;
257                }
258        case (osgGA::GUIEventAdapter::RESIZE):  // todo: adapt distortion mesh to new screen size
259                {
260                        OSG_ALWAYS<<"RESIZE!"<<std::endl;
261                        break;
262                }
263
264        default:
265            return false;
266    }
267    return false;
268}
269
270void DistortionManipulator::resetIntensityMap()
271{
272        if(!_distortionSet.valid())
273                return;
274
275        osg::ref_ptr<osg::Image> image = _distortionSet->getIntensityMap();
276
277        OSG_ALWAYS<<"Reseting IntensityMap Blending."<<std::endl;
278
279        if (!image->isDataContiguous())
280        {
281                OSG_WARN<<"Warning: DistortionManipulator does not support working with non contiguous imagery as blendmaps!"<<std::endl;
282                return;
283        }
284
285        // Fill intensity map with 255
286        unsigned char* dataPtr = image->data(); 
287        for(unsigned int i=0;i<image->getTotalSizeInBytes();i++)
288        {
289                *dataPtr++ = 255; 
290        }
291        image->dirty();
292}
293
294void DistortionManipulator::resetDistortion()
295{
296        if(!_distortionSet.valid())
297                return;
298
299        OSG_ALWAYS<<"ToDo: resetDistortion()"<<std::endl;
300}
301
302void DistortionManipulator::showDistortionMesh(bool show)
303{
304        osg::StateSet* stateset = _distortionSet->getDistortionInternals()->getChild(DistortionSet::MESH)->getOrCreateStateSet();
305
306        // Append a PolygonMode stateset if required
307    osg::PolygonMode* polyModeObj = dynamic_cast<osg::PolygonMode*>(stateset->getAttribute(osg::StateAttribute::POLYGONMODE));
308    if (!polyModeObj)
309    {
310        polyModeObj = new osg::PolygonMode;
311        stateset->setAttribute(polyModeObj, osg::StateAttribute::PROTECTED|osg::StateAttribute::ON);
312    }
313 
314        // Set Polygonmode
315        if(show)
316                polyModeObj->setMode(osg::PolygonMode::FRONT_AND_BACK,osg::PolygonMode::LINE);
317        else
318                polyModeObj->setMode(osg::PolygonMode::FRONT_AND_BACK,osg::PolygonMode::FILL);
319}
320
321void DistortionManipulator::showIntensityMap(bool show)
322{
323        if(_distortionSet.valid())
324        {
325                osg::StateSet* stateset = _distortionSet->getDistortionInternals()->getChild(DistortionSet::MESH)->getOrCreateStateSet();
326                osg::Program* program = static_cast<osg::Program*>(stateset->getAttribute(::osg::StateAttribute::PROGRAM));
327
328                if(show)
329                {
330                        program->removeShader( _distortionSet->getShaderIntensityMap() );
331                        program->addShader( _distortionSet->getShaderIntensityMapVis() );
332                }
333                else
334                {
335                        program->removeShader( _distortionSet->getShaderIntensityMapVis() );
336                        program->addShader( _distortionSet->getShaderIntensityMap() );
337                }
338        }
339}
340
341void DistortionManipulator::createVertexHighlighter()
342{
343        osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array(1);
344        (*colors)[0] = _highlightColor;
345
346        _highlighter = new osg::Geometry;
347        _highlighter->setDataVariance( osg::Object::DYNAMIC );
348        _highlighter->setUseDisplayList( false );
349        _highlighter->setUseVertexBufferObjects( true );
350        _highlighter->setVertexArray( new osg::Vec3Array(1) );  // The highlighter vertex is updated by computeSelectedVertex(..)
351        _highlighter->setColorArray( colors.get() );
352        _highlighter->setColorBinding( osg::Geometry::BIND_OVERALL );
353        _highlighter->addPrimitiveSet( new osg::DrawArrays(GL_POINTS, 0, 1) );
354
355       
356        osg::ref_ptr<osg::Geode> geode = new osg::Geode;
357        geode->addDrawable( _highlighter.get() );
358        geode->getOrCreateStateSet()->setAttributeAndModes( new osg::Point(8.0f) );
359        geode->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
360        geode->setCullingActive(false); // disable the culling for the selector, otherwise the selector is culled away on the edge.
361
362        _distortionSet->getDistortionInternals()->addChild(geode, false);       //  = child #1  :definition: child #0 = mesh, #1 = highlighter, #2 HUD
363}
364
365void DistortionManipulator::createHUD()
366{
367        osg::ref_ptr<osg::Geode> geode = new osg::Geode;
368       
369        osg::StateSet* stateSet = geode->getOrCreateStateSet();
370        stateSet->setRenderBinDetails( 99, "RenderBin");
371        // disable depth test to ensure that it is always drawn.
372        stateSet->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
373        stateSet->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
374
375        // Modify StateSet to protect it against state changes by the stateset Manipulator
376        osg::PolygonMode* polyModeObj = new osg::PolygonMode;
377    stateSet->setAttribute(polyModeObj, osg::StateAttribute::PROTECTED|osg::StateAttribute::ON);
378
379        // Add Text:
380        osg::Vec3 position(20.0f,120.0f,0.0f);
381        osg::Vec3 delta(0.0f,-25.0f,0.0f);
382        std::string timesFont("fonts/arial.ttf");
383        {
384                osg::ref_ptr<osgText::Text> textHeader = new osgText::Text;
385                textHeader->setText("Distortion Setup:");
386                textHeader->setFont(timesFont);
387                textHeader->setPosition(position);
388                textHeader->setCharacterSize(21);
389                textHeader->setDataVariance(osg::Object::STATIC); 
390                geode->addDrawable( textHeader );
391
392                position += delta;
393
394                hudSetupMode = new osgText::Text;
395                hudSetupMode->setFont(timesFont);
396                hudSetupMode->setPosition(position);
397                hudSetupMode->setCharacterSize(21);
398                hudSetupMode->setDataVariance(osg::Object::DYNAMIC); 
399                geode->addDrawable( hudSetupMode );
400
401                position += delta;
402
403                hudDistortionMode = new osgText::Text;
404                hudDistortionMode->setFont(timesFont);
405                hudDistortionMode->setPosition(position);
406                hudDistortionMode->setCharacterSize(21);
407                hudDistortionMode->setDataVariance(osg::Object::DYNAMIC); 
408                geode->addDrawable( hudDistortionMode );
409
410                position += delta;
411
412                hudManualSetupMode = new osgText::Text;
413                hudManualSetupMode->setFont(timesFont);
414                hudManualSetupMode->setPosition(position);
415                hudManualSetupMode->setCharacterSize(21);
416                hudManualSetupMode->setDataVariance(osg::Object::DYNAMIC); 
417                geode->addDrawable( hudManualSetupMode );
418
419                position += delta;
420
421                hudVisualizationMode = new osgText::Text;
422                hudVisualizationMode->setFont(timesFont);
423                hudVisualizationMode->setPosition(position);
424                hudVisualizationMode->setCharacterSize(21);
425                hudVisualizationMode->setDataVariance(osg::Object::DYNAMIC); 
426                geode->addDrawable( hudVisualizationMode );
427        }
428       
429        updateHUD();
430        _distortionSet->getDistortionInternals()->addChild(geode, false);       //  = child #2. The definition is: child #0 = mesh, #1 = highlighter, #2 HUD
431}
432
433void DistortionManipulator::updateHUD()
434{
435        switch(activeSetupMode)
436        {
437                case DISABLED : hudSetupMode->setText("Setup Mode : DISABLED"); break;
438                case MANUAL : hudSetupMode->setText("Setup Mode : MANUAL"); break;
439                case DELEGATED : hudSetupMode->setText("Setup Mode : DELEGATED"); break;
440                default: hudSetupMode->setText("");
441        };
442        switch(activeDistortionMode)
443        {
444                case MESH : hudDistortionMode->setText("Distortion Mode : MESH"); break;
445                case TEXCOORDINATES : hudDistortionMode->setText("Distortion Mode : TEXCOORDINATES"); break;
446                default: hudDistortionMode->setText("");
447        };
448        switch(activeManualSetupMode)
449        {
450                case DISTORTION : hudManualSetupMode->setText("Manual Setup Mode : DISTORTION"); break;
451                case BLENDING : hudManualSetupMode->setText("Manual Setup Mode : BLENDING"); break;
452                default: hudManualSetupMode->setText("");
453        };
454        switch(activeVisualizationMode)
455        {
456                case DISTORTION_MESH : hudVisualizationMode->setText("Visualization Mode : DISTORTION_MESH"); break;
457                case INTENSITY_MAP : hudVisualizationMode->setText("Visualization Mode : INTENSITY_MAP"); break;
458                case NONE : hudVisualizationMode->setText("Visualization Mode : NONE"); break;
459                default: hudVisualizationMode->setText("");
460        };
461}
462
463void DistortionManipulator::computeSelectedVertex( osgUtil::LineSegmentIntersector::Intersection& result )
464{
465        osg::Geometry* geom = dynamic_cast<osg::Geometry*>( result.drawable.get() );
466        if ( !geom || geom==_highlighter )
467                return;
468
469        osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>( geom->getVertexArray() );
470        osg::Vec3Array* selVertices = dynamic_cast<osg::Vec3Array*>( _highlighter->getVertexArray() );
471        if ( !vertices || !selVertices )
472                return;
473       
474        OSG_NOTIFY(osg::ALWAYS)<<"size of vertices="<<vertices->size()<<std::endl;
475        OSG_NOTIFY(osg::ALWAYS)<<"size of selVertices="<<selVertices->size()<<std::endl;
476
477        osg::Vec3 point = result.getWorldIntersectPoint();
478        osg::Matrix matrix = osg::computeLocalToWorld( result.nodePath );       // To compute the intersection vertices in world coordinates not in model coordinates
479        OSG_NOTIFY(osg::ALWAYS) << "Intersection-indices: Size=" << result.indexList.size() << std::endl;
480        const std::vector<unsigned int>& selIndices = result.indexList;
481        {
482                double maxRatio = 0.0;
483                int closestVertexIndex = 0;
484                for ( unsigned int i=0; i<3 && i<result.ratioList.size(); i++ ) //iterate through rations and search for maxRation=nearestVertex
485                {
486                        if(result.ratioList[i] > maxRatio)
487                        {
488                                maxRatio = result.ratioList[i];
489                                closestVertexIndex = result.indexList[i];
490                        }
491                        OSG_NOTIFY(osg::ALWAYS)<<"maxRatio="<<maxRatio<<std::endl;
492                        OSG_NOTIFY(osg::ALWAYS)<<"closestVertexIndex="<<closestVertexIndex<<std::endl;
493                }
494                OSG_NOTIFY(osg::ALWAYS)<<"nearest vertex: X="<<(*vertices)[closestVertexIndex].x()<<" Y="<<(*vertices)[closestVertexIndex].y()<<" Z="<<(*vertices)[closestVertexIndex].z()<<std::endl;
495                osg::Vec3 vertex = (*vertices)[closestVertexIndex] * matrix;
496
497                selVertices->front() = vertex;         
498                OSG_NOTIFY(osg::ALWAYS)<<"selected vertice: X="<<vertex.x()<<" Y="<<vertex.y()<<" Z="<<vertex.z()<<std::endl;
499        }
500        selVertices->dirty();
501        _highlighter->dirtyBound(); 
502}
Note: See TracBrowser for help on using the repository browser.