Attached are 2 utilities to extract surface area, volume, Centre of gravity and moments of inertia from a closed surface (object). they belong in osgUtil.

Improvements to reduce number of tests in the visitor and calculate surface area.

Also I include an example based on osgViewer to display the physical properties of a shape loaded from a file.

Geoff

Attachment: InertiaVisitor
Description: Binary data


#include <osgUtil/InertiaVisitor>
// included as separate file to ensure that InertiaVisitor is compiled into
// libraries.

void InertiaVisitor::apply(osg::Geode& geode) {
        osg::NodePath nodePath = getNodePath();
        unsigned int i;
        osg::Matrix totalmat; // accumulated matrix above this point

        for(i=0; i<nodePath.size()-1; ++i)
        {
                osg::Transform* transform = nodePath[i]->asTransform();
                if (transform) { // concatenate to totalmat.
                        transform->computeLocalToWorldMatrix(totalmat, this);
                }
        }
        for (unsigned int i=0; i<geode.getNumDrawables(); i++) {
                osg::Geometry *dr=dynamic_cast<osg::Geometry 
*>(geode.getDrawable(i));
                if (dr) {
                        // 200409 use ComputeInertiaTriangleFunctor to 
calculate volume, MI etc
                        osg::TriangleFunctor<ComputeInertiaTriangleFunctor> 
inert;
                        inert.setMatrix(totalmat);
                        dr->accept(inert);
                        _m+=inert.getVolume();
                        _Cx+=inert.getCx();
                        _Cy+=inert.getCy();
                        _Cz+=inert.getCz();
                        _xx+=inert.getixx();
                        _yy+=inert.getiyy();
                        _zz+=inert.getizz();
                        _zx+=inert.getizx();
                        _yx+=inert.getiyx();
                        _zy+=inert.getizy();
                        Area+=inert.getArea();
                        ntris+=inert.getntris();
                        osg::Vec2 xr=inert.getXrange();
                        if (xr.x()<xmin) xmin=xr.x();
                        if (xr.y()>xmax) xmax=xr.y();
                        osg::Vec2 yr=inert.getYrange();
                        if (yr.x()<ymin) ymin=yr.x();
                        if (yr.y()>ymax) ymax=yr.y();
                        osg::Vec2 zr=inert.getZrange();
                        if (zr.x()<zmin) zmin=zr.x();
                        if (zr.y()>zmax) zmax=zr.y();
                }
        }
        finish(); // construct the actual Inertia from the partial sums.
}
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This application is open source and may be redistributed and/or modified * freely and without restriction, both in commericial and non commericial applications,
* as long as this copyright notice is maintained.
* * This application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

#include <osgDB/FileNameUtils>
#include <osgDB/ReadFile>
#include <osgUtil/Optimizer>
#include <osg/CoordinateSystemNode>

#include <osg/Switch>
#include <osgText/Text>

#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>

#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgGA/DriveManipulator>
#include <osgGA/KeySwitchMatrixManipulator>
#include <osgGA/StateSetManipulator>
#include <osgGA/AnimationPathManipulator>
#include <osgGA/TerrainManipulator>
#include <osgUtil/InertiaVisitor>

#include <iostream>
#include <sstream>

/* osginertiaviewer - loads an object and displays it (direct copy of osgviewer 
application)
* AND calculates the volume, centre of gravity and moments of inertia (around 
the CG).
* Use parallel axis theorem for any other rotation point.
* The calculated volume & moments of inertia need to be multiplied by density 
to give mass and
* true MI (to be used in a dynamics sumulation).
* Also the MI calculation assumes that the object displayed is:
*   i) a closed surface or sirfaces - no holes in hte surface
*  ii) is of constant density throughout (shells such as tennis balls need to 
have their
*      inner wall modelled to get an accurate value.
*/

// borrowed from osgfont.cpp
osg::Camera* createOrthoCamera(double width, double height)
{
        osg::Camera* camera = new osg::Camera();

        camera->getOrCreateStateSet()->setMode(
                GL_LIGHTING,
                osg::StateAttribute::PROTECTED | osg::StateAttribute::OFF
        );

        osg::Matrix m = osg::Matrix::ortho2D(0.0f, width, 0.0f, height);

        camera->setProjectionMatrix(m);
        camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
        camera->setViewMatrix(osg::Matrix::identity());
        camera->setClearMask(GL_DEPTH_BUFFER_BIT);
        camera->setRenderOrder(osg::Camera::POST_RENDER);

        return camera;
}

