/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant 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 GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined ( INCLUDED_SCENELIB_H ) #define INCLUDED_SCENELIB_H #include "iscenegraph.h" #include "iselection.h" #include "warnings.h" #include #include #include "math/aabb.h" #include "transformlib.h" #include "generic/callback.h" #include "generic/reference.h" #include "container/stack.h" #include "typesystem.h" class Selector; class SelectionTest; class VolumeTest; template class BasicVector3; typedef BasicVector3 Vector3; template class BasicVector4; typedef BasicVector4 Vector4; class Matrix4; typedef Vector4 Quaternion; class AABB; class ComponentSelectionTestable { public: STRING_CONSTANT( Name, "ComponentSelectionTestable" ); virtual bool isSelectedComponents() const = 0; virtual void setSelectedComponents( bool select, SelectionSystem::EComponentMode mode ) = 0; virtual void testSelectComponents( Selector& selector, SelectionTest& test, SelectionSystem::EComponentMode mode ) = 0; }; class ComponentEditable { public: STRING_CONSTANT( Name, "ComponentEditable" ); virtual const AABB& getSelectedComponentsBounds() const = 0; }; class ComponentSnappable { public: STRING_CONSTANT( Name, "ComponentSnappable" ); virtual void snapComponents( float snap ) = 0; }; class Bounded { public: STRING_CONSTANT( Name, "Bounded" ); virtual const AABB& localAABB() const = 0; }; class BrushDoom3 { public: STRING_CONSTANT( Name, "BrushDoom3" ); virtual void setDoom3GroupOrigin( const Vector3& origin ) = 0; }; typedef TypeCastTable NodeTypeCastTable; template class NodeType : public StaticTypeSystemInitialiser { TypeId m_typeId; public: typedef typename Type::Name Name; NodeType() : m_typeId( NODETYPEID_NONE ){ StaticTypeSystemInitialiser::instance().addInitialiser( InitialiseCaller( *this ) ); } void initialise(){ m_typeId = GlobalSceneGraph().getNodeTypeId( Name() ); } typedef MemberCaller, &NodeType::initialise> InitialiseCaller; TypeId getTypeId(){ #if defined( _DEBUG ) ASSERT_MESSAGE( m_typeId != NODETYPEID_NONE, "node-type " << makeQuoted( Name() ) << " used before being initialised" ); #endif return m_typeId; } }; template class StaticNodeType { public: enum unnamed0 { SIZE = NODETYPEID_MAX }; static TypeId getTypeId(){ return Static< NodeType >::instance().getTypeId(); } }; template class NodeStaticCast : public CastInstaller< StaticNodeType, StaticCast > { }; template class NodeContainedCast : public CastInstaller< StaticNodeType, ContainedCast > { }; template class NodeIdentityCast : public CastInstaller< StaticNodeType, IdentityCast > { }; namespace scene { class Node { public: enum unnamed0 { eVisible = 0 }; enum unnamed1 { eHidden = 1 << 0 }; enum unnamed2 { eFiltered = 1 << 1 }; enum unnamed3 { eExcluded = 1 << 2 }; class Symbiot { public: virtual void release() = 0; virtual ~Symbiot(){ } }; private: unsigned int m_state; std::size_t m_refcount; Symbiot* m_symbiot; void* m_node; NodeTypeCastTable& m_casts; public: bool m_isRoot; bool isRoot(){ return m_isRoot; } Node( Symbiot* symbiot, void* node, NodeTypeCastTable& casts ) : m_state( eVisible ), m_refcount( 0 ), m_symbiot( symbiot ), m_node( node ), m_casts( casts ), m_isRoot( false ){ } ~Node(){ } void IncRef(){ ASSERT_MESSAGE( m_refcount < ( 1 << 24 ), "Node::decref: uninitialised refcount" ); ++m_refcount; } void DecRef(){ ASSERT_MESSAGE( m_refcount < ( 1 << 24 ), "Node::decref: uninitialised refcount" ); if ( --m_refcount == 0 ) { m_symbiot->release(); } } std::size_t getReferenceCount() const { return m_refcount; } void* cast( TypeId typeId ) const { return m_casts.cast( typeId, m_node ); } void enable( unsigned int state ){ m_state |= state; } void disable( unsigned int state ){ m_state &= ~state; } bool visible(){ return m_state == eVisible; } bool excluded(){ return ( m_state & eExcluded ) != 0; } bool operator<( const scene::Node& other ){ return this < &other; } bool operator==( const scene::Node& other ){ return this == &other; } bool operator!=( const scene::Node& other ){ return this != &other; } }; class NullNode : public Node::Symbiot { NodeTypeCastTable m_casts; Node m_node; public: NullNode() : m_node( this, 0, m_casts ){ } void release(){ delete this; } scene::Node& node(){ return m_node; } }; } template class NodeTypeCast { public: static Type* cast( scene::Node& node ){ return static_cast( node.cast( StaticNodeType::getTypeId() ) ); } static const Type* cast( const scene::Node& node ){ return static_cast( node.cast( StaticNodeType::getTypeId() ) ); } }; inline scene::Instantiable* Node_getInstantiable( scene::Node& node ){ return NodeTypeCast::cast( node ); } inline scene::Traversable* Node_getTraversable( scene::Node& node ){ return NodeTypeCast::cast( node ); } inline void Node_traverseSubgraph( scene::Node& node, const scene::Traversable::Walker& walker ){ if ( walker.pre( node ) ) { scene::Traversable* traversable = Node_getTraversable( node ); if ( traversable != 0 ) { traversable->traverse( walker ); } } walker.post( node ); } inline TransformNode* Node_getTransformNode( scene::Node& node ){ return NodeTypeCast::cast( node ); } inline scene::Node& NewNullNode(){ return ( new scene::NullNode )->node(); } inline void Path_deleteTop( const scene::Path& path ){ Node_getTraversable( path.parent() )->erase( path.top() ); } class delete_all : public scene::Traversable::Walker { scene::Node& m_parent; public: delete_all( scene::Node& parent ) : m_parent( parent ){ } bool pre( scene::Node& node ) const { return false; } void post( scene::Node& node ) const { Node_getTraversable( m_parent )->erase( node ); } }; inline void DeleteSubgraph( scene::Node& subgraph ){ Node_getTraversable( subgraph )->traverse( delete_all( subgraph ) ); } class EntityUndefined { public: STRING_CONSTANT( Name, "Entity" ); }; inline bool Node_isEntity( scene::Node& node ){ return NodeTypeCast::cast( node ) != 0; } template class EntityWalker : public scene::Graph::Walker { const Functor& functor; public: EntityWalker( const Functor& functor ) : functor( functor ){ } bool pre( const scene::Path& path, scene::Instance& instance ) const { if ( Node_isEntity( path.top() ) ) { functor( instance ); return false; } return true; } }; template inline const Functor& Scene_forEachEntity( const Functor& functor ){ GlobalSceneGraph().traverse( EntityWalker( functor ) ); return functor; } class BrushUndefined { public: STRING_CONSTANT( Name, "Brush" ); }; inline bool Node_isBrush( scene::Node& node ){ return NodeTypeCast::cast( node ) != 0; } class PatchUndefined { public: STRING_CONSTANT( Name, "Patch" ); }; inline bool Node_isPatch( scene::Node& node ){ return NodeTypeCast::cast( node ) != 0; } inline bool Node_isPrimitive( scene::Node& node ){ #if 1 return Node_isBrush( node ) || Node_isPatch( node ); #else return !node.isRoot(); #endif } class ParentBrushes : public scene::Traversable::Walker { scene::Node& m_parent; public: ParentBrushes( scene::Node& parent ) : m_parent( parent ){ } bool pre( scene::Node& node ) const { return false; } void post( scene::Node& node ) const { if ( Node_isPrimitive( node ) ) { Node_getTraversable( m_parent )->insert( node ); } } }; inline void parentBrushes( scene::Node& subgraph, scene::Node& parent ){ Node_getTraversable( subgraph )->traverse( ParentBrushes( parent ) ); } class HasBrushes : public scene::Traversable::Walker { bool& m_hasBrushes; public: HasBrushes( bool& hasBrushes ) : m_hasBrushes( hasBrushes ){ m_hasBrushes = true; } bool pre( scene::Node& node ) const { if ( !Node_isPrimitive( node ) ) { m_hasBrushes = false; } return false; } }; inline bool node_is_group( scene::Node& node ){ scene::Traversable* traversable = Node_getTraversable( node ); if ( traversable != 0 ) { bool hasBrushes = false; traversable->traverse( HasBrushes( hasBrushes ) ); return hasBrushes; } return false; } typedef TypeCastTable InstanceTypeCastTable; template class InstanceType : public StaticTypeSystemInitialiser { TypeId m_typeId; public: typedef typename Type::Name Name; InstanceType() : m_typeId( INSTANCETYPEID_NONE ){ StaticTypeSystemInitialiser::instance().addInitialiser( InitialiseCaller( *this ) ); } void initialise(){ m_typeId = GlobalSceneGraph().getInstanceTypeId( Name() ); } typedef MemberCaller, &InstanceType::initialise> InitialiseCaller; TypeId getTypeId(){ #if defined( _DEBUG ) ASSERT_MESSAGE( m_typeId != INSTANCETYPEID_NONE, "instance-type " << makeQuoted( Name() ) << " used before being initialised" ); #endif return m_typeId; } }; template class StaticInstanceType { public: enum unnamed0 { SIZE = INSTANCETYPEID_MAX }; static TypeId getTypeId(){ return Static< InstanceType >::instance().getTypeId(); } }; template class InstanceStaticCast : public CastInstaller< StaticInstanceType, StaticCast > { }; template class InstanceContainedCast : public CastInstaller< StaticInstanceType, ContainedCast > { }; template class InstanceIdentityCast : public CastInstaller< StaticInstanceType, IdentityCast > { }; inline Selectable* Instance_getSelectable( scene::Instance& instance ); inline const Selectable* Instance_getSelectable( const scene::Instance& instance ); inline Bounded* Instance_getBounded( scene::Instance& instance ); inline const Bounded* Instance_getBounded( const scene::Instance& instance ); namespace scene { class Instance { class AABBAccumulateWalker : public scene::Graph::Walker { AABB& m_aabb; mutable std::size_t m_depth; public: AABBAccumulateWalker( AABB& aabb ) : m_aabb( aabb ), m_depth( 0 ){ } bool pre( const scene::Path& path, scene::Instance& instance ) const { if ( m_depth == 1 ) { aabb_extend_by_aabb_safe( m_aabb, instance.worldAABB() ); } return ++m_depth != 2; } void post( const scene::Path& path, scene::Instance& instance ) const { --m_depth; } }; class TransformChangedWalker : public scene::Graph::Walker { public: bool pre( const scene::Path& path, scene::Instance& instance ) const { instance.transformChangedLocal(); return true; } }; class ParentSelectedChangedWalker : public scene::Graph::Walker { public: bool pre( const scene::Path& path, scene::Instance& instance ) const { instance.parentSelectedChanged(); return true; } }; class ChildSelectedWalker : public scene::Graph::Walker { bool& m_childSelected; mutable std::size_t m_depth; public: ChildSelectedWalker( bool& childSelected ) : m_childSelected( childSelected ), m_depth( 0 ){ m_childSelected = false; } bool pre( const scene::Path& path, scene::Instance& instance ) const { if ( m_depth == 1 && !m_childSelected ) { m_childSelected = instance.isSelected() || instance.childSelected(); } return ++m_depth != 2; } void post( const scene::Path& path, scene::Instance& instance ) const { --m_depth; } }; Path m_path; Instance* m_parent; void* m_instance; InstanceTypeCastTable& m_casts; mutable Matrix4 m_local2world; mutable AABB m_bounds; mutable AABB m_childBounds; mutable bool m_transformChanged; mutable bool m_transformMutex; mutable bool m_boundsChanged; mutable bool m_boundsMutex; mutable bool m_childBoundsChanged; mutable bool m_childBoundsMutex; mutable bool m_isSelected; mutable bool m_isSelectedChanged; mutable bool m_childSelected; mutable bool m_childSelectedChanged; mutable bool m_parentSelected; mutable bool m_parentSelectedChanged; Callback m_childSelectedChangedCallback; Callback m_transformChangedCallback; void evaluateTransform() const { if ( m_transformChanged ) { ASSERT_MESSAGE( !m_transformMutex, "re-entering transform evaluation" ); m_transformMutex = true; m_local2world = ( m_parent != 0 ) ? m_parent->localToWorld() : g_matrix4_identity; TransformNode* transformNode = Node_getTransformNode( m_path.top() ); if ( transformNode != 0 ) { matrix4_multiply_by_matrix4( m_local2world, transformNode->localToParent() ); } m_transformMutex = false; m_transformChanged = false; } } void evaluateChildBounds() const { if ( m_childBoundsChanged ) { ASSERT_MESSAGE( !m_childBoundsMutex, "re-entering bounds evaluation" ); m_childBoundsMutex = true; m_childBounds = AABB(); GlobalSceneGraph().traverse_subgraph( AABBAccumulateWalker( m_childBounds ), m_path ); m_childBoundsMutex = false; m_childBoundsChanged = false; } } void evaluateBounds() const { if ( m_boundsChanged ) { ASSERT_MESSAGE( !m_boundsMutex, "re-entering bounds evaluation" ); m_boundsMutex = true; m_bounds = childBounds(); const Bounded* bounded = Instance_getBounded( *this ); if ( bounded != 0 ) { aabb_extend_by_aabb_safe( m_bounds, aabb_for_oriented_aabb_safe( bounded->localAABB(), localToWorld() ) ); } m_boundsMutex = false; m_boundsChanged = false; } } Instance( const scene::Instance& other ); Instance& operator=( const scene::Instance& other ); public: Instance( const scene::Path& path, Instance* parent, void* instance, InstanceTypeCastTable& casts ) : m_path( path ), m_parent( parent ), m_instance( instance ), m_casts( casts ), m_local2world( g_matrix4_identity ), m_transformChanged( true ), m_transformMutex( false ), m_boundsChanged( true ), m_boundsMutex( false ), m_childBoundsChanged( true ), m_childBoundsMutex( false ), m_isSelectedChanged( true ), m_childSelectedChanged( true ), m_parentSelectedChanged( true ){ ASSERT_MESSAGE( ( parent == 0 ) == ( path.size() == 1 ), "instance has invalid parent" ); } virtual ~Instance(){ } const scene::Path& path() const { return m_path; } void* cast( TypeId typeId ) const { return m_casts.cast( typeId, m_instance ); } const Matrix4& localToWorld() const { evaluateTransform(); return m_local2world; } void transformChangedLocal(){ ASSERT_NOTNULL( m_parent ); m_transformChanged = true; m_boundsChanged = true; m_childBoundsChanged = true; m_transformChangedCallback(); } void transformChanged(){ GlobalSceneGraph().traverse_subgraph( TransformChangedWalker(), m_path ); boundsChanged(); } void setTransformChangedCallback( const Callback& callback ){ m_transformChangedCallback = callback; } const AABB& worldAABB() const { evaluateBounds(); return m_bounds; } const AABB& childBounds() const { evaluateChildBounds(); return m_childBounds; } void boundsChanged(){ m_boundsChanged = true; m_childBoundsChanged = true; if ( m_parent != 0 ) { m_parent->boundsChanged(); } GlobalSceneGraph().boundsChanged(); } void childSelectedChanged(){ m_childSelectedChanged = true; m_childSelectedChangedCallback(); if ( m_parent != 0 ) { m_parent->childSelectedChanged(); } } bool childSelected() const { if ( m_childSelectedChanged ) { m_childSelectedChanged = false; GlobalSceneGraph().traverse_subgraph( ChildSelectedWalker( m_childSelected ), m_path ); } return m_childSelected; } void setChildSelectedChangedCallback( const Callback& callback ){ m_childSelectedChangedCallback = callback; } void selectedChanged(){ m_isSelectedChanged = true; if ( m_parent != 0 ) { m_parent->childSelectedChanged(); } GlobalSceneGraph().traverse_subgraph( ParentSelectedChangedWalker(), m_path ); } bool isSelected() const { if ( m_isSelectedChanged ) { m_isSelectedChanged = false; const Selectable* selectable = Instance_getSelectable( *this ); m_isSelected = selectable != 0 && selectable->isSelected(); } return m_isSelected; } void parentSelectedChanged(){ m_parentSelectedChanged = true; } bool parentSelected() const { if ( m_parentSelectedChanged ) { m_parentSelectedChanged = false; m_parentSelected = m_parent != 0 && ( m_parent->isSelected() || m_parent->parentSelected() ); } return m_parentSelected; } }; } template class InstanceTypeCast { public: static Type* cast( scene::Instance& instance ){ return static_cast( instance.cast( StaticInstanceType::getTypeId() ) ); } static const Type* cast( const scene::Instance& instance ){ return static_cast( instance.cast( StaticInstanceType::getTypeId() ) ); } }; template class InstanceWalker : public scene::Graph::Walker { const Functor& m_functor; public: InstanceWalker( const Functor& functor ) : m_functor( functor ){ } bool pre( const scene::Path& path, scene::Instance& instance ) const { m_functor( instance ); return true; } }; template class ChildInstanceWalker : public scene::Graph::Walker { const Functor& m_functor; mutable std::size_t m_depth; public: ChildInstanceWalker( const Functor& functor ) : m_functor( functor ), m_depth( 0 ){ } bool pre( const scene::Path& path, scene::Instance& instance ) const { if ( m_depth == 1 ) { m_functor( instance ); } return ++m_depth != 2; } void post( const scene::Path& path, scene::Instance& instance ) const { --m_depth; } }; template class InstanceApply : public Functor { public: InstanceApply( const Functor& functor ) : Functor( functor ){ } void operator()( scene::Instance& instance ) const { Type* result = InstanceTypeCast::cast( instance ); if ( result != 0 ) { Functor::operator()( *result ); } } }; inline Selectable* Instance_getSelectable( scene::Instance& instance ){ return InstanceTypeCast::cast( instance ); } inline const Selectable* Instance_getSelectable( const scene::Instance& instance ){ return InstanceTypeCast::cast( instance ); } template inline void Scene_forEachChildSelectable( const Functor& functor, const scene::Path& path ){ GlobalSceneGraph().traverse_subgraph( ChildInstanceWalker< InstanceApply >( functor ), path ); } class SelectableSetSelected { bool m_selected; public: SelectableSetSelected( bool selected ) : m_selected( selected ){ } void operator()( Selectable& selectable ) const { selectable.setSelected( m_selected ); } }; inline Bounded* Instance_getBounded( scene::Instance& instance ){ return InstanceTypeCast::cast( instance ); } inline const Bounded* Instance_getBounded( const scene::Instance& instance ){ return InstanceTypeCast::cast( instance ); } inline Transformable* Instance_getTransformable( scene::Instance& instance ){ return InstanceTypeCast::cast( instance ); } inline const Transformable* Instance_getTransformable( const scene::Instance& instance ){ return InstanceTypeCast::cast( instance ); } inline ComponentSelectionTestable* Instance_getComponentSelectionTestable( scene::Instance& instance ){ return InstanceTypeCast::cast( instance ); } inline ComponentEditable* Instance_getComponentEditable( scene::Instance& instance ){ return InstanceTypeCast::cast( instance ); } inline ComponentSnappable* Instance_getComponentSnappable( scene::Instance& instance ){ return InstanceTypeCast::cast( instance ); } inline void Instance_setSelected( scene::Instance& instance, bool selected ){ Selectable* selectable = Instance_getSelectable( instance ); if ( selectable != 0 ) { selectable->setSelected( selected ); } } inline bool Instance_isSelected( scene::Instance& instance ){ Selectable* selectable = Instance_getSelectable( instance ); if ( selectable != 0 ) { return selectable->isSelected(); } return false; } inline scene::Instance& findInstance( const scene::Path& path ){ scene::Instance* instance = GlobalSceneGraph().find( path ); ASSERT_MESSAGE( instance != 0, "findInstance: path not found in scene-graph" ); return *instance; } inline void selectPath( const scene::Path& path, bool selected ){ Instance_setSelected( findInstance( path ), selected ); } class SelectChildren : public scene::Traversable::Walker { mutable scene::Path m_path; public: SelectChildren( const scene::Path& root ) : m_path( root ){ } bool pre( scene::Node& node ) const { m_path.push( makeReference( node ) ); selectPath( m_path, true ); return false; } void post( scene::Node& node ) const { m_path.pop(); } }; inline void Entity_setSelected( scene::Instance& entity, bool selected ){ scene::Node& node = entity.path().top(); if ( node_is_group( node ) ) { Node_getTraversable( node )->traverse( SelectChildren( entity.path() ) ); } else { Instance_setSelected( entity, selected ); } } inline bool Entity_isSelected( scene::Instance& entity ){ if ( node_is_group( entity.path().top() ) ) { return entity.childSelected(); } return Instance_isSelected( entity ); } class InstanceCounter { public: unsigned int m_count; InstanceCounter() : m_count( 0 ){ } }; class Counter { public: virtual void increment() = 0; virtual void decrement() = 0; }; #include "generic/callback.h" class SimpleCounter : public Counter { Callback m_countChanged; std::size_t m_count; public: void setCountChangedCallback( const Callback& countChanged ){ m_countChanged = countChanged; } void increment(){ ++m_count; m_countChanged(); } void decrement(){ --m_count; m_countChanged(); } std::size_t get() const { return m_count; } }; template class ConstReference; typedef ConstReference PathConstReference; #include "generic/referencecounted.h" typedef SmartReference > NodeSmartReference; #endif