diff --git a/modules/gfx/pymod/CMakeLists.txt b/modules/gfx/pymod/CMakeLists.txt
index 386dba45cfc14e32c41471e35407b62c2999d33a..a3d8b251dda72f07f6e7394ad4fb3d0996fb4bd4 100644
--- a/modules/gfx/pymod/CMakeLists.txt
+++ b/modules/gfx/pymod/CMakeLists.txt
@@ -21,7 +21,7 @@ if (ENABLE_IMG)
   set(OST_GFX_PYMOD_SOURCES ${OST_GFX_PYMOD_SOURCES} export_map.cc)
 endif()
 
-pymod(NAME gfx CPP ${OST_GFX_PYMOD_SOURCES} PY __init__.py)
+pymod(NAME gfx CPP ${OST_GFX_PYMOD_SOURCES} PY __init__.py py_gfx_obj.py)
 
 set(GRADIENT_FILE
   gradients.xml
diff --git a/modules/gfx/pymod/__init__.py b/modules/gfx/pymod/__init__.py
index 4d96c534e4e7978849ae4406dc2b828524d860c2..be887c4ab071e3c4f2da12a771e7e1edad6b8b30 100644
--- a/modules/gfx/pymod/__init__.py
+++ b/modules/gfx/pymod/__init__.py
@@ -17,6 +17,7 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 #------------------------------------------------------------------------------
 from _ost_gfx import *
+from py_gfx_obj import PyGfxObj
 
 WHITE=Color(1.0,1.0,1.0)
 BLACK=Color(0.0,0.0,0.0)
diff --git a/modules/gfx/pymod/export_gfx_obj.cc b/modules/gfx/pymod/export_gfx_obj.cc
index 1d2ed62cd446fa5689230ff4eeda73cb5f7d787a..e05fb239c545b17a9ab3f11b7fb92bfadef5c66b 100644
--- a/modules/gfx/pymod/export_gfx_obj.cc
+++ b/modules/gfx/pymod/export_gfx_obj.cc
@@ -58,6 +58,50 @@ namespace {
     LOG_INFO("AALines(bool) is deprecated, use SetAALines(bool) instead");
     b->SetAALines(f);
   }
+
+  class GfxObjWrap: public GfxObj, public wrapper<GfxObj>
+  {
+  public:
+    GfxObjWrap(const std::string& name):
+      GfxObj(name)
+    {}
+
+    virtual void CustomRenderGL(RenderPass pass) {
+      if(override f = this->get_override("_CustomRenderGL")) {
+        f(pass);
+      } else {
+        GfxObj::CustomRenderGL(pass);
+      }
+    }
+
+    void default_CustomRenderGL(RenderPass pass) {
+        GfxObj::CustomRenderGL(pass);
+    }
+
+    virtual void CustomPreRenderGL(bool rebuild) {
+      if(override f = this->get_override("_CustomPreRenderGL")) {
+        f(rebuild);
+      } else {
+        GfxObj::CustomPreRenderGL(rebuild);
+      }
+    }
+
+    void default_CustomPreRenderGL(bool rebuild) {
+        GfxObj::CustomPreRenderGL(rebuild);
+    }
+
+    virtual void InitGL() {
+      if(override f = this->get_override("_InitGL")) {
+        f();
+      } else {
+        GfxObj::InitGL();
+      }
+    }
+
+    void default_InitGL() {
+        GfxObj::InitGL();
+    }
+  };
 }
 
 void export_GfxObj()
@@ -102,9 +146,13 @@ void export_GfxObj()
     .add_property("opacity",&GfxObjBase::GetOpacity,&GfxObjBase::SetOpacity)
     COLOR_BY_DEF()
    ;
-  //register_ptr_to_python<GfxObjBaseP>();
 
-  class_<GfxObj, boost::shared_ptr<GfxObj>, bases<GfxObjBase>, boost::noncopyable>("GfxObj",no_init)
+  enum_<RenderPass>("RenderPass")
+    .value("STANDARD_RENDER_PASS",STANDARD_RENDER_PASS)
+    .value("TRANSPARENT_RENDER_PASS",TRANSPARENT_RENDER_PASS)
+    ;        
+
+  class_<GfxObjWrap, bases<GfxObjBase>, boost::noncopyable>("GfxObj",init<const std::string&>())
     .def("GetTF", &GfxObj::GetTF, return_value_policy<copy_const_reference>())
     .def("SetTF", &GfxObj::SetTF)
     .def("FlagRebuild",&GfxObj::FlagRebuild)
