Browse Source

tinydisplay

David Rose 17 years ago
parent
commit
6d90cf5b70

+ 6 - 0
dtool/Config.pp

@@ -616,6 +616,12 @@
 // Similar to MIN_GL_VERSION, above.
 // Similar to MIN_GL_VERSION, above.
 #define MIN_MESA_VERSION 1 1
 #define MIN_MESA_VERSION 1 1
 
 
+// Experimental: TinySDGL, a variant on TinyGL ported to SDL.  Used
+// for fast but cheesy software rendering.
+#define TINYSDGL_IPATH
+#define TINYSDGL_LPATH
+#define TINYSDGL_LIBS TinySDGL TinyGLU SDL
+#defer HAVE_TINYSDGL $[libtest $[TINYSDGL_LPATH],$[TINYSDGL_LIBS]]
 
 
 // Is the Chromium remote-rendering library installed, and where?
 // Is the Chromium remote-rendering library installed, and where?
 // This should include libcr_opengl32.
 // This should include libcr_opengl32.

+ 7 - 0
dtool/pptempl/Global.pp

@@ -127,6 +127,13 @@
   #define gl_framework $[GL_FRAMEWORK]
   #define gl_framework $[GL_FRAMEWORK]
 #endif
 #endif
 
 
+#if $[HAVE_TINYSDGL]
+  #define tinysdgl_ipath $[wildcard $[TINYSDGL_IPATH]]
+  #define tinysdgl_lpath $[wildcard $[TINYSDGL_LPATH]]
+  #define tinysdgl_cflags $[TINYSDGL_CFLAGS]
+  #define tinysdgl_libs $[TINYSDGL_LIBS]
+#endif
+
 #if $[HAVE_MESA]
 #if $[HAVE_MESA]
   #define mesa_ipath $[wildcard $[MESA_IPATH]]
   #define mesa_ipath $[wildcard $[MESA_IPATH]]
   #define mesa_lpath $[wildcard $[MESA_LPATH]]
   #define mesa_lpath $[wildcard $[MESA_LPATH]]

+ 1 - 1
panda/src/display/graphicsStateGuardian.cxx

@@ -688,7 +688,7 @@ get_geom_munger(const RenderState *state, Thread *current_thread) {
 
 
   // Nothing in the map; create a new entry.
   // Nothing in the map; create a new entry.
   PT(GeomMunger) munger = make_geom_munger(nc_state, current_thread);
   PT(GeomMunger) munger = make_geom_munger(nc_state, current_thread);
-  nassertr(munger->is_registered(), munger);
+  nassertr(munger != (GeomMunger *)NULL && munger->is_registered(), munger);
 
 
   mi = nc_state->_mungers.insert(RenderState::Mungers::value_type(this, munger)).first;
   mi = nc_state->_mungers.insert(RenderState::Mungers::value_type(this, munger)).first;
   nc_state->_last_mi = mi;
   nc_state->_last_mi = mi;

+ 1 - 1
panda/src/glstuff/glGeomMunger_src.cxx

@@ -27,7 +27,7 @@ ALLOC_DELETED_CHAIN_DEF(CLP(GeomMunger));
 //       Access: Public
 //       Access: Public
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE CLP(GeomMunger)::
+CLP(GeomMunger)::
 CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) :
 CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) :
   StandardMunger(gsg, state, 4, NT_uint8, C_color),
   StandardMunger(gsg, state, 4, NT_uint8, C_color),
   _texture(state->get_texture()),
   _texture(state->get_texture()),

+ 34 - 0
panda/src/tinydisplay/Sources.pp

@@ -0,0 +1,34 @@
+#define BUILD_DIRECTORY $[HAVE_TINYSDGL]
+//#define BUILDING_DLL BUILDING_PANDATINYSDGL
+
+#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
+                   dtoolutil:c dtoolbase:c dtool:m
+#define USE_PACKAGES tinysdgl
+
+#begin lib_target
+  #define TARGET tinydisplay
+  #define LOCAL_LIBS \
+    gsgmisc gsgbase gobj display \
+    putil linmath mathutil pnmimage
+
+  #define SOURCES \
+    config_tinydisplay.cxx config_tinydisplay.h \
+    tinyGeomMunger.I tinyGeomMunger.cxx tinyGeomMunger.h \
+    tinyGraphicsPipe.I tinyGraphicsPipe.cxx tinyGraphicsPipe.h \
+    tinyGraphicsWindow.h tinyGraphicsWindow.I tinyGraphicsWindow.cxx \
+    tinyGraphicsStateGuardian.h tinyGraphicsStateGuardian.I \
+    tinyGraphicsStateGuardian.cxx \
+    tinyImmediateModeSender.h tinyImmediateModeSender.I \
+    tinyImmediateModeSender.cxx \
+    tinyTextureContext.I tinyTextureContext.cxx tinyTextureContext.h 
+
+  #define INSTALL_HEADERS \
+    tinyGeomMunger.I tinyGeomMunger.h \
+    tinyGraphicsPipe.I tinyGraphicsPipe.h \
+    tinyGraphicsWindow.I tinyGraphicsWindow.h \
+    tinyGraphicsStateGuardian.h tinyGraphicsStateGuardian.I \
+    tinyImmediateModeSender.h tinyImmediateModeSender.I \
+    tinyTextureContext.I tinyTextureContext.h 
+
+#end lib_target
+

+ 64 - 0
panda/src/tinydisplay/config_tinydisplay.cxx

@@ -0,0 +1,64 @@
+// Filename: config_tinydisplay.cxx
+// Created by:  drose (24Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_tinydisplay.h"
+#include "tinyGraphicsPipe.h"
+#include "tinyGraphicsWindow.h"
+#include "tinyGraphicsStateGuardian.h"
+#include "tinyGeomMunger.h"
+#include "tinyTextureContext.h"
+#include "graphicsPipeSelection.h"
+#include "dconfig.h"
+#include "pandaSystem.h"
+
+Configure(config_tinydisplay);
+NotifyCategoryDef(tinydisplay, "display");
+
+ConfigureFn(config_tinydisplay) {
+  init_libtinydisplay();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: init_libtinydisplay
+//  Description: Initializes the library.  This must be called at
+//               least once before any of the functions or classes in
+//               this library can be used.  Normally it will be
+//               called by the static initializers and need not be
+//               called explicitly, but special cases exist.
+////////////////////////////////////////////////////////////////////
+void
+init_libtinydisplay() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+
+  TinyGraphicsPipe::init_type();
+  TinyGraphicsWindow::init_type();
+  TinyGraphicsStateGuardian::init_type();
+  TinyGeomMunger::init_type();
+  TinyTextureContext::init_type();
+
+  GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
+  selection->add_pipe_type(TinyGraphicsPipe::get_class_type(),
+                           TinyGraphicsPipe::pipe_constructor);
+
+  PandaSystem *ps = PandaSystem::get_global_ptr();
+  ps->add_system("TinyGL");
+}

+ 29 - 0
panda/src/tinydisplay/config_tinydisplay.h

@@ -0,0 +1,29 @@
+// Filename: config_tinydisplay.h
+// Created by:  drose (24Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIG_TINYDISPLAY_H
+#define CONFIG_TINYDISPLAY_H
+
+#include "pandabase.h"
+#include "notifyCategoryProxy.h"
+
+NotifyCategoryDecl(tinydisplay, EXPCL_PANDAGL, EXPTP_PANDAGL);
+
+extern EXPCL_PANDAGL void init_libtinydisplay();
+
+#endif

+ 18 - 0
panda/src/tinydisplay/tinyGeomMunger.I

@@ -0,0 +1,18 @@
+// Filename: tinyGeomMunger.I
+// Created by:  drose (29Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 118 - 0
panda/src/tinydisplay/tinyGeomMunger.cxx

