Complex visibility rules per window/camera

Problems building or running the engine, queries about how to use features etc.
Post Reply
User avatar
bishopnator
Goblin
Posts: 292
Joined: Thu Apr 26, 2007 11:43 am
Location: Slovakia / Switzerland
x 10

Complex visibility rules per window/camera

Post by bishopnator »

Ogre Version: 3.x :?:
Operating System: windows :?:
Render System: d3d11 / gl3+ :?:

Does Ogre support custom visibility settings which are impossible to describe by the visibility flags? Consider a cad-like application where there are layers - a layer is assignment to the object which defines some styles (e.g. color) and also visibility per window. For example a scene representing a building can have objects assigned to layers according to the floors, rooms, etc. An application can have multiple windows rendering the same scene and in each window different visibility settings of the layers. Common format representing such CAD drawings is DWG/DXG. There can be hundreds of layers and a lot of windows - it is up to the user to manage them. How can the visibility settings represented by ogre-next?

The Renderable has a bool flag 'mRenderable Visible' (but without a space - somehow formatting replaces the correct spelling with "BRAND NAME") - but to setup it before rendering content of particular window means that it is necessary to iterate over all objects just before rendering each window. Is it the way to go? Is there something else? Visibility flags have just 32 bits which is not enough.

I am also thinking about creating a custom scene graph per window - under the root node there will be nodes for each layer so changing visibility of the layer in the window means setting a visibility of particular SceneNode in the window. It just doesn't sound as proper way - there should be one scene graph per "document" which is displayed by multiple windows.

User avatar
bishopnator
Goblin
Posts: 292
Joined: Thu Apr 26, 2007 11:43 am
Location: Slovakia / Switzerland
x 10

Re: Complex visibility rules per window/camera

Post by bishopnator »

I check the implementation a little and trying to figure out how to support such complex visibility settings. Another library I which I used, has a callback functor for the visibility - where it is possible to implemented whatever you would like. The library also expected that the callback is not necessary a fast one so it cached the results per camera and per instance (the callback receives the path from the scene graph root). I am not saying that it is the best, but such callback allows to implement very complex visibility rules. Just thinking loud - such callback (or listener) can be integrated in SceneManager::cullFrustum where outVisibleObjects are iterated. Or maybe directly in MovableObject::cullFrustum to even avoid insertion of hidden objects in the outVisibleObjects container.

However I see in that ogre-next has tendency to rather rid of the unneeded listeners rather than adding more. I am little bit lost here, how to integrate such rules - the problem is that the editor app is at the end very flexible and whatever support with limited number of data (like bits in the visibility flags) is simply not enough. Displaying a custom scene graph per window is also not very feasible with ogre - all windows display just the whole content of the scene manager and hence there is just one scene graph for all windows.

User avatar
bishopnator
Goblin
Posts: 292
Joined: Thu Apr 26, 2007 11:43 am
Location: Slovakia / Switzerland
x 10

Re: Complex visibility rules per window/camera

Post by bishopnator »

It is possible to do something like:

Code: Select all

std::unordered_map<std::pair<Camera*, MovableObject*>, bool> m_VisibilityCache;
std::functor<bool(Camera&, MovableObject&)> m_VisibilityCallback;

and in SceneManager::cullFrustum there would be additional look-up for the visibility (inside the loop which iterates through outVisibleObjects):

Code: Select all

if (m_VisibilityCallback != nullptr)
{
	auto itVisibilityCacheItem = m_VisibilityCache.find(std::make_pair(camera, *itor));
	if (itVisibilityCacheItem == m_VisibilityCache.end())
	{
		const bool isVisibleByCallback = m_VisibilityCallback(*camera, **itor);
		itVisibilityCacheItem = m_VisibilityCache.insert(std::make_pair(std::make_pair(camera, *itor), isVisibleByCallback)).first;
	}
	
if (!itVisibilityCacheItem->second)
{
	++itor;
	continue;
}
}

Additionally it is necessary to clear the cache when either Camera or MovableObject is destroyed, maybe also manually to clear the cache if it is known that the callback will (can) return different values.

Performance impact is not clear yet - probably there will be high penalty for using such callback. Just an ideas .. I am further analyzing the possibilities..

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5413
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1328
Contact:

Re: Complex visibility rules per window/camera

Post by dark_sylinc »

