Revision: 8541 http://playerstage.svn.sourceforge.net/playerstage/?rev=8541&view=rev Author: natepak Date: 2010-01-30 23:03:39 +0000 (Sat, 30 Jan 2010)
Log Message: ----------- Every visual can now set a shader to use(vertex, pixel, normal_map_objectspace, and normal_map_tangetspace Modified Paths: -------------- code/gazebo/trunk/server/AssimpLoader.cc code/gazebo/trunk/server/World.cc code/gazebo/trunk/server/World.hh code/gazebo/trunk/server/gui/MainMenu.cc code/gazebo/trunk/server/gui/MainMenu.hh code/gazebo/trunk/server/rendering/OgreVisual.cc code/gazebo/trunk/server/rendering/OgreVisual.hh code/gazebo/trunk/server/rendering/RTShaderSystem.cc code/gazebo/trunk/server/rendering/RTShaderSystem.hh Modified: code/gazebo/trunk/server/AssimpLoader.cc =================================================================== --- code/gazebo/trunk/server/AssimpLoader.cc 2010-01-30 21:39:24 UTC (rev 8540) +++ code/gazebo/trunk/server/AssimpLoader.cc 2010-01-30 23:03:39 UTC (rev 8541) @@ -198,9 +198,12 @@ subMesh->AddVertex(p.x, p.y, p.z); - p.x = aMesh->mNormals[j].x; - p.y = aMesh->mNormals[j].y; - p.z = aMesh->mNormals[j].z; + if (aMesh->HasNormals()) + { + p.x = aMesh->mNormals[j].x; + p.y = aMesh->mNormals[j].y; + p.z = aMesh->mNormals[j].z; + } subMesh->AddNormal(p.x, p.y, p.z); if (aMesh->mNumUVComponents[0]) Modified: code/gazebo/trunk/server/World.cc =================================================================== --- code/gazebo/trunk/server/World.cc 2010-01-30 21:39:24 UTC (rev 8540) +++ code/gazebo/trunk/server/World.cc 2010-01-30 23:03:39 UTC (rev 8541) @@ -64,7 +64,6 @@ this->showCameras = false; this->wireframe = false; this->showPhysics = false; - this->perPixelLighting = true; this->physicsEngine = NULL; this->server = NULL; this->graphics = NULL; @@ -780,21 +779,6 @@ } //////////////////////////////////////////////////////////////////////////////// -/// Set to use perpixel lighting or pervertex lighting -void World::SetPerPixelLighting( bool pp ) -{ - this->perPixelLighting = pp; - this->perPixelLightingSignal(pp); -} - -//////////////////////////////////////////////////////////////////////////////// -/// Get to use perpixel lighting or pervertex lighting -bool World::GetPerPixelLighting() -{ - return this->perPixelLighting; -} - -//////////////////////////////////////////////////////////////////////////////// // Update the simulation interface void World::UpdateSimulationIface() { Modified: code/gazebo/trunk/server/World.hh =================================================================== --- code/gazebo/trunk/server/World.hh 2010-01-30 21:39:24 UTC (rev 8540) +++ code/gazebo/trunk/server/World.hh 2010-01-30 23:03:39 UTC (rev 8541) @@ -218,12 +218,6 @@ /// \brief Get whether to view as wireframe public: bool GetShowPhysics(); - /// \brief Set to use perpixel lighting or pervertex lighting - public: void SetPerPixelLighting( bool pp ); - - /// \brief Get use perpixel lighting or pervertex lighting - public: bool GetPerPixelLighting(); - /// \brief Goto a position in time public: void GotoTime(double pos); @@ -252,9 +246,7 @@ private: bool wireframe; - private: bool perPixelLighting; - /// \brief Load a model /// \param node Pointer to the XMLConfig node /// \param parent The parent model @@ -325,14 +317,6 @@ { showBoundingBoxesSignal.connect(subscriber); } - - /// \brief Connect a boost::slot the use per pixel lighting signal - public: template<typename T> - void ConnectPerPixelLightingSignal( T subscriber ) - { - perPixelLightingSignal.connect(subscriber); - } - /// \brief Get the names of interfaces defined in the tree of a model private: void GetInterfaceNames(Entity* m, std::vector<std::string>& list); @@ -388,7 +372,6 @@ private: boost::signal<void (bool)> showPhysicsSignal; private: boost::signal<void (bool)> showJointsSignal; private: boost::signal<void (bool)> showBoundingBoxesSignal; - private: boost::signal<void (bool)> perPixelLightingSignal; private: std::deque<WorldState> worldStates; private: std::deque<WorldState>::iterator worldStatesInsertIter; Modified: code/gazebo/trunk/server/gui/MainMenu.cc =================================================================== --- code/gazebo/trunk/server/gui/MainMenu.cc 2010-01-30 21:39:24 UTC (rev 8540) +++ code/gazebo/trunk/server/gui/MainMenu.cc 2010-01-30 23:03:39 UTC (rev 8541) @@ -65,7 +65,6 @@ { "Show Contacts", 0, &gazebo::MainMenu::ShowContactsCB,0, FL_MENU_TOGGLE, FL_NORMAL_LABEL, 0, 14, 0}, { "Show Lights", 0, &gazebo::MainMenu::ShowLightsCB,0, FL_MENU_TOGGLE, FL_NORMAL_LABEL, 0, 14, 0}, { "Show Cameras", 0, &gazebo::MainMenu::ShowCamerasCB,0, FL_MENU_TOGGLE, FL_NORMAL_LABEL, 0, 14, 0}, - { "Per-Pixel Lighting", 0, &gazebo::MainMenu::PerPixelLightingCB,0, FL_MENU_TOGGLE|FL_MENU_VALUE, FL_NORMAL_LABEL, 0, 14, 0}, { 0 }, { 0 } @@ -176,10 +175,3 @@ { World::Instance()->SetShowCameras( !World::Instance()->GetShowCameras() ); } - -//////////////////////////////////////////////////////////////////////////////// -// Use per-pixel lighting -void MainMenu::PerPixelLightingCB(Fl_Widget * /*w*/, void * /*data*/) -{ - World::Instance()->SetPerPixelLighting( !World::Instance()->GetPerPixelLighting() ); -} Modified: code/gazebo/trunk/server/gui/MainMenu.hh =================================================================== --- code/gazebo/trunk/server/gui/MainMenu.hh 2010-01-30 21:39:24 UTC (rev 8540) +++ code/gazebo/trunk/server/gui/MainMenu.hh 2010-01-30 23:03:39 UTC (rev 8541) @@ -61,8 +61,6 @@ public: static void ShowCamerasCB(Fl_Widget * /*w*/, void * /*data*/); - public: static void PerPixelLightingCB(Fl_Widget * /*w*/, void * /*data*/); - }; } Modified: code/gazebo/trunk/server/rendering/OgreVisual.cc =================================================================== --- code/gazebo/trunk/server/rendering/OgreVisual.cc 2010-01-30 21:39:24 UTC (rev 8540) +++ code/gazebo/trunk/server/rendering/OgreVisual.cc 2010-01-30 23:03:39 UTC (rev 8541) @@ -67,6 +67,8 @@ this->visible = true; this->ConstructorHelper(pnode, isStatic); + + RTShaderSystem::Instance()->AttachEntity(this); } //////////////////////////////////////////////////////////////////////////////// @@ -107,6 +109,13 @@ this->scaleP = new ParamT<Vector3>("scale", Vector3(1,1,1), 0); this->sizeP = new ParamT<Vector3>("size", Vector3(1,1,1), 0); + + this->normalMapNameP = new ParamT<std::string>("normalMap", + std::string("none"),0); + this->normalMapNameP->Callback( &OgreVisual::SetNormalMap, this ); + + this->shaderP = new ParamT<std::string>("shader", std::string("pixel"),0); + this->shaderP->Callback( &OgreVisual::SetShader, this ); Param::End(); this->boundingBoxNode = NULL; @@ -160,9 +169,7 @@ this->sceneNode->removeAndDestroyAllChildren(); */ - for (int i=0; i < this->sceneNode->numAttachedObjects(); i++) - RTShaderSystem::Instance()->DetachEntity( - (Ogre::Entity*)(this->sceneNode->getAttachedObject(i)) ); + RTShaderSystem::Instance()->DetachEntity(this); if (this->sceneNode) OgreAdaptor::Instance()->sceneMgr->destroySceneNode(this->sceneNode); @@ -189,6 +196,8 @@ this->meshTileP->Load(node); this->materialNameP->Load(node); this->castShadowsP->Load(node); + this->shaderP->Load(node); + this->normalMapNameP->Load(node); // Read the desired position and rotation of the mesh pose.pos = this->xyzP->GetValue(); @@ -306,9 +315,7 @@ return; this->sceneNode->attachObject(obj); - Ogre::Entity *ent = dynamic_cast<Ogre::Entity*>(obj); - if (ent) - RTShaderSystem::Instance()->AttachEntity(ent); + RTShaderSystem::Instance()->UpdateShaders(); obj->setUserAny( Ogre::Any(this) ); } @@ -958,3 +965,33 @@ { this->sceneNode->setAutoTracking(false); } + +//////////////////////////////////////////////////////////////////////////////// +/// Get the normal map +std::string OgreVisual::GetNormalMap() const +{ + return (**this->normalMapNameP); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set the normal map +void OgreVisual::SetNormalMap(const std::string &nmap) +{ + this->normalMapNameP->SetValue(nmap); + RTShaderSystem::Instance()->UpdateShaders(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Get the shader +std::string OgreVisual::GetShader() const +{ + return (**this->shaderP); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set the shader +void OgreVisual::SetShader(const std::string &shader) +{ + this->shaderP->SetValue(shader); + RTShaderSystem::Instance()->UpdateShaders(); +} Modified: code/gazebo/trunk/server/rendering/OgreVisual.hh =================================================================== --- code/gazebo/trunk/server/rendering/OgreVisual.hh 2010-01-30 21:39:24 UTC (rev 8540) +++ code/gazebo/trunk/server/rendering/OgreVisual.hh 2010-01-30 23:03:39 UTC (rev 8541) @@ -163,6 +163,18 @@ /// \brief Disable tracking of a visual public: void DisableTrackVisual(); + /// \brief Get the normal map + public: std::string GetNormalMap() const; + + /// \brief Set the normal map + public: void SetNormalMap(const std::string &nmap); + + /// \brief Get the shader + public: std::string GetShader() const; + + /// \brief Set the shader + public: void SetShader(const std::string &shader); + private: Ogre::MaterialPtr origMaterial; private: Ogre::MaterialPtr myMaterial; private: std::string myMaterialName; @@ -170,7 +182,7 @@ private: Ogre::SceneBlendType sceneBlendType; private: Ogre::SceneNode *parentNode; - private: Ogre::SceneNode *sceneNode; + public: Ogre::SceneNode *sceneNode; private: Ogre::SceneNode *boundingBoxNode; private: float transparency; @@ -183,6 +195,8 @@ private: ParamT<Quatern> *rpyP; private: ParamT<std::string> *meshNameP; private: ParamT<std::string> *materialNameP; + private: ParamT<std::string> *normalMapNameP; + private: ParamT<std::string> *shaderP; private: ParamT<bool> *castShadowsP; private: ParamT<Vector3> *sizeP; private: ParamT<Vector3> *scaleP; Modified: code/gazebo/trunk/server/rendering/RTShaderSystem.cc =================================================================== --- code/gazebo/trunk/server/rendering/RTShaderSystem.cc 2010-01-30 21:39:24 UTC (rev 8540) +++ code/gazebo/trunk/server/rendering/RTShaderSystem.cc 2010-01-30 23:03:39 UTC (rev 8541) @@ -25,6 +25,8 @@ */ #include <boost/bind.hpp> + +#include "OgreVisual.hh" #include "World.hh" #include "GazeboError.hh" #include "GazeboMessage.hh" @@ -37,11 +39,6 @@ /// Constructor RTShaderSystem::RTShaderSystem() { -#if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR >= 7 - World::Instance()->ConnectPerPixelLightingSignal( boost::bind(&RTShaderSystem::SetPerPixelLighting, this, _1) ); - this->curLightingModel = RTShaderSystem::SSLM_PerPixelLighting; - //this->curLightingModel = RTShaderSystem::SSLM_NormalMapLightingObjectSpace; -#endif } //////////////////////////////////////////////////////////////////////////////// @@ -154,136 +151,123 @@ //////////////////////////////////////////////////////////////////////////////// // Set an Ogre::Entity to use RT shaders -void RTShaderSystem::AttachEntity(Ogre::Entity *entity) +void RTShaderSystem::AttachEntity(OgreVisual *vis) { #if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR >= 7 - this->GenerateShaders(entity); - this->entities.push_back(entity); + this->GenerateShaders(vis); + this->entities.push_back(vis); #endif } //////////////////////////////////////////////////////////////////////////////// // Remove and entity -void RTShaderSystem::DetachEntity(Ogre::Entity *entity) +void RTShaderSystem::DetachEntity(OgreVisual *vis) { #if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR >= 7 - this->entities.remove(entity); + this->entities.remove(vis); #endif } //////////////////////////////////////////////////////////////////////////////// -/// Set the lighting model -void RTShaderSystem::SetLightingModel(LightingModel model) +/// Update the shaders +void RTShaderSystem::UpdateShaders() { #if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR >= 7 - if (model == this->curLightingModel) - return; + std::list<OgreVisual*>::iterator iter; - this->curLightingModel = model; - - std::list<Ogre::Entity*>::iterator iter; - // Update all the shaders for (iter = this->entities.begin(); iter != this->entities.end(); iter++) - { this->GenerateShaders(*iter); - } #endif } //////////////////////////////////////////////////////////////////////////////// -// Set the lighting model to per pixel or per vertex -void RTShaderSystem::SetPerPixelLighting( bool s) -{ - if (s) - this->SetLightingModel( SSLM_PerPixelLighting ); - else - this->SetLightingModel( SSLM_PerVertexLighting ); -} - -//////////////////////////////////////////////////////////////////////////////// /// Generate shaders for an entity -void RTShaderSystem::GenerateShaders(Ogre::Entity *entity) +void RTShaderSystem::GenerateShaders(OgreVisual *vis) { #if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR >= 7 - for (unsigned int i=0; i < entity->getNumSubEntities(); ++i) + for (unsigned int k=0; k < vis->sceneNode->numAttachedObjects(); k++) { - Ogre::SubEntity* curSubEntity = entity->getSubEntity(i); - const Ogre::String& curMaterialName = curSubEntity->getMaterialName(); - bool success; + Ogre::MovableObject *obj = vis->sceneNode->getAttachedObject(k); + Ogre::Entity *entity = dynamic_cast<Ogre::Entity*>(obj); + if (!entity) + continue; - // Create the shader based technique of this material. - success = this->shaderGenerator->createShaderBasedTechnique(curMaterialName, - Ogre::MaterialManager::DEFAULT_SCHEME_NAME, - Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME); - - // Setup custom shader sub render states according to current setup. - if (success) + for (unsigned int i=0; i < entity->getNumSubEntities(); ++i) { - Ogre::MaterialPtr curMaterial = - Ogre::MaterialManager::getSingleton().getByName(curMaterialName); + Ogre::SubEntity* curSubEntity = entity->getSubEntity(i); + const Ogre::String& curMaterialName = curSubEntity->getMaterialName(); + bool success; - Ogre::Pass* curPass = curMaterial->getTechnique(0)->getPass(0); + // Create the shader based technique of this material. + success = this->shaderGenerator->createShaderBasedTechnique(curMaterialName, + Ogre::MaterialManager::DEFAULT_SCHEME_NAME, + Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME); - // Grab the first pass render state. - // NOTE: For more complicated samples iterate over the passes and build - // each one of them as desired. - Ogre::RTShader::RenderState* renderState = this->shaderGenerator->getRenderState(Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME, curMaterialName, 0); + // Setup custom shader sub render states according to current setup. + if (success) + { + Ogre::MaterialPtr curMaterial = + Ogre::MaterialManager::getSingleton().getByName(curMaterialName); - // Remove all sub render states. - renderState->reset(); + Ogre::Pass* curPass = curMaterial->getTechnique(0)->getPass(0); + // Grab the first pass render state. + // NOTE: For more complicated samples iterate over the passes and build + // each one of them as desired. + Ogre::RTShader::RenderState* renderState = this->shaderGenerator->getRenderState(Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME, curMaterialName, 0); + + // Remove all sub render states. + renderState->reset(); + #ifdef RTSHADER_SYSTEM_BUILD_CORE_SHADERS - if (this->curLightingModel == SSLM_PerVertexLighting) - { - Ogre::RTShader::SubRenderState* perPerVertexLightModel = this->shaderGenerator->createSubRenderState(Ogre::RTShader::FFPLighting::Type); + if (vis->GetShader() == "vertex") + { + Ogre::RTShader::SubRenderState* perPerVertexLightModel = this->shaderGenerator->createSubRenderState(Ogre::RTShader::FFPLighting::Type); - renderState->addSubRenderState(perPerVertexLightModel); - } + renderState->addSubRenderState(perPerVertexLightModel); + } #endif #ifdef RTSHADER_SYSTEM_BUILD_EXT_SHADERS - else if (this->curLightingModel == SSLM_PerPixelLighting) - { - Ogre::RTShader::SubRenderState* perPixelLightModel = this->shaderGenerator->createSubRenderState(Ogre::RTShader::PerPixelLighting::Type); + else if (vis->GetShader() == "pixel") + { + Ogre::RTShader::SubRenderState* perPixelLightModel = this->shaderGenerator->createSubRenderState(Ogre::RTShader::PerPixelLighting::Type); - renderState->addSubRenderState(perPixelLightModel); - } + renderState->addSubRenderState(perPixelLightModel); + } - else if (this->curLightingModel == SSLM_NormalMapLightingObjectSpace) - { - Ogre::RTShader::SubRenderState* subRenderState = this->shaderGenerator->createSubRenderState(Ogre::RTShader::NormalMapLighting::Type); - Ogre::RTShader::NormalMapLighting* normalMapSubRS = static_cast<Ogre::RTShader::NormalMapLighting*>(subRenderState); - Ogre::UserObjectBindings* rtssBindings = Ogre::any_cast<Ogre::UserObjectBindings*>(curPass->getUserObjectBindings().getUserAny(Ogre::RTShader::ShaderGenerator::BINDING_OBJECT_KEY)); + else if (vis->GetShader() == "normal_map_objectspace") + { + Ogre::RTShader::SubRenderState* subRenderState = this->shaderGenerator->createSubRenderState(Ogre::RTShader::NormalMapLighting::Type); + Ogre::RTShader::NormalMapLighting* normalMapSubRS = static_cast<Ogre::RTShader::NormalMapLighting*>(subRenderState); + Ogre::UserObjectBindings* rtssBindings = Ogre::any_cast<Ogre::UserObjectBindings*>(curPass->getUserObjectBindings().getUserAny(Ogre::RTShader::ShaderGenerator::BINDING_OBJECT_KEY)); - normalMapSubRS->setNormalMapSpace(Ogre::RTShader::NormalMapLighting::NMS_OBJECT); - rtssBindings->setUserAny(Ogre::RTShader::NormalMapLighting::NormalMapTextureNameKey, Ogre::Any(Ogre::String("skin.tga"))); - renderState->addSubRenderState(normalMapSubRS); - } + normalMapSubRS->setNormalMapSpace(Ogre::RTShader::NormalMapLighting::NMS_OBJECT); + rtssBindings->setUserAny(Ogre::RTShader::NormalMapLighting::NormalMapTextureNameKey, Ogre::Any(Ogre::String(vis->GetNormalMap()))); + renderState->addSubRenderState(normalMapSubRS); + } + else if (vis->GetShader() == "normal_map_tangetspace") + { + Ogre::RTShader::SubRenderState* subRenderState = this->shaderGenerator->createSubRenderState(Ogre::RTShader::NormalMapLighting::Type); + Ogre::RTShader::NormalMapLighting* normalMapSubRS = static_cast<Ogre::RTShader::NormalMapLighting*>(subRenderState); + Ogre::UserObjectBindings* rtssBindings = Ogre::any_cast<Ogre::UserObjectBindings*>(curPass->getUserObjectBindings().getUserAny(Ogre::RTShader::ShaderGenerator::BINDING_OBJECT_KEY)); - else if (this->curLightingModel == SSLM_NormalMapLightingTangentSpace) - { - Ogre::RTShader::SubRenderState* subRenderState = this->shaderGenerator->createSubRenderState(Ogre::RTShader::NormalMapLighting::Type); - Ogre::RTShader::NormalMapLighting* normalMapSubRS = static_cast<Ogre::RTShader::NormalMapLighting*>(subRenderState); - Ogre::UserObjectBindings* rtssBindings = Ogre::any_cast<Ogre::UserObjectBindings*>(curPass->getUserObjectBindings().getUserAny(Ogre::RTShader::ShaderGenerator::BINDING_OBJECT_KEY)); + normalMapSubRS->setNormalMapSpace(Ogre::RTShader::NormalMapLighting::NMS_TANGENT); + rtssBindings->setUserAny(Ogre::RTShader::NormalMapLighting::NormalMapTextureNameKey, Ogre::Any(Ogre::String(vis->GetNormalMap()))); - normalMapSubRS->setNormalMapSpace(Ogre::RTShader::NormalMapLighting::NMS_TANGENT); - rtssBindings->setUserAny(Ogre::RTShader::NormalMapLighting::NormalMapTextureNameKey, Ogre::Any(Ogre::String("skin.tga"))); + renderState->addSubRenderState(normalMapSubRS); + } +#endif - renderState->addSubRenderState(normalMapSubRS); } -#endif - } - // Invalidate this material in order to re-generate its shaders. - this->shaderGenerator->invalidateMaterial(Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME, curMaterialName); + // Invalidate this material in order to re-generate its shaders. + this->shaderGenerator->invalidateMaterial(Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME, curMaterialName); + } } - - #endif } - - Modified: code/gazebo/trunk/server/rendering/RTShaderSystem.hh =================================================================== --- code/gazebo/trunk/server/rendering/RTShaderSystem.hh 2010-01-30 21:39:24 UTC (rev 8540) +++ code/gazebo/trunk/server/rendering/RTShaderSystem.hh 2010-01-30 23:03:39 UTC (rev 8541) @@ -40,6 +40,7 @@ namespace gazebo { class ShaderGeneratorTechniqueResolverListener; + class OgreVisual; class RTShaderSystem : public SingletonT<RTShaderSystem> { @@ -60,14 +61,14 @@ /// \brief Init the run time shader system public: void Init(); - /// \brief Set the lighting model - public: void SetLightingModel(LightingModel model); + /// \brief Update the shaders + public: void UpdateShaders(); /// \brief Set an Ogre::Entity to use RT shaders - public: void AttachEntity(Ogre::Entity *entity); + public: void AttachEntity(OgreVisual *vis); /// \brief Remove and entity - public: void DetachEntity(Ogre::Entity *entity); + public: void DetachEntity(OgreVisual *vis); /// \brief Set a viewport to use shaders public: static void AttachViewport(Ogre::Viewport *vp) @@ -82,14 +83,13 @@ public: void SetPerPixelLighting( bool s); /// \brief Generate shaders for an entity - private: void GenerateShaders(Ogre::Entity *entity); + private: void GenerateShaders(OgreVisual *vis); #if OGRE_VERSION_MAJOR == 1 && OGRE_VERSION_MINOR >= 7 private: Ogre::RTShader::ShaderGenerator *shaderGenerator; private: ShaderGeneratorTechniqueResolverListener *materialMgrListener; - private: LightingModel curLightingModel; - private: std::list<Ogre::Entity*> entities; + private: std::list<OgreVisual*> entities; #endif private: friend class DestroyerT<RTShaderSystem>; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ The Planet: dedicated and managed hosting, cloud storage, colocation Stay online with enterprise data centers and the best network in the business Choose flexible plans and management services without long-term contracts Personal 24x7 support from experience hosting pros just a phone call away. http://p.sf.net/sfu/theplanet-com _______________________________________________ Playerstage-commit mailing list Playerstage-commit@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/playerstage-commit