@@ -116,7 +164,9 @@ void export_GfxObj()
     .def("GetAALines",&GfxObj::GetAALines)
     .def("GetLineWidth",&GfxObj::GetLineWidth)
     .def("GetLineHalo",&GfxObj::GetLineHalo)
-    ;
-  //register_ptr_to_python<GfxObjP>();
+    .def("_CustomRenderGL",&GfxObj::CustomRenderGL, &GfxObjWrap::default_CustomRenderGL)
+    .def("_CustomPreRenderGL",&GfxObj::CustomPreRenderGL, &GfxObjWrap::default_CustomPreRenderGL)
+    .def("_InitGL",&GfxObj::InitGL, &GfxObjWrap::default_InitGL)
+    ;    
 
 }
diff --git a/modules/gfx/pymod/py_gfx_obj.py b/modules/gfx/pymod/py_gfx_obj.py
new file mode 100644
index 0000000000000000000000000000000000000000..ac91e9073a47b061ee57145e0d3d7b6b6a51dcc5
--- /dev/null
+++ b/modules/gfx/pymod/py_gfx_obj.py
@@ -0,0 +1,79 @@
+#------------------------------------------------------------------------------
+# This file is part of the OpenStructure project <www.openstructure.org>
+#
+# Copyright (C) 2008-2011 by the OpenStructure authors
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 3.0 of the License, or (at your option)
+# any later version.
+# This library 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 Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+#------------------------------------------------------------------------------
+import traceback
+from _ost_gfx import *
+
+class PyGfxObj(GfxObj):
+  def __init__(self,name):
+    """
+    requires a unique name not yet utilized in the Scene;
+    do not place OpenGL calls in the ctor, use InitGL for
+    that purpose
+    """
+    GfxObj.__init__(self,name)
+    self._valid_flag=False
+
+  def _InitGL(self):
+    try:
+      self.InitGL()
+      self._valid_flag=True
+    except:
+      traceback.print_exc()
+      
+  def InitGL(self):
+    """
+    called once for each OpenGL context (usually one time),
+    allows one-time OpenGL initialization to be implemented,
+    such as vbo allocation
+    """
+    pass
+
+
+  def _CustomPreRenderGL(self,rebuild):
+    if not self._valid_flag:
+      return
+    try:
+      self.CustomPreRenderGL(rebuild)
+    except:
+      self._valid_flag=False
+      traceback.print_exc()
+
+  def CustomPreRenderGL(self,rebuild):
+    """
+    called just before CustomRenderGL is called; the flag
+    indicates that a rebuild is required or was requested
+    """
+    pass
+
+
+  def _CustomRenderGL(self,render_pass):
+    if not self._valid_flag:
+      return
+    try:
+      self.CustomRenderGL(render_pass)
+    except:
+      self._valid_flag=False
+      traceback.print_exc()
+
+  def CustomRenderGL(self,render_pass):
+    """
+    called for each scene refresh
+    """
+    pass
+
diff --git a/modules/gfx/src/gfx_object.cc b/modules/gfx/src/gfx_object.cc
index 10f6f53c2c648e171416fc98925a84cafab02f53..1f0486017c6139adc74134f712d72409486744cd 100644
--- a/modules/gfx/src/gfx_object.cc
+++ b/modules/gfx/src/gfx_object.cc
@@ -181,6 +181,9 @@ void GfxObj::RenderGL(RenderPass pass)
   }
 }
 