Does Ogre support custom visibility settings which are impossible to describe by the visibility flags?(...)

The short answer is no.

Now onto solutions (thinking out loud, just like you're doing):

Toggling visibility

it is necessary to iterate over all objects just before rendering each window. Is it the way to go? Is there something else?

Considering the alternative you were proposing, this isn't actually a bad idea :lol:

You could iterate each MovableObject and call MovableObject::setVisible() or a different pair of visibility flags.
You could do this in CompositorWorkspaceListener.

Suggestion:

For maximum performance you could use MovableObject::_getObjectData and iterate ObjectData::mVisibilityFlags instead. This is crazy fast and can also be parallelized.

Code: Select all

for( size_t i = 0u; i < NUM_SCENE_MEMORY_MANAGER_TYPES; ++i )
{
	ObjectMemoryManager &objMemoryManager =
		sceneManager->_getEntityMemoryManager( static_cast<SceneMemoryMgrTypes>( i ) );

	const size_t numRenderQueues = objMemoryManager.getNumRenderQueues();
	for( size_t j = 0u; j < numRenderQueues; ++j )
	{
		ObjectData objData;
		const size_t totalObjs = objMemoryManager.getFirstObjectData( objData, j );

		for( size_t k = 0u; k < totalObjs; k += ARRAY_PACKED_REALS )
		{
			for( size_t l = 0; l < ARRAY_PACKED_REALS; ++l )
			{
				// owner is guaranteed to NOT be a nullptr.
				// However it can be a dummy ptr, see ObjectMemoryManager::mDummyObject.
				// Do not set the dummy ptr to being visible.
				MovableObject *owner = objData.mOwner[l];
				objData.mVisibilityFlags[l] = whatever; // based on owner.
			}

			objData.advanceCullLightPack();
		}
	}
}

Remember that you can use MovableObject::getUserObjectBindings for storing arbitrary data (though this isn't exactly fast I think).

Separate Scene Graphs

Beware that a MovableObject can only be attached one SceneNode, it can't be attached to multiple SceneNodes at the same time. Furthermore SceneNode::setVisible just ends up calling MovableObject::setVisible.

However you can have multiple MovableObjects (i.e. duplicated Items) attached to the same SceneNode with different flags. That would work.

If memory is a concern, you could try creating a custom MovableObject (see Samples/2.0/ApiUsage/CustomRenderable) but unlike MyCustomRenderable, you only derive from MovableObject but not Renderable. Then add the SubItem's to MyCustomRenderable::mRenderables. Something like this (pseudo C++):

Code: Select all

class MyCustomRenderable final : public MovableObject
{
   MyCustomRenderable( /*...*/ , Item *item )
   {
        // Do not worry about the WorldAabb/Radius. They'll fill themselves.
        mObjectData.mLocalAabb->setFromAabb( item->getLocalAabb(), mObjectData.mIndex );
        mObjectData.mLocalRadius[mObjectData.mIndex] = item->getLocalRadius();
        
for( each subitem in item ) mRenderables.push_back( subitem ); }
/* Other overrides like MyCustomRenderable::getWorldTransforms et al stay the same */ }

I don't know if it will work, but it may; as long as you're not using skeletons (if you do, it may need a bit more work so your MyCustomRenderable replicates the Item's functionality and then use useSkeletonInstanceFrom).

The advantage is that you will have:

  1. Same SceneNode (share position, rotation, scale) as long as you attach the clones to the same node.

  2. Same materials (since they share the SubItems).

  3. Separate visibility settings.

  4. Separate RenderQueue IDs.

Custom Compositor Pass

Copy paste OgreCompositorPassScene.cpp and its headers into your project. Then render however you like.
See Colibri which uses a custom compositor pass to render the 2D UI.

It delegates all the work to m_colibriManager->prepareRenderCommands() and m_colibriManager->render().

This sounds a bit overkill and you will be duplicating a lot of work that OgreNext is already doing, so I am not convinced of this approach.

Thus in short the easiest and simplest way is just toggle settings by iterating through them.
Using an Item clone (or an custom MovableObject that references SubItems) would be more efficient on the long run.

This also depends on the number of Items you have. If you have 100 items in your scene and 10 windows, 1000 iterations may very well be acceptable.
If you have 100k Items, then the clone may be a better deal.

Post Reply