gtkradiant/libs/scenelib.h

1104 lines
24 KiB
C
Raw Normal View History

/*
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 <cstddef>
#include <string.h>
#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<typename Element> class BasicVector3;
typedef BasicVector3<float> Vector3;
template<typename Element> class BasicVector4;
typedef BasicVector4<float> 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<NODETYPEID_MAX> NodeTypeCastTable;
template<typename Type>
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<Type>, &NodeType<Type>::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<typename Type>
class StaticNodeType
{
public:
enum unnamed0 { SIZE = NODETYPEID_MAX };
static TypeId getTypeId()
{
return Static< NodeType<Type> >::instance().getTypeId();
}
};
template<typename Type, typename Base>
class NodeStaticCast :
public CastInstaller<
StaticNodeType<Base>,
StaticCast<Type, Base>
>
{
};
template<typename Type, typename Contained>
class NodeContainedCast :
public CastInstaller<
StaticNodeType<Contained>,
ContainedCast<Type, Contained>
>
{
};
template<typename Type>
class NodeIdentityCast :
public CastInstaller<
StaticNodeType<Type>,
IdentityCast<Type>
>
{
};
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<typename Type>
class NodeTypeCast
{
public:
static Type* cast(scene::Node& node)
{
return static_cast<Type*>(node.cast(StaticNodeType<Type>::getTypeId()));
}
static const Type* cast(const scene::Node& node)
{
return static_cast<const Type*>(node.cast(StaticNodeType<Type>::getTypeId()));
}
};
inline scene::Instantiable* Node_getInstantiable(scene::Node& node)
{
return NodeTypeCast<scene::Instantiable>::cast(node);
}
inline scene::Traversable* Node_getTraversable(scene::Node& node)
{
return NodeTypeCast<scene::Traversable>::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<TransformNode>::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<EntityUndefined>::cast(node) != 0;
}
template<typename Functor>
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<typename Functor>
inline const Functor& Scene_forEachEntity(const Functor& functor)
{
GlobalSceneGraph().traverse(EntityWalker<Functor>(functor));
return functor;
}
class BrushUndefined
{
public:
STRING_CONSTANT(Name, "Brush");
};
inline bool Node_isBrush(scene::Node& node)
{
return NodeTypeCast<BrushUndefined>::cast(node) != 0;
}
class PatchUndefined
{
public:
STRING_CONSTANT(Name, "Patch");
};
inline bool Node_isPatch(scene::Node& node)
{
return NodeTypeCast<PatchUndefined>::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<INSTANCETYPEID_MAX> InstanceTypeCastTable;
template<typename Type>
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<Type>, &InstanceType<Type>::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<typename Type>
class StaticInstanceType
{
public:
enum unnamed0 { SIZE = INSTANCETYPEID_MAX };
static TypeId getTypeId()
{
return Static< InstanceType<Type> >::instance().getTypeId();
}
};
template<typename Type, typename Base>
class InstanceStaticCast :
public CastInstaller<
StaticInstanceType<Base>,
StaticCast<Type, Base>
>
{
};
template<typename Type, typename Contained>
class InstanceContainedCast :
public CastInstaller<
StaticInstanceType<Contained>,
ContainedCast<Type, Contained>
>
{
};
template<typename Type>
class InstanceIdentityCast :
public CastInstaller<
StaticInstanceType<Type>,
IdentityCast<Type>
>
{
};
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<typename Type>
class InstanceTypeCast
{
public:
static Type* cast(scene::Instance& instance)
{
return static_cast<Type*>(instance.cast(StaticInstanceType<Type>::getTypeId()));
}
static const Type* cast(const scene::Instance& instance)
{
return static_cast<const Type*>(instance.cast(StaticInstanceType<Type>::getTypeId()));
}
};
template<typename Functor>
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<typename Functor>
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<typename Type, typename Functor>
class InstanceApply : public Functor
{
public:
InstanceApply(const Functor& functor) : Functor(functor)
{
}
void operator()(scene::Instance& instance) const
{
Type* result = InstanceTypeCast<Type>::cast(instance);
if(result != 0)
{
Functor::operator()(*result);
}
}
};
inline Selectable* Instance_getSelectable(scene::Instance& instance)
{
return InstanceTypeCast<Selectable>::cast(instance);
}
inline const Selectable* Instance_getSelectable(const scene::Instance& instance)
{
return InstanceTypeCast<Selectable>::cast(instance);
}
template<typename Functor>
inline void Scene_forEachChildSelectable(const Functor& functor, const scene::Path& path)
{
GlobalSceneGraph().traverse_subgraph(ChildInstanceWalker< InstanceApply<Selectable, Functor> >(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<Bounded>::cast(instance);
}
inline const Bounded* Instance_getBounded(const scene::Instance& instance)
{
return InstanceTypeCast<Bounded>::cast(instance);
}
inline Transformable* Instance_getTransformable(scene::Instance& instance)
{
return InstanceTypeCast<Transformable>::cast(instance);
}
inline const Transformable* Instance_getTransformable(const scene::Instance& instance)
{
return InstanceTypeCast<Transformable>::cast(instance);
}
inline ComponentSelectionTestable* Instance_getComponentSelectionTestable(scene::Instance& instance)
{
return InstanceTypeCast<ComponentSelectionTestable>::cast(instance);
}
inline ComponentEditable* Instance_getComponentEditable(scene::Instance& instance)
{
return InstanceTypeCast<ComponentEditable>::cast(instance);
}
inline ComponentSnappable* Instance_getComponentSnappable(scene::Instance& instance)
{
return InstanceTypeCast<ComponentSnappable>::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<typename Contained>
class ConstReference;
typedef ConstReference<scene::Path> PathConstReference;
#include "generic/referencecounted.h"
typedef SmartReference<scene::Node, IncRefDecRefCounter<scene::Node> > NodeSmartReference;
#endif