+void GfxObj::InitGL()
+{
+}
 
 void GfxObj::RenderPov(PovState& pov)
 {
diff --git a/modules/gfx/src/gfx_object.hh b/modules/gfx/src/gfx_object.hh
index e4b800f3253877d3ae389be666de9c947b25b443..d241be75e19f15158d173554f01384b26cdab15f 100644
--- a/modules/gfx/src/gfx_object.hh
+++ b/modules/gfx/src/gfx_object.hh
@@ -120,6 +120,15 @@ public:
   */
   virtual void CustomRenderGL(RenderPass pass);
 
+  // implemented in derived classes to deal with initialization etc
+  // called just before CustomRenderGL is called
+  // the boolean flag indicated that a re-build was requested
+  virtual void CustomPreRenderGL(bool rebuild);
+
+  // implemented in derived classes for first GL initialization
+  // which should be done here, not in the ctor
+  virtual void InitGL();
+
   // implemented in derived classes for the actual POVray export
   virtual void CustomRenderPov(PovState& pov);
 
@@ -188,7 +197,6 @@ public:
  protected:
   
   void PreRenderGL(bool flag);
-  virtual void CustomPreRenderGL(bool flag);
 
  private:
   GfxObj(const GfxObj& o);
diff --git a/modules/gfx/src/scene.cc b/modules/gfx/src/scene.cc
index bbc615ee5d0ae3a66861509bc6fc7e12a3a74b1c..68ba5111af78c1e62e36d2bbd99d0e1f41e5a7bb 100644
--- a/modules/gfx/src/scene.cc
+++ b/modules/gfx/src/scene.cc
@@ -311,12 +311,18 @@ void Scene::SetBeaconOff()
 
 namespace {
 
-void set_light_dir(Vec3 ld)
-{
-  GLfloat l_pos[]={0.0, 0.0, 0.0, 0.0};
-  l_pos[0]=-ld[0]; l_pos[1]=-ld[1]; l_pos[2]=-ld[2];
-  glLightfv(GL_LIGHT0, GL_POSITION, l_pos);
-}
+  void set_light_dir(Vec3 ld)
+  {
+    GLfloat l_pos[]={0.0, 0.0, 0.0, 0.0};
+    l_pos[0]=-ld[0]; l_pos[1]=-ld[1]; l_pos[2]=-ld[2];
+    glLightfv(GL_LIGHT0, GL_POSITION, l_pos);
+  }
+
+  struct GfxObjInitGL: public GfxNodeVisitor {
+    virtual void VisitObject(GfxObj* o, const Stack& st) {
+      o->InitGL();
+    }
+  };
 
 }
 
@@ -460,6 +466,10 @@ void Scene::InitGL(bool full)
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
   glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, GL_REPLACE);
 
+  LOG_DEBUG("Scene: calling gl init for all objects");
+  GfxObjInitGL initgl;
+  this->Apply(initgl);
+
   LOG_DEBUG("Scene: gl init done");
   gl_init_=true;
   
@@ -778,9 +788,13 @@ void Scene::Add(const GfxNodeP& n, bool redraw)
 
   LOG_DEBUG("Scene: graphical object added @" << n.get() << std::endl);
 
-  if(root_node_->GetChildCount()==0) {
-    GfxObjP go = boost::dynamic_pointer_cast<GfxObj>(n);
-    if(go) {
+  GfxObjP go = boost::dynamic_pointer_cast<GfxObj>(n);
+
+  if(go) {
+    if(gl_init_) {
+      go->InitGL();
+    }
+    if(root_node_->GetChildCount()==0) {
       SetCenter(go->GetCenter());
     }
     do_autoslab_=true;
diff --git a/modules/gfx/tests/test_gfx.py b/modules/gfx/tests/test_gfx.py
index 5214a3dd751302c6aa3d218b5b12fac2f56845bc..362535b28229333910947f350269ef41bcdf5e82 100644
--- a/modules/gfx/tests/test_gfx.py
+++ b/modules/gfx/tests/test_gfx.py
@@ -21,12 +21,26 @@ else:
 def col_delta(c1,c2):
   return geom.Distance(geom.Vec3(c1[0],c1[1],c1[2]),geom.Vec3(c2[0],c2[1],c2[2]))
 
+class MyGfxObj(gfx.GfxObj):
+  def __init__(self,name):
+    gfx.GfxObj.__init__(self,name)
+    self.rendered=False
+    
+  def CustomRenderGL(self,render_pass):
+    self.rendered=True
+
 class TestGfx(unittest.TestCase):
   def runTest(self):
     self.test_gradient()
     self.test_color()
     self.test_primlist()
     self.test_entity_reset()
+    self.test_custom_gfx_obj()
+
+  def test_custom_gfx_obj(self):
+    myobj=MyGfxObj("foo")
+    gfx.Scene().Add(myobj)
+    #self.assertTrue(myobj.rendered)
 
   def test_entity_reset(self):
     eh=mol.CreateEntity()