auto str = StringOutputStream()(bla) use form was not doing copy elision or move, but copy
264 lines
6.5 KiB
C++
264 lines
6.5 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
|
|
*/
|
|
|
|
#include "scenegraph.h"
|
|
|
|
#include "debugging/debugging.h"
|
|
|
|
#include <map>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
#include "string/string.h"
|
|
#include "signal/signal.h"
|
|
#include "scenelib.h"
|
|
#include "instancelib.h"
|
|
#include "treemodel.h"
|
|
|
|
template<std::size_t SIZE>
|
|
class TypeIdMap
|
|
{
|
|
typedef const char* TypeName;
|
|
typedef TypeName TypeNames[SIZE];
|
|
TypeNames m_typeNames;
|
|
TypeName* m_typeNamesEnd;
|
|
|
|
public:
|
|
TypeIdMap() : m_typeNamesEnd( m_typeNames ){
|
|
}
|
|
TypeId getTypeId( const char* name ){
|
|
TypeName* i = std::find_if( m_typeNames, m_typeNamesEnd, [name]( const char* other ){ return string_equal( name, other ); } );
|
|
if ( i == m_typeNamesEnd ) {
|
|
ASSERT_MESSAGE( m_typeNamesEnd != m_typeNames + SIZE, "reached maximum number of type names supported (" << SIZE << ')' );
|
|
*m_typeNamesEnd++ = name;
|
|
}
|
|
return i - m_typeNames;
|
|
}
|
|
};
|
|
|
|
class CompiledGraph final : public scene::Graph, public scene::Instantiable::Observer
|
|
{
|
|
typedef std::map<PathConstReference, scene::Instance*> InstanceMap;
|
|
|
|
InstanceMap m_instances;
|
|
scene::Instantiable::Observer* m_observer;
|
|
Signal0 m_boundsChanged;
|
|
scene::Path m_rootpath;
|
|
Signal0 m_sceneChangedCallbacks;
|
|
|
|
TypeIdMap<NODETYPEID_MAX> m_nodeTypeIds;
|
|
TypeIdMap<INSTANCETYPEID_MAX> m_instanceTypeIds;
|
|
|
|
public:
|
|
|
|
CompiledGraph( scene::Instantiable::Observer* observer )
|
|
: m_observer( observer ){
|
|
}
|
|
|
|
void addSceneChangedCallback( const SignalHandler& handler ){
|
|
m_sceneChangedCallbacks.connectLast( handler );
|
|
}
|
|
void sceneChanged(){
|
|
m_sceneChangedCallbacks();
|
|
}
|
|
|
|
scene::Node& root(){
|
|
ASSERT_MESSAGE( !m_rootpath.empty(), "scenegraph root does not exist" );
|
|
return m_rootpath.top();
|
|
}
|
|
void insert_root( scene::Node& root ){
|
|
//globalOutputStream() << "insert_root\n";
|
|
|
|
ASSERT_MESSAGE( m_rootpath.empty(), "scenegraph root already exists" );
|
|
|
|
root.IncRef();
|
|
|
|
Node_traverseSubgraph( root, InstanceSubgraphWalker( this, scene::Path(), 0 ) );
|
|
|
|
m_rootpath.push( makeReference( root ) );
|
|
}
|
|
void erase_root(){
|
|
//globalOutputStream() << "erase_root\n";
|
|
|
|
ASSERT_MESSAGE( !m_rootpath.empty(), "scenegraph root does not exist" );
|
|
|
|
scene::Node& root = m_rootpath.top();
|
|
|
|
m_rootpath.pop();
|
|
|
|
Node_traverseSubgraph( root, UninstanceSubgraphWalker( this, scene::Path() ) );
|
|
|
|
root.DecRef();
|
|
}
|
|
void boundsChanged(){
|
|
m_boundsChanged();
|
|
}
|
|
|
|
void traverse( const Walker& walker ){
|
|
traverse_subgraph( walker, m_instances.begin() );
|
|
}
|
|
|
|
void traverse_subgraph( const Walker& walker, const scene::Path& start ){
|
|
if ( !m_instances.empty() ) {
|
|
traverse_subgraph( walker, m_instances.find( PathConstReference( start ) ) );
|
|
}
|
|
}
|
|
|
|
scene::Instance* find( const scene::Path& path ){
|
|
InstanceMap::iterator i = m_instances.find( PathConstReference( path ) );
|
|
if ( i == m_instances.end() ) {
|
|
return 0;
|
|
}
|
|
return ( *i ).second;
|
|
}
|
|
|
|
void insert( scene::Instance* instance ){
|
|
m_instances.insert( InstanceMap::value_type( PathConstReference( instance->path() ), instance ) );
|
|
|
|
m_observer->insert( instance );
|
|
}
|
|
void erase( scene::Instance* instance ){
|
|
m_observer->erase( instance );
|
|
|
|
m_instances.erase( PathConstReference( instance->path() ) );
|
|
}
|
|
|
|
SignalHandlerId addBoundsChangedCallback( const SignalHandler& boundsChanged ){
|
|
return m_boundsChanged.connectLast( boundsChanged );
|
|
}
|
|
void removeBoundsChangedCallback( SignalHandlerId id ){
|
|
m_boundsChanged.disconnect( id );
|
|
}
|
|
|
|
TypeId getNodeTypeId( const char* name ){
|
|
return m_nodeTypeIds.getTypeId( name );
|
|
}
|
|
|
|
TypeId getInstanceTypeId( const char* name ){
|
|
return m_instanceTypeIds.getTypeId( name );
|
|
}
|
|
|
|
private:
|
|
|
|
bool pre( const Walker& walker, const InstanceMap::iterator& i ){
|
|
return walker.pre( i->first, *i->second );
|
|
}
|
|
|
|
void post( const Walker& walker, const InstanceMap::iterator& i ){
|
|
walker.post( i->first, *i->second );
|
|
}
|
|
|
|
void traverse_subgraph( const Walker& walker, InstanceMap::iterator i ){
|
|
Stack<InstanceMap::iterator> stack;
|
|
if ( i != m_instances.end() ) {
|
|
const std::size_t startSize = ( *i ).first.get().size();
|
|
do
|
|
{
|
|
if ( i != m_instances.end()
|
|
&& stack.size() < ( ( *i ).first.get().size() - startSize + 1 ) ) {
|
|
stack.push( i );
|
|
++i;
|
|
if ( !pre( walker, stack.top() ) ) {
|
|
// skip subgraph
|
|
while ( i != m_instances.end()
|
|
&& stack.size() < ( ( *i ).first.get().size() - startSize + 1 ) )
|
|
{
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
post( walker, stack.top() );
|
|
stack.pop();
|
|
}
|
|
}
|
|
while ( !stack.empty() );
|
|
}
|
|
}
|
|
};
|
|
|
|
namespace
|
|
{
|
|
CompiledGraph* g_sceneGraph;
|
|
GraphTreeModel* g_tree_model;
|
|
}
|
|
|
|
GraphTreeModel* scene_graph_get_tree_model(){
|
|
return g_tree_model;
|
|
}
|
|
|
|
|
|
class SceneGraphObserver : public scene::Instantiable::Observer
|
|
{
|
|
public:
|
|
void insert( scene::Instance* instance ){
|
|
g_sceneGraph->sceneChanged();
|
|
graph_tree_model_insert( g_tree_model, *instance );
|
|
}
|
|
void erase( scene::Instance* instance ){
|
|
g_sceneGraph->sceneChanged();
|
|
graph_tree_model_erase( g_tree_model, *instance );
|
|
}
|
|
};
|
|
|
|
SceneGraphObserver g_SceneGraphObserver;
|
|
|
|
void SceneGraph_Construct(){
|
|
g_tree_model = graph_tree_model_new();
|
|
|
|
g_sceneGraph = new CompiledGraph( &g_SceneGraphObserver );
|
|
}
|
|
|
|
void SceneGraph_Destroy(){
|
|
delete g_sceneGraph;
|
|
|
|
graph_tree_model_delete( g_tree_model );
|
|
}
|
|
|
|
|
|
#include "modulesystem/singletonmodule.h"
|
|
#include "modulesystem/moduleregistry.h"
|
|
|
|
class SceneGraphAPI
|
|
{
|
|
scene::Graph* m_scenegraph;
|
|
public:
|
|
typedef scene::Graph Type;
|
|
STRING_CONSTANT( Name, "*" );
|
|
|
|
SceneGraphAPI(){
|
|
SceneGraph_Construct();
|
|
|
|
m_scenegraph = g_sceneGraph;
|
|
}
|
|
~SceneGraphAPI(){
|
|
SceneGraph_Destroy();
|
|
}
|
|
scene::Graph* getTable(){
|
|
return m_scenegraph;
|
|
}
|
|
};
|
|
|
|
typedef SingletonModule<SceneGraphAPI> SceneGraphModule;
|
|
typedef Static<SceneGraphModule> StaticSceneGraphModule;
|
|
StaticRegisterModule staticRegisterSceneGraph( StaticSceneGraphModule::instance() );
|