Hi again,
Forgot to attach the last two files with fixes to the virtual inheritance
warnings...
I have also attached the fix to the type shadowing problem in
ConvexPolyhedron.cpp
With this I think that the OSG Core group of projects compiles without warnings
on Visual Studio 2015.
Regards
Björn
>-----Original Message-----
>From: osg-users [mailto:[email protected]] On
>Behalf Of Björn Blissing
>Sent: Friday, June 3, 2016 3:35 PM
>To: OpenSceneGraph Users <[email protected]>
>Subject: Re: [osg-users] Preparing to make 3.5.3 dev release, please test
>
>Hi again,
>
>This is the fix for the rest of the virtual inheritance warnings. Same as for
>issues as for the Operation class.
>
>Regards
>Björn
>
>>-----Original Message-----
>>From: osg-users [mailto:[email protected]] On
>>Behalf Of Björn Blissing
>>Sent: Friday, June 3, 2016 2:39 PM
>>To: OpenSceneGraph Users <[email protected]>
>>Subject: Re: [osg-users] Preparing to make 3.5.3 dev release, please test
>>
>>Hi Robert,
>>
>>The warnings relating the Operation class comes from the two protected
>>constructors. Since the class uses virtual inheritance it is initialized by
>>the
>most
>>derived class. So the initializer for the virtual base class is ignored.
>>
>>So just removing the base class initializer for Operation will remove this
>>warning. I do not think this will have any negative side effects, since base
>class
>>initializer should be ignored by all compliant compilers anyway.
>>
>>I have attached the changed file.
>>
>>Regards
>>Björn
>>
>>>-----Original Message-----
>>>From: osg-users [mailto:[email protected]] On
>>>Behalf Of Robert Osfield
>>>Sent: Friday, June 3, 2016 1:46 PM
>>>To: OpenSceneGraph Users <[email protected]>
>>>Subject: Re: [osg-users] Preparing to make 3.5.3 dev release, please test
>>>
>>>Hi Bjorn,
>>>
>>>On 3 June 2016 at 09:48, Björn Blissing <[email protected]> wrote:
>>>>
>>>> I compiled the latest master with Visual Studio 2015.
>>>>
>>>> I got a couple of warnings. First of all I got a ton of these, which all
>>originates
>>>from the same origin:
>>>>
>>>>
>>>> Code:
>>>>
>>>> ...
>>>> OpenSceneGraph\include\osg/OperationThread(80): warning C4589:
>>>Constructor of abstract class 'osg::Operation' ignores initializer for
>>>virtual
>>base
>>>class 'osg::Referenced' (compiling source file
>>>D:\github\OpenSceneGraph\src\osg\AnimationPath.cpp)
>>>> OpenSceneGraph\include\osg/OperationThread(80): note: virtual base
>>>classes are only initialized by the most-derived type (compiling source file
>>>D:\github\OpenSceneGraph\src\osg\AnimationPath.cpp)
>>>> ...
>>>>
>>>>
>>>>
>>>>
>>>> And the same warnings for the following classes:
>>>>
>>>>
>>>> Code:
>>>>
>>>> OpenSceneGraph\src\osgGA\CameraManipulator.cpp(24): warning
>C4589:
>>>Constructor of abstract class 'osgGA::CameraManipulator' ignores initializer
>>for
>>>virtual base class 'osg::Callback'
>>>> OpenSceneGraph\src\osgGA\CameraManipulator.cpp(24): note: virtual
>>>base classes are only initialized by the most-derived type
>>>> OpenSceneGraph\src\osgGA\StandardManipulator.cpp(51): warning
>>C4589:
>>>Constructor of abstract class 'osgGA::StandardManipulator' ignores
>initializer
>>>for virtual base class 'osg::Callback'
>>>> OpenSceneGraph\src\osgGA\StandardManipulator.cpp(51): note:
>virtual
>>>base classes are only initialized by the most-derived type
>>>> OpenSceneGraph\src\osgViewer\ViewerBase.cpp(44): warning C4589:
>>>Constructor of abstract class 'osgViewer::ViewerBase' ignores initializer for
>>>virtual base class 'osg::Object'
>>>> OpenSceneGraph\src\osgViewer\ViewerBase.cpp(44): note: virtual
>base
>>>classes are only initialized by the most-derived type
>>>> OpenSceneGraph\src\osgViewer\ViewerBase.cpp(50): warning C4589:
>>>Constructor of abstract class 'osgViewer::ViewerBase' ignores initializer for
>>>virtual base class 'osg::Object'
>>>> OpenSceneGraph\src\osgViewer\ViewerBase.cpp(50): note: virtual
>base
>>>classes are only initialized by the most-derived type
>>>> OpenSceneGraph\src\osgAnimation\AnimationManagerBase.cpp(65):
>>>warning C4589: Constructor of abstract class
>>>'osgAnimation::AnimationManagerBase' ignores initializer for virtual base
>>class
>>>'osg::Callback'
>>>> OpenSceneGraph\src\osgAnimation\AnimationManagerBase.cpp(65):
>>>note: virtual base classes are only initialized by the most-derived type
>>>
>>>Unfortunately I don't see these warnings with the compiler I have
>>>available with the settings that are currently available. Do you know
>>>of what to enable these warnings in gcc or Clang?
>>>
>>>The nature of these warnings are such that I really need to be able to
>>>see the warning first hand and attempt a local fix to see the affect.
>>>
>>>Cheers,
>>>Robert.
>>>_______________________________________________
>>>osg-users mailing list
>>>[email protected]
>>>http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org
/* -*-c++-*-
* Copyright (C) 2008 Cedric Pinson <[email protected]>
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library 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. See the
* OpenSceneGraph Public License for more details.
*/
#include <osgAnimation/AnimationManagerBase>
#include <osgAnimation/LinkVisitor>
#include <algorithm>
using namespace osgAnimation;
AnimationManagerBase::~AnimationManagerBase() {}
AnimationManagerBase::AnimationManagerBase()
{
_needToLink = false;
_automaticLink = true;
}
void AnimationManagerBase::clearTargets()
{
for (TargetSet::iterator it = _targets.begin(); it != _targets.end(); ++it)
(*it).get()->reset();
}
void AnimationManagerBase::dirty()
{
_needToLink = true;
}
void AnimationManagerBase::setAutomaticLink(bool state) { _automaticLink =
state; }
bool AnimationManagerBase::getAutomaticLink() const { return _automaticLink; }
void AnimationManagerBase::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
{
if (needToLink())
{
/** manager need to link, it means that an animation has been added
so we need to relink all item animated with all animations.
We apply the linker visitor on the manager node to affect
all its children.
But it should not be done here, it should be done in the
update of AnimationManager
*/
link(node);
}
const osg::FrameStamp* fs = nv->getFrameStamp();
update(fs->getSimulationTime());
}
traverse(node,nv);
}
AnimationManagerBase::AnimationManagerBase(const AnimationManagerBase& b, const
osg::CopyOp& copyop) : osg::NodeCallback(b,copyop) // TODO check this
{
const AnimationList& animationList = b.getAnimationList();
for (AnimationList::const_iterator it = animationList.begin();
it != animationList.end();
++it)
{
Animation* animation =
dynamic_cast<osgAnimation::Animation*>(it->get()->clone(copyop));
_animations.push_back(animation);
}
_needToLink = true;
_automaticLink = b._automaticLink;
buildTargetReference();
}
void AnimationManagerBase::buildTargetReference()
{
_targets.clear();
for( AnimationList::iterator iterAnim = _animations.begin(); iterAnim !=
_animations.end(); ++iterAnim )
{
Animation* anim = (*iterAnim).get();
for (ChannelList::iterator it = anim->getChannels().begin();
it != anim->getChannels().end();
++it)
_targets.insert((*it)->getTarget());
}
}
void AnimationManagerBase::registerAnimation (Animation* animation)
{
_needToLink = true;
_animations.push_back(animation);
buildTargetReference();
}
void AnimationManagerBase::unregisterAnimation (Animation* animation)
{
AnimationList::iterator it = std::find(_animations.begin(),
_animations.end(), animation);
if (it != _animations.end())
{
_animations.erase(it);
}
buildTargetReference();
}
bool AnimationManagerBase::needToLink() const { return _needToLink &&
getAutomaticLink(); }
void AnimationManagerBase::setLinkVisitor(LinkVisitor* visitor)
{
_linker = visitor;
}
LinkVisitor* AnimationManagerBase::getOrCreateLinkVisitor()
{
if (!_linker.valid())
_linker = new LinkVisitor;
return _linker.get();
}
void AnimationManagerBase::link(osg::Node* subgraph)
{
LinkVisitor* linker = getOrCreateLinkVisitor();
linker->getAnimationList().clear();
linker->getAnimationList() = _animations;
subgraph->accept(*linker);
_needToLink = false;
buildTargetReference();
}
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library 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. See the
* OpenSceneGraph Public License for more details.
*/
#include <stdlib.h>
#include <string.h>
#include <osgViewer/ViewerBase>
#include <osgViewer/View>
#include <osgViewer/Renderer>
#include <osg/io_utils>
#include <osg/TextureCubeMap>
#include <osg/TextureRectangle>
#include <osg/TexMat>
#include <osg/DeleteHandler>
#include <osgDB/Registry>
#include <osgUtil/Optimizer>
#include <osgUtil/IntersectionVisitor>
#include <osgUtil/Statistics>
static osg::ApplicationUsageProxy
ViewerBase_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_CONFIG_FILE
<filename>","Specify a viewer configuration file to load by default.");
static osg::ApplicationUsageProxy
ViewerBase_e1(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_THREADING
<value>","Set the threading model using by Viewer, <value> can be
SingleThreaded, CullDrawThreadPerContext, DrawThreadPerContext or
CullThreadPerCameraDrawThreadPerContext.");
static osg::ApplicationUsageProxy
ViewerBase_e2(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_SCREEN
<value>","Set the default screen that windows should open up on.");
static osg::ApplicationUsageProxy
ViewerBase_e3(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_WINDOW x y
width height","Set the default window dimensions that windows should open up
on.");
static osg::ApplicationUsageProxy
ViewerBase_e4(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_RUN_FRAME_SCHEME","Frame
rate manage scheme that viewer run should use, ON_DEMAND or CONTINUOUS
(default).");
static osg::ApplicationUsageProxy
ViewerBase_e5(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_RUN_MAX_FRAME_RATE","Set
the maximum number of frame as second that viewer run. 0.0 is default and
disables an frame rate capping.");
using namespace osgViewer;
ViewerBase::ViewerBase()
{
viewerBaseInit();
}
ViewerBase::ViewerBase(const ViewerBase&)
{
viewerBaseInit();
}
void ViewerBase::viewerBaseInit()
{
_firstFrame = true;
_done = false;
_keyEventSetsDone = osgGA::GUIEventAdapter::KEY_Escape;
_quitEventSetsDone = true;
_releaseContextAtEndOfFrameHint = true;
_threadingModel = AutomaticSelection;
_threadsRunning = false;
_endBarrierPosition = AfterSwapBuffers;
_endBarrierOperation = osg::BarrierOperation::NO_OPERATION;
_requestRedraw = true;
_requestContinousUpdate = false;
_runFrameScheme = CONTINUOUS;
_runMaxFrameRate = 0.0f;
const char* str = getenv("OSG_RUN_FRAME_SCHEME");
if (str)
{
if (strcmp(str, "ON_DEMAND")==0) _runFrameScheme = ON_DEMAND;
else if (strcmp(str, "CONTINUOUS")==0) _runFrameScheme = CONTINUOUS;
}
str = getenv("OSG_RUN_MAX_FRAME_RATE");
if (str)
{
_runMaxFrameRate = osg::asciiToDouble(str);
}
}
void ViewerBase::setThreadingModel(ThreadingModel threadingModel)
{
if (_threadingModel == threadingModel) return;
if (_threadsRunning) stopThreading();
_threadingModel = threadingModel;
if (isRealized() && _threadingModel!=SingleThreaded) startThreading();
}
ViewerBase::ThreadingModel ViewerBase::suggestBestThreadingModel()
{
const char* str = getenv("OSG_THREADING");
if (str)
{
if (strcmp(str,"SingleThreaded")==0) return SingleThreaded;
else if (strcmp(str,"CullDrawThreadPerContext")==0) return
CullDrawThreadPerContext;
else if (strcmp(str,"DrawThreadPerContext")==0) return
DrawThreadPerContext;
else if (strcmp(str,"CullThreadPerCameraDrawThreadPerContext")==0)
return CullThreadPerCameraDrawThreadPerContext;
}
Contexts contexts;
getContexts(contexts);
if (contexts.empty()) return SingleThreaded;
#if 0
// temporary hack to disable multi-threading under Windows till we find
good solutions for
// crashes that users are seeing.
return SingleThreaded;
#endif
Cameras cameras;
getCameras(cameras);
if (cameras.empty()) return SingleThreaded;
int numProcessors = OpenThreads::GetNumberOfProcessors();
if (contexts.size()==1)
{
if (numProcessors==1) return SingleThreaded;
else return DrawThreadPerContext;
}
#if 1
if (numProcessors >= static_cast<int>(cameras.size()+contexts.size()))
{
return CullThreadPerCameraDrawThreadPerContext;
}
#endif
return DrawThreadPerContext;
}
void ViewerBase::setUpThreading()
{
Contexts contexts;
getContexts(contexts);
if (_threadingModel==SingleThreaded)
{
if (_threadsRunning) stopThreading();
else
{
// we'll set processor affinity here to help single threaded apps
// with multiple processor cores, and using the database pager.
int numProcessors = OpenThreads::GetNumberOfProcessors();
bool affinity = numProcessors>1;
if (affinity)
{
OpenThreads::SetProcessorAffinityOfCurrentThread(0);
Scenes scenes;
getScenes(scenes);
for(Scenes::iterator scitr = scenes.begin();
scitr != scenes.end();
++scitr)
{
if ((*scitr)->getSceneData())
{
// update the scene graph so that it has enough GL
object buffer memory for the graphics contexts that will be using it.
(*scitr)->getSceneData()->resizeGLObjectBuffers(osg::DisplaySettings::instance()->getMaxNumberOfGraphicsContexts());
}
}
}
}
}
else
{
if (!_threadsRunning) startThreading();
}
}
void ViewerBase::setEndBarrierPosition(BarrierPosition bp)
{
if (_endBarrierPosition == bp) return;
if (_threadsRunning) stopThreading();
_endBarrierPosition = bp;
if (_threadingModel!=SingleThreaded) startThreading();
}
void ViewerBase::setEndBarrierOperation(osg::BarrierOperation::PreBlockOp op)
{
if (_endBarrierOperation == op) return;
if (_threadsRunning) stopThreading();
_endBarrierOperation = op;
if (_threadingModel!=SingleThreaded) startThreading();
}
void ViewerBase::stopThreading()
{
if (!_threadsRunning) return;
OSG_INFO<<"ViewerBase::stopThreading() - stopping threading"<<std::endl;
Contexts contexts;
getContexts(contexts);
Cameras cameras;
getCameras(cameras);
Contexts::iterator gcitr;
Cameras::iterator citr;
for(Cameras::iterator camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
Renderer* renderer = dynamic_cast<Renderer*>(camera->getRenderer());
if (renderer) renderer->release();
}
// delete all the graphics threads.
for(gcitr = contexts.begin();
gcitr != contexts.end();
++gcitr)
{
(*gcitr)->setGraphicsThread(0);
}
// delete all the camera threads.
for(citr = cameras.begin();
citr != cameras.end();
++citr)
{
(*citr)->setCameraThread(0);
}
for(Cameras::iterator camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
Renderer* renderer = dynamic_cast<Renderer*>(camera->getRenderer());
if (renderer)
{
renderer->setGraphicsThreadDoesCull( true );
renderer->setDone(false);
}
}
_threadsRunning = false;
_startRenderingBarrier = 0;
_endRenderingDispatchBarrier = 0;
_endDynamicDrawBlock = 0;
OSG_INFO<<"Viewer::stopThreading() - stopped threading."<<std::endl;
}
void ViewerBase::startThreading()
{
if (_threadsRunning) return;
OSG_INFO<<"Viewer::startThreading() - starting threading"<<std::endl;
// release any context held by the main thread.
releaseContext();
_threadingModel = _threadingModel==AutomaticSelection ?
suggestBestThreadingModel() : _threadingModel;
Contexts contexts;
getContexts(contexts);
OSG_INFO<<"Viewer::startThreading() -
contexts.size()="<<contexts.size()<<std::endl;
Cameras cameras;
getCameras(cameras);
unsigned int numThreadsOnStartBarrier = 0;
unsigned int numThreadsOnEndBarrier = 0;
switch(_threadingModel)
{
case(SingleThreaded):
numThreadsOnStartBarrier = 1;
numThreadsOnEndBarrier = 1;
return;
case(CullDrawThreadPerContext):
numThreadsOnStartBarrier = contexts.size()+1;
numThreadsOnEndBarrier = contexts.size()+1;
break;
case(DrawThreadPerContext):
numThreadsOnStartBarrier = 1;
numThreadsOnEndBarrier = 1;
break;
case(CullThreadPerCameraDrawThreadPerContext):
numThreadsOnStartBarrier = cameras.size()+1;
numThreadsOnEndBarrier = 1;
break;
default:
OSG_NOTICE<<"Error: Threading model not selected"<<std::endl;
return;
}
// using multi-threading so make sure that new objects are allocated with
thread safe ref/unref
osg::Referenced::setThreadSafeReferenceCounting(true);
Scenes scenes;
getScenes(scenes);
for(Scenes::iterator scitr = scenes.begin();
scitr != scenes.end();
++scitr)
{
if ((*scitr)->getSceneData())
{
OSG_INFO<<"Making scene thread safe"<<std::endl;
// make sure that existing scene graph objects are allocated with
thread safe ref/unref
(*scitr)->getSceneData()->setThreadSafeRefUnref(true);
// update the scene graph so that it has enough GL object buffer
memory for the graphics contexts that will be using it.
(*scitr)->getSceneData()->resizeGLObjectBuffers(osg::DisplaySettings::instance()->getMaxNumberOfGraphicsContexts());
}
}
int numProcessors = OpenThreads::GetNumberOfProcessors();
bool affinity = numProcessors>1;
Contexts::iterator citr;
unsigned int numViewerDoubleBufferedRenderingOperation = 0;
bool graphicsThreadsDoesCull = _threadingModel == CullDrawThreadPerContext
|| _threadingModel==SingleThreaded;
for(Cameras::iterator camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
Renderer* renderer = dynamic_cast<Renderer*>(camera->getRenderer());
if (renderer)
{
renderer->setGraphicsThreadDoesCull(graphicsThreadsDoesCull);
renderer->setDone(false);
renderer->reset();
++numViewerDoubleBufferedRenderingOperation;
}
}
if (_threadingModel==CullDrawThreadPerContext)
{
_startRenderingBarrier = 0;
_endRenderingDispatchBarrier = 0;
_endDynamicDrawBlock = 0;
}
else if (_threadingModel==DrawThreadPerContext ||
_threadingModel==CullThreadPerCameraDrawThreadPerContext)
{
_startRenderingBarrier = 0;
_endRenderingDispatchBarrier = 0;
_endDynamicDrawBlock = new
osg::EndOfDynamicDrawBlock(numViewerDoubleBufferedRenderingOperation);
#ifndef OSGUTIL_RENDERBACKEND_USE_REF_PTR
if (!osg::Referenced::getDeleteHandler())
osg::Referenced::setDeleteHandler(new osg::DeleteHandler(2));
else
osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(2);
#endif
}
if (numThreadsOnStartBarrier>1)
{
_startRenderingBarrier = new
osg::BarrierOperation(numThreadsOnStartBarrier,
osg::BarrierOperation::NO_OPERATION);
}
if (numThreadsOnEndBarrier>1)
{
_endRenderingDispatchBarrier = new
osg::BarrierOperation(numThreadsOnEndBarrier, _endBarrierOperation);
}
osg::ref_ptr<osg::BarrierOperation> swapReadyBarrier = contexts.empty() ? 0
: new osg::BarrierOperation(contexts.size(),
osg::BarrierOperation::NO_OPERATION);
osg::ref_ptr<osg::SwapBuffersOperation> swapOp = new
osg::SwapBuffersOperation();
typedef std::map<OpenThreads::Thread*, int> ThreadAffinityMap;
ThreadAffinityMap threadAffinityMap;
unsigned int processNum = 1;
for(citr = contexts.begin();
citr != contexts.end();
++citr, ++processNum)
{
osg::GraphicsContext* gc = (*citr);
if (!gc->isRealized())
{
OSG_INFO<<"ViewerBase::startThreading() : Realizng window
"<<gc<<std::endl;
gc->realize();
}
gc->getState()->setDynamicObjectRenderingCompletedCallback(_endDynamicDrawBlock.get());
// create the a graphics thread for this context
gc->createGraphicsThread();
if (affinity) gc->getGraphicsThread()->setProcessorAffinity(processNum
% numProcessors);
threadAffinityMap[gc->getGraphicsThread()] = processNum % numProcessors;
// add the startRenderingBarrier
if (_threadingModel==CullDrawThreadPerContext &&
_startRenderingBarrier.valid())
gc->getGraphicsThread()->add(_startRenderingBarrier.get());
// add the rendering operation itself.
gc->getGraphicsThread()->add(new osg::RunOperations());
if (_threadingModel==CullDrawThreadPerContext &&
_endBarrierPosition==BeforeSwapBuffers && _endRenderingDispatchBarrier.valid())
{
// add the endRenderingDispatchBarrier
gc->getGraphicsThread()->add(_endRenderingDispatchBarrier.get());
}
if (swapReadyBarrier.valid())
gc->getGraphicsThread()->add(swapReadyBarrier.get());
// add the swap buffers
gc->getGraphicsThread()->add(swapOp.get());
if (_threadingModel==CullDrawThreadPerContext &&
_endBarrierPosition==AfterSwapBuffers && _endRenderingDispatchBarrier.valid())
{
// add the endRenderingDispatchBarrier
gc->getGraphicsThread()->add(_endRenderingDispatchBarrier.get());
}
}
if (_threadingModel==CullThreadPerCameraDrawThreadPerContext &&
numThreadsOnStartBarrier>1)
{
Cameras::iterator camItr;
for(camItr = cameras.begin();
camItr != cameras.end();
++camItr, ++processNum)
{
osg::Camera* camera = *camItr;
camera->createCameraThread();
if (affinity)
camera->getCameraThread()->setProcessorAffinity(processNum % numProcessors);
threadAffinityMap[camera->getCameraThread()] = processNum %
numProcessors;
osg::GraphicsContext* gc = camera->getGraphicsContext();
// add the startRenderingBarrier
if (_startRenderingBarrier.valid())
camera->getCameraThread()->add(_startRenderingBarrier.get());
Renderer* renderer = dynamic_cast<Renderer*>(camera->getRenderer());
renderer->setGraphicsThreadDoesCull(false);
camera->getCameraThread()->add(renderer);
if (_endRenderingDispatchBarrier.valid())
{
// add the endRenderingDispatchBarrier
gc->getGraphicsThread()->add(_endRenderingDispatchBarrier.get());
}
}
for(camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
if (camera->getCameraThread() &&
!camera->getCameraThread()->isRunning())
{
OSG_INFO<<" camera->getCameraThread()->
"<<camera->getCameraThread()<<std::endl;
camera->getCameraThread()->startThread();
}
}
}
#if 0
if (affinity)
{
OpenThreads::SetProcessorAffinityOfCurrentThread(0);
if (_scene.valid() && _scene->getDatabasePager())
{
#if 0
_scene->getDatabasePager()->setProcessorAffinity(1);
#else
_scene->getDatabasePager()->setProcessorAffinity(0);
#endif
}
}
#endif
#if 0
if (affinity)
{
for(ThreadAffinityMap::iterator titr = threadAffinityMap.begin();
titr != threadAffinityMap.end();
++titr)
{
titr->first->setProcessorAffinity(titr->second);
}
}
#endif
for(citr = contexts.begin();
citr != contexts.end();
++citr)
{
osg::GraphicsContext* gc = (*citr);
if (gc->getGraphicsThread() && !gc->getGraphicsThread()->isRunning())
{
OSG_INFO<<" gc->getGraphicsThread()->startThread()
"<<gc->getGraphicsThread()<<std::endl;
gc->getGraphicsThread()->startThread();
// OpenThreads::Thread::YieldCurrentThread();
}
}
_threadsRunning = true;
OSG_INFO<<"Set up threading"<<std::endl;
}
void ViewerBase::getWindows(Windows& windows, bool onlyValid)
{
windows.clear();
Contexts contexts;
getContexts(contexts, onlyValid);
for(Contexts::iterator itr = contexts.begin();
itr != contexts.end();
++itr)
{
osgViewer::GraphicsWindow* gw =
dynamic_cast<osgViewer::GraphicsWindow*>(*itr);
if (gw) windows.push_back(gw);
}
}
void ViewerBase::checkWindowStatus()
{
Contexts contexts;
getContexts(contexts);
checkWindowStatus(contexts);
}
void ViewerBase::checkWindowStatus(const Contexts& contexts)
{
if (contexts.size()==0)
{
_done = true;
if (areThreadsRunning()) stopThreading();
}
}
void ViewerBase::addUpdateOperation(osg::Operation* operation)
{
if (!operation) return;
if (!_updateOperations) _updateOperations = new osg::OperationQueue;
_updateOperations->add(operation);
}
void ViewerBase::removeUpdateOperation(osg::Operation* operation)
{
if (!operation) return;
if (_updateOperations.valid())
{
_updateOperations->remove(operation);
}
}
void
ViewerBase::setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation*
ico)
{
if (_incrementalCompileOperation == ico) return;
Contexts contexts;
getContexts(contexts, false);
if (_incrementalCompileOperation.valid())
_incrementalCompileOperation->removeContexts(contexts);
// assign new operation
_incrementalCompileOperation = ico;
Scenes scenes;
getScenes(scenes,false);
for(Scenes::iterator itr = scenes.begin();
itr != scenes.end();
++itr)
{
osgDB::DatabasePager* dp = (*itr)->getDatabasePager();
dp->setIncrementalCompileOperation(ico);
}
if (_incrementalCompileOperation)
_incrementalCompileOperation->assignContexts(contexts);
}
int ViewerBase::run()
{
if (!isRealized())
{
realize();
}
const char* run_frame_count_str = getenv("OSG_RUN_FRAME_COUNT");
unsigned int runTillFrameNumber = run_frame_count_str==0 ?
osg::UNINITIALIZED_FRAME_NUMBER : atoi(run_frame_count_str);
while(!done() && (run_frame_count_str==0 ||
getViewerFrameStamp()->getFrameNumber()<runTillFrameNumber))
{
double minFrameTime = _runMaxFrameRate>0.0 ? 1.0/_runMaxFrameRate : 0.0;
osg::Timer_t startFrameTick = osg::Timer::instance()->tick();
if (_runFrameScheme==ON_DEMAND)
{
if (checkNeedToDoFrame())
{
frame();
}
else
{
// we don't need to render a frame but we don't want to spin
the run loop so make sure the minimum
// loop time is 1/100th of second, if not otherwise set, so
enabling the frame microSleep below to
// avoid consume excessive CPU resources.
if (minFrameTime==0.0) minFrameTime=0.01;
}
}
else
{
frame();
}
// work out if we need to force a sleep to hold back the frame rate
osg::Timer_t endFrameTick = osg::Timer::instance()->tick();
double frameTime = osg::Timer::instance()->delta_s(startFrameTick,
endFrameTick);
if (frameTime < minFrameTime)
OpenThreads::Thread::microSleep(static_cast<unsigned
int>(1000000.0*(minFrameTime-frameTime)));
}
return 0;
}
void ViewerBase::frame(double simulationTime)
{
if (_done) return;
// OSG_NOTICE<<std::endl<<"CompositeViewer::frame()"<<std::endl<<std::endl;
if (_firstFrame)
{
viewerInit();
if (!isRealized())
{
realize();
}
_firstFrame = false;
}
advance(simulationTime);
eventTraversal();
updateTraversal();
renderingTraversals();
}
void ViewerBase::renderingTraversals()
{
bool _outputMasterCameraLocation = false;
if (_outputMasterCameraLocation)
{
Views views;
getViews(views);
for(Views::iterator itr = views.begin();
itr != views.end();
++itr)
{
osgViewer::View* view = *itr;
if (view)
{
const osg::Matrixd& m =
view->getCamera()->getInverseViewMatrix();
OSG_NOTICE<<"View "<<view<<", Master Camera
position("<<m.getTrans()<<"), rotation("<<m.getRotate()<<")"<<std::endl;
}
}
}
Contexts contexts;
getContexts(contexts);
// check to see if windows are still valid
checkWindowStatus(contexts);
if (_done) return;
double beginRenderingTraversals = elapsedTime();
osg::FrameStamp* frameStamp = getViewerFrameStamp();
if (getViewerStats() && getViewerStats()->collectStats("scene"))
{
unsigned int frameNumber = frameStamp ? frameStamp->getFrameNumber() :
0;
Views views;
getViews(views);
for(Views::iterator vitr = views.begin();
vitr != views.end();
++vitr)
{
View* view = *vitr;
osg::Stats* stats = view->getStats();
osg::Node* sceneRoot = view->getSceneData();
if (sceneRoot && stats)
{
osgUtil::StatsVisitor statsVisitor;
sceneRoot->accept(statsVisitor);
statsVisitor.totalUpStats();
unsigned int unique_primitives = 0;
osgUtil::Statistics::PrimitiveCountMap::iterator pcmitr;
for(pcmitr = statsVisitor._uniqueStats.GetPrimitivesBegin();
pcmitr != statsVisitor._uniqueStats.GetPrimitivesEnd();
++pcmitr)
{
unique_primitives += pcmitr->second;
}
stats->setAttribute(frameNumber, "Number of unique StateSet",
static_cast<double>(statsVisitor._statesetSet.size()));
stats->setAttribute(frameNumber, "Number of unique Group",
static_cast<double>(statsVisitor._groupSet.size()));
stats->setAttribute(frameNumber, "Number of unique Transform",
static_cast<double>(statsVisitor._transformSet.size()));
stats->setAttribute(frameNumber, "Number of unique LOD",
static_cast<double>(statsVisitor._lodSet.size()));
stats->setAttribute(frameNumber, "Number of unique Switch",
static_cast<double>(statsVisitor._switchSet.size()));
stats->setAttribute(frameNumber, "Number of unique Geode",
static_cast<double>(statsVisitor._geodeSet.size()));
stats->setAttribute(frameNumber, "Number of unique Drawable",
static_cast<double>(statsVisitor._drawableSet.size()));
stats->setAttribute(frameNumber, "Number of unique Geometry",
static_cast<double>(statsVisitor._geometrySet.size()));
stats->setAttribute(frameNumber, "Number of unique Vertices",
static_cast<double>(statsVisitor._uniqueStats._vertexCount));
stats->setAttribute(frameNumber, "Number of unique Primitives",
static_cast<double>(unique_primitives));
unsigned int instanced_primitives = 0;
for(pcmitr = statsVisitor._instancedStats.GetPrimitivesBegin();
pcmitr != statsVisitor._instancedStats.GetPrimitivesEnd();
++pcmitr)
{
instanced_primitives += pcmitr->second;
}
stats->setAttribute(frameNumber, "Number of instanced
Stateset", static_cast<double>(statsVisitor._numInstancedStateSet));
stats->setAttribute(frameNumber, "Number of instanced Group",
static_cast<double>(statsVisitor._numInstancedGroup));
stats->setAttribute(frameNumber, "Number of instanced
Transform", static_cast<double>(statsVisitor._numInstancedTransform));
stats->setAttribute(frameNumber, "Number of instanced LOD",
static_cast<double>(statsVisitor._numInstancedLOD));
stats->setAttribute(frameNumber, "Number of instanced Switch",
static_cast<double>(statsVisitor._numInstancedSwitch));
stats->setAttribute(frameNumber, "Number of instanced Geode",
static_cast<double>(statsVisitor._numInstancedGeode));
stats->setAttribute(frameNumber, "Number of instanced
Drawable", static_cast<double>(statsVisitor._numInstancedDrawable));
stats->setAttribute(frameNumber, "Number of instanced
Geometry", static_cast<double>(statsVisitor._numInstancedGeometry));
stats->setAttribute(frameNumber, "Number of instanced
Vertices", static_cast<double>(statsVisitor._instancedStats._vertexCount));
stats->setAttribute(frameNumber, "Number of instanced
Primitives", static_cast<double>(instanced_primitives));
}
}
}
Scenes scenes;
getScenes(scenes);
for(Scenes::iterator sitr = scenes.begin();
sitr != scenes.end();
++sitr)
{
Scene* scene = *sitr;
osgDB::DatabasePager* dp = scene ? scene->getDatabasePager() : 0;
if (dp) dp->signalBeginFrame(frameStamp);
osgDB::ImagePager* ip = scene ? scene->getImagePager() : 0;
if (ip) ip->signalBeginFrame(frameStamp);
if (scene->getSceneData())
{
// fire off a build of the bounding volumes while we
// are still running single threaded.
scene->getSceneData()->getBound();
}
}
// OSG_NOTICE<<std::endl<<"Start frame"<<std::endl;
Cameras cameras;
getCameras(cameras);
Contexts::iterator itr;
bool doneMakeCurrentInThisThread = false;
if (_endDynamicDrawBlock.valid())
{
_endDynamicDrawBlock->reset();
}
// dispatch the rendering threads
if (_startRenderingBarrier.valid()) _startRenderingBarrier->block();
// reset any double buffer graphics objects
for(Cameras::iterator camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
Renderer* renderer = dynamic_cast<Renderer*>(camera->getRenderer());
if (renderer)
{
if (!renderer->getGraphicsThreadDoesCull() &&
!(camera->getCameraThread()))
{
renderer->cull();
}
}
}
for(itr = contexts.begin();
itr != contexts.end() && !_done;
++itr)
{
if (!((*itr)->getGraphicsThread()) && (*itr)->valid())
{
doneMakeCurrentInThisThread = true;
makeCurrent(*itr);
(*itr)->runOperations();
}
}
// OSG_NOTICE<<"Joing _endRenderingDispatchBarrier block
"<<_endRenderingDispatchBarrier.get()<<std::endl;
// wait till the rendering dispatch is done.
if (_endRenderingDispatchBarrier.valid())
_endRenderingDispatchBarrier->block();
for(itr = contexts.begin();
itr != contexts.end() && !_done;
++itr)
{
if (!((*itr)->getGraphicsThread()) && (*itr)->valid())
{
doneMakeCurrentInThisThread = true;
makeCurrent(*itr);
(*itr)->swapBuffers();
}
}
for(Scenes::iterator sitr = scenes.begin();
sitr != scenes.end();
++sitr)
{
Scene* scene = *sitr;
osgDB::DatabasePager* dp = scene ? scene->getDatabasePager() : 0;
if (dp) dp->signalEndFrame();
osgDB::ImagePager* ip = scene ? scene->getImagePager() : 0;
if (ip) ip->signalEndFrame();
}
// wait till the dynamic draw is complete.
if (_endDynamicDrawBlock.valid())
{
// osg::Timer_t startTick = osg::Timer::instance()->tick();
_endDynamicDrawBlock->block();
// OSG_NOTICE<<"Time waiting
"<<osg::Timer::instance()->delta_m(startTick,
osg::Timer::instance()->tick())<<std::endl;;
}
if (_releaseContextAtEndOfFrameHint && doneMakeCurrentInThisThread)
{
//OSG_NOTICE<<"Doing release context"<<std::endl;
releaseContext();
}
if (getViewerStats() && getViewerStats()->collectStats("update"))
{
double endRenderingTraversals = elapsedTime();
// update current frames stats
getViewerStats()->setAttribute(frameStamp->getFrameNumber(), "Rendering
traversals begin time ", beginRenderingTraversals);
getViewerStats()->setAttribute(frameStamp->getFrameNumber(), "Rendering
traversals end time ", endRenderingTraversals);
getViewerStats()->setAttribute(frameStamp->getFrameNumber(), "Rendering
traversals time taken", endRenderingTraversals-beginRenderingTraversals);
}
_requestRedraw = false;
}
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library 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. See the
* OpenSceneGraph Public License for more details.
*
* ViewDependentShadow codes Copyright (C) 2008 Wojciech Lewandowski
* Thanks to to my company http://www.ai.com.pl for allowing me free this work.
*/
#include<osgShadow/ConvexPolyhedron>
#include<osg/Group>
#include<osg/Geode>
#include<osgDB/WriteFile>
#include<cassert>
#include<deque>
#include<algorithm>
#include<iterator>
#include <stdio.h>
#include <string.h>
using namespace osgShadow;
#if defined( DEBUG ) || defined( _DEBUG ) || defined( _DEBUG_ )
// ConvexPolyhedron may produce tons of warnings when it becomes non convex.
// Unfortuantely this condition often happens in daily routine of shadow usage
// due precision errors mixed with repeating frustum cuts performed by
MinimalShadowClasses.
// However, in most of above cases this condition is not fatal
// because polyhedron becomes concave by very small margin (mesuring deep the
hole).
// Unfortunately warnings are produced even for such small margin cases and can
// easily flood the console.
// So I leave MAKE_CHECKS commented out. Its really useful only for a guy who
debugs
// larger concaveness issues which means most developers will want to keep it
commented.
// #define MAKE_CHECKS 1
#endif
#if MAKE_CHECKS
#define WARN OSG_WARN
#define CONVEX_POLYHEDRON_CHECK_COHERENCY 1
#define CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA 2
#define CONVEX_POLYHEDRON_DUMP_ON_INCOHERENT_DATA 2
#define CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON 1
#define CONVEX_POLYHEDRON_DUMP_ON_BAD_POLYGON 1
#define CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON 1
#define CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON 1
#define CONVEX_POLYHEDRON_WARN_ON_INCORRECT_FACE_CUT 1
#define CONVEX_POLYHEDRON_DUMP_ON_INCORRECT_FACE_CUT 1
#else
#define WARN OSG_WARN
#define CONVEX_POLYHEDRON_CHECK_COHERENCY 0
#define CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA 0
#define CONVEX_POLYHEDRON_DUMP_ON_INCOHERENT_DATA 0
#define CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON 0
#define CONVEX_POLYHEDRON_DUMP_ON_BAD_POLYGON 0
#define CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON 0
#define CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON 0
#define CONVEX_POLYHEDRON_WARN_ON_INCORRECT_FACE_CUT 0
#define CONVEX_POLYHEDRON_DUMP_ON_INCORRECT_FACE_CUT 0
#endif
const osg::Matrix & ConvexPolyhedron::defaultMatrix = *(osg::Matrix*)NULL;
static const double epsi = pow( 2.0, -20.0 ); //circa 8 times float prec(~2^-23)
static const double plane_hull_tolerance = 1.0e-5;
static const double point_plane_tolerance = 0.0;
static const double point_point_equivalence = 0.;
// Tim Moore modifications for GCC 4.3 August 15, 2008
// they correspond to Adrian Egli tweaks for VS 7.3 on August 19, 2008
namespace
{
typedef std::vector< double > Distances;
typedef std::vector< osg::Vec4d > Points;
// Auxiliary params continued per face
struct FaceDistances
{
ConvexPolyhedron::Faces::iterator itr;
Points points;
Distances distances;
int below, above, on;
};
} // namespace
ConvexPolyhedron::ConvexPolyhedron( const osg::Matrix& matrix,
const osg::Matrix& inverse,
const osg::BoundingBox& bb )
{
setToBoundingBox( bb );
if( &matrix != &defaultMatrix && &inverse != &defaultMatrix )
transform( matrix, inverse );
else if( &matrix != &defaultMatrix && &inverse == &defaultMatrix )
transform( matrix, osg::Matrix::inverse( matrix ) );
else if( &matrix == &defaultMatrix && &inverse != &defaultMatrix )
transform( osg::Matrix::inverse( inverse ), matrix );
}
void ConvexPolyhedron::setToUnitFrustum(bool withNear, bool withFar)
{
const osg::Vec3d v000(-1.0,-1.0,-1.0);
const osg::Vec3d v010(-1.0,1.0,-1.0);
const osg::Vec3d v001(-1.0,-1.0,1.0);
const osg::Vec3d v011(-1.0,1.0,1.0);
const osg::Vec3d v100(1.0,-1.0,-1.0);
const osg::Vec3d v110(1.0,1.0,-1.0);
const osg::Vec3d v101(1.0,-1.0,1.0);
const osg::Vec3d v111(1.0,1.0,1.0);
_faces.clear();
{ // left plane.
Face& face = createFace();
face.name = "left";
face.plane.set(1.0,0.0,0.0,1.0);
face.vertices.push_back(v000);
face.vertices.push_back(v001);
face.vertices.push_back(v011);
face.vertices.push_back(v010);
}
{ // right plane.
Face& face = createFace();
face.name = "right";
face.plane.set(-1.0,0.0,0.0,1.0);
face.vertices.push_back(v100);
face.vertices.push_back(v110);
face.vertices.push_back(v111);
face.vertices.push_back(v101);
}
{ // bottom plane.
Face& face = createFace();
face.name = "bottom";
face.plane.set(0.0,1.0,0.0,1.0);
face.vertices.push_back(v000);
face.vertices.push_back(v100);
face.vertices.push_back(v101);
face.vertices.push_back(v001);
}
{ // top plane.
Face& face = createFace();
face.name = "top";
face.plane.set(0.0,-1.0,0.0,1.0);
face.vertices.push_back(v010);
face.vertices.push_back(v011);
face.vertices.push_back(v111);
face.vertices.push_back(v110);
}
if (withNear)
{ // near plane
Face& face = createFace();
face.name = "near";
face.plane.set(0.0,0.0,1.0,1.0);
face.vertices.push_back(v000);
face.vertices.push_back(v010);
face.vertices.push_back(v110);
face.vertices.push_back(v100);
}
if (withFar)
{ // far plane
Face& face = createFace();
face.name = "far";
face.plane.set(0.0,0.0,-1.0,1.0);
face.vertices.push_back(v001);
face.vertices.push_back(v101);
face.vertices.push_back(v111);
face.vertices.push_back(v011);
}
}
void ConvexPolyhedron::setToBoundingBox(const osg::BoundingBox& bb)
{
_faces.clear();
// Ignore invalid and one dimensional boxes
if( bb._min[0] >= bb._max[0] ||
bb._min[1] >= bb._max[1] ||
bb._min[2] >= bb._max[2] ) return;
const osg::Vec3d v000(bb.xMin(),bb.yMin(), bb.zMin());
const osg::Vec3d v010(bb.xMin(),bb.yMax(), bb.zMin());
const osg::Vec3d v001(bb.xMin(),bb.yMin(), bb.zMax());
const osg::Vec3d v011(bb.xMin(),bb.yMax(), bb.zMax());
const osg::Vec3d v100(bb.xMax(),bb.yMin(), bb.zMin());
const osg::Vec3d v110(bb.xMax(),bb.yMax(), bb.zMin());
const osg::Vec3d v101(bb.xMax(),bb.yMin(), bb.zMax());
const osg::Vec3d v111(bb.xMax(),bb.yMax(), bb.zMax());
{ // x min plane
Face& face = createFace();
face.name = "xMin";
face.plane.set(1.0,0.0,0.0,-bb.xMin());
face.vertices.push_back(v000);
face.vertices.push_back(v001);
face.vertices.push_back(v011);
face.vertices.push_back(v010);
}
{ // x max plane.
Face& face = createFace();
face.name = "xMax";
face.plane.set(-1.0,0.0,0.0,bb.xMax());
face.vertices.push_back(v100);
face.vertices.push_back(v110);
face.vertices.push_back(v111);
face.vertices.push_back(v101);
}
{ // y min plane.
Face& face = createFace();
face.name = "yMin";
face.plane.set(0.0,1.0,0.0,-bb.yMin());
face.vertices.push_back(v000);
face.vertices.push_back(v100);
face.vertices.push_back(v101);
face.vertices.push_back(v001);
}
{ // y max plane.
Face& face = createFace();
face.name = "yMax";
face.plane.set(0.0,-1.0,0.0,bb.yMax());
face.vertices.push_back(v010);
face.vertices.push_back(v011);
face.vertices.push_back(v111);
face.vertices.push_back(v110);
}
{ // z min plane
Face& face = createFace();
face.name = "zMin";
face.plane.set(0.0,0.0,1.0,-bb.zMin());
face.vertices.push_back(v000);
face.vertices.push_back(v010);
face.vertices.push_back(v110);
face.vertices.push_back(v100);
}
{ // z max plane
Face& face = createFace();
face.name = "zMax";
face.plane.set(0.0,0.0,-1.0,bb.zMax());
face.vertices.push_back(v001);
face.vertices.push_back(v101);
face.vertices.push_back(v111);
face.vertices.push_back(v011);
}
}
void ConvexPolyhedron::transform(const osg::Matrix& matrix, const osg::Matrix&
inverse)
{
bool requires_infinite_plane_clip = false;
ConvexPolyhedron cp = *this;
for(Faces::iterator itr = _faces.begin();
itr != _faces.end() && !requires_infinite_plane_clip;
++itr)
{
Face& face = *itr;
face.plane.transformProvidingInverse(inverse);
for(Vertices::iterator vitr = face.vertices.begin();
vitr != face.vertices.end();
++vitr)
{
osg::Vec4d v( *vitr, 1.0 );
v = v * matrix;
if( v[3] <= 0 ) {
requires_infinite_plane_clip = true;
break;
}
vitr->set( v[0]/v[3], v[1]/v[3], v[2]/v[3] );
}
}
if( requires_infinite_plane_clip ) {
*this = cp;
transformClip( matrix, inverse );
// cp.dumpGeometry(&cp._faces.back(),&cp._faces.back().plane,this);
}
// Perpective transforms and lack of precision
// occasionaly cause removal of some points
removeDuplicateVertices( );
checkCoherency( true, "ConvexPolyhedron::transform" );
}
void ConvexPolyhedron::transformClip(const osg::Matrix& matrix, const
osg::Matrix& inverse)
{
double tolerance = 0.00001;
if( _faces.empty() ) return;
ConvexPolyhedron cp( *this );
typedef std::vector< FaceDistances > FaceDistancesList;
FaceDistancesList faceDistances;
faceDistances.resize( _faces.size() );
double min = FLT_MAX, max = -FLT_MAX; //Hull max & min point distances
// First step compute each face points distances to cutting plane
Faces::iterator faces_itr = _faces.begin();
for(FaceDistancesList::iterator fd = faceDistances.begin();
faces_itr != _faces.end();
++faces_itr, ++fd)
{
fd->itr = faces_itr;
fd->distances.reserve( faces_itr->vertices.size() );
fd->on = 0;
fd->above = 0;
fd->below = 0;
faces_itr->plane.transformProvidingInverse(inverse);
for( Vertices::iterator vitr = faces_itr->vertices.begin();
vitr != faces_itr->vertices.end();
++vitr)
{
osg::Vec4d p( *vitr, 1.0 );
p = p * matrix;
vitr->set( p[0]/p[3], p[1]/p[3], p[2]/p[3] );
double d = p[3]-tolerance;
fd->points.push_back( p );
fd->distances.push_back( d );
if ( d>point_plane_tolerance ) ++fd->above;
else if ( d<-point_plane_tolerance ) ++fd->below;
else ++fd->on;
min = osg::minimum( min, d );
max = osg::maximum( max, d );
}
}
if( max <= plane_hull_tolerance ) { // All points on or below cutting plane
_faces.clear();
return;
}
if( min >= -plane_hull_tolerance ) { // All points on or above cutting plane
return;
}
typedef std::pair<osg::Vec3d, osg::Vec3d> Edge;
typedef std::set< Edge > Edges;
Edges edges;
for( FaceDistancesList::iterator fd = faceDistances.begin();
fd != faceDistances.end();
++fd )
{
if ( fd->below == 0 )
{ // skip face if all points on or above cutting plane ( below == 0 )
continue;
}
if ( /* fd->below > 0 && */ fd->above == 0 && fd->on == 0 )
{
_faces.erase( fd->itr ); // remove face if points below or on
continue;
}
// cut the face if some points above and below plane
// assert( fd->below > 0 && fd->above > 0 );
Face& face = *(fd->itr);
Points& vertices = fd->points;
Distances& distances = fd->distances;
Vertices newFaceVertices;
Vertices newVertices;
for(unsigned int i=0; i < vertices.size(); ++i)
{
osg::Vec4d &va = vertices[i];
osg::Vec4d &vb = vertices[(i+1)%vertices.size()];
double &distance_a = distances[i];
double &distance_b = distances[(i+1)%vertices.size()];
// Is first edge point above or on the plane?
if ( -point_plane_tolerance <= distance_a ) {
osg::Vec3d v( vertices[i][0], vertices[i][1], vertices[i][2] );
v /= vertices[i][3];
if( newVertices.empty() || v != newVertices.back() )
newVertices.push_back( v );
if ( distance_a <= point_plane_tolerance ) {
if( newFaceVertices.empty() || v != newFaceVertices.back() )
newFaceVertices.push_back( v );
}
}
// Does edge intersect plane ?
if ( ( distance_a < -point_plane_tolerance && distance_b >
point_plane_tolerance ) ||
( distance_b < -point_plane_tolerance && distance_a >
point_plane_tolerance ) )
{
osg::Vec4d intersection; // Inserting vertex
double da = fabs( distance_a ), db = fabs( distance_b );
// tweaks to improve coherency of polytope after cut
if( da <= point_point_equivalence && da <= db ) {
intersection = va;
} else if( db <= point_point_equivalence && db <= da ) {
intersection = vb;
} else {
double dab4 = 0.25 * ( da + db );
if( dab4 < da && dab4 < db ) {
intersection = (vb*distance_a -
va*distance_b)/(distance_a-distance_b);
} else {
osg::Vec4d v = (vb - va)/(distance_a-distance_b);
if( da < db )
intersection = va + v * distance_a;
else
intersection = vb + v * distance_b;
}
}
osg::Vec3d v( intersection[0], intersection[1], intersection[2]
);
v /= intersection[3];
if( newVertices.empty() || v != newVertices.back() )
newVertices.push_back( v );
if( newFaceVertices.empty() || v != newFaceVertices.back() )
newFaceVertices.push_back( v );
}
}
if( newVertices.size() && newVertices.front() == newVertices.back() )
newVertices.pop_back();
if( newFaceVertices.size() && newFaceVertices.front() ==
newFaceVertices.back() )
newFaceVertices.pop_back();
if( newFaceVertices.size() == 1 ) { // This is very rare but correct
WARN
<< "ConvexPolyhedron::transformClip - Slicing face polygon
returns "
<< newFaceVertices.size()
<< " points. Should be 2 (usually) or 1 (rarely)."
<< std::endl;
} else if( newFaceVertices.size() == 2 ) {
if( newFaceVertices[0] < newFaceVertices[1] ) {
edges.insert( Edge( newFaceVertices[0], newFaceVertices[1] ) );
} else {
edges.insert( Edge( newFaceVertices[1], newFaceVertices[0] ) );
}
} else if( newFaceVertices.size() > 2 ) {
#if CONVEX_POLYHEDRON_WARN_ON_INCORRECT_FACE_CUT
// This may happen if face polygon is not planar or convex.
// It may happen when face was distorted by projection transform
// or when some polygon vertices land in incorrect positions after
// some transformation by badly conditioned matrix (weak precison)
WARN
<< "ConvexPolyhedron::transformClip - Slicing face polygon
returns "
<< newFaceVertices.size()
<< " points. Should be 2 (usually) or 1 (rarely)."
<< std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_INCORRECT_FACE_CUT
dumpGeometry( &face, NULL, &cp );
#endif
// Lets try to recover from this uneasy situation
// by comparing current face polygon edges cut by new plane
// with edges selected for new plane
unsigned i0 = 0, i1 = newFaceVertices.size() - 1;
unsigned j0 = 0, j1 = newVertices.size() - 1;
for( ; i0 < newFaceVertices.size(); i1 = i0++, j1 = j0++ ) {
while( newFaceVertices[i0] != newVertices[j0] ) j1 = j0++;
if( newFaceVertices[i1] == newVertices[j1] )
{
if( newFaceVertices[i0] < newFaceVertices[i1] ) {
edges.insert( Edge( newFaceVertices[i0],
newFaceVertices[i1] ) );
} else {
edges.insert( Edge( newFaceVertices[i1],
newFaceVertices[i0] ) );
}
}
}
}
if( newVertices.size() >= 3 ) { //Add faces with at least 3 points
#if ( CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON ||
CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON )
int convex = isFacePolygonConvex( face );
face.vertices.swap( newVertices );
if( convex && !isFacePolygonConvex( face ) ) {
#if CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON
WARN << "ConvexPolyhedron::transformClip - polygon output non
convex."
<< " This may lead to other issues in ConvexPolyhedron math" <<
std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON
dumpGeometry( &face, NULL, &cp );
#endif
}
#else
face.vertices.swap( newVertices );
#endif
} else //Remove face reduced to 0, 1, 2 points
_faces.erase( fd->itr );
}
osg::Vec3d center( 0, 0, 0 );
unsigned count = 0;
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr )
{
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
{
center += *vitr;
count ++;
}
}
center /= count;
if ( edges.size() > 1 ) //Ignore faces reduced to 0, 1, 2 points
{
Face face;
face.name = "Almost infinite plane";
std::deque< osg::Vec3d > vertices;
Edges::iterator itr = edges.begin();
vertices.push_back( itr->first );
vertices.push_back( itr->second );
edges.erase( itr++ );
for( unsigned int vertices_size = 0;
vertices_size < vertices.size(); )
{
vertices_size = vertices.size();
for( itr = edges.begin(); itr != edges.end(); )
{
bool not_added = false;
if( itr->first == vertices.back() )
vertices.push_back( itr->second );
else if ( itr->first == vertices.front() )
vertices.push_front( itr->second );
else if ( itr->second == vertices.back() )
vertices.push_back( itr->first );
else if ( itr->second == vertices.front() )
vertices.push_front( itr->first );
else
not_added = true;
if( not_added )
++itr;
else
edges.erase( itr++ );
}
}
#if CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON
if( !edges.empty() ) {
WARN
<< "ConvexPolyhedron::transformClip - Building new face polygon
- "
<< "Found edges not matching former polygon ends"
<< std::endl;
}
#endif
std::copy(vertices.begin(), vertices.end(),
std::back_inserter(face.vertices));
face.plane.set( vertices[0], vertices[1], vertices[2] );
if( face.plane.distance( center ) < 0.0 ) face.plane.flip();
_faces.push_back(face);
// Last vertex is duplicated - remove one instance
if( face.vertices.front() == face.vertices.back() )
face.vertices.pop_back();
else {// If not duplicated then it may mean we have open polygon ;-(
#if CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON
WARN
<< "ConvexPolyhedron::transformClip - Building new face polygon - "
<< " Polygon not properly closed."
<< std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_BAD_POLYGON
dumpGeometry( &_faces.back(), NULL, &cp );
#endif
}
#if ( CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON ||
CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON )
if( !isFacePolygonConvex( face ) ) {
#if CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON
WARN << "ConvexPolyhedron::transformClip - new face polygon non
convex."
<< " This may lead to other issues in ConvexPolyhedron math" <<
std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON
ConvexPolyhedron cp;
cp.createFace() = face;
cp.dumpGeometry( );
#endif
}
#endif
}
// Perpective transforms and lack of precision
// occasionaly cause removal of some points
removeDuplicateVertices( );
checkCoherency( true, "ConvexPolyhedron::transformClip" );
}
bool ConvexPolyhedron::mergeFaces
( const Face & face0, const Face & face1, Face & face )
{
typedef std::pair< osg::Vec3d, osg::Vec3d > Edge;
typedef std::set<Edge> Edges;
Edges edges;
for(unsigned int i=0; i<face0.vertices.size(); ++i)
{
const osg::Vec3d* va = &face0.vertices[i];
const osg::Vec3d* vb = &face0.vertices[(i+1)%face0.vertices.size()];
if( *vb == *va ) continue; // ignore duplicated point edges
if( *vb < *va ) std::swap( va, vb );
edges.insert( Edge( *va, *vb ) );
}
bool intersection = false;
for(unsigned int i=0; i<face1.vertices.size(); ++i)
{
const osg::Vec3d* va = &face1.vertices[i];
const osg::Vec3d* vb = &face1.vertices[(i+1)%face1.vertices.size()];
if( *vb == *va ) continue; // ignore duplicated point edges
if( *vb < *va ) std::swap( va, vb );
Edge edge( *va, *vb );
Edges::iterator itr = edges.find( edge );
if( itr == edges.end() )
edges.insert( edge );
else {
edges.erase( itr );
intersection = true;
}
}
if( intersection )
face.vertices.clear();
if( intersection && edges.size() > 1 ) //Ignore faces reduced to 0, 1, 2
points
{
std::deque< osg::Vec3d > vertices;
Edges::iterator itr = edges.begin();
vertices.push_back( itr->first );
vertices.push_back( itr->second );
edges.erase( itr++ );
for( unsigned int vertices_size = 0;
vertices_size < vertices.size(); )
{
vertices_size = vertices.size();
for( itr = edges.begin(); itr != edges.end(); )
{
bool not_added = false;
if( itr->first == vertices.back() )
vertices.push_back( itr->second );
else if ( itr->first == vertices.front() )
vertices.push_front( itr->second );
else if ( itr->second == vertices.back() )
vertices.push_back( itr->first );
else if ( itr->second == vertices.front() )
vertices.push_front( itr->first );
else
not_added = true;
if( not_added )
++itr;
else
edges.erase( itr++ );
}
}
#if CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON
if( !edges.empty() ) {
WARN
<< "ConvexPolyhedron::mergeFaces - Building new face polygon - "
<< "Found edges not matching former polygon ends."
<< std::endl;
}
#endif
// Last vertex is duplicated - remove one instance
if( vertices.front() == vertices.back() )
vertices.pop_back();
else {// If not duplicated then it may mean we have open polygon ;-(
#if CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON
WARN
<< "ConvexPolyhedron::mergeFaces - Building new face polygon - "
<< " Polygon not properly closed."
<< std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_BAD_POLYGON
#endif
}
#if 1 // Resulting plane will be the same as face0
face.plane = face0.plane;
std::copy(vertices.begin(), vertices.end(),
std::back_inserter(face.vertices));
#else // Compute resulting plane as average of faces (not a good idea)
osg::Vec3d normal = face0.plane.getNormal() + face1.plane.getNormal();
normal.normalize();
osg::Vec3d center;
for( unsigned int i = 0; i < vertices.size(); ++i )
{
center += vertices[i];
face.vertices.push_back( vertices[i] );
}
center /= vertices.size();
face.plane.set( normal, center );
#endif
face.name = face0.name + " + " + face1.name;
// No testing for concave polys
// Its possible to build concave polygon from two merged faces
// But after all coplanar faces are merged final result should be
convex.
}
return intersection;
}
void ConvexPolyhedron::mergeCoplanarFaces
( const double & dot_tolerance, const double & delta_tolerance )
{
for(Faces::iterator itr0 = _faces.begin();
itr0 != _faces.end();
++itr0 )
{
double tolerance = delta_tolerance;
for( unsigned i = 0; i < itr0->vertices.size(); ++i ) {
tolerance = osg::maximum( tolerance,
fabs( itr0->plane.distance( itr0->vertices[i] ) ) );
}
for(Faces::iterator itr1 = _faces.begin();
itr1 != _faces.end();
)
{
if( itr1 == itr0 ) {
++itr1;
continue;
}
bool attempt_merge = true;
for( unsigned i = 0; i < itr1->vertices.size(); ++i ) {
if( fabs( itr0->plane.distance( itr1->vertices[i] ) ) >
tolerance )
{
attempt_merge = false;
break;
}
}
if( !attempt_merge &&
1.0 - itr0->plane.getNormal() * itr1->plane.getNormal() <
dot_tolerance &&
fabs( itr0->plane.ptr()[3] - itr1->plane.ptr()[3] ) <
delta_tolerance )
attempt_merge = true;
if( attempt_merge && mergeFaces( *itr0, *itr1, *itr0 ) )
itr1 = _faces.erase( itr1 );
else
++itr1;
}
}
}
void ConvexPolyhedron::removeDuplicateVertices( void )
{
#if 1
// Aggressive removal, find very close points and replace them
// with their average. Second step wil do the rest.
typedef std::map< osg::Vec3f, osg::Vec4d > PointMap;
typedef std::set< osg::Vec3d > VertexSet;
VertexSet vertexSet;
PointMap points;
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr )
{
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
{
vertexSet.insert( *vitr );
}
}
for( VertexSet::iterator vitr = vertexSet.begin();
vitr != vertexSet.end();
++vitr )
{
points[ *vitr ] += osg::Vec4d( *vitr, 1.0 );
}
for(PointMap::iterator itr = points.begin();
itr != points.end();
++itr )
{
if( itr->second[3] > 1.0 )
itr->second /= itr->second[3];
}
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr )
{
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
{
osg::Vec4d &v = points[ *vitr ];
*vitr = osg::Vec3d( v[0], v[1], v[2] );
}
}
#endif
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
)
{
assert( itr->vertices.size() > 0 );
osg::Vec3d prev = itr->vertices.back();
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
)
{
if( *vitr == prev ) {
vitr = itr->vertices.erase( vitr );
} else {
prev = *vitr;
++vitr;
}
}
if( itr->vertices.size() < 3 )
itr = _faces.erase( itr );
else
++itr;
}
mergeCoplanarFaces();
#if 1
// Experimentally remove vertices on colinear edge chains
// effectivelyy replacing them with one edge
typedef std::map<osg::Vec3d,int> VertexCounter;
VertexCounter vertexCounter;
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
{
++vertexCounter[ *vitr ];
}
}
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
)
{
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
)
{
if( vertexCounter[ *vitr ] == 2 ) {
#if 0 // Sanity check if we could remove this point without changing poly shape
Vertices::iterator next = ( vitr + 1 == itr->vertices.end() ?
itr->vertices.begin() :
vitr + 1 );
Vertices::iterator prev = ( vitr == itr->vertices.begin() ?
itr->vertices.end() - 1 :
vitr - 1 );
osg::Vec3d va = *vitr - *prev;
osg::Vec3d vb = *next - *vitr;
double da = va.normalize();
double db = vb.normalize();
if( 0.001 < da && 0.001 < db )
{
double dot = va * vb, limit = cos( 0.01 );
if( dot < limit ) {
WARN << "ConvexPolyhedron::removeDuplicateVertices"
<< " - removed mid point connecting two non
colinear edges."
<< " Angle(deg): " << osg::RadiansToDegrees( acos(
dot ) )
<< " Length1: " << da
<< " Length2: " << db
<< std::endl;
}
} else {
if( da == 0.0 || db == 0.0 )
WARN << "ConvexPolyhedron::removeDuplicateVertices"
<< " - removed degenerated mid point connecting
two edges"
<< std::endl;
}
#endif
vitr = itr->vertices.erase( vitr );
} else {
++vitr;
}
}
if( itr->vertices.size() < 3 )
itr = _faces.erase( itr );
else
++itr;
}
#endif
checkCoherency( false, "Leave ConvexPolyhedron::removeDuplicateVertices" );
}
int ConvexPolyhedron::pointsColinear
( const osg::Vec3d & a, const osg::Vec3d & b, const osg::Vec3d & c,
const double & dot_tolerance, const double & delta_tolerance )
{
osg::Vec3d va = b - a;
osg::Vec3d vb = c - b;
double da = va.normalize();
double db = vb.normalize();
if( delta_tolerance >= da || delta_tolerance >= db )
return -1; // assume collinearity if one of the edges is zero length
if( 1.0 - fabs( va * vb ) <= dot_tolerance )
return 1; // edge normals match collinearity condition
return 0; // nope. not collinear
}
int ConvexPolyhedron::isFacePolygonConvex( Face & face, bool
ignoreColinearVertices )
{
int positive = 0, negative = 0, colinear = 0;
for( unsigned int i = 0; i < face.vertices.size(); ++i )
{
osg::Vec3d va = face.vertices[i];
osg::Vec3d vb = face.vertices[(i+1)%face.vertices.size()];
osg::Vec3d vc = face.vertices[(i+2)%face.vertices.size()];
#if ( CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA > 1 ||
CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON )
double dist = fabs( face.plane.distance( va ) );
if( dist > 0.0001 )
{
WARN << "ConvexPolyhedron::isFacePolygonConvex - plane point too
far from plane (" << dist <<")" << std::endl;
}
#endif
// cast points on a plane
va -= face.plane.getNormal() * face.plane.distance( va );
vb -= face.plane.getNormal() * face.plane.distance( vb );
vc -= face.plane.getNormal() * face.plane.distance( vc );
if( pointsColinear( va, vb, vc ) ) {
colinear++;
} else {
double side =( ( vc - vb ) ^ ( vb - va ) ) *
face.plane.getNormal();
if( side < 0 ) negative++;
if( side > 0 ) positive++;
}
}
if( !ignoreColinearVertices && colinear > 0 )
return 0;
if( !negative && !positive )
return 0;
if( (negative + colinear) == static_cast<int>(face.vertices.size()) )
return -( negative + colinear );
if( (positive + colinear) == static_cast<int>(face.vertices.size()) )
return +( positive + colinear );
return 0;
}
bool ConvexPolyhedron::checkCoherency
( bool checkForNonConvexPolys, const char * errorPrefix )
{
bool result = true;
bool convex = true;
#if CONVEX_POLYHEDRON_CHECK_COHERENCY
if( !errorPrefix ) errorPrefix = "ConvexPolyhedron";
typedef std::pair<osg::Vec3d, osg::Vec3d> Edge;
typedef std::map<Edge,int> EdgeCounter;
typedef std::map<osg::Vec3d,int> VertexCounter;
EdgeCounter edgeCounter;
VertexCounter vertexCounter;
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
Face& face = *itr;
if( checkForNonConvexPolys && !isFacePolygonConvex( face ) ) {
convex = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA ||
CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON )
WARN << errorPrefix <<
" - coherency fail - face polygon concave" << std::endl;
#endif
#if ( 1 < CONVEX_POLYHEDRON_DUMP_ON_INCOHERENT_DATA ||
CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON )
dumpGeometry( &face );
#endif
}
Vertices& vertices = face.vertices;
for(unsigned int i=0; i<vertices.size(); ++i)
{
osg::Vec3d& a = vertices[ i ];
osg::Vec3d& b = vertices[ (i+1) % vertices.size()];
++vertexCounter[ a ];
if (a<b)
++edgeCounter[Edge(a,b)];
else
++edgeCounter[Edge(b,a)];
}
}
for( EdgeCounter::iterator itr = edgeCounter.begin();
itr != edgeCounter.end();
++itr)
{
const Edge &e = itr->first;
if( e.first.isNaN() ) {
result = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
WARN << errorPrefix <<
" - coherency fail - Vertex is NaN." << std::endl;
#endif
}
if( e.second.isNaN() ) {
result = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
WARN << errorPrefix <<
" - coherency fail - Vertex is NaN." << std::endl;
#endif
}
if( e.first == e.second ) {
result = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
WARN << errorPrefix <<
" - coherency fail - Edge with identical vertices." <<
std::endl;
#endif
}
if( vertexCounter[ e.first ] < 3 ) {
result = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
WARN << errorPrefix <<
" - coherency fail - Vertex present " << vertexCounter[ e.first
] << " times" << std::endl;
#endif
}
if( vertexCounter[ e.second ] < 3 ) {
result = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
WARN << errorPrefix <<
" - coherency fail - Vertex present " << vertexCounter[
e.second ] << " times" << std::endl;
#endif
}
if( itr->second != 2 ) {
result = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
WARN << errorPrefix <<
" - coherency fail - Edge present " << itr->second << " times"
<< std::endl;
#endif
}
}
#if ( 1 == CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
if( !convex )
WARN << errorPrefix
<< " - coherency fail - non convex output" << std::endl;
if ( !result )
WARN << errorPrefix
<< " - coherency fail - incoherent output" << std::endl;
#endif
#if ( 1 == CONVEX_POLYHEDRON_DUMP_ON_INCOHERENT_DATA )
if( !result || !convex )
dumpGeometry( );
#endif
#if ( 1 < CONVEX_POLYHEDRON_DUMP_ON_INCOHERENT_DATA )
if( !result )
dumpGeometry( );
#endif
#endif // CONVEX_POLYHEDRON_CHECK_COHERENCY
return result && convex;
}
osg::BoundingBox ConvexPolyhedron::computeBoundingBox( const osg::Matrix & m )
const
{
osg::BoundingBox bb;
if( &m != &defaultMatrix ) {
for( Faces::const_iterator itr = _faces.begin(); itr != _faces.end();
++itr )
for( Vertices::const_iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
bb.expandBy( *vitr * m );
} else {
for( Faces::const_iterator itr = _faces.begin(); itr != _faces.end();
++itr )
for( Vertices::const_iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
bb.expandBy( *vitr );
}
return bb;
}
void ConvexPolyhedron::cut(const osg::Polytope & polytope)
{
const char * apc[6] = { "left", "right", "bottom", "top", "near", "far" };
char ac[16];
int i = 0;
for(osg::Polytope::PlaneList::const_iterator itr =
polytope.getPlaneList().begin();
itr != polytope.getPlaneList().end();
++itr)
{
const char* arg;
if (i < 6) {
arg = apc[i];
} else {
sprintf(ac, "%d", i);
arg = ac;
}
cut(*itr, std::string( arg ) );
i++;
}
removeDuplicateVertices();
}
void ConvexPolyhedron::cut(const ConvexPolyhedron& polytope)
{
for(Faces::const_iterator itr = polytope._faces.begin();
itr != polytope._faces.end();
++itr)
{
cut(itr->plane, itr->name);
}
removeDuplicateVertices();
}
void ConvexPolyhedron::cut(const osg::Plane& plane, const std::string& name)
{
if( _faces.empty() ) return;
ConvexPolyhedron cp( *this );
typedef std::vector< FaceDistances > FaceDistancesList;
FaceDistancesList faceDistances;
faceDistances.resize( _faces.size() );
double min = FLT_MAX, max = -FLT_MAX; //Hull max & min point distances
// First step compute each face points distances to cutting plane
Faces::iterator faces_itr = _faces.begin();
for(FaceDistancesList::iterator fd = faceDistances.begin();
faces_itr != _faces.end();
++faces_itr, ++fd )
{
fd->itr = faces_itr;
fd->distances.reserve( faces_itr->vertices.size() );
fd->on = 0;
fd->above = 0;
fd->below = 0;
#if 0 // Skip if cutting plane the same as one of faces
if( plane.ptr()[0] == faces_itr->plane.ptr()[0] &&
plane.ptr()[1] == faces_itr->plane.ptr()[1] &&
plane.ptr()[2] == faces_itr->plane.ptr()[2] &&
#else // check plane using less precise float values
if( float( plane.ptr()[0] ) == float( faces_itr->plane.ptr()[0] ) &&
float( plane.ptr()[1] ) == float( faces_itr->plane.ptr()[1] ) &&
float( plane.ptr()[2] ) == float( faces_itr->plane.ptr()[2] ) &&
#endif
plane_hull_tolerance >= fabs( float( plane.ptr()[3] )- float(
faces_itr->plane.ptr()[3] ) ) )
return;
for(Vertices::iterator vitr = faces_itr->vertices.begin();
vitr != faces_itr->vertices.end();
++vitr)
{
double d = plane.distance( *vitr );
fd->distances.push_back( d );
if ( d>point_plane_tolerance ) ++fd->above;
else if ( d<-point_plane_tolerance ) ++fd->below;
else ++fd->on;
min = osg::minimum( min, d );
max = osg::maximum( max, d );
}
}
if( max <= plane_hull_tolerance ) { // All points on or below cutting plane
_faces.clear();
return;
}
if( min >= -plane_hull_tolerance ) { // All points on or above cutting plane
return;
}
typedef std::pair<osg::Vec3d, osg::Vec3d> Edge;
typedef std::set< Edge > Edges;
Edges edges;
for( FaceDistancesList::iterator fd = faceDistances.begin();
fd != faceDistances.end();
++fd )
{
if ( fd->below == 0 )
{ // skip face if all points on or above cutting plane ( below == 0 )
continue;
}
if ( /* fd->below > 0 && */ fd->above == 0 && fd->on == 0 )
{
_faces.erase( fd->itr ); // remove face if points below or on
continue;
}
// cut the face if some points above and below plane
// assert( fd->below > 0 && fd->above > 0 );
Face& face = *(fd->itr);
Vertices& vertices = face.vertices;
Distances& distances = fd->distances;
Vertices newFaceVertices;
Vertices newVertices;
for(unsigned int i=0; i < vertices.size(); ++i)
{
osg::Vec3d &va = vertices[i];
osg::Vec3d &vb = vertices[(i+1)%vertices.size()];
double &distance_a = distances[i];
double &distance_b = distances[(i+1)%vertices.size()];
// Is first edge point above or on the plane?
if ( -point_plane_tolerance <= distance_a ) {
if( newVertices.empty() || vertices[i] != newVertices.back() )
newVertices.push_back( vertices[i] );
if ( distance_a <= point_plane_tolerance ) {
if( newFaceVertices.empty() || vertices[i] !=
newFaceVertices.back() )
newFaceVertices.push_back( vertices[i] );
}
}
// Does edge intersect plane ?
if ( ( distance_a < -point_plane_tolerance && distance_b >
point_plane_tolerance ) ||
( distance_b < -point_plane_tolerance && distance_a >
point_plane_tolerance ) )
{
osg::Vec3d intersection; // Inserting vertex
double da = fabs( distance_a ), db = fabs( distance_b );
// tweaks to improve coherency of polytope after cut
if( da <= point_point_equivalence && da <= db ) {
intersection = va;
} else if( db <= point_point_equivalence && db <= da ) {
intersection = vb;
} else {
double dab4 = 0.25 * ( da + db );
if( dab4 < da && dab4 < db ) {
intersection = (vb*distance_a -
va*distance_b)/(distance_a-distance_b);
} else {
osg::Vec3d v = (vb - va)/(distance_a-distance_b);
if( da < db )
intersection = va + v * distance_a;
else
intersection = vb + v * distance_b;
}
}
if( newVertices.empty() || intersection != newVertices.back() )
newVertices.push_back( intersection );
if( newFaceVertices.empty() || intersection !=
newFaceVertices.back() )
newFaceVertices.push_back( intersection );
}
}
if( newVertices.size() && newVertices.front() == newVertices.back() )
newVertices.pop_back();
if( newFaceVertices.size() && newFaceVertices.front() ==
newFaceVertices.back() )
newFaceVertices.pop_back();
if( newFaceVertices.size() == 1 ) { // This is very rare but correct
WARN
<< "ConvexPolyhedron::cut - Slicing face polygon returns "
<< newFaceVertices.size()
<< " points. Should be 2 (usually) or 1 (rarely)."
<< std::endl;
} else if( newFaceVertices.size() == 2 ) {
if( newFaceVertices[0] < newFaceVertices[1] ) {
edges.insert( Edge( newFaceVertices[0], newFaceVertices[1] ) );
} else {
edges.insert( Edge( newFaceVertices[1], newFaceVertices[0] ) );
}
} else if( newFaceVertices.size() > 2 ) {
#if CONVEX_POLYHEDRON_WARN_ON_INCORRECT_FACE_CUT
// This may happen if face polygon is not planar or convex.
// It may happen when face was distorted by projection transform
// or when some polygon vertices land in incorrect positions after
// some transformation by badly conditioned matrix (weak precison)
WARN
<< "ConvexPolyhedron::cut - Slicing face polygon returns "
<< newFaceVertices.size()
<< " points. Should be 2 (usually) or 1 (rarely)."
<< std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_INCORRECT_FACE_CUT
dumpGeometry( &face, &plane );
#endif
// Let try to recover from this uneasy situation
// by comparing current face polygon edges cut by new plane
// with edges selected for new plane
unsigned i0 = 0, i1 = newFaceVertices.size() - 1;
unsigned j0 = 0, j1 = newVertices.size() - 1;
for( ; i0 < newFaceVertices.size(); i1 = i0++, j1 = j0++ ) {
while( newFaceVertices[i0] != newVertices[j0] ) j1 = j0++;
if( newFaceVertices[i1] == newVertices[j1] )
{
if( newFaceVertices[i0] < newFaceVertices[i1] ) {
edges.insert( Edge( newFaceVertices[i0],
newFaceVertices[i1] ) );
} else {
edges.insert( Edge( newFaceVertices[i1],
newFaceVertices[i0] ) );
}
}
}
}
if( newVertices.size() >= 3 ) { //Add faces with at least 3 points
#if ( CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON ||
CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON )
int convex = isFacePolygonConvex( face );
vertices.swap( newVertices );
if( convex && !isFacePolygonConvex( face ) ) {
#if CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON
WARN << "ConvexPolyhedron::cut - polygon output non convex."
<< " This may lead to other issues in ConvexPolyhedron math" <<
std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON
dumpGeometry( &face, &plane, &cp );
#endif
}
#else
vertices.swap( newVertices );
#endif
} else //Remove face reduced to 0, 1, 2 points
_faces.erase( fd->itr );
}
if ( edges.size() > 1 ) //Ignore faces reduced to 0, 1, 2 points
{
Face face;
face.name = name;
face.plane = plane;
std::deque< osg::Vec3d > vertices;
Edges::iterator itr = edges.begin();
vertices.push_back( itr->first );
vertices.push_back( itr->second );
edges.erase( itr++ );
for( unsigned int vertices_size = 0;
vertices_size < vertices.size(); )
{
vertices_size = vertices.size();
for( itr = edges.begin(); itr != edges.end(); )
{
bool not_added = false;
if( itr->first == vertices.back() )
vertices.push_back( itr->second );
else if ( itr->first == vertices.front() )
vertices.push_front( itr->second );
else if ( itr->second == vertices.back() )
vertices.push_back( itr->first );
else if ( itr->second == vertices.front() )
vertices.push_front( itr->first );
else
not_added = true;
if( not_added )
++itr;
else
edges.erase( itr++ );
}
}
#if CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON
if( !edges.empty() ) {
WARN
<< "ConvexPolyhedron::cut - Building new face polygon - "
<< "Found edges not matching former polygon ends"
<< std::endl;
}
#endif
std::copy(vertices.begin(), vertices.end(),
std::back_inserter(face.vertices));
_faces.push_back(face);
// Last vertex is duplicated - remove one instance
if( face.vertices.front() == face.vertices.back() )
face.vertices.pop_back();
else {// If not duplicated then it may mean we have open polygon ;-(
#if CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON
WARN
<< "ConvexPolyhedron::cut - Building new face polygon - "
<< " Polygon not properly closed."
<< std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_BAD_POLYGON
dumpGeometry( &_faces.back(), &plane, &cp );
#endif
}
#if ( CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON ||
CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON )
if( !isFacePolygonConvex( face ) ) {
#if CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON
ConvexPolyhedron cp;
cp.createFace() = face;
cp.dumpGeometry( );
#endif
#if CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON
WARN << "ConvexPolyhedron::cut - new face polygon non convex."
<< " This may lead to other issues in ConvexPolyhedron math" <<
std::endl;
#endif
}
#endif
}
// removeDuplicateVertices( );
}
////////////////////////////////////////////////////////////////////////////
void ConvexPolyhedron::extrude( const osg::Vec3d & offset )
{
if( offset.length2() == 0 ) return;
typedef std::pair<osg::Vec3d, osg::Vec3d> Edge;
typedef std::vector<Face*> EdgeFaces;
typedef std::map<Edge, EdgeFaces> EdgeMap;
EdgeMap edgeMap;
// Build edge maps
for(Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
Face& face = *itr;
for(unsigned int i=0; i<face.vertices.size(); ++i)
{
osg::Vec3d& va = face.vertices[i];
osg::Vec3d& vb = face.vertices[(i+1)%face.vertices.size()];
if (va < vb) edgeMap[Edge(va,vb)].push_back(&face);
else edgeMap[Edge(vb,va)].push_back(&face);
}
}
// Offset faces
for(Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
Face& face = *itr;
double dotOffset = face.plane.dotProductNormal( offset );
if( dotOffset >= 0 ) continue;
face.plane.ptr()[3] -= dotOffset;
for(unsigned int i=0; i<face.vertices.size(); ++i)
{
face.vertices[i] += offset;
}
}
typedef std::set< Edge > SilhouetteEdges;
typedef std::map< Face*, SilhouetteEdges > SilhouetteFaces;
SilhouetteFaces silhouetteFaces;
int new_face_counter = 0;
// Now add new faces from slhouette edges extrusion
for(EdgeMap::iterator eitr = edgeMap.begin();
eitr != edgeMap.end();
++eitr)
{
const Edge& edge = eitr->first;
const EdgeFaces& edgeFaces = eitr->second;
if ( edgeFaces.size()==1 )
{
// WL: Execution should not reach this line.
// If you got here let me know: [email protected]
assert( 0 );
}
else if ( edgeFaces.size()==2 )
{
#if 0 // Use float normal computations
osg::Vec3f vf( offset );
double dotOffset0 = osg::Vec3f( edgeFaces[0]->plane.getNormal() ) *
vf;
double dotOffset1 = osg::Vec3f( edgeFaces[1]->plane.getNormal() ) *
vf;
#else
double dotOffset0 = edgeFaces[0]->plane.getNormal() * offset;
double dotOffset1 = edgeFaces[1]->plane.getNormal() * offset;
#endif
//Select orthogonal faces and vertices appropriate for offsetting
if( (dotOffset0 == 0.0 && dotOffset1 < 0.0) ||
(dotOffset1 == 0.0 && dotOffset0 < 0.0) )
{
Face * face = ( dotOffset0 == 0 ? edgeFaces[0] : edgeFaces[1] );
silhouetteFaces[ face ].insert( edge );
}
if( (dotOffset0 < 0.0 && dotOffset1 > 0.0) ||
(dotOffset1 < 0.0 && dotOffset0 > 0.0) )
{
Face & face = createFace();
char ac[40] = "Side plane from edge extrude ";
sprintf(ac + strlen(ac), "%d", new_face_counter++);
face.name = ac;
// Compute face plane
face.vertices.push_back( edge.first );
face.vertices.push_back( edge.second );
osg::Vec3d n = face.vertices[0] - face.vertices[1];
n.normalize();
n = ( n ^ offset );
n.normalize();
if( n * ( edgeFaces[1]->plane.getNormal() +
edgeFaces[0]->plane.getNormal() ) < 0 )
{
n = -n;
std::swap( face.vertices[1], face.vertices[0] );
}
face.vertices.push_back( face.vertices[1] + offset );
face.vertices.push_back( face.vertices[0] + offset );
face.plane.set( n,(face.vertices[0] + face.vertices[1] +
face.vertices[2] + face.vertices[3]) * .25 );
}
}
else if( edgeFaces.size() > 2 )
{
assert( 0 );
// WL: Execution should not reach this line.
// If you got here let me know: [email protected]
}
}
// Finally update faces which are orthogonal to our normal
for(SilhouetteFaces::iterator itr = silhouetteFaces.begin();
itr != silhouetteFaces.end();
++itr)
{
SilhouetteEdges & edges = itr->second;
Vertices & vertices = itr->first->vertices;
Vertices newVertices;
for( unsigned int i = 0; i < vertices.size(); i++ )
{
osg::Vec3d
&va = vertices[ ( i + vertices.size() - 1 ) % vertices.size() ],
&vb = vertices[ i ],
&vc = vertices[ ( i + 1 ) % vertices.size() ];
Edge eab = va < vb ? Edge( va, vb ) : Edge( vb, va );
Edge ebc = vb < vc ? Edge( vb, vc ) : Edge( vc, vb );
bool abFound = edges.find( eab ) != edges.end();
bool bcFound = edges.find( ebc ) != edges.end();
if( abFound && bcFound ) {
newVertices.push_back( vb + offset );
} else if( !abFound && !bcFound ) {
newVertices.push_back( vb );
} else if( !abFound && bcFound ) {
newVertices.push_back( vb );
newVertices.push_back( vb + offset );
} else if( abFound && !bcFound ) {
newVertices.push_back( vb + offset );
newVertices.push_back( vb );
}
}
vertices.swap( newVertices );
}
removeDuplicateVertices( );
checkCoherency( true, "ConvexPolyhedron::extrude" );
}
////////////////////////////////////////////////////////////////////////////
void ConvexPolyhedron::translate( const osg::Vec3d & offset )
{
for( Faces::iterator itr = _faces.begin(); itr != _faces.end(); ++itr )
{
itr->plane.ptr()[3] -= itr->plane.dotProductNormal( offset );
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
{
*vitr += offset;
}
}
}
////////////////////////////////////////////////////////////////////////////
void ConvexPolyhedron::getPolytope(osg::Polytope& polytope) const
{
for(Faces::const_iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
polytope.add(itr->plane);
}
}
////////////////////////////////////////////////////////////////////////////
void ConvexPolyhedron::getPoints(Vertices& vertices) const
{
typedef std::set<osg::Vec3d> VerticesSet;
VerticesSet verticesSet;
for(Faces::const_iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
const Face& face = *itr;
for(Vertices::const_iterator vitr = face.vertices.begin();
vitr != face.vertices.end();
++vitr)
{
verticesSet.insert(*vitr);
}
}
for(VerticesSet::iterator sitr = verticesSet.begin();
sitr != verticesSet.end();
++sitr)
{
vertices.push_back(*sitr);
}
}
////////////////////////////////////////////////////////////////////////////
osg::Geometry* ConvexPolyhedron::buildGeometry( const osg::Vec4d& colorOutline,
const osg::Vec4d& colorInside,
osg::Geometry* geometry ) const
{
if( !geometry ) {
geometry = new osg::Geometry;
} else {
geometry->getPrimitiveSetList( ).clear();
}
osg::Vec3dArray* vertices = new osg::Vec3dArray;
geometry->setVertexArray(vertices);
osg::Vec4Array* colors = new osg::Vec4Array;
geometry->setColorArray(colors, osg::Array::BIND_PER_PRIMITIVE_SET);
for(Faces::const_iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
if( colorInside[3] > 0 ) {
geometry->addPrimitiveSet( new osg::DrawArrays( GL_TRIANGLE_FAN,
vertices->size(), itr->vertices.size() ) );
colors->push_back( colorInside );
}
if( colorOutline[3] > 0 ) {
geometry->addPrimitiveSet( new osg::DrawArrays( GL_LINE_LOOP,
vertices->size(), itr->vertices.size() ) );
colors->push_back( colorOutline );
}
vertices->insert
( vertices->end(), itr->vertices.begin(), itr->vertices.end() );
}
osg::StateSet* stateset = geometry->getOrCreateStateSet();
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
stateset->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF);
stateset->setTextureMode(1, GL_TEXTURE_2D, osg::StateAttribute::OFF);
return geometry;
}
////////////////////////////////////////////////////////////////////////////
bool ConvexPolyhedron::dumpGeometry
( const Face * face,
const osg::Plane * plane,
ConvexPolyhedron * base,
const char * filename,
const osg::Vec4d& colorOutline,
const osg::Vec4d& colorInside,
const osg::Vec4d& faceColorOutline,
const osg::Vec4d& faceColorInside,
const osg::Vec4d& planeColorOutline,
const osg::Vec4d& planeColorInside,
const osg::Vec4d& baseColorOutline,
const osg::Vec4d& baseColorInside ) const
{
osg::Group * group = new osg::Group();
osg::Geode * geode = new osg::Geode();
geode->getOrCreateStateSet()->setMode( GL_BLEND, osg::StateAttribute::ON );
geode->getOrCreateStateSet()->setMode( GL_CULL_FACE,
osg::StateAttribute::OFF );
geode->getOrCreateStateSet()->setMode( GL_DEPTH_TEST,
osg::StateAttribute::OFF );
group->addChild( geode );
Vertices vertices;
getPoints( vertices );
osg::BoundingBox bb;
for( unsigned int i = 0; i < vertices.size(); i++ )
bb.expandBy( vertices[i] );
ConvexPolyhedron cph( *this ), cpFace;
for( Faces::iterator itr = cph._faces.begin(); itr != cph._faces.end(); )
{
bool found = ( face &&
itr->name == face->name &&
itr->plane == face->plane &&
itr->vertices == face->vertices );
#if 1
if( cph.isFacePolygonConvex( *itr ) < 0 )
std::reverse( itr->vertices.begin(), itr->vertices.end() );
#endif
if( found ) {
cpFace.createFace() = *face;
itr = cph._faces.erase( itr );
} else {
++itr;
}
}
osg::Geometry * geometry = cph.buildGeometry( colorOutline, colorInside );
geometry->getOrCreateStateSet()->setMode( GL_CULL_FACE,
osg::StateAttribute::ON );
geode->addDrawable( geometry );
if( face )
geode->addDrawable( cpFace.buildGeometry( faceColorOutline,
faceColorInside ) );
if( plane )
{
ConvexPolyhedron cp;
Face & cp_face = cp.createFace();
cp_face.plane = *plane;
osg::Vec3d normal = cp_face.plane.getNormal();
osg::Vec3d side = fabs(normal.x()) < fabs(normal.y()) ?
osg::Vec3d(1.0, 0.0, 0.0) :
osg::Vec3d(0.0, 1.0, 0.0);
osg::Vec3d v = normal ^ side;
v.normalize();
v *= bb.radius();
osg::Vec3d u = v ^ normal;
u.normalize();
u *= bb.radius();
osg::Vec3d c = bb.center();
c -= cp_face.plane.getNormal() * cp_face.plane.distance( c );
cp_face.vertices.push_back( c - u - v );
cp_face.vertices.push_back( c - u + v );
cp_face.vertices.push_back( c + u + v );
cp_face.vertices.push_back( c + u - v );
geode->addDrawable( cp.buildGeometry( planeColorOutline,
planeColorInside ) );
}
if( base )
geode->addDrawable( base->buildGeometry( baseColorOutline,
baseColorInside ) );
return osgDB::writeNodeFile( *group, std::string( filename ) );
}
////////////////////////////////////////////////////////////////////////////
_______________________________________________
osg-users mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org