[osg-users] Unwanted culling in 3.6.4 vs 3.5.1

Anders Backman andersb at cs.umu.se
Thu Jan 9 06:02:10 PST 2020


[image: image.png]
Actually, I managed to reproduce it, sort of in a sample application.

When running this exact code in 3.6.4 I get this:

A quick blink (of a white box), then nothing and lots of culling warnings:
CullVisitor::apply(Geode&) detected NaN,
    depth=nan, center=(0 0 7.125),
    matrix={
        -nan(ind) -nan(ind) -nan(ind) -nan(ind)
        -nan(ind) -nan(ind) -nan(ind) -nan(ind)
        -nan(ind) -nan(ind) -nan(ind) -nan(ind)
        -0.5 -7.125 -45.6982 1



But with 3.5.1 I get:

[image: image.png]
Which is what I would expect.
Anyone that  can reproduce this? No models needed, just compile the code
and run.

Cheers,
Anders



On Thu, Jan 9, 2020 at 2:56 PM Anders Backman <andersb at cs.umu.se> wrote:

> That is so strange..!
>
> > osgversion
> OpenSceneGraph Library 3.6.4
>
> Captured a short video of the issue:
>  osgviewer --window 0 0 1024 768 osg_3.6.4_culled.osgt
>
> https://gofile.io/?c=A6LCw6
>
> What on earth could be the difference here? Have tested on several
> computers (only Windows though).
> Windows 10, NVida GeForce card.
>
> Also, how it works in 3.5.1:
>
> ">"osgversion
> OpenSceneGraph Library 3.5.1
>
> osgviewer --window 0 0 1024 768 osg_3.5.4_not_culled.osgt
> https://gofile.io/?c=i2Ssx0
>
> On Thu, Jan 9, 2020 at 2:00 PM Voerman, L. <l.voerman at rug.nl> wrote:
>
>> Hi Anders,
>> I did have a quick look, but I can't see any unwanted culling, nor do I
>> get the warnings you write about.
>> tried versions 3.6.3 3.6.4 and 3.6.5 on windows with
>> OSG_NOTIFY_LEVEL=INFO
>> osgviewer osg_3.6.4_culled.osgt
>>
>> Looking in the file I see a lot of empty osg::Text nodes - I guess that
>> causes an empty bounding sphere in the specific version of osg you have.
>> Laurens.
>>
>> On Thu, Jan 9, 2020 at 1:19 PM Anders Backman <andersb at cs.umu.se> wrote:
>>
>>> Another issue I discovered with 3.6.4 is that we now suddenly get
>>> unwanted culling.
>>> At first it looks like a small feature culling thing (which we disable
>>> at global level with:
>>>
>>>   // Don't do small feature culling
>>>   osg::CullStack::CullingMode cullingMode =
>>> m_viewer->getCamera()->getCullingMode();
>>>   cullingMode &= ~(osg::CullStack::SMALL_FEATURE_CULLING);
>>>   m_viewer->getCamera()->setCullingMode(cullingMode);
>>>
>>>
>>> However, this does not look like a small feature thing to me at all.
>>> Actually, it culls even when you get close to the objects.
>>> Attached are two osgt-files.
>>>
>>> One is saved from OSG 3.5.1 (works as intended).
>>>
>>> Second one is saved from OSG 3.6.4 (where we get the unwanted culling).
>>>
>>>
>>> If you use a later version of OSG (3.6.4) the one from 3.6.4 generates
>>> lots of warnings:
>>>
>>> CullVisitor::apply(Geode&) detected NaN,
>>>     depth=nan, center=(0 0 7.125),
>>>     matrix={
>>>         -nan(ind) -nan(ind) -nan(ind) -nan(ind)
>>>         -nan(ind) -nan(ind) -nan(ind) -nan(ind)
>>>         -nan(ind) -nan(ind) -nan(ind) -nan(ind)
>>>         0.187249 -0.470484 -6.20285 1
>>>
>>> whereas the one from 3.5.1 does not.
>>>
>>> Anyone give me a hand on this? I am really stuck.
>>> I was first totally into small feature culling, trying to dig up old
>>> code where people was trying to disable small feature culling on subgraphs
>>> etc. But that is not the issue here.
>>>
>>> Any suggestion would help. I tried to make the scene as small as
>>> possible.
>>>
>>> I tried to attach the files, but it made the message too big.
>>> Instead I shared the files using gofile: https://gofile.io/?c=M5xPmU
>>> There are two files osg_3.5.1_not_culled.osgt
>>> and osg_3.6.4_culled.osgt
>>>
>>> Thanks,
>>> Anders
>>>
>>>
>>> --
>>> __________________________________________
>>> Anders Backman, HPC2N
>>> 90187 Umeå University, Sweden
>>> anders at cs.umu.se http://www.hpc2n.umu.se
>>> Cell: +46-70-392 64 67
>>> _______________________________________________
>>> osg-users mailing list
>>> osg-users at lists.openscenegraph.org
>>> http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org
>>>
>> _______________________________________________
>> osg-users mailing list
>> osg-users at lists.openscenegraph.org
>> http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org
>>
>
>
> --
> __________________________________________
> Anders Backman, HPC2N
> 90187 Umeå University, Sweden
> anders at cs.umu.se http://www.hpc2n.umu.se
> Cell: +46-70-392 64 67
>


-- 
__________________________________________
Anders Backman, HPC2N
90187 Umeå University, Sweden
anders at cs.umu.se http://www.hpc2n.umu.se
Cell: +46-70-392 64 67
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openscenegraph.org/pipermail/osg-users-openscenegraph.org/attachments/20200109/fd611d59/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: image.png
Type: image/png
Size: 23601 bytes
Desc: not available
URL: <http://lists.openscenegraph.org/pipermail/osg-users-openscenegraph.org/attachments/20200109/fd611d59/attachment-0002.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: image.png
Type: image/png
Size: 24890 bytes
Desc: not available
URL: <http://lists.openscenegraph.org/pipermail/osg-users-openscenegraph.org/attachments/20200109/fd611d59/attachment-0003.png>
-------------- next part --------------
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 Robert Osfield
 *
 * This application is open source and may be redistributed and/or modified
 * freely and without restriction, both in commercial and non commercial 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 file to get the include of windows.h right, which is a bit tricky.
#ifdef _WIN32
#  ifndef WIN32_LEAN_AND_MEAN
#    define WIN32_LEAN_AND_MEAN
#  endif
#  ifndef NOMINMAX
#    define NOMINMAX
#  endif
#  include <windows.h>
#endif

#ifdef far // Defined in WinDef.h
#  undef far
#endif

#ifdef near // Defined in WinDef.h
#  undef near
#endif

#ifdef FAR  // Defined in WinDef.h; has to be defined again
#  undef FAR
#  define FAR
#endif

#ifdef NEAR // Defined in WinDef.h; has to be defined again
#  undef NEAR
#  define NEAR
#endif



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

#include <osg/Switch>
#include <osg/Types>
#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 <osgGA/SphericalManipulator>

#include <osgGA/Device>
#include <osg/GL2Extensions>
#include <osg/GLExtensions>
#include <osg/Version>
#include <osg/GraphicsContext>
#include <osg/Notify>
#include <iostream>


#include <WinBase.h>
#include <osg/ComputeBoundsVisitor>
#include <osg/MatrixTransform>

class BoundingBoxCallback : public osg::NodeCallback
{
public:
  virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
  {
    osg::BoundingBox bb;
    for (unsigned int i = 0; i < _nodesToCompute.size(); ++i)
    {
      osg::Node* node = _nodesToCompute[i];
      if (!node->getNumParents())
        continue;

      osg::ComputeBoundsVisitor cbbv;
      node->accept(cbbv);

      osg::BoundingBox localBB = cbbv.getBoundingBox();
      osg::Matrix localToWorld = osg::computeLocalToWorld(node->getParent(0)->getParentalNodePaths()[0]);
      for (unsigned int y = 0; y < 8; ++y)
        bb.expandBy(localBB.corner(y) * localToWorld);
    }

    osg::MatrixTransform* trans = static_cast<osg::MatrixTransform*>(node);
    trans->setMatrix(
      osg::Matrix::scale(bb.xMax() - bb.xMin(), bb.yMax() - bb.yMin(), bb.zMax() - bb.zMin()) *
      osg::Matrix::translate(bb.center()));

    traverse(node, nv);
  }

  osg::NodePath _nodesToCompute;
};

#include <osg/ShapeDrawable>
#include <osg/AutoTransform>


osg::Geode* createContactShapes()
{
  const float sphereRadius = 3.f;
  const float cylinderRadius = 0.5f;
  const float cylinderLength = 12.f;
  const float coneHeight = 3.f;

  osg::ref_ptr< osg::Shape > shapes[] = {
                                          new osg::Sphere(osg::Vec3(), sphereRadius),
                                          new osg::Cylinder(osg::Vec3(0.f, 0.f, sphereRadius + 0.5f * cylinderLength), cylinderRadius, cylinderLength),
                                          new osg::Cone(osg::Vec3(0.f, 0.f, sphereRadius + cylinderLength), 1.7f * cylinderRadius, coneHeight)
  };
  const size_t numShapes = sizeof(shapes) / sizeof(osg::ref_ptr< osg::Shape >);

  osg::Geode* geode = new osg::Geode();
  for (size_t i = 0; i < numShapes; ++i) {
    osg::TessellationHints* th = new osg::TessellationHints();
    th->setDetailRatio(0.35f);

    osg::ShapeDrawable* sd = new osg::ShapeDrawable(shapes[i], th);



    geode->addDrawable(sd);
  }

  return geode;
}

osg::Node *build_scene()
{
  auto parent = new osg::Group();
  auto child = new osg::Group();
  parent->addChild(child);

  float scale = 1.0;

  float positions[] = { -0.5, -0.5, 0.5, 0.5 };

  for (int i = 0; i < 4; i++)
  {
    auto mt = new osg::MatrixTransform();

    mt->setMatrix(osg::Matrix::translate(positions[i], positions[i], 0));
    auto obj = new osg::AutoTransform();

    mt->addChild(obj);

    obj->setAutoRotateMode(osg::AutoTransform::NO_ROTATION);
    obj->setAutoScaleToScreen(true);
    obj->setAutoScaleTransitionWidthRatio(0.f);
    obj->setMaximumScale(0.015 * (double)scale);
    obj->setMinimumScale(0.001 * (double)scale);
    obj->setPivotPoint(osg::Vec3(0, 0, 0));

    auto shape = createContactShapes();
    obj->addChild(shape);

    child->addChild(mt);
  }

  /*
  */
  // World bounding box callback & node
  osg::ref_ptr<BoundingBoxCallback> bbcb = new BoundingBoxCallback;
  bbcb->_nodesToCompute.push_back(child);

  osg::ref_ptr<osg::Geode> bbgeode = new osg::Geode;
  bbgeode->addDrawable(new osg::ShapeDrawable(new osg::Box));

  osg::ref_ptr<osg::MatrixTransform> boundingBoxNode = new osg::MatrixTransform;
  boundingBoxNode->addChild(bbgeode);
  boundingBoxNode->addUpdateCallback(bbcb.get());
  boundingBoxNode->getOrCreateStateSet()->setAttributeAndModes(
    new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE));
  boundingBoxNode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);

  parent->addChild(boundingBoxNode);

  return parent;
}


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 the standard OpenSceneGraph example which loads and visualises 3d models.");
  arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName() + " [options] filename ...");
  arguments.getApplicationUsage()->addCommandLineOption("--image <filename>", "Load an image and render it on a quad");
  arguments.getApplicationUsage()->addCommandLineOption("--dem <filename>", "Load an image/DEM and render it on a HeightField");
  arguments.getApplicationUsage()->addCommandLineOption("-p <filename>", "Play specified camera path animation file, previously saved with 'z' key.");
  arguments.getApplicationUsage()->addCommandLineOption("--speed <factor>", "Speed factor for animation playing (1 == normal speed).");
  arguments.getApplicationUsage()->addCommandLineOption("--device <device-name>", "add named device to the viewer");
  arguments.getApplicationUsage()->addCommandLineOption("--stats", "print out load and compile timing stats");

  osg::ref_ptr<osgViewer::Viewer> m_viewer = new osgViewer::Viewer(arguments);

  osg::Vec2i windowSize(1200, 720);
  m_viewer->setUpViewInWindow(0, 0, windowSize[0], windowSize[1]);


  m_viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
  m_viewer->realize();
  osgViewer::Viewer::Windows windows;
  m_viewer->getWindows(windows);
  osgViewer::GraphicsWindow* window = windows[0];
  window->setWindowRectangle(40, 40, windowSize[0], windowSize[1]);
  window->setWindowDecoration(true);
  m_viewer->frame(); // Force OSG to create an OpenGL context for us before calling agxGL::init().
  m_viewer->setReleaseContextAtEndOfFrameHint(false);
  m_viewer->frame();


  osg::CullStack::CullingMode cullingMode = m_viewer->getCamera()->getCullingMode();
  cullingMode &= ~(osg::CullStack::SMALL_FEATURE_CULLING);
  m_viewer->getCamera()->setCullingMode(cullingMode);

  m_viewer->getCamera()->setSmallFeatureCullingPixelSize(0);

  {
    osgViewer::Viewer::Windows windows;
    m_viewer->getWindows(windows);

    if (windows.empty()) {
      std::cerr << "Failed to create window when updating vsync" << std::endl;
      return 1;
    }
    osgViewer::GraphicsWindow* window = windows[0];
#if defined(OSG_VERSION_GREATER_OR_EQUAL) && OSG_VERSION_GREATER_OR_EQUAL(2,9,11)
    std::cerr << "VSYNK STATUS: " << window->getSyncToVBlank() << std::endl;
    window->setSyncToVBlank(false);
#endif
  }


  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;
  }

  bool printStats = arguments.read("--stats");



  std::string device;
  while (arguments.read("--device", device))
  {
    osg::ref_ptr<osgGA::Device> dev = osgDB::readRefFile<osgGA::Device>(device);
    if (dev.valid())
    {
      m_viewer->addDevice(dev);
    }
  }

  // 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());
    keyswitchManipulator->addMatrixManipulator('5', "Orbit", new osgGA::OrbitManipulator());
    keyswitchManipulator->addMatrixManipulator('6', "FirstPerson", new osgGA::FirstPersonManipulator());
    keyswitchManipulator->addMatrixManipulator('7', "Spherical", new osgGA::SphericalManipulator());

    std::string pathfile;
    double animationSpeed = 1.0;
    while (arguments.read("--speed", animationSpeed)) {}
    char keyForAnimationPath = '8';
    while (arguments.read("-p", pathfile))
    {
      osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator(pathfile);
      if (apm && !apm->getAnimationPath()->empty())
      {
        apm->setTimeScale(animationSpeed);

        unsigned int num = keyswitchManipulator->getNumMatrixManipulators();
        keyswitchManipulator->addMatrixManipulator(keyForAnimationPath, "Path", apm);
        keyswitchManipulator->selectMatrixManipulator(num);
        ++keyForAnimationPath;
      }
    }

    m_viewer->setCameraManipulator(keyswitchManipulator.get());
  }

  // add the state manipulator
  m_viewer->addEventHandler(new osgGA::StateSetManipulator(m_viewer->getCamera()->getOrCreateStateSet()));

  // add the thread model handler
  m_viewer->addEventHandler(new osgViewer::ThreadingHandler);

  // add the window size toggle handler
  m_viewer->addEventHandler(new osgViewer::WindowSizeHandler);

  // add the stats handler
  m_viewer->addEventHandler(new osgViewer::StatsHandler);

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

  // add the record camera path handler
  m_viewer->addEventHandler(new osgViewer::RecordCameraPathHandler);

  // add the LOD Scale handler
  m_viewer->addEventHandler(new osgViewer::LODScaleHandler);

  // add the screen capture handler
  m_viewer->addEventHandler(new osgViewer::ScreenCaptureHandler);

  osg::ElapsedTime elapsedTime;

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

  if (printStats)
  {
    double loadTime = elapsedTime.elapsedTime_m();
    std::cout << "Load time " << loadTime << "ms" << std::endl;

    m_viewer->getStats()->collectStats("compile", true);
  }
#else
  osg::ref_ptr<osg::Node> loadedModel = build_scene();
#endif

  // 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);

  m_viewer->setSceneData(loadedModel);

  m_viewer->realize();

  return m_viewer->run();

}


More information about the osg-users mailing list