mirror of
https://github.com/UberGames/GtkRadiant.git
synced 2024-11-26 22:01:38 +00:00
8c19a03806
git-svn-id: svn://svn.icculus.org/gtkradiant/GtkRadiant/trunk@17 8a3a26a2-13c4-0310-b231-cf6edde360e5
1077 lines
23 KiB
C++
1077 lines
23 KiB
C++
/*
|
|
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;
|
|
}
|
|
|
|
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
|