@@ -0,0 +1,118 @@
+// Filename: tinyGeomMunger.cxx
+// Created by:  drose (29Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "tinyGeomMunger.h"
+#include "dcast.h"
+
+TypeHandle TinyGeomMunger::_type_handle;
+
+ALLOC_DELETED_CHAIN_DEF(TinyGeomMunger);
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGeomMunger::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+TinyGeomMunger::
+TinyGeomMunger(GraphicsStateGuardian *gsg, const RenderState *state) :
+  StandardMunger(gsg, state, 4, NT_uint8, C_color)
+{
+  // The TinyGSG can apply the color and color scale at runtime.
+  //  _munge_color = false;
+  //  _munge_color_scale = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGeomMunger::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TinyGeomMunger::
+~TinyGeomMunger() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGeomMunger::munge_format_impl
+//       Access: Protected, Virtual
+//  Description: Given a source GeomVertexFormat, converts it if
+//               necessary to the appropriate format for rendering.
+////////////////////////////////////////////////////////////////////
+CPT(GeomVertexFormat) TinyGeomMunger::
+munge_format_impl(const GeomVertexFormat *orig,
+                  const GeomVertexAnimationSpec &animation) {
+  PT(GeomVertexFormat) new_format = new GeomVertexFormat(*orig);
+  new_format->set_animation(animation);
+
+  /*
+  const GeomVertexColumn *color_type = orig->get_color_column();
+  if (color_type != (GeomVertexColumn *)NULL &&
+      color_type->get_numeric_type() == NT_packed_dabc) {
+    // We need to convert the color format; OpenGL doesn't support the
+    // byte order of DirectX's packed ARGB format.
+    int color_array = orig->get_array_with(InternalName::get_color());
+
+    PT(GeomVertexArrayFormat) new_array_format = new_format->modify_array(color_array);
+
+    // Replace the existing color format with the new format.
+    new_array_format->add_column
+      (InternalName::get_color(), 4, NT_uint8,
+       C_color, color_type->get_start());
+  }
+  */
+
+  CPT(GeomVertexFormat) format = GeomVertexFormat::register_format(new_format);
+
+  return format;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGeomMunger::premunge_format_impl
+//       Access: Protected, Virtual
+//  Description: Given a source GeomVertexFormat, converts it if
+//               necessary to the appropriate format for rendering.
+////////////////////////////////////////////////////////////////////
+CPT(GeomVertexFormat) TinyGeomMunger::
+premunge_format_impl(const GeomVertexFormat *orig) {
+  return orig;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGeomMunger::compare_to_impl
+//       Access: Protected, Virtual
+//  Description: Called to compare two GeomMungers who are known to be
+//               of the same type, for an apples-to-apples comparison.
+//               This will never be called on two pointers of a
+//               different type.
+////////////////////////////////////////////////////////////////////
+int TinyGeomMunger::
+compare_to_impl(const GeomMunger *other) const {
+  return StandardMunger::compare_to_impl(other);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGeomMunger::geom_compare_to_impl
+//       Access: Protected, Virtual
+//  Description: Called to compare two GeomMungers who are known to be
+//               of the same type, for an apples-to-apples comparison.
+//               This will never be called on two pointers of a
+//               different type.
+////////////////////////////////////////////////////////////////////
+int TinyGeomMunger::
+geom_compare_to_impl(const GeomMunger *other) const {
+  return StandardMunger::compare_to_impl(other);
+}

+ 67 - 0
panda/src/tinydisplay/tinyGeomMunger.h

@@ -0,0 +1,67 @@
+// Filename: tinyGeomMunger.h
+// Created by:  drose (29Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef TINYGEOMMUNGER_H
+#define TINYGEOMMUNGER_H
+
+#include "pandabase.h"
+#include "standardMunger.h"
+#include "graphicsStateGuardian.h"
+#include "renderState.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : TinyGeomMunger
+// Description : This specialization on GeomMunger finesses vertices
+//               for TinyGL rendering.  In particular, it makes sure
+//               colors aren't stored in DirectX's packed_argb format.
+////////////////////////////////////////////////////////////////////
+class TinyGeomMunger : public StandardMunger {
+public:
+  TinyGeomMunger(GraphicsStateGuardian *gsg, const RenderState *state);
+  virtual ~TinyGeomMunger();
+  ALLOC_DELETED_CHAIN_DECL(TinyGeomMunger);
+
+protected:
+  virtual CPT(GeomVertexFormat) munge_format_impl(const GeomVertexFormat *orig,
+                                                  const GeomVertexAnimationSpec &animation);
+  virtual CPT(GeomVertexFormat) premunge_format_impl(const GeomVertexFormat *orig);
+
+  virtual int compare_to_impl(const GeomMunger *other) const;
+  virtual int geom_compare_to_impl(const GeomMunger *other) const;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    StandardMunger::init_type();
+    register_type(_type_handle, "TinyGeomMunger",
+                  StandardMunger::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "tinyGeomMunger.I"
+
+#endif

+ 18 - 0
panda/src/tinydisplay/tinyGraphicsPipe.I

@@ -0,0 +1,18 @@
+// Filename: tinyGraphicsPipe.I
+// Created by:  drose (24Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 125 - 0
panda/src/tinydisplay/tinyGraphicsPipe.cxx

@@ -0,0 +1,125 @@
+// Filename: tinyGraphicsPipe.cxx
+// Created by:  drose (24Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "tinyGraphicsPipe.h"
+#include "tinyGraphicsWindow.h"
+#include "tinyGraphicsStateGuardian.h"
+#include "config_tinydisplay.h"
+#include "frameBufferProperties.h"
+
+TypeHandle TinyGraphicsPipe::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsPipe::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TinyGraphicsPipe::
+TinyGraphicsPipe() {
+  _is_valid = true;
+
+  if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+    tinydisplay_cat.error()
+      << "Cannot initialize SDL video.\n";
+    _is_valid = false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsPipe::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TinyGraphicsPipe::
+~TinyGraphicsPipe() {
+  if (SDL_WasInit(SDL_INIT_VIDEO)) {
+    SDL_QuitSubSystem(SDL_INIT_VIDEO);
+  }
+
+  SDL_Quit();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsPipe::get_interface_name
+//       Access: Published, Virtual
+//  Description: Returns the name of the rendering interface
+//               associated with this GraphicsPipe.  This is used to
+//               present to the user to allow him/her to choose
+//               between several possible GraphicsPipes available on a
+//               particular platform, so the name should be meaningful
+//               and unique for a given platform.
+////////////////////////////////////////////////////////////////////
+string TinyGraphicsPipe::
+get_interface_name() const {
+  return "TinyGL";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsPipe::pipe_constructor
+//       Access: Public, Static
+//  Description: This function is passed to the GraphicsPipeSelection
+//               object to allow the user to make a default
+//               TinyGraphicsPipe.
+////////////////////////////////////////////////////////////////////
+PT(GraphicsPipe) TinyGraphicsPipe::
+pipe_constructor() {
+  return new TinyGraphicsPipe;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsPipe::make_output
+//       Access: Protected, Virtual
+//  Description: Creates a new window on the pipe, if possible.
+////////////////////////////////////////////////////////////////////
+PT(GraphicsOutput) TinyGraphicsPipe::
+make_output(const string &name,
+            const FrameBufferProperties &fb_prop,
+            const WindowProperties &win_prop,
+            int flags,
+            GraphicsStateGuardian *gsg,
+            GraphicsOutput *host,
+            int retry,
+            bool &precertify) {
+  if (!_is_valid) {
+    return NULL;
+  }
+
+  TinyGraphicsStateGuardian *glxgsg = 0;
+  if (gsg != 0) {
+    DCAST_INTO_R(glxgsg, gsg, NULL);
+  }
+
+  // First thing to try: a TinyGraphicsWindow
+
+  if (retry == 0) {
+    if (((flags&BF_require_parasite)!=0)||
+        ((flags&BF_refuse_window)!=0)||
+        ((flags&BF_resizeable)!=0)||
+        ((flags&BF_size_track_host)!=0)||
+        ((flags&BF_rtt_cumulative)!=0)||
+        ((flags&BF_can_bind_color)!=0)||
+        ((flags&BF_can_bind_every)!=0)) {
+      return NULL;
+    }
+    return new TinyGraphicsWindow(this, name, fb_prop, win_prop,
+                                  flags, gsg, host);
+  }
+  
+  // Nothing else left to try.
+  return NULL;
+}

+ 71 - 0
panda/src/tinydisplay/tinyGraphicsPipe.h

@@ -0,0 +1,71 @@
+// Filename: tinyGraphicsPipe.h
+// Created by:  drose (24Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef TINYGRAPHICSPIPE_H
+#define TINYGRAPHICSPIPE_H
+
+#include "pandabase.h"
+#include "graphicsWindow.h"
+#include "graphicsPipe.h"
+
+class FrameBufferProperties;
+
+////////////////////////////////////////////////////////////////////
+//       Class : TinyGraphicsPipe
+// Description : This graphics pipe represents the interface for
+//               to TinySDGL (an implementation of TinyGL over SDL).
+////////////////////////////////////////////////////////////////////
+class TinyGraphicsPipe : public GraphicsPipe {
+public:
+  TinyGraphicsPipe();
+  virtual ~TinyGraphicsPipe();
+
+  virtual string get_interface_name() const;
+  static PT(GraphicsPipe) pipe_constructor();
+
+protected:
+  virtual PT(GraphicsOutput) make_output(const string &name,
+                                         const FrameBufferProperties &fb_prop,
+                                         const WindowProperties &win_prop,
+                                         int flags,
+                                         GraphicsStateGuardian *gsg,
+                                         GraphicsOutput *host,
+                                         int retry,
+                                         bool &precertify);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GraphicsPipe::init_type();
+    register_type(_type_handle, "TinyGraphicsPipe",
+                  GraphicsPipe::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "tinyGraphicsPipe.I"
+
+#endif

+ 28 - 0
panda/src/tinydisplay/tinyGraphicsStateGuardian.I

@@ -0,0 +1,28 @@
+// Filename: tinyGraphicsStateGuardian.I
+// Created by:  drose (24Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::get_light_id
+//       Access: Private, Static
+//  Description: Convert index to gl light id
+////////////////////////////////////////////////////////////////////
+INLINE GLenum TinyGraphicsStateGuardian::
+get_light_id(int index) {
+  return GL_LIGHT0 + index;
+}

+ 1309 - 0
panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx

@@ -0,0 +1,1309 @@
+// Filename: tinyGraphicsStateGuardian.cxx
+// Created by:  drose (24Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "tinyGraphicsStateGuardian.h"
+#include "tinyGeomMunger.h"
+#include "tinyTextureContext.h"
+#include "config_tinydisplay.h"
+#include "pStatTimer.h"
+
+TypeHandle TinyGraphicsStateGuardian::_type_handle;
+
+PStatCollector TinyGraphicsStateGuardian::_vertices_immediate_pcollector("Vertices:Immediate mode");
+
+////////////////////////////////////////////////////////////////////
+//     Function: uchar_bgr_to_rgb
+//  Description: Recopies the given array of pixels, converting from
+//               BGR to RGB arrangement.
+////////////////////////////////////////////////////////////////////
+static void
+uchar_bgr_to_rgb(unsigned char *dest, const unsigned char *source,
+                 int num_pixels) {
+  for (int i = 0; i < num_pixels; i++) {
+    dest[0] = source[2];
+    dest[1] = source[1];
+    dest[2] = source[0];
+    dest += 3;
+    source += 3;
+  }
+}
+
+/*
+////////////////////////////////////////////////////////////////////
+//     Function: uchar_bgra_to_rgba
+//  Description: Recopies the given array of pixels, converting from
+//               BGRA to RGBA arrangement.
+////////////////////////////////////////////////////////////////////
+static void
+uchar_bgra_to_rgba(unsigned char *dest, const unsigned char *source,
+                   int num_pixels) {
+  for (int i = 0; i < num_pixels; i++) {
+    dest[0] = source[2];
+    dest[1] = source[1];
+    dest[2] = source[0];
+    dest[3] = source[3];
+    dest += 4;
+    source += 4;
+  }
+}
+*/
+
+////////////////////////////////////////////////////////////////////
+//     Function: uchar_bgra_to_rgb
+//  Description: Recopies the given array of pixels, converting from
+//               BGRA to RGB arrangement, dropping alpha.
+////////////////////////////////////////////////////////////////////
+static void
+uchar_bgra_to_rgb(unsigned char *dest, const unsigned char *source,
+                  int num_pixels) {
+  for (int i = 0; i < num_pixels; i++) {
+    dest[0] = source[2] * source[3] / 255;
+    dest[1] = source[1] * source[3] / 255;
+    dest[2] = source[0] * source[3] / 255;
+    dest += 3;
+    source += 4;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: uchar_lum_to_rgb
+//  Description: Recopies the given array of pixels, converting from
+//               luminance to RGB arrangement.
+////////////////////////////////////////////////////////////////////
+static void
+uchar_lum_to_rgb(unsigned char *dest, const unsigned char *source,
+                 int num_pixels) {
+  for (int i = 0; i < num_pixels; i++) {
+    dest[0] = source[0];
+    dest[1] = source[0];
+    dest[2] = source[0];
+    dest += 3;
+    source += 1;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: uchar_la_to_rgb
+//  Description: Recopies the given array of pixels, converting from
+//               luminance-alpha to RGB arrangement.
+////////////////////////////////////////////////////////////////////
+static void
+uchar_la_to_rgb(unsigned char *dest, const unsigned char *source,
+                 int num_pixels) {
+  for (int i = 0; i < num_pixels; i++) {
+    dest[2] = dest[1] = dest[0] = source[0] * source[1] / 255;
+    dest += 3;
+    source += 2;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: restructure_image
+//  Description: Converts the pixels of the image to the appropriate
+//               format for TinyGL (i.e. GL_RGB).
+////////////////////////////////////////////////////////////////////
+static PTA_uchar
+restructure_image(Texture *tex) {
+  int num_pixels = tex->get_x_size() * tex->get_y_size();
+  CPTA_uchar orig_image = tex->get_ram_image();
+  PTA_uchar new_image = PTA_uchar::empty_array(num_pixels * 3);
+
+  switch (tex->get_format()) {
+  case Texture::F_rgb:
+  case Texture::F_rgb5:
+  case Texture::F_rgb8:
+  case Texture::F_rgb12:
+  case Texture::F_rgb332:
+    uchar_bgr_to_rgb(new_image, orig_image, num_pixels);
+    break;
+
+  case Texture::F_rgba:
+  case Texture::F_rgbm:
+  case Texture::F_rgba4:
+  case Texture::F_rgba5:
+  case Texture::F_rgba8:
+  case Texture::F_rgba12:
+  case Texture::F_rgba16:
+  case Texture::F_rgba32:
+    uchar_bgra_to_rgb(new_image, orig_image, num_pixels);
+    break;
+
+  case Texture::F_luminance:
+  case Texture::F_alpha:
+    uchar_lum_to_rgb(new_image, orig_image, num_pixels);
+    break;
+
+  case Texture::F_luminance_alphamask:
+  case Texture::F_luminance_alpha:
+    uchar_la_to_rgb(new_image, orig_image, num_pixels);
+    break;
+
+  default:
+    break;
+  }
+
+  return new_image;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+TinyGraphicsStateGuardian::
+TinyGraphicsStateGuardian(GraphicsPipe *pipe,
+			 TinyGraphicsStateGuardian *share_with) :
+  GraphicsStateGuardian(CS_yup_right, pipe)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+TinyGraphicsStateGuardian::
+~TinyGraphicsStateGuardian() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::reset
+//       Access: Public, Virtual
+//  Description: Resets all internal state as if the gsg were newly
+//               created.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+reset() {
+  free_pointers();
+  GraphicsStateGuardian::reset();
+
+  _supported_geom_rendering =
+    Geom::GR_point | 
+    Geom::GR_indexed_other |
+    Geom::GR_flat_last_vertex;
+
+  _max_texture_dimension = 256;
+
+  // Count the max number of lights
+  GLint max_lights;
+  glGetIntegerv(GL_MAX_LIGHTS, &max_lights);
+  _max_lights = max_lights;
+
+  _color_scale_via_lighting = false;
+  _alpha_scale_via_texture = false;
+
+  // Now that the GSG has been initialized, make it available for
+  // optimizations.
+  add_gsg(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::make_geom_munger
+//       Access: Public, Virtual
+//  Description: Creates a new GeomMunger object to munge vertices
+//               appropriate to this GSG for the indicated state.
+////////////////////////////////////////////////////////////////////
+PT(GeomMunger) TinyGraphicsStateGuardian::
+make_geom_munger(const RenderState *state, Thread *current_thread) {
+  PT(TinyGeomMunger) munger = new TinyGeomMunger(this, state);
+  return GeomMunger::register_munger(munger, current_thread);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::clear
+//       Access: Public
+//  Description: Clears the framebuffer within the current
+//               DisplayRegion, according to the flags indicated by
+//               the given DrawableRegion object.
+//
+//               This does not set the DisplayRegion first.  You
+//               should call prepare_display_region() to specify the
+//               region you wish the clear operation to apply to.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+clear(DrawableRegion *clearable) {
+  PStatTimer timer(_clear_pcollector);
+
+  if ((!clearable->get_clear_color_active())&&
+      (!clearable->get_clear_depth_active())&&
+      (!clearable->get_clear_stencil_active())) {
+    return;
+  }
+  
+  set_state_and_transform(RenderState::make_empty(), _internal_transform);
+
+  int mask = 0;
+  
+  if (clearable->get_clear_color_active()) {
+    Colorf v = clearable->get_clear_color();
+    glClearColor(v[0],v[1],v[2],v[3]);
+    mask |= GL_COLOR_BUFFER_BIT;
+  }
+  
+  if (clearable->get_clear_depth_active()) {
+    glClearDepth(clearable->get_clear_depth());
+    mask |= GL_DEPTH_BUFFER_BIT;
+  }
+  
+  glClear(mask);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::prepare_display_region
+//       Access: Public, Virtual
+//  Description: Prepare a display region for rendering (set up
+//               scissor region and viewport)
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+prepare_display_region(DisplayRegionPipelineReader *dr,
+                       Lens::StereoChannel stereo_channel) {
+  nassertv(dr != (DisplayRegionPipelineReader *)NULL);
+  GraphicsStateGuardian::prepare_display_region(dr, stereo_channel);
+
+  int l, b, w, h;
+  dr->get_region_pixels(l, b, w, h);
+  GLint x = GLint(l);
+  GLint y = GLint(b);
+  GLsizei width = GLsizei(w);
+  GLsizei height = GLsizei(h);
+  
+  glViewport(x, y, width, height);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::calc_projection_mat
+//       Access: Public, Virtual
+//  Description: Given a lens, calculates the appropriate projection
+//               matrix for use with this gsg.  Note that the
+//               projection matrix depends a lot upon the coordinate
+//               system of the rendering API.
+//
+//               The return value is a TransformState if the lens is
+//               acceptable, NULL if it is not.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) TinyGraphicsStateGuardian::
+calc_projection_mat(const Lens *lens) {
+  if (lens == (Lens *)NULL) {
+    return NULL;
+  }
+
+  if (!lens->is_linear()) {
+    return NULL;
+  }
+
+  // The projection matrix must always be right-handed Y-up, even if
+  // our coordinate system of choice is otherwise, because certain GL
+  // calls (specifically glTexGen(GL_SPHERE_MAP)) assume this kind of
+  // a coordinate system.  Sigh.  In order to implement a Z-up (or
+  // other arbitrary) coordinate system, we'll use a Y-up projection
+  // matrix, and store the conversion to our coordinate system of
+  // choice in the modelview matrix.
+
+  LMatrix4f result =
+    LMatrix4f::convert_mat(CS_yup_right, _current_lens->get_coordinate_system()) *
+    lens->get_projection_mat(_current_stereo_channel);
+
+  if (_scene_setup->get_inverted()) {
+    // If the scene is supposed to be inverted, then invert the
+    // projection matrix.
+    result *= LMatrix4f::scale_mat(1.0f, -1.0f, 1.0f);
+  }
+
+  return TransformState::make_mat(result);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::prepare_lens
+//       Access: Public, Virtual
+//  Description: Makes the current lens (whichever lens was most
+//               recently specified with set_scene()) active, so
+//               that it will transform future rendered geometry.
+//               Normally this is only called from the draw process,
+//               and usually it is called by set_scene().
+//
+//               The return value is true if the lens is acceptable,
+//               false if it is not.
+////////////////////////////////////////////////////////////////////
+bool TinyGraphicsStateGuardian::
+prepare_lens() {
+  if (tinydisplay_cat.is_spam()) {
+    tinydisplay_cat.spam()
+      << "glLoadMatrix(GL_PROJECTION): " << _projection_mat->get_mat() << endl;
+  }
+  glMatrixMode(GL_PROJECTION);
+  glLoadMatrixf(_projection_mat->get_mat().get_data());
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::begin_frame
+//       Access: Public, Virtual
+//  Description: Called before each frame is rendered, to allow the
+//               GSG a chance to do any internal cleanup before
+//               beginning the frame.
+//
+//               The return value is true if successful (in which case
+//               the frame will be drawn and end_frame() will be
+//               called later), or false if unsuccessful (in which
+//               case nothing will be drawn and end_frame() will not
+//               be called).
+////////////////////////////////////////////////////////////////////
+bool TinyGraphicsStateGuardian::
+begin_frame(Thread *current_thread) {
+  if (!GraphicsStateGuardian::begin_frame(current_thread)) {
+    return false;
+  }
+
+#ifdef DO_PSTATS
+  _vertices_immediate_pcollector.clear_level();
+#endif
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::begin_scene
+//       Access: Public, Virtual
+//  Description: Called between begin_frame() and end_frame() to mark
+//               the beginning of drawing commands for a "scene"
+//               (usually a particular DisplayRegion) within a frame.
+//               All 3-D drawing commands, except the clear operation,
+//               must be enclosed within begin_scene() .. end_scene().
+//
+//               The return value is true if successful (in which case
+//               the scene will be drawn and end_scene() will be
+//               called later), or false if unsuccessful (in which
+//               case nothing will be drawn and end_scene() will not
+//               be called).
+////////////////////////////////////////////////////////////////////
+bool TinyGraphicsStateGuardian::
+begin_scene() {
+  return GraphicsStateGuardian::begin_scene();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::end_scene
+//       Access: Protected, Virtual
+//  Description: Called between begin_frame() and end_frame() to mark
+//               the end of drawing commands for a "scene" (usually a
+//               particular DisplayRegion) within a frame.  All 3-D
+//               drawing commands, except the clear operation, must be
+//               enclosed within begin_scene() .. end_scene().
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+end_scene() {
+  GraphicsStateGuardian::end_scene();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::end_frame
+//       Access: Public, Virtual
+//  Description: Called after each frame is rendered, to allow the
+//               GSG a chance to do any internal cleanup after
+//               rendering the frame, and before the window flips.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+end_frame(Thread *current_thread) {
+  GraphicsStateGuardian::end_frame(current_thread);
+
+  // Flush any PCollectors specific to this kind of GSG.
+  _vertices_immediate_pcollector.flush_level();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::begin_draw_primitives
+//       Access: Public, Virtual
+//  Description: Called before a sequence of draw_primitive()
+//               functions are called, this should prepare the vertex
+//               data for rendering.  It returns true if the vertices
+//               are ok, false to abort this group of primitives.
+////////////////////////////////////////////////////////////////////
+bool TinyGraphicsStateGuardian::
+begin_draw_primitives(const GeomPipelineReader *geom_reader,
+                      const GeomMunger *munger,
+                      const GeomVertexDataPipelineReader *data_reader,
+                      bool force) {
+#ifndef NDEBUG
+  if (tinydisplay_cat.is_spam()) {
+    tinydisplay_cat.spam() << "begin_draw_primitives: " << *(data_reader->get_object()) << "\n";
+  }
+#endif  // NDEBUG
+
+  if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger, data_reader, force)) {
+    return false;
+  }
+  nassertr(_data_reader != (GeomVertexDataPipelineReader *)NULL, false);
+
+  // We must use immediate mode to render primitives.
+  _sender.clear();
+
+  _sender.add_column(_data_reader, InternalName::get_normal(),
+                     NULL, NULL, glNormal3f, NULL);
+  if (!_sender.add_column(_data_reader, InternalName::get_color(),
+                          NULL, NULL, glColor3f, glColor4f)) {
+    // If we didn't have a color column, the item color is white.
+    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+  }
+
+  // TinyGL only supports single-texturing, so only bother with the
+  // first texture stage.
+  int max_stage_index = _effective_texture->get_num_on_ff_stages();
+  if (max_stage_index > 0) {
+    TextureStage *stage = _effective_texture->get_on_ff_stage(0);
+    const InternalName *name = stage->get_texcoord_name();
+    _sender.add_column(_data_reader, name,
+                       NULL, glTexCoord2f, NULL, NULL);
+  }
+
+  // We must add vertex last, because glVertex3f() is the key
+  // function call that actually issues the vertex.
+  _sender.add_column(_data_reader, InternalName::get_vertex(),
+                     NULL, glVertex2f, glVertex3f, glVertex4f);
+
+  if (_transform_stale) {
+    glMatrixMode(GL_MODELVIEW);
+    glLoadMatrixf(_internal_transform->get_mat().get_data());
+  }
+
+  if (_data_reader->is_vertex_transformed()) {
+    // If the vertex data claims to be already transformed into clip
+    // coordinates, wipe out the current projection and modelview
+    // matrix (so we don't attempt to transform it again).
+    glMatrixMode(GL_PROJECTION);
+    glPushMatrix();
+    glLoadIdentity();
+    glMatrixMode(GL_MODELVIEW);
+    glPushMatrix();
+    glLoadIdentity();
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::draw_triangles
+//       Access: Public, Virtual
+//  Description: Draws a series of disconnected triangles.
+////////////////////////////////////////////////////////////////////
+bool TinyGraphicsStateGuardian::
+draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) {
+  PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
+
+#ifndef NDEBUG
+  if (tinydisplay_cat.is_spam()) {
+    tinydisplay_cat.spam() << "draw_triangles: " << *(reader->get_object()) << "\n";
+  }
+#endif  // NDEBUG
+
+  draw_immediate_simple_primitives(reader, GL_TRIANGLES);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::draw_lines
+//       Access: Public, Virtual
+//  Description: Draws a series of disconnected line segments.
+////////////////////////////////////////////////////////////////////
+bool TinyGraphicsStateGuardian::
+draw_lines(const GeomPrimitivePipelineReader *reader, bool force) {
+  PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
+#ifndef NDEBUG
+  if (tinydisplay_cat.is_spam()) {
+    tinydisplay_cat.spam() << "draw_lines: " << *(reader->get_object()) << "\n";
+  }
+#endif  // NDEBUG
+
+  draw_immediate_simple_primitives(reader, GL_LINES);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::draw_points
+//       Access: Public, Virtual
+//  Description: Draws a series of disconnected points.
+////////////////////////////////////////////////////////////////////
+bool TinyGraphicsStateGuardian::
+draw_points(const GeomPrimitivePipelineReader *reader, bool force) {
+  PStatTimer timer(_draw_primitive_pcollector, reader->get_current_thread());
+#ifndef NDEBUG
+  if (tinydisplay_cat.is_spam()) {
+    tinydisplay_cat.spam() << "draw_points: " << *(reader->get_object()) << "\n";
+  }
+#endif  // NDEBUG
+
+  draw_immediate_simple_primitives(reader, GL_POINTS);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::end_draw_primitives()
+//       Access: Public, Virtual
+//  Description: Called after a sequence of draw_primitive()
+//               functions are called, this should do whatever cleanup
+//               is appropriate.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+end_draw_primitives() {
+  if (_transform_stale) {
+    glMatrixMode(GL_MODELVIEW);
+    glLoadMatrixf(_internal_transform->get_mat().get_data());
+  }
+
+  if (_data_reader->is_vertex_transformed()) {
+    // Restore the matrices that we pushed above.
+    glMatrixMode(GL_PROJECTION);
+    glPopMatrix();
+    glMatrixMode(GL_MODELVIEW);
+    glPopMatrix();
+  }
+
+  GraphicsStateGuardian::end_draw_primitives();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::framebuffer_copy_to_texture
+//       Access: Public, Virtual
+//  Description: Copy the pixels within the indicated display
+//               region from the framebuffer into texture memory.
+//
+//               If z > -1, it is the cube map index into which to
+//               copy.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
+                            const RenderBuffer &rb) {
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::framebuffer_copy_to_ram
+//       Access: Public, Virtual
+//  Description: Copy the pixels within the indicated display region
+//               from the framebuffer into system memory, not texture
+//               memory.  Returns true on success, false on failure.
+//
+//               This completely redefines the ram image of the
+//               indicated texture.
+////////////////////////////////////////////////////////////////////
+bool TinyGraphicsStateGuardian::
+framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr,
+                        const RenderBuffer &rb) {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::set_state_and_transform
+//       Access: Public, Virtual
+//  Description: Simultaneously resets the render state and the
+//               transform state.
+//
+//               This transform specified is the "internal" net
+//               transform, already converted into the GSG's internal
+//               coordinate space by composing it to
+//               get_cs_transform().  (Previously, this used to be the
+//               "external" net transform, with the assumption that
+//               that GSG would convert it internally, but that is no
+//               longer the case.)
+//
+//               Special case: if (state==NULL), then the target
+//               state is already stored in _target.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+set_state_and_transform(const RenderState *target,
+                        const TransformState *transform) {
+#ifndef NDEBUG
+  if (tinydisplay_cat.is_spam()) {
+    tinydisplay_cat.spam()
+      << "Setting GSG state to " << (void *)target << ":\n";
+    target->write(tinydisplay_cat.spam(false), 2);
+  }
+#endif
+
+  _state_pcollector.add_level(1);
+  PStatTimer timer1(_draw_set_state_pcollector);
+
+  if (transform != _internal_transform) {
+    PStatTimer timer(_draw_set_state_transform_pcollector);
+    _state_pcollector.add_level(1);
+    _internal_transform = transform;
+    do_issue_transform();
+  }
+
+  if (target == _state_rs) {
+    return;
+  }
+  _target_rs = target;
+  _target.clear_to_defaults();
+  target->store_into_slots(&_target);
+  _state_rs = 0;
+
+  if (_target._color != _state._color ||
+      _target._color_scale != _state._color_scale) {
+    PStatTimer timer(_draw_set_state_color_pcollector);
+    do_issue_color();
+    do_issue_color_scale();
+    _state._color = _target._color;
+    _state._color_scale = _target._color_scale;
+  }
+
+  if (_target._cull_face != _state._cull_face) {
+    PStatTimer timer(_draw_set_state_cull_face_pcollector);
+    do_issue_cull_face();
+    _state._cull_face = _target._cull_face;
+  }
+
+  if (_target._render_mode != _state._render_mode) {
+    PStatTimer timer(_draw_set_state_render_mode_pcollector);
+    do_issue_render_mode();
+    _state._render_mode = _target._render_mode;
+  }
+
+  if (_target._shade_model != _state._shade_model) {
+    PStatTimer timer(_draw_set_state_shade_model_pcollector);
+    do_issue_shade_model();
+    _state._shade_model = _target._shade_model;
+  }
+
+  if ((_target._transparency != _state._transparency)||
+      (_target._color_write != _state._color_write)||
+      (_target._color_blend != _state._color_blend)) {
+    PStatTimer timer(_draw_set_state_blending_pcollector);
+    do_issue_blending();
+    _state._transparency = _target._transparency;
+    _state._color_write = _target._color_write;
+    _state._color_blend = _target._color_blend;
+  }
+
+  if (_target._texture != _state._texture) {
+    PStatTimer timer(_draw_set_state_texture_pcollector);
+    determine_effective_texture();
+    do_issue_texture();
+    _state._texture = _target._texture;
+  }
+  
+  if (_target._material != _state._material) {
+    PStatTimer timer(_draw_set_state_material_pcollector);
+    do_issue_material();
+    _state._material = _target._material;
+  }
+
+  if (_target._light != _state._light) {
+    PStatTimer timer(_draw_set_state_light_pcollector);
+    do_issue_light();
+    _state._light = _target._light;
+  }
+
+  _state_rs = _target_rs;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::prepare_texture
+//       Access: Public, Virtual
+//  Description: Creates whatever structures the GSG requires to
+//               represent the texture internally, and returns a
+//               newly-allocated TextureContext object with this data.
+//               It is the responsibility of the calling function to
+//               later call release_texture() with this same pointer
+//               (which will also delete the pointer).
+//
+//               This function should not be called directly to
+//               prepare a texture.  Instead, call Texture::prepare().
+////////////////////////////////////////////////////////////////////
+TextureContext *TinyGraphicsStateGuardian::
+prepare_texture(Texture *tex) {
+  if (tex->get_texture_type() != Texture::TT_2d_texture) {
+    tinydisplay_cat.info()
+      << "not loading texture " << tex->get_name() << ": "
+      << tex->get_texture_type() << "\n";
+    return NULL;
+  }
+  if (tex->get_ram_image_compression() != Texture::CM_off) {
+    tinydisplay_cat.info()
+      << "not loading texture " << tex->get_name() << ": "
+      << tex->get_ram_image_compression() << "\n";
+    return NULL;
+  }
+  if (tex->get_component_type() != Texture::T_unsigned_byte) {
+    tinydisplay_cat.info()
+      << "not loading texture " << tex->get_name() << ": "
+      << tex->get_component_type() << "\n";
+    return NULL;
+  }
+
+  TinyTextureContext *gtc = new TinyTextureContext(_prepared_objects, tex);
+  glGenTextures(1, &gtc->_index);
+
+  apply_texture(gtc);
+  return gtc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::release_texture
+//       Access: Public, Virtual
+//  Description: Frees the GL resources previously allocated for the
+//               texture.  This function should never be called
+//               directly; instead, call Texture::release() (or simply
+//               let the Texture destruct).
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+release_texture(TextureContext *tc) {
+  TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
+
+  glDeleteTextures(1, &gtc->_index);
+
+  gtc->_index = 0;
+  delete gtc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::enable_lighting
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by a derived class to
+//               enable or disable the use of lighting overall.  This
+//               is called by do_issue_light() according to whether any
+//               lights are in use or not.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+enable_lighting(bool enable) {
+  static PStatCollector _draw_set_state_light_enable_lighting_pcollector("Draw:Set State:Light:Enable lighting");
+  PStatTimer timer(_draw_set_state_light_enable_lighting_pcollector);
+  
+  if (enable) {
+    glEnable(GL_LIGHTING);
+  } else {
+    glDisable(GL_LIGHTING);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::set_ambient_light
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by a derived class to
+//               indicate the color of the ambient light that should
+//               be in effect.  This is called by do_issue_light() after
+//               all other lights have been enabled or disabled.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+set_ambient_light(const Colorf &color) {
+  static PStatCollector _draw_set_state_light_ambient_pcollector("Draw:Set State:Light:Ambient");
+  PStatTimer timer(_draw_set_state_light_ambient_pcollector);
+  
+  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (float *)color.get_data());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::enable_light
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by a derived class to
+//               enable the indicated light id.  A specific Light will
+//               already have been bound to this id via bind_light().
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+enable_light(int light_id, bool enable) {
+  static PStatCollector _draw_set_state_light_enable_light_pcollector("Draw:Set State:Light:Enable light");
+  PStatTimer timer(_draw_set_state_light_enable_light_pcollector);
+  
+  if (enable) {
+    glEnable(get_light_id(light_id));
+  } else {
+    glDisable(get_light_id(light_id));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::begin_bind_lights
+//       Access: Protected, Virtual
+//  Description: Called immediately before bind_light() is called,
+//               this is intended to provide the derived class a hook
+//               in which to set up some state (like transform) that
+//               might apply to several lights.
+//
+//               The sequence is: begin_bind_lights() will be called,
+//               then one or more bind_light() calls, then
+//               end_bind_lights().
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+begin_bind_lights() {
+  static PStatCollector _draw_set_state_light_begin_bind_pcollector("Draw:Set State:Light:Begin bind");
+  PStatTimer timer(_draw_set_state_light_begin_bind_pcollector);
+  
+  // We need to temporarily load a new matrix so we can define the
+  // light in a known coordinate system.  We pick the transform of the
+  // root.  (Alternatively, we could leave the current transform where
+  // it is and compute the light position relative to that transform
+  // instead of relative to the root, by composing with the matrix
+  // computed by _internal_transform->invert_compose(render_transform).
+  // But I think loading a completely new matrix is simpler.)
+  CPT(TransformState) render_transform =
+    _cs_transform->compose(_scene_setup->get_world_transform());
+
+  glMatrixMode(GL_MODELVIEW);
+  glPushMatrix();
+  glLoadMatrixf(render_transform->get_mat().get_data());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::end_bind_lights
+//       Access: Protected, Virtual
+//  Description: Called after before bind_light() has been called one
+//               or more times (but before any geometry is issued or
+//               additional state is changed), this is intended to
+//               clean up any temporary changes to the state that may
+//               have been made by begin_bind_lights().
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+end_bind_lights() {
+  static PStatCollector _draw_set_state_light_end_bind_pcollector("Draw:Set State:Light:End bind");
+  PStatTimer timer(_draw_set_state_light_end_bind_pcollector);
+  
+  glMatrixMode(GL_MODELVIEW);
+  glPopMatrix();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::do_issue_transform
+//       Access: Protected
+//  Description: Sends the indicated transform matrix to the graphics
+//               API to be applied to future vertices.
+//
+//               This transform is the internal_transform, already
+//               converted into the GSG's internal coordinate system.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+do_issue_transform() {
+  const TransformState *transform = _internal_transform;
+  if (tinydisplay_cat.is_spam()) {
+    tinydisplay_cat.spam()
+      << "glLoadMatrix(GL_MODELVIEW): " << transform->get_mat() << endl;
+  }
+
+  _transform_state_pcollector.add_level(1);
+  glMatrixMode(GL_MODELVIEW);
+  glLoadMatrixf(transform->get_mat().get_data());
+  _transform_stale = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::do_issue_shade_model
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+do_issue_shade_model() {
+  const ShadeModelAttrib *attrib = _target._shade_model;
+  switch (attrib->get_mode()) {
+  case ShadeModelAttrib::M_smooth:
+    glShadeModel(GL_SMOOTH);
+    break;
+
+  case ShadeModelAttrib::M_flat:
+    glShadeModel(GL_FLAT);
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::do_issue_render_mode
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+do_issue_render_mode() {
+  const RenderModeAttrib *attrib = _target._render_mode;
+
+  switch (attrib->get_mode()) {
+  case RenderModeAttrib::M_unchanged:
+  case RenderModeAttrib::M_filled:
+    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+    break;
+
+  case RenderModeAttrib::M_wireframe:
+    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+    break;
+
+  case RenderModeAttrib::M_point:
+    glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
+    break;
+
+  default:
+    tinydisplay_cat.error()
+      << "Unknown render mode " << (int)attrib->get_mode() << endl;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::do_issue_cull_face
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+do_issue_cull_face() {
+  const CullFaceAttrib *attrib = _target._cull_face;
+  CullFaceAttrib::Mode mode = attrib->get_effective_mode();
+
+  switch (mode) {
+  case CullFaceAttrib::M_cull_none:
+    glDisable(GL_CULL_FACE);
+    break;
+  case CullFaceAttrib::M_cull_clockwise:
+    glEnable(GL_CULL_FACE);
+    glCullFace(GL_BACK);
+    break;
+  case CullFaceAttrib::M_cull_counter_clockwise:
+    glEnable(GL_CULL_FACE);
+    glCullFace(GL_FRONT);
+    break;
+  default:
+    tinydisplay_cat.error()
+      << "invalid cull face mode " << (int)mode << endl;
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::do_issue_material
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+do_issue_material() {
+  static Material empty;
+  const Material *material;
+  if (_target._material == (MaterialAttrib *)NULL ||
+      _target._material->is_off()) {
+    material = &empty;
+  } else {
+    material = _target._material->get_material();
+  }
+
+  GLenum face = material->get_twoside() ? GL_FRONT_AND_BACK : GL_FRONT;
+
+  glMaterialfv(face, GL_SPECULAR, (GLfloat *)material->get_specular().get_data());
+  glMaterialfv(face, GL_EMISSION, (GLfloat *)material->get_emission().get_data());
+  glMaterialf(face, GL_SHININESS, material->get_shininess());
+
+  if (material->has_ambient() && material->has_diffuse()) {
+    // The material has both an ambient and diffuse specified.  This
+    // means we do not need glMaterialColor().
+    glDisable(GL_COLOR_MATERIAL);
+    glMaterialfv(face, GL_AMBIENT, (GLfloat *)material->get_ambient().get_data());
+    glMaterialfv(face, GL_DIFFUSE, (GLfloat *)material->get_diffuse().get_data());
+
+  } else if (material->has_ambient()) {
+    // The material specifies an ambient, but not a diffuse component.
+    // The diffuse component comes from the object's color.
+    glMaterialfv(face, GL_AMBIENT, (GLfloat *)material->get_ambient().get_data());
+    if (_has_material_force_color) {
+      glDisable(GL_COLOR_MATERIAL);
+      glMaterialfv(face, GL_DIFFUSE, (GLfloat *)_material_force_color.get_data());
+    } else {
+      glColorMaterial(face, GL_DIFFUSE);
+      glEnable(GL_COLOR_MATERIAL);
+    }
+
+  } else if (material->has_diffuse()) {
+    // The material specifies a diffuse, but not an ambient component.
+    // The ambient component comes from the object's color.
+    glMaterialfv(face, GL_DIFFUSE, (GLfloat *)material->get_diffuse().get_data());
+    if (_has_material_force_color) {
+      glDisable(GL_COLOR_MATERIAL);
+      glMaterialfv(face, GL_AMBIENT, (GLfloat *)_material_force_color.get_data());
+    } else {
+      glColorMaterial(face, GL_AMBIENT);
+      glEnable(GL_COLOR_MATERIAL);
+    }
+
+  } else {
+    // The material specifies neither a diffuse nor an ambient
+    // component.  Both components come from the object's color.
+    if (_has_material_force_color) {
+      glDisable(GL_COLOR_MATERIAL);
+      glMaterialfv(face, GL_AMBIENT, (GLfloat *)_material_force_color.get_data());
+      glMaterialfv(face, GL_DIFFUSE, (GLfloat *)_material_force_color.get_data());
+    } else {
+      glColorMaterial(face, GL_AMBIENT_AND_DIFFUSE);
+      glEnable(GL_COLOR_MATERIAL);
+    }
+  }
+
+  glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, material->get_local());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::do_issue_texture
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+do_issue_texture() {
+  glDisable(GL_TEXTURE_2D);
+
+  int num_stages = _effective_texture->get_num_on_ff_stages();
+  if (num_stages == 0) {
+    // No texturing.
+    return;
+  }
+  nassertv(num_stages == 1);
+
+  TextureStage *stage = _effective_texture->get_on_ff_stage(0);
+  Texture *texture = _effective_texture->get_on_texture(stage);
+  nassertv(texture != (Texture *)NULL);
+    
+  TextureContext *tc = texture->prepare_now(_prepared_objects, this);
+  if (tc == (TextureContext *)NULL) {
+    // Something wrong with this texture; skip it.
+    return;
+  }
+    
+  // Then, turn on the current texture mode.
+  glEnable(GL_TEXTURE_2D);
+  apply_texture(tc);
+
+  /*
+  GLint glmode = get_texture_apply_mode_type(stage->get_mode());
+  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, glmode);
+  */
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::do_issue_blending
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+do_issue_blending() {
+  /*
+  // Handle the color_write attrib.  If color_write is off, then
+  // all the other blending-related stuff doesn't matter.  If the
+  // device doesn't support color-write, we use blending tricks
+  // to effectively disable color write.
+  unsigned int color_channels =
+    _target._color_write->get_channels() & _color_write_mask;
+  if (color_channels == ColorWriteAttrib::C_off) {
+    if (_target._color_write != _state._color_write) {
+      enable_multisample_alpha_one(false);
+      enable_multisample_alpha_mask(false);
+      if (CLP(color_mask)) {
+        enable_blend(false);
+        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+      } else {
+        enable_blend(true);
+        _glBlendEquation(GL_FUNC_ADD);
+        glBlendFunc(GL_ZERO, GL_ONE);
+      }
+    }
+    return;
+  } else {
+    if (_target._color_write != _state._color_write) {
+      if (CLP(color_mask)) {
+        glColorMask((color_channels & ColorWriteAttrib::C_red) != 0,
+                       (color_channels & ColorWriteAttrib::C_green) != 0,
+                       (color_channels & ColorWriteAttrib::C_blue) != 0,
+                       (color_channels & ColorWriteAttrib::C_alpha) != 0);
+      }
+    }
+  }
+
+  CPT(ColorBlendAttrib) color_blend = _target._color_blend;
+  ColorBlendAttrib::Mode color_blend_mode = _target._color_blend->get_mode();
+  TransparencyAttrib::Mode transparency_mode = _target._transparency->get_mode();
+
+  _color_blend_involves_color_scale = color_blend->involves_color_scale();
+
+  // Is there a color blend set?
+  if (color_blend_mode != ColorBlendAttrib::M_none) {
+    enable_multisample_alpha_one(false);
+    enable_multisample_alpha_mask(false);
+    enable_blend(true);
+    _glBlendEquation(get_blend_equation_type(color_blend_mode));
+    glBlendFunc(get_blend_func(color_blend->get_operand_a()),
+                   get_blend_func(color_blend->get_operand_b()));
+
+    if (_color_blend_involves_color_scale) {
+      // Apply the current color scale to the blend mode.
+      _glBlendColor(_current_color_scale[0], _current_color_scale[1],
+                    _current_color_scale[2], _current_color_scale[3]);
+
+    } else {
+      Colorf c = color_blend->get_color();
+      _glBlendColor(c[0], c[1], c[2], c[3]);
+    }
+    return;
+  }
+
+  // No color blend; is there a transparency set?
+  switch (transparency_mode) {
+  case TransparencyAttrib::M_none:
+  case TransparencyAttrib::M_binary:
+    break;
+
+  case TransparencyAttrib::M_alpha:
+  case TransparencyAttrib::M_dual:
+    enable_multisample_alpha_one(false);
+    enable_multisample_alpha_mask(false);
+    enable_blend(true);
+    _glBlendEquation(GL_FUNC_ADD);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    return;
+
+  case TransparencyAttrib::M_multisample:
+    // We need to enable *both* of these in M_multisample case.
+    enable_multisample_alpha_one(true);
+    enable_multisample_alpha_mask(true);
+    enable_blend(false);
+    return;
+
+  case TransparencyAttrib::M_multisample_mask:
+    enable_multisample_alpha_one(false);
+    enable_multisample_alpha_mask(true);
+    enable_blend(false);
+    return;
+
+  default:
+    tinydisplay_cat.error()
+      << "invalid transparency mode " << (int)transparency_mode << endl;
+    break;
+  }
+
+  if (_line_smooth_enabled || _point_smooth_enabled) {
+    // If we have either of these turned on, we also need to have
+    // blend mode enabled in order to see it.
+    enable_multisample_alpha_one(false);
+    enable_multisample_alpha_mask(false);
+    enable_blend(true);
+    _glBlendEquation(GL_FUNC_ADD);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    return;
+  }
+
+  // For best polygon smoothing, we need:
+  // (1) a frame buffer that supports alpha
+  // (2) sort polygons front-to-back
+  // (3) glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE);
+  //
+  // Since these modes have other implications for the application, we
+  // don't attempt to do this by default.  If you really want good
+  // polygon smoothing (and you don't have multisample support), do
+  // all this yourself.
+
+  // Nothing's set, so disable blending.
+  enable_multisample_alpha_one(false);
+  enable_multisample_alpha_mask(false);
+  enable_blend(false);
+  */
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::apply_texture
+//       Access: Protected
+//  Description: Updates TinyGL with the current information for this
+//               texture, and makes it the current texture available
+//               for rendering.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+apply_texture(TextureContext *tc) {
+  TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
+
+  gtc->set_active(true);
+  glBindTexture(GL_TEXTURE_2D, gtc->_index);
+
+  if (gtc->was_image_modified()) {
+    // If the texture image was modified, reload the texture.
+    if (!upload_texture(gtc)) {
+      glDisable(GL_TEXTURE_2D);
+    }
+    gtc->mark_loaded();
+
+  } else if (gtc->was_properties_modified()) {
+    // If only the properties have been modified, we don't need to
+    // reload the texture.
+    gtc->mark_loaded();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::upload_texture
+//       Access: Protected
+//  Description: Uploads the texture image to TinyGL.
+//
+//               The return value is true if successful, or false if
+//               the texture has no image.
+////////////////////////////////////////////////////////////////////
+bool TinyGraphicsStateGuardian::
+upload_texture(TinyTextureContext *gtc) {
+  Texture *tex = gtc->get_texture();
+  PStatTimer timer(_load_texture_pcollector);
+
+  if (tinydisplay_cat.is_debug()) {
+    tinydisplay_cat.debug()
+      << "loading texture " << tex->get_name() << "\n";
+  }
+  CPTA_uchar image = tex->get_ram_image();
+  nassertr(!image.is_null(), false)
+
+  int width = tex->get_x_size();
+  int height = tex->get_y_size();
+
+  PTA_uchar new_image = restructure_image(tex);
+  const unsigned char *image_ptr = new_image.p();
+
+#ifdef DO_PSTATS
+  _data_transferred_pcollector.add_level(new_image.size());
+#endif
+  glTexImage2D(GL_TEXTURE_2D, 0, 3,
+               width, height, 0,
+               GL_RGB, GL_UNSIGNED_BYTE, (void *)image_ptr);
+  
+#ifdef DO_PSTATS 
+  gtc->update_data_size_bytes(256 * 256 * 3);
+#endif
+  
+  tex->texture_uploaded();
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::draw_immediate_simple_primitives
+//       Access: Private
+//  Description: Uses the ImmediateModeSender to draw a series of
+//               primitives of the indicated type.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsStateGuardian::
+draw_immediate_simple_primitives(const GeomPrimitivePipelineReader *reader, GLenum mode) {
+  int num_vertices = reader->get_num_vertices();
+  _vertices_immediate_pcollector.add_level(num_vertices);
+  glBegin(mode);
+
+  if (reader->is_indexed()) {
+    for (int v = 0; v < num_vertices; ++v) {
+      _sender.set_vertex(reader->get_vertex(v));
+      _sender.issue_vertex();
+    }
+
+  } else {
+    _sender.set_vertex(reader->get_first_vertex());
+    for (int v = 0; v < num_vertices; ++v) {
+      _sender.issue_vertex();
+    }
+  }
+
+  glEnd();
+}

+ 132 - 0
panda/src/tinydisplay/tinyGraphicsStateGuardian.h

@@ -0,0 +1,132 @@
+// Filename: tinyGraphicsStateGuardian.h
+// Created by:  drose (24Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef TINYGRAPHICSSTATEGUARDIAN_H
+#define TINYGRAPHICSSTATEGUARDIAN_H
+
+#include "pandabase.h"
+
+#include "graphicsStateGuardian.h"
+#include "tinyGraphicsPipe.h"
+#include "tinyImmediateModeSender.h"
+
+// These are actually the TinyGL headers, not the system OpenGL headers.
+#include "GL/gl.h"
+
+class TinyTextureContext;
+
+////////////////////////////////////////////////////////////////////
+//       Class : TinyGraphicsStateGuardian
+// Description : An interface to TinySDGL (an implementation of TinyGL
+//               over SDL).
+////////////////////////////////////////////////////////////////////
+class TinyGraphicsStateGuardian : public GraphicsStateGuardian {
+public:
+  TinyGraphicsStateGuardian(GraphicsPipe *pipe,
+                            TinyGraphicsStateGuardian *share_with);
+
+  virtual ~TinyGraphicsStateGuardian();
+
+  virtual void reset();
+
+  virtual PT(GeomMunger) make_geom_munger(const RenderState *state,
+                                          Thread *current_thread);
+
+  virtual void clear(DrawableRegion *clearable);
+
+  virtual void prepare_display_region(DisplayRegionPipelineReader *dr,
+                                      Lens::StereoChannel stereo_channel);
+  virtual CPT(TransformState) calc_projection_mat(const Lens *lens);
+  virtual bool prepare_lens();
+
+  virtual bool begin_frame(Thread *current_thread);
+  virtual bool begin_scene();
+  virtual void end_scene();
+  virtual void end_frame(Thread *current_thread);
+
+  virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader,
+                                     const GeomMunger *munger,
+                                     const GeomVertexDataPipelineReader *data_reader,
+                                     bool force);
+  virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader,
+                              bool force);
+  virtual bool draw_lines(const GeomPrimitivePipelineReader *reader,
+                          bool force);
+  virtual bool draw_points(const GeomPrimitivePipelineReader *reader,
+                           bool force);
+  virtual void end_draw_primitives();
+
+  virtual void framebuffer_copy_to_texture
+  (Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb);
+  virtual bool framebuffer_copy_to_ram
+  (Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb);
+
+  virtual void set_state_and_transform(const RenderState *state,
+                                       const TransformState *transform);
+
+  virtual TextureContext *prepare_texture(Texture *tex);
+  virtual void release_texture(TextureContext *tc);
+
+  virtual void enable_lighting(bool enable);
+  virtual void set_ambient_light(const Colorf &color);
+  virtual void enable_light(int light_id, bool enable);
+  virtual void begin_bind_lights();
+  virtual void end_bind_lights();
+
+private:
+  void do_issue_transform();
+  void do_issue_render_mode();
+  void do_issue_cull_face();
+  void do_issue_shade_model();
+  void do_issue_material();
+  void do_issue_texture();
+  void do_issue_blending();
+
+  void apply_texture(TextureContext *tc);
+  bool upload_texture(TinyTextureContext *gtc);
+
+  void draw_immediate_simple_primitives(const GeomPrimitivePipelineReader *reader, GLenum mode);
+
+  INLINE static GLenum get_light_id(int index);
+
+private:
+  TinyImmediateModeSender _sender;
+
+  static PStatCollector _vertices_immediate_pcollector;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GraphicsStateGuardian::init_type();
+    register_type(_type_handle, "TinyGraphicsStateGuardian",
+                  GraphicsStateGuardian::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "tinyGraphicsStateGuardian.I"
+
+#endif

+ 18 - 0
panda/src/tinydisplay/tinyGraphicsWindow.I

@@ -0,0 +1,18 @@
+// Filename: tinyGraphicsWindow.I
+// Created by:  drose (24Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 472 - 0
panda/src/tinydisplay/tinyGraphicsWindow.cxx

@@ -0,0 +1,472 @@
+// Filename: tinyGraphicsWindow.cxx
+// Created by:  drose (24Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "tinyGraphicsWindow.h"
+#include "tinyGraphicsStateGuardian.h"
+#include "config_tinydisplay.h"
+#include "tinyGraphicsPipe.h"
+#include "mouseButton.h"
+#include "keyboardButton.h"
+#include "graphicsPipe.h"
+
+// These are actually the TinyGL headers, not the system OpenGL headers.
+#include "GL/gl.h"
+
+TypeHandle TinyGraphicsWindow::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsWindow::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+TinyGraphicsWindow::
+TinyGraphicsWindow(GraphicsPipe *pipe, 
+                   const string &name,
+                   const FrameBufferProperties &fb_prop,
+                   const WindowProperties &win_prop,
+                   int flags,
+                   GraphicsStateGuardian *gsg,
+                   GraphicsOutput *host) :
+  GraphicsWindow(pipe, name, fb_prop, win_prop, flags, gsg, host)
+{
+  _screen = NULL;
+  _frame_buffer = NULL;
+  _pitch = 0;
+
+  GraphicsWindowInputDevice device =
+    GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard/mouse");
+  add_input_device(device);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsWindow::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+TinyGraphicsWindow::
+~TinyGraphicsWindow() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsWindow::begin_frame
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               before beginning rendering for a given frame.  It
+//               should do whatever setup is required, and return true
+//               if the frame should be rendered, or false if it
+//               should be skipped.
+////////////////////////////////////////////////////////////////////
+bool TinyGraphicsWindow::
+begin_frame(FrameMode mode, Thread *current_thread) {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsWindow::end_frame
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               after rendering is completed for a given frame.  It
+//               should do whatever finalization is required.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsWindow::
+end_frame(FrameMode mode, Thread *current_thread) {
+  if (mode == FM_render) {
+    trigger_flip();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsWindow::begin_flip
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               after end_frame() has been called on all windows, to
+//               initiate the exchange of the front and back buffers.
+//
+//               This should instruct the window to prepare for the
+//               flip at the next video sync, but it should not wait.
+//
+//               We have the two separate functions, begin_flip() and
+//               end_flip(), to make it easier to flip all of the
+//               windows at the same time.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsWindow::
+begin_flip() {
+  if (SDL_MUSTLOCK(_screen)) {
+    if (SDL_LockSurface(_screen) < 0) {
+      tinydisplay_cat.error()
+        << "Can't lock screen: " << SDL_GetError() << "\n";
+    }
+  }
+  ZB_copyFrameBuffer(_frame_buffer, _screen->pixels, _pitch);
+
+  if (SDL_MUSTLOCK(_screen)) {
+    SDL_UnlockSurface(_screen); 
+  }
+
+  SDL_Flip(_screen);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsWindow::process_events
+//       Access: Public, Virtual
+//  Description: Do whatever processing is necessary to ensure that
+//               the window responds to user events.  Also, honor any
+//               requests recently made via request_properties()
+//
+//               This function is called only within the window
+//               thread.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsWindow::
+process_events() {
+  GraphicsWindow::process_events();
+
+  if (_screen == NULL) {
+    return;
+  }
+
+  WindowProperties properties;
+
+  SDL_Event evt;
+  ButtonHandle button;
+  while (SDL_PollEvent(&evt)) {
+    switch(evt.type) {
+    case SDL_KEYDOWN:
+      if (evt.key.keysym.unicode) {
+        _input_devices[0].keystroke(evt.key.keysym.unicode);
+      }
+      button = get_keyboard_button(evt.key.keysym.sym);
+      if (button != ButtonHandle::none()) {
+        _input_devices[0].button_down(button);
+      }
+      break;
+
+    case SDL_KEYUP:
+      button = get_keyboard_button(evt.key.keysym.sym);
+      if (button != ButtonHandle::none()) {
+        _input_devices[0].button_up(button);
+      }
+      break;
+
+    case SDL_MOUSEBUTTONDOWN:
+      button = get_mouse_button(evt.button.button);
+      _input_devices[0].set_pointer_in_window(evt.button.x, evt.button.y);
+      _input_devices[0].button_down(button);
+      break;
+
+    case SDL_MOUSEBUTTONUP:
+      button = get_mouse_button(evt.button.button);
+      _input_devices[0].set_pointer_in_window(evt.button.x, evt.button.y);
+      _input_devices[0].button_up(button);
+      break;
+
+    case SDL_MOUSEMOTION:
+      _input_devices[0].set_pointer_in_window(evt.motion.x, evt.motion.y);
+      break;
+      
+    case SDL_QUIT:
+      // The window was closed by the user.
+      close_window();
+      properties.set_open(false);
+      system_changed_properties(properties);
+      break;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsWindow::set_properties_now
+//       Access: Public, Virtual
+//  Description: Applies the requested set of properties to the
+//               window, if possible, for instance to request a change
+//               in size or minimization status.
+//
+//               The window properties are applied immediately, rather
+//               than waiting until the next frame.  This implies that
+//               this method may *only* be called from within the
+//               window thread.
+//
+//               The return value is true if the properties are set,
+//               false if they are ignored.  This is mainly useful for
+//               derived classes to implement extensions to this
+//               function.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsWindow::
+set_properties_now(WindowProperties &properties) {
+  GraphicsWindow::set_properties_now(properties);
+  if (!properties.is_any_specified()) {
+    // The base class has already handled this case.
+    return;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsWindow::close_window
+//       Access: Protected, Virtual
+//  Description: Closes the window right now.  Called from the window
+//               thread.
+////////////////////////////////////////////////////////////////////
+void TinyGraphicsWindow::
+close_window() {
+  GraphicsWindow::close_window();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsWindow::open_window
+//       Access: Protected, Virtual
+//  Description: Opens the window right now.  Called from the window
+//               thread.  Returns true if the window is successfully
+//               opened, or false if there was a problem.
+////////////////////////////////////////////////////////////////////
+bool TinyGraphicsWindow::
+open_window() {
+
+  // GSG Creation/Initialization
+  TinyGraphicsStateGuardian *tinygsg;
+  if (_gsg == 0) {
+    // There is no old gsg.  Create a new one.
+    tinygsg = new TinyGraphicsStateGuardian(_pipe, NULL);
+    _gsg = tinygsg;
+
+  } else {
+    DCAST_INTO_R(tinygsg, _gsg, false);
+  }
+  
+  _screen = SDL_SetVideoMode(_properties.get_x_size(), _properties.get_y_size(), 32, SDL_SWSURFACE);
+
+  if (_screen == NULL) {
+    tinydisplay_cat.error()
+      << "Video mode set failed.\n";
+    return false;
+  }
+
+  // initialize TinyGL:
+  int mode;
+  switch (_screen->format->BitsPerPixel) {
+  case  8:
+    tinydisplay_cat.error()
+      << "SDL Palettes are currently not supported.\n";
+    return false;
+
+  case 16:
+    _pitch = _screen->pitch;
+    mode = ZB_MODE_5R6G5B;
+    break;
+  case 24:
+    _pitch = (_screen->pitch * 2) / 3;
+    mode = ZB_MODE_RGB24;
+    break;
+  case 32:
+    _pitch = _screen->pitch / 2;
+    mode = ZB_MODE_RGBA;
+    break;
+
+  default:
+    return false;
+    break;
+  }
+
+  _frame_buffer = ZB_open(_properties.get_x_size(), _properties.get_y_size(), mode, 0, 0, 0, 0);
+  glInit(_frame_buffer);
+
+  // Now that we have made the context current to a window, we can
+  // reset the GSG state if this is the first time it has been used.
+  // (We can't just call reset() when we construct the GSG, because
+  // reset() requires having a current context.)
+  tinygsg->reset_if_new();
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsWindow::get_keyboard_button
+//       Access: Private, Static
+//  Description: Maps from an SDL keysym to the corresponding Panda
+//               ButtonHandle.
+////////////////////////////////////////////////////////////////////
+ButtonHandle TinyGraphicsWindow::
+get_keyboard_button(SDLKey sym) {
+  switch (sym) {
+  case SDLK_BACKSPACE: return KeyboardButton::backspace();
+  case SDLK_TAB: return KeyboardButton::tab();
+    //  case SDLK_CLEAR: return KeyboardButton::clear();
+  case SDLK_RETURN: return KeyboardButton::enter();
+    //  case SDLK_PAUSE: return KeyboardButton::pause();
+  case SDLK_ESCAPE: return KeyboardButton::escape();
+  case SDLK_SPACE: return KeyboardButton::space();
+  case SDLK_EXCLAIM: return KeyboardButton::ascii_key('!');
+  case SDLK_QUOTEDBL: return KeyboardButton::ascii_key('"');
+  case SDLK_HASH: return KeyboardButton::ascii_key('#');
+  case SDLK_DOLLAR: return KeyboardButton::ascii_key('$');
+  case SDLK_AMPERSAND: return KeyboardButton::ascii_key('&');
+  case SDLK_QUOTE: return KeyboardButton::ascii_key('\'');
+  case SDLK_LEFTPAREN: return KeyboardButton::ascii_key('(');
+  case SDLK_RIGHTPAREN: return KeyboardButton::ascii_key(')');
+  case SDLK_ASTERISK: return KeyboardButton::ascii_key('*');
+  case SDLK_PLUS: return KeyboardButton::ascii_key('+');
+  case SDLK_COMMA: return KeyboardButton::ascii_key(',');
+  case SDLK_MINUS: return KeyboardButton::ascii_key('-');
+  case SDLK_PERIOD: return KeyboardButton::ascii_key('.');
+  case SDLK_SLASH: return KeyboardButton::ascii_key('/');
+  case SDLK_0: return KeyboardButton::ascii_key('0');
+  case SDLK_1: return KeyboardButton::ascii_key('1');
+  case SDLK_2: return KeyboardButton::ascii_key('2');
+  case SDLK_3: return KeyboardButton::ascii_key('3');
+  case SDLK_4: return KeyboardButton::ascii_key('4');
+  case SDLK_5: return KeyboardButton::ascii_key('5');
+  case SDLK_6: return KeyboardButton::ascii_key('6');
+  case SDLK_7: return KeyboardButton::ascii_key('7');
+  case SDLK_8: return KeyboardButton::ascii_key('8');
+  case SDLK_9: return KeyboardButton::ascii_key('9');
+  case SDLK_COLON: return KeyboardButton::ascii_key(':');
+  case SDLK_SEMICOLON: return KeyboardButton::ascii_key(';');
+  case SDLK_LESS: return KeyboardButton::ascii_key('<');
+  case SDLK_EQUALS: return KeyboardButton::ascii_key('=');
+  case SDLK_GREATER: return KeyboardButton::ascii_key('>');
+  case SDLK_QUESTION: return KeyboardButton::ascii_key('?');
+  case SDLK_AT: return KeyboardButton::ascii_key('@');
+  case SDLK_LEFTBRACKET: return KeyboardButton::ascii_key('[');
+  case SDLK_BACKSLASH: return KeyboardButton::ascii_key('\\');
+  case SDLK_RIGHTBRACKET: return KeyboardButton::ascii_key(']');
+  case SDLK_CARET: return KeyboardButton::ascii_key('^');
+  case SDLK_UNDERSCORE: return KeyboardButton::ascii_key('_');
+  case SDLK_BACKQUOTE: return KeyboardButton::ascii_key('`');
+  case SDLK_a: return KeyboardButton::ascii_key('a');
+  case SDLK_b: return KeyboardButton::ascii_key('b');
+  case SDLK_c: return KeyboardButton::ascii_key('c');
+  case SDLK_d: return KeyboardButton::ascii_key('d');
+  case SDLK_e: return KeyboardButton::ascii_key('e');
+  case SDLK_f: return KeyboardButton::ascii_key('f');
+  case SDLK_g: return KeyboardButton::ascii_key('g');
+  case SDLK_h: return KeyboardButton::ascii_key('h');
+  case SDLK_i: return KeyboardButton::ascii_key('i');
+  case SDLK_j: return KeyboardButton::ascii_key('j');
+  case SDLK_k: return KeyboardButton::ascii_key('k');
+  case SDLK_l: return KeyboardButton::ascii_key('l');
+  case SDLK_m: return KeyboardButton::ascii_key('m');
+  case SDLK_n: return KeyboardButton::ascii_key('n');
+  case SDLK_o: return KeyboardButton::ascii_key('o');
+  case SDLK_p: return KeyboardButton::ascii_key('p');
+  case SDLK_q: return KeyboardButton::ascii_key('q');
+  case SDLK_r: return KeyboardButton::ascii_key('r');
+  case SDLK_s: return KeyboardButton::ascii_key('s');
+  case SDLK_t: return KeyboardButton::ascii_key('t');
+  case SDLK_u: return KeyboardButton::ascii_key('u');
+  case SDLK_v: return KeyboardButton::ascii_key('v');
+  case SDLK_w: return KeyboardButton::ascii_key('w');
+  case SDLK_x: return KeyboardButton::ascii_key('x');
+  case SDLK_y: return KeyboardButton::ascii_key('y');
+  case SDLK_z: return KeyboardButton::ascii_key('z');
+  case SDLK_DELETE: return KeyboardButton::del();
+  case SDLK_KP0: return KeyboardButton::ascii_key('0');
+  case SDLK_KP1: return KeyboardButton::ascii_key('1');
+  case SDLK_KP2: return KeyboardButton::ascii_key('2');
+  case SDLK_KP3: return KeyboardButton::ascii_key('3');
+  case SDLK_KP4: return KeyboardButton::ascii_key('4');
+  case SDLK_KP5: return KeyboardButton::ascii_key('5');
+  case SDLK_KP6: return KeyboardButton::ascii_key('6');
+  case SDLK_KP7: return KeyboardButton::ascii_key('7');
+  case SDLK_KP8: return KeyboardButton::ascii_key('8');
+  case SDLK_KP9: return KeyboardButton::ascii_key('9');
+  case SDLK_KP_PERIOD: return KeyboardButton::ascii_key('.');
+  case SDLK_KP_DIVIDE: return KeyboardButton::ascii_key('/');
+  case SDLK_KP_MULTIPLY: return KeyboardButton::ascii_key('*');
+  case SDLK_KP_MINUS: return KeyboardButton::ascii_key('-');
+  case SDLK_KP_PLUS: return KeyboardButton::ascii_key('+');
+  case SDLK_KP_ENTER: return KeyboardButton::enter();
+  case SDLK_KP_EQUALS: return KeyboardButton::ascii_key('=');
+  case SDLK_UP: return KeyboardButton::up();
+  case SDLK_DOWN: return KeyboardButton::down();
+  case SDLK_RIGHT: return KeyboardButton::right();
+  case SDLK_LEFT: return KeyboardButton::left();
+  case SDLK_INSERT: return KeyboardButton::insert();
+  case SDLK_HOME: return KeyboardButton::home();
+  case SDLK_END: return KeyboardButton::end();
+  case SDLK_PAGEUP: return KeyboardButton::page_up();
+  case SDLK_PAGEDOWN: return KeyboardButton::page_down();
+  case SDLK_F1: return KeyboardButton::f1();
+  case SDLK_F2: return KeyboardButton::f2();
+  case SDLK_F3: return KeyboardButton::f3();
+  case SDLK_F4: return KeyboardButton::f4();
+  case SDLK_F5: return KeyboardButton::f5();
+  case SDLK_F6: return KeyboardButton::f6();
+  case SDLK_F7: return KeyboardButton::f7();
+  case SDLK_F8: return KeyboardButton::f8();
+  case SDLK_F9: return KeyboardButton::f9();
+  case SDLK_F10: return KeyboardButton::f10();
+  case SDLK_F11: return KeyboardButton::f11();
+  case SDLK_F12: return KeyboardButton::f12();
+  case SDLK_F13: return KeyboardButton::f13();
+  case SDLK_F14: return KeyboardButton::f14();
+  case SDLK_F15: return KeyboardButton::f15();
+    //  case SDLK_NUMLOCK: return KeyboardButton::numlock();
+    //  case SDLK_CAPSLOCK: return KeyboardButton::capslock();
+    //  case SDLK_SCROLLOCK: return KeyboardButton::scrollock();
+  case SDLK_RSHIFT: return KeyboardButton::rshift();
+  case SDLK_LSHIFT: return KeyboardButton::lshift();
+  case SDLK_RCTRL: return KeyboardButton::rcontrol();
+  case SDLK_LCTRL: return KeyboardButton::lcontrol();
+  case SDLK_RALT: return KeyboardButton::ralt();
+  case SDLK_LALT: return KeyboardButton::lalt();
+  case SDLK_RMETA: return KeyboardButton::ralt();
+  case SDLK_LMETA: return KeyboardButton::lalt();
+    //  case SDLK_LSUPER: return KeyboardButton::left();
+    //  case SDLK_RSUPER: return KeyboardButton::right();
+    //  case SDLK_MODE: return KeyboardButton::mode();
+  case SDLK_HELP: return KeyboardButton::help();
+    //  case SDLK_PRINT: return KeyboardButton::print-screen();
+    //  case SDLK_SYSREQ: return KeyboardButton::SysRq();
+    //  case SDLK_BREAK: return KeyboardButton::break();
+    //  case SDLK_MENU: return KeyboardButton::menu();
+    //  case SDLK_POWER: return KeyboardButton::power();
+    //  case SDLK_EURO: return KeyboardButton::euro();
+  }
+  tinydisplay_cat.info()
+    << "unhandled keyboard button " << sym << "\n";
+
+  return ButtonHandle::none();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsWindow::get_mouse_button
+//       Access: Private, Static
+//  Description: Maps from an SDL mouse button index to the
+//               corresponding Panda ButtonHandle.
+////////////////////////////////////////////////////////////////////
+ButtonHandle TinyGraphicsWindow::
+get_mouse_button(Uint8 button) {
+  switch (button) {
+  case SDL_BUTTON_LEFT:
+    return MouseButton::one();
+
+  case SDL_BUTTON_MIDDLE:
+    return MouseButton::two();
+
+  case SDL_BUTTON_RIGHT:
+    return MouseButton::three();
+
+  case SDL_BUTTON_WHEELUP:
+    return MouseButton::wheel_up();
+
+  case SDL_BUTTON_WHEELDOWN:
+    return MouseButton::wheel_down();
+  }
+  tinydisplay_cat.info()
+    << "unhandled mouse button " << button << "\n";
+
+  return ButtonHandle::none();
+}

+ 89 - 0
panda/src/tinydisplay/tinyGraphicsWindow.h

@@ -0,0 +1,89 @@
+// Filename: tinyGraphicsWindow.h
+// Created by:  drose (24Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef TINYGRAPHICSWINDOW_H
+#define TINYGRAPHICSWINDOW_H
+
+#include "pandabase.h"
+
+#include "tinyGraphicsPipe.h"
+#include "graphicsWindow.h"
+#include "buttonHandle.h"
+
+extern "C" { 
+  #include "SDL.h"
+  #include "zbuffer.h"
+}
+
+////////////////////////////////////////////////////////////////////
+//       Class : TinyGraphicsWindow
+// Description : An interface to TinySDGL (an implementation of TinyGL
+//               over SDL).
+////////////////////////////////////////////////////////////////////
+class TinyGraphicsWindow : public GraphicsWindow {
+public:
+  TinyGraphicsWindow(GraphicsPipe *pipe, 
+                     const string &name,
+                     const FrameBufferProperties &fb_prop,
+                     const WindowProperties &win_prop,
+                     int flags,
+                     GraphicsStateGuardian *gsg,
+                     GraphicsOutput *host);
+  virtual ~TinyGraphicsWindow();
+
+  virtual bool begin_frame(FrameMode mode, Thread *current_thread);
+  virtual void end_frame(FrameMode mode, Thread *current_thread);
+  virtual void begin_flip();
+
+  virtual void process_events();
+  virtual void set_properties_now(WindowProperties &properties);
+
+protected:
+  virtual void close_window();
+  virtual bool open_window();
+
+private:
+  static ButtonHandle get_keyboard_button(SDLKey sym);
+  static ButtonHandle get_mouse_button(Uint8 button);
+
+private:
+  SDL_Surface *_screen;
+  ZBuffer *_frame_buffer;
+  unsigned int _pitch;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GraphicsWindow::init_type();
+    register_type(_type_handle, "TinyGraphicsWindow",
+                  GraphicsWindow::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "tinyGraphicsWindow.I"
+
+#endif

+ 101 - 0
panda/src/tinydisplay/tinyImmediateModeSender.I

@@ -0,0 +1,101 @@
+// Filename: glImmediateModeSender_src.I
+// Created by:  drose (15Aug05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE TinyImmediateModeSender::
+TinyImmediateModeSender() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::ComponentSender::Constructor
+//       Access: Public
+//  Description: The ComponentSender becomes the owner of the
+//               GeomVertexReader pointer, and will delete it when it
+//               is done.
+////////////////////////////////////////////////////////////////////
+INLINE TinyImmediateModeSender::ComponentSender::
+ComponentSender(GeomVertexReader *reader) :
+  _reader(reader)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::ComponentSender::set_vertex
+//       Access: Public
+//  Description: Specifies the vertex index of the next vertex to
+//               send.  If this is not called, the next consecutive
+//               vertex will be sent.
+////////////////////////////////////////////////////////////////////
+INLINE void TinyImmediateModeSender::ComponentSender::
+set_vertex(int vertex_index) {
+  _reader->set_row(vertex_index);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::ComponentSender1f::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE TinyImmediateModeSender::ComponentSender1f::
+ComponentSender1f(GeomVertexReader *reader, Func1f *func) :
+  ComponentSender(reader),
+  _func(func)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::ComponentSender2f::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE TinyImmediateModeSender::ComponentSender2f::
+ComponentSender2f(GeomVertexReader *reader, Func2f *func) :
+  ComponentSender(reader),
+  _func(func)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::ComponentSender3f::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE TinyImmediateModeSender::ComponentSender3f::
+ComponentSender3f(GeomVertexReader *reader, Func3f *func) :
+  ComponentSender(reader),
+  _func(func)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::ComponentSender4f::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE TinyImmediateModeSender::ComponentSender4f::
+ComponentSender4f(GeomVertexReader *reader, Func4f *func) :
+  ComponentSender(reader),
+  _func(func)
+{
+}

+ 232 - 0
panda/src/tinydisplay/tinyImmediateModeSender.cxx

@@ -0,0 +1,232 @@
+// Filename: glImmediateModeSender_src.cxx
+// Created by:  drose (15Aug05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "tinyImmediateModeSender.h"
+#include "config_tinydisplay.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TinyImmediateModeSender::
+~TinyImmediateModeSender() {
+  clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::clear
+//       Access: Public
+//  Description: Removes (and deletes) all of the senders from the
+//               object.
+////////////////////////////////////////////////////////////////////
+void TinyImmediateModeSender::
+clear() {
+  ComponentSenders::iterator si;
+  for (si = _senders.begin(); si != _senders.end(); ++si) {
+    delete (*si);
+  }
+  _senders.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::set_vertex
+//       Access: Public
+//  Description: Specifies the vertex index of the next vertex to
+//               send.  If this is not called, the next consecutive
+//               vertex will be sent.
+////////////////////////////////////////////////////////////////////
+void TinyImmediateModeSender::
+set_vertex(int vertex_index) {
+  ComponentSenders::iterator si;
+  for (si = _senders.begin(); si != _senders.end(); ++si) {
+    (*si)->set_vertex(vertex_index);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::issue_vertex
+//       Access: Public
+//  Description: Sends the next vertex to the OpenGL API.
+////////////////////////////////////////////////////////////////////
+void TinyImmediateModeSender::
+issue_vertex() {
+  ComponentSenders::iterator si;
+  for (si = _senders.begin(); si != _senders.end(); ++si) {
+    (*si)->issue_vertex();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::add_column
+//       Access: Public
+//  Description: Creates a new ComponentSender for the named data
+//               column, if it exists in the vertex data, and adds it
+//               to the list of senders for this object.
+//
+//               The four function pointers are the four variants on
+//               the function pointer for the possible number of
+//               components of the data column.  The appropriate
+//               pointer will be used, depending on the number of
+//               components the data column actually uses.
+//
+//               The return value is true if the column is added,
+//               false if it is not for some reason (for instance, the
+//               named column doesn't exist in the vertex data).
+////////////////////////////////////////////////////////////////////
+bool TinyImmediateModeSender::
+add_column(const GeomVertexDataPipelineReader *data_reader, const InternalName *name,
+           Func1f *func1f, Func2f *func2f, Func3f *func3f, Func4f *func4f) {
+  if (data_reader->has_column(name)) {
+    GeomVertexReader *reader = new GeomVertexReader(data_reader, name);
+    ComponentSender *sender = NULL;
+    const GeomVertexColumn *column = reader->get_column();
+    switch (column->get_num_components()) {
+    case 1:
+      if (func1f != (Func1f *)NULL) {
+        sender = new ComponentSender1f(reader, func1f);
+      }
+      break;
+
+    case 2:
+      if (func2f != (Func2f *)NULL) {
+        sender = new ComponentSender2f(reader, func2f);
+      }
+      break;
+
+    case 3:
+      if (func3f != (Func3f *)NULL) {
+        sender = new ComponentSender3f(reader, func3f);
+      }
+      break;
+
+    case 4:
+      if (func4f != (Func4f *)NULL) {
+        sender = new ComponentSender4f(reader, func4f);
+      }
+      break;
+    }
+
+    if (sender != (ComponentSender *)NULL) {
+      // Ok, we've got a valid sender; add it to the list.
+      _senders.push_back(sender);
+      return true;
+
+    } else {
+      // We didn't get a valid sender; clean up and return.
+      delete reader;
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::add_sender
+//       Access: Public
+//  Description: Adds a new ComponentSender to the list of senders for
+//               this object.  The GLImmediateModeSender object
+//               becomes the owner of the ComponentSender pointer and
+//               will delete it when it is done.
+////////////////////////////////////////////////////////////////////
+void TinyImmediateModeSender::
+add_sender(ComponentSender *sender) {
+  _senders.push_back(sender);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::ComponentSender::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+TinyImmediateModeSender::ComponentSender::
+~ComponentSender() {
+  delete _reader;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::ComponentSender1f::issue_vertex
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TinyImmediateModeSender::ComponentSender1f::
+issue_vertex() {
+  float d = _reader->get_data1f();
+#ifndef NDEBUG
+  if (tinydisplay_cat.is_spam()) {
+    tinydisplay_cat.spam()
+      << *_reader->get_column()->get_name() << ": " << d << "\n";
+  }
+#endif  // NDEBUG
+
+  (*_func)(d);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::ComponentSender2f::issue_vertex
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TinyImmediateModeSender::ComponentSender2f::
+issue_vertex() {
+  const LVecBase2f &d = _reader->get_data2f();
+#ifndef NDEBUG
+  if (tinydisplay_cat.is_spam()) {
+    tinydisplay_cat.spam()
+      << *_reader->get_column()->get_name() << ": " << d << "\n";
+  }
+#endif  // NDEBUG
+
+  (*_func)(d[0], d[1]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::ComponentSender3f::issue_vertex
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TinyImmediateModeSender::ComponentSender3f::
+issue_vertex() {
+  const LVecBase3f &d = _reader->get_data3f();
+#ifndef NDEBUG
+  if (tinydisplay_cat.is_spam()) {
+    tinydisplay_cat.spam()
+      << *_reader->get_column()->get_name() << ": " << d << "\n";
+  }
+#endif  // NDEBUG
+
+  (*_func)(d[0], d[1], d[2]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyImmediateModeSender::ComponentSender4f::issue_vertex
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void TinyImmediateModeSender::ComponentSender4f::
+issue_vertex() {
+  const LVecBase4f &d = _reader->get_data4f();
+#ifndef NDEBUG
+  if (tinydisplay_cat.is_spam()) {
+    tinydisplay_cat.spam()
+      << *_reader->get_column()->get_name() << ": " << d << "\n";
+  }
+#endif  // NDEBUG
+
+  (*_func)(d[0], d[1], d[2], d[3]);
+}

+ 114 - 0
panda/src/tinydisplay/tinyImmediateModeSender.h

@@ -0,0 +1,114 @@
+// Filename: tinyImmediateModeSender.h
+// Created by:  drose (29Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef TINYIMMEDIATEMODESENDER_H
+#define TINYIMMEDIATEMODESENDER_H
+
+#include "pandabase.h"
+#include "geomVertexReader.h"
+
+// These are actually the TinyGL headers, not the system OpenGL headers.
+#include "GL/gl.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : TinyImmediateModeSender
+// Description : This class collects together a handful of objects
+//               that will issue immediate-mode commands like
+//               glVertex, glTexCoord, etc., for the purposes of
+//               sending an object's vertices using the immediate mode
+//               functions.
+//
+//               Since unlike OpenGL, TinyGL prefers the
+//               immediate-mode interface, this is used all of the
+//               time.
+////////////////////////////////////////////////////////////////////
+class TinyImmediateModeSender {
+public:
+  INLINE TinyImmediateModeSender();
+  ~TinyImmediateModeSender();
+
+  void clear();
+  
+  void set_vertex(int vertex_index);
+  void issue_vertex();
+
+  class ComponentSender;
+  typedef void Func1f(GLfloat a);
+  typedef void Func2f(GLfloat a, GLfloat b);
+  typedef void Func3f(GLfloat a, GLfloat b, GLfloat c);
+  typedef void Func4f(GLfloat a, GLfloat b, GLfloat c, GLfloat d);
+
+  bool add_column(const GeomVertexDataPipelineReader *data_reader, 
+                  const InternalName *name, Func1f *func1f, 
+                  Func2f *func2f, Func3f *func3f, Func4f *func4f);
+
+  void add_sender(ComponentSender *sender);
+
+public:
+
+  class ComponentSender {
+  public:
+    INLINE ComponentSender(GeomVertexReader *reader);
+    virtual ~ComponentSender();
+    INLINE void set_vertex(int vertex_index);
+    virtual void issue_vertex()=0;
+  protected:
+    GeomVertexReader *_reader;
+  };
+
+  class ComponentSender1f : public ComponentSender {
+  public:
+    INLINE ComponentSender1f(GeomVertexReader *reader, Func1f *func);
+    virtual void issue_vertex();
+  private:
+    Func1f *_func;
+  };
+
+  class ComponentSender2f : public ComponentSender {
+  public:
+    INLINE ComponentSender2f(GeomVertexReader *reader, Func2f *func);
+    virtual void issue_vertex();
+  private:
+    Func2f *_func;
+  };
+
+  class ComponentSender3f : public ComponentSender {
+  public:
+    INLINE ComponentSender3f(GeomVertexReader *reader, Func3f *func);
+    virtual void issue_vertex();
+  private:
+    Func3f *_func;
+  };
+
+  class ComponentSender4f : public ComponentSender {
+  public:
+    INLINE ComponentSender4f(GeomVertexReader *reader, Func4f *func);
+    virtual void issue_vertex();
+  private:
+    Func4f *_func;
+  };
+
+private:
+  typedef pvector<ComponentSender *> ComponentSenders;
+  ComponentSenders _senders;
+};
+
+#include "tinyImmediateModeSender.I"
+
+#endif  // SUPPORT_IMMEDIATE_MODE
+

+ 30 - 0
panda/src/tinydisplay/tinyTextureContext.I

@@ -0,0 +1,30 @@
+// Filename: tinyTextureContext.I
+// Created by:  drose (30Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TinyTextureContext::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE TinyTextureContext::
+TinyTextureContext(PreparedGraphicsObjects *pgo, Texture *tex) :
+  TextureContext(pgo, tex)
+{
+  _index = 0;
+}

+ 21 - 0
panda/src/tinydisplay/tinyTextureContext.cxx

@@ -0,0 +1,21 @@
+// Filename: tinyTextureContext.cxx
+// Created by:  drose (30Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "tinyTextureContext.h"
+
+TypeHandle TinyTextureContext::_type_handle;

+ 61 - 0
panda/src/tinydisplay/tinyTextureContext.h

@@ -0,0 +1,61 @@
+// Filename: tinyTextureContext.h
+// Created by:  drose (30Apr08)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef TINYTEXTURECONTEXT_H
+#define TINYTEXTURECONTEXT_H
+
+#include "pandabase.h"
+#include "textureContext.h"
+#include "deletedChain.h"
+
+// These are actually the TinyGL headers, not the system OpenGL headers.
+#include "GL/gl.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : TinyTextureContext
+// Description :
+////////////////////////////////////////////////////////////////////
+class TinyTextureContext : public TextureContext {
+public:
+  INLINE TinyTextureContext(PreparedGraphicsObjects *pgo, Texture *tex);
+  ALLOC_DELETED_CHAIN(TinyTextureContext);
+
+  // This is the GL "name" of the texture object.
+  GLuint _index;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TextureContext::init_type();
+    register_type(_type_handle, "TinyTextureContext",
+                  TextureContext::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "tinyTextureContext.I"
+
+#endif