osgText::Text* createLabel(const std::string& l, osgText::Font* font, unsigned 
int size)
// borrowed and modified from osgfont.cpp
{
        static osg::Vec3 pos(0.0f, 160.0f, 0.0f);

        osgText::Text* label = new osgText::Text();

        label->setFont(font);
        label->setCharacterSize(size);
        label->setFontResolution(size, size);
        label->setColor(osg::Vec4(1.0f, 1.0f, .5f, 1.0f));
        label->setPosition(pos);
        label->setAlignment(osgText::Text::LEFT_BOTTOM);
        label->setAxisAlignment(osgText::Text::SCREEN);
        label->setCharacterSizeMode(osgText::Text::SCREEN_COORDS);
        label->setText(l);

        pos.y() -= size*.75f;

        return label;
}


int main(int argc, char** argv)
{
   // use an ArgumentParser object to manage the program arguments.
   osg::ArgumentParser arguments(&argc,argv);

   
arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
   arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" 
is an OpenSceneGraph example to load and visualise a 3d model and calculate its Mass, centre 
of gravity and Moments of Inertia.");
   
arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+"
 [options] filename ...");

   osgViewer::Viewer viewer(arguments);

   unsigned int helpType = 0;
   if ((helpType = arguments.readHelpType()))
   {
       arguments.getApplicationUsage()->write(std::cout, helpType);
       return 1;
   }
// report any errors if they have occurred when parsing the program arguments.
   if (arguments.errors())
   {
       arguments.writeErrorMessages(std::cout);
       return 1;
   }
if (arguments.argc()<=1)
   {
       
arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION);
       return 1;
   }

   std::string url, username, password;
   while(arguments.read("--login",url, username, password))
   {
       if (!osgDB::Registry::instance()->getAuthenticationMap())
       {
           osgDB::Registry::instance()->setAuthenticationMap(new 
osgDB::AuthenticationMap);
           
osgDB::Registry::instance()->getAuthenticationMap()->addAuthenticationDetails(
               url,
               new osgDB::AuthenticationDetails(username, password)
           );
       }
   }

   // set up the camera manipulators.
   {
       osg::ref_ptr<osgGA::KeySwitchMatrixManipulator> keyswitchManipulator = 
new osgGA::KeySwitchMatrixManipulator;

       keyswitchManipulator->addMatrixManipulator( '1', "Trackball", new 
osgGA::TrackballManipulator() );
       keyswitchManipulator->addMatrixManipulator( '2', "Flight", new 
osgGA::FlightManipulator() );
       keyswitchManipulator->addMatrixManipulator( '3', "Drive", new 
osgGA::DriveManipulator() );
       keyswitchManipulator->addMatrixManipulator( '4', "Terrain", new 
osgGA::TerrainManipulator() );

       std::string pathfile;
       char keyForAnimationPath = '5';
       while (arguments.read("-p",pathfile))
       {
           osgGA::AnimationPathManipulator* apm = new 
osgGA::AnimationPathManipulator(pathfile);
if (apm || !apm->valid()) {
               unsigned int num = 
keyswitchManipulator->getNumMatrixManipulators();
               keyswitchManipulator->addMatrixManipulator( keyForAnimationPath, 
"Path", apm );
               keyswitchManipulator->selectMatrixManipulator(num);
               ++keyForAnimationPath;
           }
       }

       viewer.setCameraManipulator( keyswitchManipulator.get() );
   }

   // add the state manipulator
   viewer.addEventHandler( new 
osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
// add the thread model handler
   viewer.addEventHandler(new osgViewer::ThreadingHandler);

   // add the window size toggle handler
   viewer.addEventHandler(new osgViewer::WindowSizeHandler);
// add the stats handler
   viewer.addEventHandler(new osgViewer::StatsHandler);

   // add the help handler
   viewer.addEventHandler(new 
osgViewer::HelpHandler(arguments.getApplicationUsage()));

   // add the record camera path handler
   viewer.addEventHandler(new osgViewer::RecordCameraPathHandler);

   // add the LOD Scale handler
   viewer.addEventHandler(new osgViewer::LODScaleHandler);

   // add the screen capture handler
   viewer.addEventHandler(new osgViewer::ScreenCaptureHandler);

   // load the data
   osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
if (!loadedModel) {
       std::cout << arguments.getApplicationName() <<": No data loaded" << 
std::endl;
       return 1;
   }

   // any option left unread are converted into errors to write out later.
   arguments.reportRemainingOptionsAsUnrecognized();

   // report any errors if they have occurred when parsing the program 
arguments.
   if (arguments.errors())
   {
       arguments.writeErrorMessages(std::cout);
       return 1;
   }


   // optimize the scene graph, remove redundant nodes and state etc.
   osgUtil::Optimizer optimizer;
   optimizer.optimize(loadedModel.get());
                osg::Group* toplevel = new osg::Group(); // overall holder
        toplevel->addChild(loadedModel.get());
        {
                InertiaVisitor inert;
                // automatically called: inert.zeroInertiaTensor(); // call if 
reusing
                loadedModel->accept(inert);
                osg::Vec2 xr=inert.getXrange();
                osg::Vec2 yr=inert.getYrange();
                osg::Vec2 zr=inert.getZrange();
                osg::Vec3 centreOfGravity=inert.getCentroid();
                osg::notify(osg::WARN) << "* MI sizes " << xr.y()-xr.x()<< ","<<yr.y()-yr.x()<< 
","<<zr.y()-zr.x() << std::endl;
                osg::notify(osg::WARN) << "* MI dxx " << xr.x()<< ","<<xr.y()<< 
std::endl;
                osg::notify(osg::WARN) << "* MI dyy " << yr.x()<< ","<<yr.y()<< 
std::endl;
                osg::notify(osg::WARN) << "* MI dzz " << zr.x()<< ","<<zr.y()<< 
std::endl;
                osg::notify(osg::WARN) << "* MI volume " << 
inert.getVolume()<<std::endl;
                osg::notify(osg::WARN) << "* Surface Area " << 
inert.getArea()<<std::endl;
                osg::notify(osg::WARN) << "* C gravity " << centreOfGravity.x() <<","<<centreOfGravity.y() 
<<","<<centreOfGravity.z() << std::endl;
                osg::Matrix inertia=inert.getInertiaTensor();
                osg::notify(osg::WARN) << "* MI tensor " << inertia(0,0) << " "<< inertia(1,0) << 
" "<< inertia(2,0) << std::endl;
                osg::notify(osg::WARN) << "* MI tensor " << inertia(0,1) << " "<< inertia(1,1) << 
" "<< inertia(2,1) << std::endl;
                osg::notify(osg::WARN) << "* MI tensor " << inertia(0,2) << " "<< inertia(1,2) << 
" "<< inertia(2,2) << std::endl;
                unsigned int size=28; // size of text.
                osg::Camera* camera = createOrthoCamera(1280.0f, 1024.0f);
                osgText::Font* font  = osgText::readFontFile("fonts/arial.ttf");

                osg::Geode* geode = new osg::Geode(); // for text display of 
inertia
                std::stringstream strvol;
                strvol.setf(std::ios::fixed,std::ios::floatfield);   // 
floatfield set to fixed
                strvol.precision(5);
                strvol.str("Inertia:");
                for (int ic = 0 ; ic<argc ; ic++) {
                        strvol<<" " << osgDB::getSimpleFileName(argv[ic]);
                }
                geode->addDrawable(createLabel(strvol.str(), font, size));

                strvol.str("");
                strvol<<"Volume " << inert.getVolume() << " Area "<< 
inert.getArea();
                geode->addDrawable(createLabel(strvol.str(), font, size));

                strvol.str("");
                strvol<<"Sizes " << xr.y()-xr.x()<< ","<<yr.y()-yr.x()<< 
","<<zr.y()-zr.x();
                geode->addDrawable(createLabel(strvol.str(), font, size));

                strvol.str("");
                strvol<<"C gravity " << centreOfGravity.x() <<","<<centreOfGravity.y() 
<<","<<centreOfGravity.z();
                geode->addDrawable( createLabel(strvol.str(), font, size));

                strvol.str("");
                strvol<<"MI tensor " << inertia(0,0) << " "<< inertia(0,1) << " 
"<< inertia(0,2);
                geode->addDrawable( createLabel(strvol.str(), font, size));
                strvol.str("");
                strvol<<"MI tensor " << inertia(1,0) << " "<< inertia(1,1) << " 
"<< inertia(1,2);
                geode->addDrawable( createLabel(strvol.str(), font, size));
                strvol.str("");
                strvol<<"MI tensor " << inertia(2,0) << " "<< inertia(2,1) << " 
"<< inertia(2,2);
                geode->addDrawable( createLabel(strvol.str(), font, size));

                camera->addChild(geode);
                toplevel->addChild(camera);

        }

   viewer.setSceneData( toplevel );

   viewer.realize();

   return viewer.run();

}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to