/* 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; }; 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; } }; 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 bool operator<(scene::Node& node, scene::Node& other) { return &node < &other; } inline bool operator==(scene::Node& node, scene::Node& other) { return &node == &other; } inline bool operator!=(scene::Node& node, scene::Node& other) { return !::operator==(node, other); } 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